base-7.0.3.1/LICENSE0000664000577000060420000001114613557101274012423 0ustar anjaesctlCopyright (c) 1991-2011 UChicago Argonne LLC. Copyright (c) 1991-2006 The Regents of the University of California. Copyright (c) 2006-2011. Los Alamos National Security, LLC. Some of this material was produced under U.S. Government contract DE-AC52-06NA25396 for Los Alamos National Laboratory (LANL), which is operated by Los Alamos National Security, LLC for the U.S. Department of Energy. EPICS BASE is distributed subject to the following license conditions: SOFTWARE LICENSE AGREEMENT Software: EPICS BASE 1. The "Software", below, refers to EPICS BASE (in either source code, or binary form and accompanying documentation). Each licensee is addressed as "you" or "Licensee." 2. The copyright holders shown above and their third-party licensors hereby grant Licensee a royalty-free nonexclusive license, subject to the limitations stated herein and U.S. Government license rights. 3. You may modify and make a copy or copies of the Software for use within your organization, if you meet the following conditions: a. Copies in source code must include the copyright notice and this Software License Agreement. b. Copies in binary form must include the copyright notice and this Software License Agreement in the documentation and/or other materials provided with the copy. 4. You may modify a copy or copies of the Software or any portion of it, thus forming a work based on the Software, and distribute copies of such work outside your organization, if you meet all of the following conditions: a. Copies in source code must include the copyright notice and this Software License Agreement; b. Copies in binary form must include the copyright notice and this Software License Agreement in the documentation and/or other materials provided with the copy; c. Modified copies and works based on the Software must carry prominent notices stating that you changed specified portions of the Software. 5. Portions of the Software resulted from work developed under a U.S. Government contract and are subject to the following license: the Government is granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable worldwide license in this computer software to reproduce, prepare derivative works, and perform publicly and display publicly. 6. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL BE CORRECTED. 7. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGES. ________________________________________________________________________ This software is in part copyrighted by the BERLINER SPEICHERRING GESELLSCHAFT FUER SYNCHROTRONSTRAHLUNG M.B.H. (BESSY), BERLIN, GERMANY. In no event shall BESSY be liable to any party for direct, indirect, special, incidental, or consequential damages arising out of the use of this software, its documentation, or any derivatives thereof, even if BESSY has been advised of the possibility of such damage. BESSY specifically disclaims any warranties, including, but not limited to, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement. This software is provided on an "as is" basis, and BESSY has no obligation to provide maintenance, support, updates, enhancements, or modifications. ________________________________________________________________________ base-7.0.3.1/Makefile0000664000577000060420000000141613557101274013055 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* TOP = . include $(TOP)/configure/CONFIG # Bootstrap resolution: tools not installed yet TOOLS = $(TOP)/src/tools DIRS += configure src src_DEPEND_DIRS = configure DIRS += test test_DEPEND_DIRS = src DIRS += modules modules_DEPEND_DIRS = src include $(TOP)/configure/RULES_TOP base-7.0.3.1/README0000664000577000060420000000200613557101274012271 0ustar anjaesctl--------------------------------------------------------- EPICS Base - the central core of a control system toolkit --------------------------------------------------------- Copyright UChicago Argonne LLC, as Operator of Argonne National Laboratory. Copyright (c) 1991-2003 The Regents of the University of California, as Operator of Los Alamos National Laboratory. EPICS Base is distributed subject to a Software License Agreement found in the file LICENSE that is included with this distribution. --------------------------------------------------------- Installation and release information can be found in the various files in the documentation subdirectory. Additional information about EPICS including mailing list archives and subscription instructions, documentation and training materials, additional components, links to other websites etc. is available on the EPICS home page at https://epics.anl.gov/ Fri, 1 Nov 2019 14:08:44 -0500 34834dfe973a577f342503441e7b33b7fe322de5 https://code.launchpad.net/epics-base base-7.0.3.1/configure/CONFIG0000664000577000060420000000663613557101274014277 0ustar anjaesctl#************************************************************************* # Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* # # Common build definitions # ifneq ($(wildcard $(TOP)/configure/CONFIG_BASE_VERSION),) EPICS_BASE = $(INSTALL_LOCATION) CONFIG = $(TOP)/configure BASE_TOP=YES else CONFIG ?= $(EPICS_BASE)/configure endif # Provide a default if the user hasn't set EPICS_HOST_ARCH # ifeq ($(origin EPICS_HOST_ARCH), undefined) # Bootstrapping ... EHA := $(firstword $(wildcard $(EPICS_BASE)/lib/perl/EpicsHostArch.pl \ $(TOP)/src/tools/EpicsHostArch.pl)) # NB: We use a simply expanded variable here for performance: export EPICS_HOST_ARCH := $(shell perl $(EHA)) EHA := endif -include $(CONFIG)/RELEASE -include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH) -include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH).Common ifdef T_A -include $(CONFIG)/RELEASE.Common.$(T_A) -include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH).$(T_A) endif include $(CONFIG)/CONFIG_COMMON include $(CONFIG)/CONFIG_FILE_TYPE # Base-specific build options # include $(CONFIG)/CONFIG_BASE # Site-specific build options # include $(CONFIG)/CONFIG_SITE # Version numbering # include $(CONFIG)/CONFIG_BASE_VERSION # Host architecture specific definitions # include $(CONFIG)/os/CONFIG.$(EPICS_HOST_ARCH).Common -include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).Common # Parse configure/RELEASE # except when building Base itself, where this file is empty, # and would error in src/tools/ anyway. ifndef BASE_TOP RELEASE_TOPS := $(shell $(CONVERTRELEASE) -T $(TOP) releaseTops) endif ifdef T_A # Cross compile specific definitions # ifneq ($(EPICS_HOST_ARCH),$(T_A)) include $(CONFIG)/CONFIG.CrossCommon endif # Target architecture specific definitions # -include $(CONFIG)/os/CONFIG.Common.$(T_A) # Host-Target architecture specific definitions # -include $(CONFIG)/os/CONFIG.$(EPICS_HOST_ARCH).$(T_A) # Site specific target and host-target definitions and overrides # -include $(CONFIG)/os/CONFIG_SITE.Common.$(T_A) -include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) # RELEASE file specific definitions # ifneq ($(CONFIG),$(TOP)/configure) -include $(CONFIG)/CONFIG_APP_INCLUDE endif endif # ifdef T_A # Include /cfg/CONFIG* definitions from tops defined in RELEASE* files # ifneq ($(CONFIG),$(TOP)/configure) RELEASE_TOPS_REVERSE := $(shell \ $(PERL) -e '$$,=" ";print reverse @ARGV' $(RELEASE_TOPS)) RELEASE_CFG_CONFIGS = $(foreach top, $(RELEASE_TOPS_REVERSE), \ $(wildcard $($(top))/cfg/CONFIG*)) ifneq ($(RELEASE_CFG_CONFIGS),) include $(RELEASE_CFG_CONFIGS) endif endif # Include $(INSTALL_CFG)/CONFIG* definitions # TOP_CFG_CONFIGS = $(wildcard $(INSTALL_CFG)/CONFIG*) ifneq ($(TOP_CFG_CONFIGS),) include $(TOP_CFG_CONFIGS) endif # User specific definitions # -include $(HOME)/configure/CONFIG_USER -include $(HOME)/configure/CONFIG_USER.$(EPICS_HOST_ARCH) ifdef T_A -include $(HOME)/configure/CONFIG_USER.Common.$(T_A) -include $(HOME)/configure/CONFIG_USER.$(EPICS_HOST_ARCH).$(T_A) endif base-7.0.3.1/configure/CONFIG.CrossCommon0000664000577000060420000000227313557101274016531 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # Cross compiler default definitions # Build class: either HOST or CROSS # Used to determine OPT and WARN compiler flags BUILD_CLASS = CROSS # Cross build: either defined or not # Used in os/CONFIG.Common. files # ifdef CROSS looks better than ifeq ($(BUILD_CLASS),CROSS) CROSS = YES GNU_TARGET_INCLUDE_DIR = $(wildcard $(GNU_TARGET:%=$(GNU_DIR)/%/include)) GNU_TARGET_LIB_DIR = $(wildcard $(GNU_TARGET:%=$(GNU_DIR)/%/lib)) CROSS_INCLUDES = $(GNU_TARGET_INCLUDE_DIR:%=-I%) CROSS_LDFLAGS = $(GNU_TARGET_LIB_DIR:%=-L%) CMPLR_PREFIX_CROSS = $(addsuffix -,$(GNU_TARGET)) CMPLR_PREFIX = $(CMPLR_PREFIX_$(BUILD_CLASS)) # Cross builds usually use the gnu compiler include $(CONFIG)/CONFIG.gnuCommon base-7.0.3.1/configure/CONFIG.gnuCommon0000664000577000060420000000400113557101274016220 0ustar anjaesctl#************************************************************************* # 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. # 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. #************************************************************************* # GNU compiler defaults GNU = YES CMPLR_CLASS = gcc GNU_BIN = $(GNU_DIR)/bin GNU_LIB = $(GNU_DIR)/lib CC = $(GNU_BIN)/$(CMPLR_PREFIX)gcc$(CMPLR_SUFFIX) CCC = $(GNU_BIN)/$(CMPLR_PREFIX)g++$(CMPLR_SUFFIX) AR = $(GNU_BIN)/$(CMPLR_PREFIX)ar$(CMPLR_SUFFIX) -rc LD = $(GNU_BIN)/$(CMPLR_PREFIX)ld$(CMPLR_SUFFIX) -r CPP = $(CC) -x c -E RANLIB = $(GNU_BIN)/$(CMPLR_PREFIX)ranlib$(CMPLR_SUFFIX) # Requires at least GCC 4.8 or LLVM (clang) 3.1 ASAN_FLAGS_YES = -fsanitize=address ASAN_LDFLAGS_YES = $(ASAN_FLAGS_YES) PROF_CFLAGS_YES = -p GPROF_CFLAGS_YES = -pg CODE_CFLAGS = $(PROF_CFLAGS_$(PROFILE)) $(GPROF_CFLAGS_$(GPROF)) CODE_CFLAGS += $(ASAN_FLAGS_$(ENABLE_ASAN)) WARN_CFLAGS_YES = -Wall WARN_CFLAGS_NO = -w OPT_CFLAGS_YES = -O3 OPT_CFLAGS_NO = -g PROF_CXXFLAGS_YES = -p GPROF_CXXFLAGS_YES = -pg CODE_CXXFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) CODE_CXXFLAGS += $(ASAN_FLAGS_$(ENABLE_ASAN)) WARN_CXXFLAGS_YES = -Wall WARN_CXXFLAGS_NO = -w OPT_CXXFLAGS_YES = -O3 OPT_CXXFLAGS_NO = -g CODE_LDFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) CODE_LDFLAGS += $(ASAN_LDFLAGS_$(ENABLE_ASAN)) PIPE_CFLAGS_YES_YES = -pipe PIPE_CFLAGS = $(PIPE_CFLAGS_$(GCC_PIPE)_$(GNU)) PIPE_CXXFLAGS = $(PIPE_CFLAGS) STATIC_LDFLAGS_YES = -static STATIC_LDFLAGS_NO = SHRLIB_CFLAGS = -fPIC SHRLIB_LDFLAGS = -shared -fPIC LOADABLE_SHRLIB_LDFLAGS = -shared -fPIC GNU_LDLIBS_YES = -lgcc # Use compiler flags to generate header dependancies files HDEPENDS_METHOD = COMP HDEPENDS_COMPFLAGS = -MM -MF $@ base-7.0.3.1/configure/CONFIG_ADDONS0000664000577000060420000003553313557101274015325 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # # check for add-on CFLAGS and CXXFLAGS # # Rules: # 1) USR_CFLAGS is used # 2) if there is a special USR_CFLAGS_$(OS_CLASS), it's # appended to 1) # 3) if there is no special defined, but a generic USR_CFLAGS_DEFAULT, # this one is appended # 4) if you have the special case that your USR_CFLAGS_$(OS_CLASS) is # empty but you don't want 3), you have to define it as '-nil-', e.g.: # USR_CFLAGS = # USR_CFLAGS_WIN = -nil- # USR_CFLAGS_DEFAULT = # # These rules apply to these Makefile-variables: # USR_CFLAGS C flags # USR_CXXFLAGS C++ flags # USR_CPPFLAGS c preprocesser flags # SRCS source files for building libraries and prods # USR_SRCS source files for building libraries and prods # PROD_SRCS source files for building prods # LIB_SRCS source files for building libraries # LIBSRCS source files for building libraries (deprecated) # PROD_OBJS object files for building prods # LIB_OBJS object files for building libraries # USR_OBJS object files for building libraries and prods # USR_LIBS libs needed by PROD and TESTPROD and LIBRARY # PROD_LIBS libs needed by PROD and TESTPROD # LIB_LIBS libs needed by shared LIBRARY # SHRLIB_LIBS libs needed by shared LIBRARY # USR_SYS_LIBS system libs needed building libraries and prods # PROD_SYS_LIBS system libs needed for building prods # LIB_SYS_LIBS system libs needed for building libraries # USR_LDFLAGS ld flags for building libraries and prods # PROD_LDFLAGS ld flags for building prods # LIB_LDFLAGS ld flags for building libraries # PROD products to build and install # PROD_HOST products to build and install # PROD_IOC products to build and install # TESTPROD products to build # TESTPROD_HOST products to build # TESTPROD_IOC products to build # LIBRARY libraries to build and install # LIBRARY_HOST libraries to build and install # LIBRARY_IOC libraries to build and install # TESTLIBRARY libraries to build # TESTLIBRARY_HOST libraries to build # TESTLIBRARY_IOC libraries to build # LOADABLE_LIBRARY libraries to build and install # LOADABLE_LIBRARY_HOST libraries to build and install # LOADABLE_LIBRARY_IOC libraries to build and install # SCRIPTS scripts and install # SCRIPTS_HOST host system scripts to install # SCRIPTS_IOC ioc system scripts to install # TESTSCRIPTS scripts # TESTSCRIPTS_HOST host system scripts # TESTSCRIPTS_IOC ioc system scripts # OBJS object files to build and install # OBJS_HOST host system object files to build and install # OBJS_IOC ioc system object files to build and install # USR_INCLUDES include directories # BIN_INSTALLS binaries to install # LIB_INSTALLS library binaries to install # RCS win32 resource files for building libraries and prods # PROD_RCS win32 resource files for building prods # LIB_RCS win32 resource files for building libraries # # Remark: # If you define INC, e.g. INC = getopt.h, the source # (getopt.h) must be in the source directory (..) and/or # in one or more ../os/ directories. # # Additional target architecture, T_A, Rules for USR_CFLAGS, USR_CXXFLAGS, # and USR_CPPFLAGS which are applied before the above os_class Rules: # 1) USR_CFLAGS_$(OS_CLASS) is used # 2) if there is a special $(USR_CFLAGS_$(T_A)), it's # appended to 1) # 3) if there is no special defined, but a generic USR_CFLAGS_$(OS_CLASS)_DEFAULT, # this one is appended # 4) if you have the special case that your $(USR_CFLAGS_$(T_A)) is # empty but you don't want 3), you have to define it as '-nil-', e.g.: # USR_CFLAGS_vxWorks = # USR_CFLAGS_vxWorks-68040 = -nil- # USR_CFLAGS_vxWorks_DEFAULT = # # ifneq ($(strip $(USR_CFLAGS_$(T_A))),) USR_CFLAGS_$(OS_CLASS)+=$(subst -nil-,,$(USR_CFLAGS_$(T_A))) else ifdef USR_CFLAGS_$(OS_CLASS)_DEFAULT USR_CFLAGS_$(OS_CLASS)+=$(USR_CFLAGS_$(OS_CLASS)_DEFAULT) endif endif ifneq ($(strip $(USR_CFLAGS_$(OS_CLASS))),) USR_CFLAGS+=$(subst -nil-,,$(USR_CFLAGS_$(OS_CLASS))) else ifdef USR_CFLAGS_DEFAULT USR_CFLAGS+=$(USR_CFLAGS_DEFAULT) endif endif ifneq ($(strip $(USR_INCLUDES_$(OS_CLASS))),) USR_INCLUDES+=$(subst -nil-,,$(USR_INCLUDES_$(OS_CLASS))) else ifdef USR_INCLUDES_DEFAULT USR_INCLUDES+=$(USR_INCLUDES_DEFAULT) endif endif ifneq ($(strip $(USR_CXXFLAGS_$(T_A))),) USR_CXXFLAGS_$(OS_CLASS)+=$(subst -nil-,,$(USR_CXXFLAGS_$(T_A))) else ifdef USR_CXXFLAGS_$(OS_CLASS)_DEFAULT USR_CXXFLAGS_$(OS_CLASS)+=$(USR_CXXFLAGS_$(OS_CLASS)_DEFAULT) endif endif ifneq ($(strip $(USR_CXXFLAGS_$(OS_CLASS))),) USR_CXXFLAGS+=$(subst -nil-,,$(USR_CXXFLAGS_$(OS_CLASS))) else ifdef USR_CXXFLAGS_DEFAULT USR_CXXFLAGS+=$(USR_CXXFLAGS_DEFAULT) endif endif ifneq ($(strip $(USR_CPPFLAGS_$(T_A))),) USR_CPPFLAGS_$(OS_CLASS)+=$(subst -nil-,,$(USR_CPPFLAGS_$(T_A))) else ifdef USR_CPPFLAGS_$(OS_CLASS)_DEFAULT USR_CPPFLAGS_$(OS_CLASS)+=$(USR_CPPFLAGS_$(OS_CLASS)_DEFAULT) endif endif ifneq ($(strip $(USR_CPPFLAGS_$(OS_CLASS))),) USR_CPPFLAGS+=$(subst -nil-,,$(USR_CPPFLAGS_$(OS_CLASS))) else ifdef USR_CPPFLAGS_DEFAULT USR_CPPFLAGS+=$(USR_CPPFLAGS_DEFAULT) endif endif ifneq ($(strip $(USR_LDFLAGS_$(OS_CLASS))),) USR_LDFLAGS+=$(subst -nil-,,$(USR_LDFLAGS_$(OS_CLASS))) else ifdef USR_LDFLAGS_DEFAULT USR_LDFLAGS+=$(USR_LDFLAGS_DEFAULT) endif endif ifneq ($(strip $(PROD_LDFLAGS_$(OS_CLASS))),) PROD_LDFLAGS+=$(subst -nil-,,$(PROD_LDFLAGS_$(OS_CLASS))) else ifdef PROD_LDFLAGS_DEFAULT PROD_LDFLAGS+=$(PROD_LDFLAGS_DEFAULT) endif endif ifneq ($(strip $(LIB_LDFLAGS_$(OS_CLASS))),) LIB_LDFLAGS+=$(subst -nil-,,$(LIB_LDFLAGS_$(OS_CLASS))) else ifdef LIB_LDFLAGS_DEFAULT LIB_LDFLAGS+=$(LIB_LDFLAGS_DEFAULT) endif endif ifneq ($(strip $(LIBSRCS_$(OS_CLASS))),) LIBSRCS += $(subst -nil-,,$(LIBSRCS_$(OS_CLASS))) else ifdef LIBSRCS_DEFAULT LIBSRCS+=$(LIBSRCS_DEFAULT) endif endif ifneq ($(strip $(LIB_SRCS_$(OS_CLASS))),) LIB_SRCS += $(subst -nil-,,$(LIB_SRCS_$(OS_CLASS))) else ifdef LIB_SRCS_DEFAULT LIB_SRCS+=$(LIB_SRCS_DEFAULT) endif endif ifneq ($(strip $(SRCS_$(OS_CLASS))),) SRCS += $(subst -nil-,,$(SRCS_$(OS_CLASS))) else ifdef SRCS_DEFAULT SRCS+=$(SRCS_DEFAULT) endif endif ifneq ($(strip $(USR_SRCS_$(OS_CLASS))),) USR_SRCS += $(subst -nil-,,$(USR_SRCS_$(OS_CLASS))) else ifdef USR_SRCS_DEFAULT USR_SRCS+=$(USR_SRCS_DEFAULT) endif endif ifneq ($(strip $(PROD_SRCS_$(OS_CLASS))),) PROD_SRCS += $(subst -nil-,,$(PROD_SRCS_$(OS_CLASS))) else ifdef PROD_SRCS_DEFAULT PROD_SRCS+=$(PROD_SRCS_DEFAULT) endif endif ifneq ($(strip $(BIN_INSTALLS_$(OS_CLASS))),) BIN_INSTALLS+=$(subst -nil-,,$(BIN_INSTALLS_$(OS_CLASS))) else ifdef BIN_INSTALLS_DEFAULT BIN_INSTALLS+=$(BIN_INSTALLS_DEFAULT) endif endif ifneq ($(strip $(LIB_INSTALLS_$(OS_CLASS))),) LIB_INSTALLS+=$(subst -nil-,,$(LIB_INSTALLS_$(OS_CLASS))) else ifdef LIB_INSTALLS_DEFAULT LIB_INSTALLS+=$(LIB_INSTALLS_DEFAULT) endif endif ifneq ($(strip $(PROD_OBJS_$(OS_CLASS))),) PROD_OBJS+=$(subst -nil-,,$(PROD_OBJS_$(OS_CLASS))) else ifneq (,$(strip $(PROD_OBJS_DEFAULT))) PROD_OBJS+=$(PROD_OBJS_DEFAULT) endif endif ifneq ($(strip $(USR_OBJS_$(OS_CLASS))),) USR_OBJS+=$(subst -nil-,,$(USR_OBJS_$(OS_CLASS))) else ifneq (,$(strip $(USR_OBJS_DEFAULT))) USR_OBJS+=$(USR_OBJS_DEFAULT) endif endif ifneq ($(strip $(OBJS_$(OS_CLASS))),) OBJS+=$(subst -nil-,,$(OBJS_$(OS_CLASS))) else ifneq (,$(strip $(OBJS_DEFAULT))) OBJS+=$(OBJS_DEFAULT) endif endif ifneq ($(strip $(OBJS_IOC_$(OS_CLASS))),) OBJS_IOC+=$(subst -nil-,,$(OBJS_IOC_$(OS_CLASS))) else ifneq (,$(strip $(OBJS_IOC_DEFAULT))) OBJS_IOC+=$(OBJS_IOC_DEFAULT) endif endif ifneq ($(strip $(OBJS_HOST_$(OS_CLASS))),) OBJS_HOST+=$(subst -nil-,,$(OBJS_HOST_$(OS_CLASS))) else ifneq (,$(strip $(OBJS_HOST_DEFAULT))) OBJS_HOST+=$(OBJS_HOST_DEFAULT) endif endif ifneq ($(strip $(LIB_OBJS_$(OS_CLASS))),) LIB_OBJS+=$(subst -nil-,,$(LIB_OBJS_$(OS_CLASS))) else ifneq (,$(strip $(LIB_OBJS_DEFAULT))) LIB_OBJS+=$(LIB_OBJS_DEFAULT) endif endif ifneq ($(strip $(LIBRARY_$(OS_CLASS))),) LIBRARY+=$(subst -nil-,,$(LIBRARY_$(OS_CLASS))) else ifneq (,$(strip $(LIBRARY_DEFAULT))) LIBRARY+=$(LIBRARY_DEFAULT) endif endif ifneq ($(strip $(LIBRARY_IOC_$(OS_CLASS))),) LIBRARY_IOC+=$(subst -nil-,,$(LIBRARY_IOC_$(OS_CLASS))) else ifneq (,$(strip $(LIBRARY_IOC_DEFAULT))) LIBRARY_IOC+=$(LIBRARY_IOC_DEFAULT) endif endif ifneq ($(strip $(LIBRARY_HOST_$(OS_CLASS))),) LIBRARY_HOST+=$(subst -nil-,,$(LIBRARY_HOST_$(OS_CLASS))) else ifneq (,$(strip $(LIBRARY_HOST_DEFAULT))) LIBRARY_HOST+=$(LIBRARY_HOST_DEFAULT) endif endif ifneq ($(strip $(TESTLIBRARY_$(OS_CLASS))),) TESTLIBRARY+=$(subst -nil-,,$(TESTLIBRARY_$(OS_CLASS))) else ifneq (,$(strip $(TESTLIBRARY_DEFAULT))) TESTLIBRARY+=$(TESTLIBRARY_DEFAULT) endif endif ifneq ($(strip $(TESTLIBRARY_IOC_$(OS_CLASS))),) TESTLIBRARY_IOC+=$(subst -nil-,,$(TESTLIBRARY_IOC_$(OS_CLASS))) else ifneq (,$(strip $(TESTLIBRARY_IOC_DEFAULT))) TESTLIBRARY_IOC+=$(TESTLIBRARY_IOC_DEFAULT) endif endif ifneq ($(strip $(TESTLIBRARY_HOST_$(OS_CLASS))),) TESTLIBRARY_HOST+=$(subst -nil-,,$(TESTLIBRARY_HOST_$(OS_CLASS))) else ifneq (,$(strip $(TESTLIBRARY_HOST_DEFAULT))) TESTLIBRARY_HOST+=$(TESTLIBRARY_HOST_DEFAULT) endif endif ifneq ($(strip $(LOADABLE_LIBRARY_$(OS_CLASS))),) LOADABLE_LIBRARY+=$(subst -nil-,,$(LOADABLE_LIBRARY_$(OS_CLASS))) else ifneq (,$(strip $(LOADABLE_LIBRARY_DEFAULT))) LOADABLE_LIBRARY+=$(LOADABLE_LIBRARY_DEFAULT) endif endif ifneq ($(strip $(LOADABLE_LIBRARY_HOST_$(OS_CLASS))),) LOADABLE_LIBRARY_HOST+=$(subst -nil-,,$(LOADABLE_LIBRARY_HOST_$(OS_CLASS))) else ifneq (,$(strip $(LOADABLE_LIBRARY_HOST_DEFAULT))) LOADABLE_LIBRARY_HOST+=$(LOADABLE_LIBRARY_HOST_DEFAULT) endif endif ifneq ($(strip $(LOADABLE_LIBRARY_IOC_$(OS_CLASS))),) LOADABLE_LIBRARY_IOC+=$(subst -nil-,,$(LOADABLE_LIBRARY_IOC_$(OS_CLASS))) else ifneq (,$(strip $(LOADABLE_LIBRARY_IOC_DEFAULT))) LOADABLE_LIBRARY_IOC+=$(LOADABLE_LIBRARY_IOC_DEFAULT) endif endif ifneq ($(strip $(PROD_LIBS_$(OS_CLASS))),) PROD_LIBS += $(subst -nil-,,$(PROD_LIBS_$(OS_CLASS))) else ifdef PROD_LIBS_DEFAULT PROD_LIBS += $(PROD_LIBS_DEFAULT) endif endif # SHRLIB_LIBS deprecated ifneq ($(strip $(SHRLIB_LIBS_$(OS_CLASS))),) SHRLIB_LIBS += $(subst -nil-,,$(SHRLIB_LIBS_$(OS_CLASS))) else ifdef SHRLIB_LIBS_DEFAULT SHRLIB_LIBS += $(SHRLIB_LIBS_DEFAULT) endif endif ifneq ($(strip $(LIB_LIBS_$(OS_CLASS))),) LIB_LIBS += $(subst -nil-,,$(LIB_LIBS_$(OS_CLASS))) else ifdef LIB_LIBS_DEFAULT LIB_LIBS += $(LIB_LIBS_DEFAULT) endif endif ifneq ($(strip $(USR_LIBS_$(OS_CLASS))),) USR_LIBS += $(subst -nil-,,$(USR_LIBS_$(OS_CLASS))) else ifdef USR_LIBS_DEFAULT USR_LIBS += $(USR_LIBS_DEFAULT) endif endif # # concat specific library contents (if defined) to SYS_PROD_LIBS # # SYS_PROD_LIBS deprecated ifneq ($(strip $(SYS_PROD_LIBS_$(OS_CLASS))),) SYS_PROD_LIBS += $(subst -nil-,,$(SYS_PROD_LIBS_$(OS_CLASS))) else ifdef SYS_PROD_LIBS_DEFAULT SYS_PROD_LIBS += $(SYS_PROD_LIBS_DEFAULT) endif endif PROD_SYS_LIBS+=$(SYS_PROD_LIBS) ifneq ($(strip $(PROD_SYS_LIBS_$(OS_CLASS))),) PROD_SYS_LIBS += $(subst -nil-,,$(PROD_SYS_LIBS_$(OS_CLASS))) else ifdef PROD_SYS_LIBS_DEFAULT PROD_SYS_LIBS += $(PROD_SYS_LIBS_DEFAULT) endif endif ifneq ($(strip $(LIB_SYS_LIBS_$(OS_CLASS))),) LIB_SYS_LIBS += $(subst -nil-,,$(LIB_SYS_LIBS_$(OS_CLASS))) else ifdef LIB_SYS_LIBS_DEFAULT LIB_SYS_LIBS += $(LIB_SYS_LIBS_DEFAULT) endif endif ifneq ($(strip $(USR_SYS_LIBS_$(OS_CLASS))),) USR_SYS_LIBS += $(subst -nil-,,$(USR_SYS_LIBS_$(OS_CLASS))) else ifdef USR_SYS_LIBS_DEFAULT USR_SYS_LIBS += $(USR_SYS_LIBS_DEFAULT) endif endif # # concat specific products # ifneq ($(strip $(PROD_$(OS_CLASS))),) PROD+=$(subst -nil-,,$(PROD_$(OS_CLASS))) else ifneq (,$(strip $(PROD_DEFAULT))) PROD+=$(PROD_DEFAULT) endif endif ifneq ($(strip $(PROD_IOC_$(OS_CLASS))),) PROD_IOC+=$(subst -nil-,,$(PROD_IOC_$(OS_CLASS))) else ifneq (,$(strip $(PROD_IOC_DEFAULT))) PROD_IOC+=$(PROD_IOC_DEFAULT) endif endif ifneq ($(strip $(PROD_HOST_$(OS_CLASS))),) PROD_HOST+=$(subst -nil-,,$(PROD_HOST_$(OS_CLASS))) else ifneq (,$(strip $(PROD_HOST_DEFAULT))) PROD_HOST+=$(PROD_HOST_DEFAULT) endif endif # # concat specific products # ifneq ($(strip $(TESTPROD_$(OS_CLASS))),) TESTPROD+=$(subst -nil-,,$(TESTPROD_$(OS_CLASS))) else ifneq (,$(strip $(TESTPROD_DEFAULT))) TESTPROD+=$(TESTPROD_DEFAULT) endif endif ifneq ($(strip $(TESTPROD_IOC_$(OS_CLASS))),) TESTPROD_IOC+=$(subst -nil-,,$(TESTPROD_IOC_$(OS_CLASS))) else ifneq (,$(strip $(TESTPROD_IOC_DEFAULT))) TESTPROD_IOC+=$(TESTPROD_IOC_DEFAULT) endif endif ifneq ($(strip $(TESTPROD_HOST_$(OS_CLASS))),) TESTPROD_HOST+=$(subst -nil-,,$(TESTPROD_HOST_$(OS_CLASS))) else ifneq (,$(strip $(TESTPROD_HOST_DEFAULT))) TESTPROD_HOST+=$(TESTPROD_HOST_DEFAULT) endif endif # # concat specific scripts # ifneq ($(strip $(SCRIPTS_$(OS_CLASS))),) SCRIPTS += $(subst -nil-,,$(SCRIPTS_$(OS_CLASS))) else ifdef SCRIPTS_DEFAULT SCRIPTS += $(SCRIPTS_DEFAULT) endif endif ifneq ($(strip $(SCRIPTS_IOC_$(OS_CLASS))),) SCRIPTS_IOC+=$(subst -nil-,,$(SCRIPTS_IOC_$(OS_CLASS))) else ifneq (,$(strip $(SCRIPTS_IOC_DEFAULT))) SCRIPTS_IOC+=$(SCRIPTS_IOC_DEFAULT) endif endif ifneq ($(strip $(SCRIPTS_HOST_$(OS_CLASS))),) SCRIPTS_HOST+=$(subst -nil-,,$(SCRIPTS_HOST_$(OS_CLASS))) else ifneq (,$(strip $(SCRIPTS_HOST_DEFAULT))) SCRIPTS_HOST+=$(SCRIPTS_HOST_DEFAULT) endif endif # # concat specific scripts # ifneq ($(strip $(TESTSCRIPTS_$(OS_CLASS))),) TESTSCRIPTS += $(subst -nil-,,$(TESTSCRIPTS_$(OS_CLASS))) else ifdef TESTSCRIPTS_DEFAULT TESTSCRIPTS += $(TESTSCRIPTS_DEFAULT) endif endif ifneq ($(strip $(TESTSCRIPTS_IOC_$(OS_CLASS))),) TESTSCRIPTS_IOC+=$(subst -nil-,,$(TESTSCRIPTS_IOC_$(OS_CLASS))) else ifneq (,$(strip $(TESTSCRIPTS_IOC_DEFAULT))) TESTSCRIPTS_IOC+=$(TESTSCRIPTS_IOC_DEFAULT) endif endif ifneq ($(strip $(TESTSCRIPTS_HOST_$(OS_CLASS))),) TESTSCRIPTS_HOST+=$(subst -nil-,,$(TESTSCRIPTS_HOST_$(OS_CLASS))) else ifneq (,$(strip $(TESTSCRIPTS_HOST_DEFAULT))) TESTSCRIPTS_HOST+=$(TESTSCRIPTS_HOST_DEFAULT) endif endif # # concat specific resource files # ifneq ($(strip $(RCS_$(OS_CLASS))),) RCS += $(subst -nil-,,$(RCS_$(OS_CLASS))) else ifdef RCS_DEFAULT RCS += $(RCS_DEFAULT) endif endif ifneq ($(strip $(PROD_RCS_$(OS_CLASS))),) PROD_RCS += $(subst -nil-,,$(PROD_RCS_$(OS_CLASS))) else ifdef PROD_RCS_DEFAULT PROD_RCS+=$(PROD_RCS_DEFAULT) endif endif ifneq ($(strip $(LIB_RCS_$(OS_CLASS))),) LIB_RCS += $(subst -nil-,,$(LIB_RCS_$(OS_CLASS))) else ifdef LIB_RCS_DEFAULT LIB_RCS+=$(LIB_RCS_DEFAULT) endif endif base-7.0.3.1/configure/CONFIG_APP_INCLUDE0000664000577000060420000000260413557101274016131 0ustar anjaesctl#************************************************************************* # Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* export TOP ifneq ($(RELEASE_TOPS),) define RELEASE_FLAGS_template export $(1) $(1)_HOST_BIN = $$(strip $$($(1)))/bin/$(EPICS_HOST_ARCH) $(1)_HOST_LIB = $$(strip $$($(1)))/lib/$(EPICS_HOST_ARCH) $(1)_BIN = $$(wildcard $$(strip $$($(1)))/bin/$(T_A)) $(1)_LIB = $$(wildcard $$(strip $$($(1)))/lib/$(T_A)) SHRLIB_SEARCH_DIRS += $$($(1)_LIB) RELEASE_INCLUDES += $$(addprefix -I,$$(wildcard $$(strip $$($(1)))/include/compiler/$(CMPLR_CLASS))) RELEASE_INCLUDES += $$(addprefix -I,$$(wildcard $$(strip $$($(1)))/include/os/$(OS_CLASS))) RELEASE_INCLUDES += $$(addprefix -I,$$(wildcard $$(strip $$($(1)))/include)) RELEASE_DBD_DIRS += $$(wildcard $$(strip $$($(1)))/dbd) RELEASE_DB_DIRS += $$(wildcard $$(strip $$($(1)))/db) RELEASE_PERL_MODULE_DIRS += $$(wildcard $$(strip $$($(1)))/lib/perl) endef $(foreach top, $(RELEASE_TOPS), \ $(eval $(call RELEASE_FLAGS_template,$(top)))) endif base-7.0.3.1/configure/CONFIG_BASE0000664000577000060420000000534413557101274015064 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* #--------------------------------------------------------------- # EPICS Base directories EPICS_BASE_HOST_BIN = $(EPICS_BASE)/bin/$(EPICS_HOST_ARCH) EPICS_BASE_HOST_LIB = $(EPICS_BASE)/lib/$(EPICS_HOST_ARCH) ifdef T_A EPICS_BASE_LIB = $(EPICS_BASE)/lib/$(T_A) EPICS_BASE_BIN = $(EPICS_BASE)/bin/$(T_A) endif #--------------------------------------------------------------- # Version number for base shared libraries (and win32 products) ifdef BASE_TOP # Unix lib.so. Darwin lib..dylib SHRLIB_VERSION = $(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION) # Windows only allows 2 levels of version numbering PROD_VERSION = $(EPICS_VERSION).$(EPICS_REVISION) endif # BASE_TOP #--------------------------------------------------------------- # Where to find the installed build tools # Windows does not like commands with relative paths starting ../ # so TOOLS must be an absolute path, although Perl scripts are OK. # FIND_TOOL is for scripts run before the build reaches src/tools. TOOLS = $(abspath $(EPICS_BASE_HOST_BIN)) FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) $(EPICS_BASE)/src/tools/$(1))) #--------------------------------------------------------------- # EPICS Base build tools and tool flags PODTOHTML = $(PERL) $(TOOLS)/podToHtml.pl CONVERTRELEASE = $(PERL) $(call FIND_TOOL,convertRelease.pl) FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl TAPTOJUNIT = $(PERL) $(TOOLS)/tap-to-junit-xml.pl PROVE = $(PERL) $(TOOLS)/epicsProve.pl GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) $(QUESTION_FLAG) MAKERPATH = $(PYTHON) $(TOOLS)/makeRPath.py #--------------------------------------------------------------- # tools for installing libraries and products INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(QUIET_FLAG) INSTALL_PRODUCT = $(INSTALL) INSTALL_LIBRARY = $(INSTALL) #--------------------------------------------------------------- # tools for making header dependencies and variable replacement MKMF = $(PERL) $(TOOLS)/mkmf.pl REPLACEVAR = $(PERL) $(TOOLS)/replaceVAR.pl #--------------------------------------------------------------- # tools for cleaning out unwanted files CVSCLEAN = $(call FIND_TOOL,cvsclean.pl) DEPCLEAN = $(call FIND_TOOL,depclean.pl) base-7.0.3.1/configure/CONFIG_BASE_VERSION0000664000577000060420000000524613557101274016272 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* # # EPICS Version information # # Only the person making an official EPICS release should make changes in # this file. # # EPICS_SITE_VERSION is defined in CONFIG_SITE for sites that want a local # version number to be included in the reported version string. # We define convenience macros for our release series to be NO or YES, so # Makefiles can detect 'Series X or later', or 'Series X only' like this: # # ifdef BASE_3_14 # true for 3.14 or later # ifdef BASE_3_15 # true for 3.15 or later # ifdef BASE_3_16 # true for 3.16 or later # ifdef BASE_7_0 # true for 7.0 or later # # ifeq ($(BASE_3_14),YES) # true for 3.14.x only # ifeq ($(BASE_3_15),YES) # true for 3.15.x only # ifeq ($(BASE_3_16),YES) # true for 3.16.x only. # ifeq ($(BASE_7_0),YES) # true for 7.0.x only. BASE_3_14 = NO BASE_3_15 = NO BASE_3_16 = NO BASE_7_0 = YES # EPICS_VERSION must be a number >0 and <256 EPICS_VERSION = 7 # EPICS_REVISION must be a number >=0 and <256 EPICS_REVISION = 0 # EPICS_MODIFICATION must be a number >=0 and <256 EPICS_MODIFICATION = 3 # 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 # Immediately after an official release the EPICS_PATCH_LEVEL is incremented # and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions) EPICS_DEV_SNAPSHOT= #EPICS_DEV_SNAPSHOT=-DEV #EPICS_DEV_SNAPSHOT=-pre1 #EPICS_DEV_SNAPSHOT=-pre1-DEV #EPICS_DEV_SNAPSHOT=-pre2 #EPICS_DEV_SNAPSHOT=-pre2-DEV #EPICS_DEV_SNAPSHOT=-rc1 #EPICS_DEV_SNAPSHOT=-rc1-DEV #EPICS_DEV_SNAPSHOT=-rc2 #EPICS_DEV_SNAPSHOT=-rc2-DEV # No changes should be needed below here ifneq ($(EPICS_PATCH_LEVEL),0) EPICS_PATCH_VSTRING=.$(EPICS_PATCH_LEVEL) endif ifneq ($(strip $(EPICS_SITE_VERSION)),) EPICS_SITE_VSTRING=-$(EPICS_SITE_VERSION) endif EPICS_SHORT_VERSION=$(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION)$(EPICS_PATCH_VSTRING) EPICS_VERSION_NUMBER=$(EPICS_SHORT_VERSION)$(EPICS_DEV_SNAPSHOT)$(EPICS_SITE_VSTRING) EPICS_VERSION_STRING="EPICS Version $(EPICS_VERSION_NUMBER)" # Provide this in case anyone is still using the old name COMMIT_DATE="-no-date-" base-7.0.3.1/configure/CONFIG_CA_MODULE0000664000577000060420000000067613557101274015705 0ustar anjaesctl#************************************************************************* # Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # Libraries needed to link a host tool EPICS_BASE_HOST_LIBS = ca Com base-7.0.3.1/configure/CONFIG_CA_VERSION0000664000577000060420000000053413557101274016036 0ustar anjaesctl# Version number for the Channel Access API and shared library EPICS_CA_MAJOR_VERSION = 4 EPICS_CA_MINOR_VERSION = 13 EPICS_CA_MAINTENANCE_VERSION = 5 # Development flag, set to zero for release versions EPICS_CA_DEVELOPMENT_FLAG = 0 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 base-7.0.3.1/configure/CONFIG_COMMON0000664000577000060420000004057413557101274015346 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # # CONFIG_COMMON # # This file is to be maintained by the community. # # Common Configuration Information #------------------------------------------------------- # POSIX is OS default POSIX=YES #------------------------------------------------------- # Divider symbol DIVIDER = . #------------------------------------------------------- # Build architectures # CROSS1 will be defined only when CROSS_COMPILER_HOST_ARCHS is NOT defined CROSS1 = $(CROSS_COMPILER_TARGET_ARCHS$(word 1,$(CROSS_COMPILER_HOST_ARCHS))) # CROSS2 will be defined only when CROSS_COMPILER_HOST_ARCHS is defined # and EPICS_HOST_ARCH is one of its words CROSS2 = $(CROSS_COMPILER_TARGET_ARCHS$(filter-out 1,$(words $(filter $(EPICS_HOST_ARCH),$(CROSS_COMPILER_HOST_ARCHS))))) BUILD_ARCHS = $(EPICS_HOST_ARCH) $(CROSS1) $(CROSS2) #------------------------------------------------------- # Default for perl if it's on the PATH, # otherwise override this in os/CONFIG_SITE..Common PERL = perl -CSD PYTHON = python #------------------------------------------------------- # Check configure/RELEASE file for consistency CHECK_RELEASE_YES = checkRelease CHECK_RELEASE_NO = noCheckRelease CHECK_RELEASE_WARN = warnRelease #------------------------------------------------------- # GNU directory # GNU_DIR definition is here because it is used to find # READLINE library even if GNU compiler is not used GNU_DIR = /usr #------------------------------------------------------- # Directories INSTALL_LOCATION = $(TOP) INSTALL_LOCATION_LIB = $(INSTALL_LOCATION)/lib INSTALL_LOCATION_BIN = $(INSTALL_LOCATION)/bin INSTALL_HOST_BIN = $(INSTALL_LOCATION_BIN)/$(EPICS_HOST_ARCH) INSTALL_HOST_LIB = $(INSTALL_LOCATION_LIB)/$(EPICS_HOST_ARCH) INSTALL_INCLUDE = $(INSTALL_LOCATION)/include INSTALL_DOC = $(INSTALL_LOCATION)/doc INSTALL_HTML = $(INSTALL_LOCATION)/html INSTALL_TEMPLATES = $(INSTALL_LOCATION)/templates INSTALL_DBD = $(INSTALL_LOCATION)/dbd INSTALL_DB = $(INSTALL_LOCATION)/db INSTALL_CONFIG = $(INSTALL_LOCATION)/configure # Directory for OS independant build created files COMMON_DIR = ../O.Common # IOC's absolute path to $(TOP), may be overridden inside the application IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION)) #------------------------------------------------------- # Silencing the build - suppress messages during 'make -s' NOP = : ECHO = @$(if $(findstring s,$(MFLAGS)),$(NOP),echo) QUIET_FLAG := $(if $(findstring s,$(MFLAGS)),-q,) #------------------------------------------------------- # Convert 'make -q' flag into '-i' for genVersionHeader.pl QUESTION_FLAG := $(if $(findstring q,$(MFLAGS)),-i,) #------------------------------------------------------- ifdef T_A INSTALL_LIB = $(INSTALL_LOCATION_LIB)/$(T_A) INSTALL_SHRLIB = $(INSTALL_LOCATION_LIB)/$(T_A) INSTALL_TCLLIB = $(INSTALL_LOCATION_LIB)/$(T_A) INSTALL_BIN = $(INSTALL_LOCATION_BIN)/$(T_A) # Directories for libraries SHRLIB_SEARCH_DIRS = $(INSTALL_LIB) #------------------------------------------------------- # Ext, app, and module configure dir targets CONFIG_INSTALLS += ../RULES_BUILD ../RELEASE* #------------------------------------------------------- # Cross compile default, HOST or CROSS, CONFIG.crossCommon will override BUILD_CLASS = HOST #------------------------------------------------------- # Build defaults, CONFIG_SITE, CONFIG, or os/CONFIG* will override STATIC_BUILD=NO SHARED_LIBRARIES=YES HOST_OPT=YES CROSS_OPT=YES HOST_WARN=YES CROSS_WARN=YES GNU=NO #------------------------------------------------------- # Run checkRelease in $(TOP)/configure/O.* CONFIG_TARGETS += $(CHECK_RELEASE_$(CHECK_RELEASE)) #------------------------------------------------------- # Prefix and suffix DEP = .d OBJ = .o CMPLR_SUFFIX = CMPLR_PREFIX = LIB_PREFIX = LIB_SUFFIX = SHRLIB_PREFIX = $(LIB_PREFIX) DLLSTUB_PREFIX = $(LIB_PREFIX) DLLSTUB_SUFFIX = $(LIB_SUFFIX) BUILDLIB_PREFIX_YES = $(DLLSTUB_PREFIX) BUILDLIB_PREFIX_NO = $(LIB_PREFIX) BUILDLIB_SUFFIX_YES = $(DLLSTUB_SUFFIX) BUILDLIB_SUFFIX_NO = $(LIB_SUFFIX) BUILDLIB_PREFIX = $(BUILDLIB_PREFIX_$(SHARED_LIBRARIES)) BUILDLIB_SUFFIX = $(BUILDLIB_SUFFIX_$(SHARED_LIBRARIES)) #-------------------------------------------------- # vpath directories POSIX_YES = os/posix GENERIC_SRC_DIRS = .. $(SRC_DIRS) OS_SRC_DIRS += . $(foreach dir, .. $(SRC_DIRS), \ $(addprefix $(dir)/, os/$(OS_CLASS) $(POSIX_$(POSIX)) os/default )) CMPLR_SRC_DIRS += . $(foreach dir, .. $(SRC_DIRS), \ $(addprefix $(dir)/, compiler/$(CMPLR_CLASS) compiler/default )) ALL_SRC_DIRS = $(CMPLR_SRC_DIRS) $(OS_SRC_DIRS) $(GENERIC_SRC_DIRS) #-------------------------------------------------- # compile line include directories INSTALL_INCLUDES += \ -I$(INSTALL_INCLUDE)/compiler/$(CMPLR_CLASS) \ -I$(INSTALL_INCLUDE)/os/$(OS_CLASS) \ -I$(INSTALL_INCLUDE) SRC_INCLUDES = -I$(COMMON_DIR) $(addprefix -I, $(wildcard $(ALL_SRC_DIRS))) #-------------------------------------------------- # Target filename definitions OBJSNAME = $(addsuffix $(OBJ),$(basename $(OBJS))) PRODNAME = $(addsuffix $(EXE),$(basename $(PROD))) TESTPRODNAME = $(addsuffix $(EXE),$(basename $(TESTPROD))) SHRLIBNAME = $(SHRLIBNAME_$(SHARED_LIBRARIES)) TESTSHRLIBNAME = $(TESTSHRLIBNAME_$(SHARED_LIBRARIES)) #-------------------------------------------------- # obj files TARGET_OBJS = $($*_LDOBJS) $(addsuffix $(OBJ),$(basename $($*_OBJS) $($*_SRCS))) PRODUCT_OBJS = $(addsuffix $(OBJ),$(basename $(SRCS) $(USR_SRCS) $(PROD_SRCS) $(USR_OBJS) $(PROD_OBJS))) PROD_LD_OBJS = $(TARGET_OBJS) $(PRODUCT_OBJS) LIBRARY_OBJS = $(addsuffix $(OBJ),$(basename $(SRCS) $(USR_SRCS) $(LIB_SRCS) $(LIBSRCS) $(USR_OBJS) $(LIB_OBJS))) LIBRARY_LD_OBJS = $(TARGET_OBJS) $(LIBRARY_OBJS) #-------------------------------------------------- # Windows resource files TARGET_RESS = $(if $(RES),$(addsuffix $(RES),$(basename $($*_RCS))),) PROD_RESS = $(if $(RES),$(addsuffix $(RES),$(basename $(RCS) $(PROD_RCS))),) PROD_LD_RESS = $(TARGET_RESS) $(PROD_RESS) LIBRARY_RESS = $(if $(RES),$(addsuffix $(RES),$(basename $(RCS) $(LIB_RCS) $(LIBRARY_RCS))),) LIBRARY_LD_RESS = $(TARGET_RESS) $(LIBRARY_RESS) #-------------------------------------------------- # C preprocessor, compiler, and linker flag defaults # Target architecture specific flags ARCH_DEP_CPPFLAGS = ARCH_DEP_CFLAGS = ARCH_DEP_CXXFLAGS = $(ARCH_DEP_CFLAGS) ARCH_DEP_LDFLAGS = ARCH_DEP_LDLIBS = # Target operating system specific flags OP_SYS_CPPFLAGS = OP_SYS_CFLAGS = OP_SYS_CXXFLAGS = $(OP_SYS_CFLAGS) OP_SYS_LDFLAGS = OP_SYS_INCLUDES = # Makefile specific flags USR_INCLUDES = USR_CFLAGS = USR_CXXFLAGS = USR_LDFLAGS = USR_LIBS = USR_CPPFLAGS = USR_DBDFLAGS = USR_ARFLAGS = # Variables to be set only on the command-line: # CMD_INCLUDES = # CMD_CFLAGS = # CMD_CXXFLAGS = # CMD_LDFLAGS = # CMD_CPPFLAGS = # CMD_DBFLAGS = # CMD_DBDFLAGS = # CMD_ARFLAGS = # Debug specific options DEBUG_CPPFLAGS = DEBUG_CFLAGS = DEBUG_CXXFLAGS = $(DEBUG_CFLAGS) DEBUG_LDFLAGS = DEBUG_LDLIBS = # Target specific options TARGET_INCLUDES = $($(basename $@)_INCLUDES_$(T_A)) TARGET_CFLAGS = $($(basename $@)_CFLAGS_$(T_A)) TARGET_CXXFLAGS = $($(basename $@)_CXXFLAGS_$(T_A)) TARGET_CPPFLAGS = $($(basename $@)_CPPFLAGS_$(T_A)) TARGET_INCLUDES += $($(basename $@)_INCLUDES_$(OS_CLASS)) $($(basename $@)_INCLUDES) TARGET_CFLAGS += $($(basename $@)_CFLAGS_$(OS_CLASS)) $($(basename $@)_CFLAGS) TARGET_CXXFLAGS += $($(basename $@)_CXXFLAGS_$(OS_CLASS)) $($(basename $@)_CXXFLAGS) TARGET_CPPFLAGS += $($(basename $@)_CPPFLAGS_$(OS_CLASS)) $($(basename $@)_CPPFLAGS) TARGET_LDFLAGS = $($*_LDFLAGS) # Warnings flags WARN_CPPFLAGS = $(WARN_CPPFLAGS_$($(BUILD_CLASS)_WARN)) WARN_CFLAGS = $(WARN_CFLAGS_$($(BUILD_CLASS)_WARN)) WARN_CXXFLAGS = $(WARN_CXXFLAGS_$($(BUILD_CLASS)_WARN)) # Optimization flags OPT_CPPFLAGS = $(OPT_CPPFLAGS_$($(BUILD_CLASS)_OPT)) OPT_CFLAGS = $(OPT_CFLAGS_$($(BUILD_CLASS)_OPT)) OPT_CXXFLAGS = $(OPT_CXXFLAGS_$($(BUILD_CLASS)_OPT)) # Static build flags STATIC_CFLAGS = $(STATIC_CFLAGS_$(STATIC_BUILD)) STATIC_CXXCFLAGS = $(STATIC_CXXFLAGS_$(STATIC_BUILD)) STATIC_LDFLAGS = $(STATIC_LDFLAGS_$(STATIC_BUILD)) STATIC_LDLIBS = $(STATIC_LDLIBS_$(STATIC_BUILD)) #-------------------------------------------------- # cflags for shared library src files (from SHRLIB_CFLAGS) LIBRARY_SRCS=$(basename $(foreach lib,$(LIBRARY) $(TESTLIBRARY) $(LOADABLE_LIBRARY),$($(lib)_OBJSNAME) $(LIBRARY_OBJS))) LIBRARY_SRC_CFLAGS=$($(patsubst $*,SHRLIB,$(findstring $*,$(LIBRARY_SRCS)))_CFLAGS) #-------------------------------------------------- # prefix, suffix, and ldflags for loadable shared libraries TARGET_LIB_LDFLAGS=$($(patsubst $*,LOADABLE_,$(findstring $*,$(LOADABLE_LIBRARY)))SHRLIB_LDFLAGS) LOADABLE_SHRLIB_PREFIX=$(SHRLIB_PREFIX) LOADABLE_SHRLIB_SUFFIX=$(SHRLIB_SUFFIX) #-------------------------------------------------- # Command-line input support default COMMANDLINE_LIBRARY = EPICS OP_SYS_LDLIBS += $(LDLIBS_$(COMMANDLINE_LIBRARY)) OP_SYS_LDFLAGS += $(LDFLAGS_$(COMMANDLINE_LIBRARY)) RUNTIME_LDFLAGS += $(RUNTIME_LDFLAGS_$(COMMANDLINE_LIBRARY)) #-------------------------------------------------- # Flags INCLUDES = -I. $(SRC_INCLUDES) $(INSTALL_INCLUDES) $(RELEASE_INCLUDES)\ $(TARGET_INCLUDES) $(USR_INCLUDES) $(CMD_INCLUDES) $(OP_SYS_INCLUDES)\ $($(BUILD_CLASS)_INCLUDES) CFLAGS = $($(BUILD_CLASS)_CFLAGS) $(POSIX_CFLAGS) $(OPT_CFLAGS)\ $(DEBUG_CFLAGS) $(PIPE_CFLAGS) $(WARN_CFLAGS) $(TARGET_CFLAGS)\ $(USR_CFLAGS) $(CMD_CFLAGS) $(ARCH_DEP_CFLAGS) $(CODE_CFLAGS)\ $(STATIC_CFLAGS) $(OP_SYS_CFLAGS) $(LIBRARY_SRC_CFLAGS) CXXFLAGS = $($(BUILD_CLASS)_CXXFLAGS) $(POSIX_CXXFLAGS) $(OPT_CXXFLAGS)\ $(DEBUG_CXXFLAGS) $(PIPE_CFLAGS) $(WARN_CXXFLAGS) $(TARGET_CXXFLAGS)\ $(USR_CXXFLAGS) $(CMD_CXXFLAGS) $(ARCH_DEP_CXXFLAGS) $(CODE_CXXFLAGS)\ $(STATIC_CXXCFLAGS) $(OP_SYS_CXXFLAGS) $(LIBRARY_SRC_CFLAGS) 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) LDLIBS = $(POSIX_LDLIBS) $(ARCH_DEP_LDLIBS) $(DEBUG_LDLIBS) $(OP_SYS_LDLIBS)\ $(GNU_LDLIBS_$(GNU)) CPPFLAGS = $($(BUILD_CLASS)_CPPFLAGS) $(POSIX_CPPFLAGS) $(OPT_CPPFLAGS)\ $(DEBUG_CPPFLAGS) $(WARN_CPPFLAGS) $(BASE_CPPFLAGS) $(TARGET_CPPFLAGS)\ $(USR_CPPFLAGS) $(CMD_CPPFLAGS) $(ARCH_DEP_CPPFLAGS) $(OP_SYS_CPPFLAGS)\ $(OP_SYS_INCLUDE_CPPFLAGS) $(CODE_CPPFLAGS) #-------------------------------------------------- # ar definition default ARFLAGS = ARCMD = $(AR) $(ARFLAGS) $(USR_ARFLAGS) $(CMD_ARFLAGS) $@ $(LIBRARY_LD_OBJS) #-------------------------------------------------- # 'Munch' link-edit MUNCH_CMD = $(LD) $(MUNCH_LDFLAGS) -o $@ $^ #-------------------------------------------------- # LEX default options # # Allow 8-bit characters LEXOPT += -8 # Generate an "interactive" scanner, solves problems at EOF. LEXOPT += -I #-------------------------------------------------- # Build compile line here COMPILE.c = $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) COMPILE.cpp = $(CCC) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) #-------------------------------------------------- # C preprocessor command PREPROCESS.cpp = $(CPP) $(CPPFLAGS) $(INCLUDES) $< > $@ #-------------------------------------------------- # genVersion header defaults # C macro name GENVERSIONMACRO = VCSVERSION # C macro default value (empty to use date+time) GENVERSIONDEFAULT = #-------------------------------------------------- # Header dependency file generation HDEPENDS_METHOD = MKMF HDEPENDS_MKMFFLAGS = -m $@ $(INCLUDES) $*$(OBJ) HDEPENDS_MKMF.c = $(MKMF) $(HDEPENDS_FLAGS) $(HDEPENDS_MKMFFLAGS) HDEPENDS_MKMF.cpp = $(MKMF) $(HDEPENDS_FLAGS) $(HDEPENDS_MKMFFLAGS) HDEPENDS_COMP.c = $(COMPILE.c) $(HDEPENDS_COMPFLAGS) $(HDEPENDS_ARCHFLAGS) HDEPENDS_COMP.cpp = $(COMPILE.cpp) $(HDEPENDS_COMPFLAGS) $(HDEPENDS_ARCHFLAGS) HDEPENDS.c = $(HDEPENDS_$(HDEPENDS_METHOD).c) HDEPENDS.cpp = $(HDEPENDS_$(HDEPENDS_METHOD).cpp) #-------------------------------------------------- # Dependency files TARGET_SRCS = $(foreach name, \ $(TESTPROD) $(PROD) $(TESTLIBRARY) $(LIBRARY) $(LOADABLE_LIBRARY), \ $($(name)_SRCS)) SRC_FILES = $(LIB_SRCS) $(LIBSRCS) $(SRCS) $(USR_SRCS) $(PROD_SRCS) $(TARGET_SRCS) HDEPENDS_FILES = $(addsuffix $(DEP),$(notdir $(basename $(SRC_FILES)))) #-------------------------------------------------- # Deprecated and no longer used in Base PATH_FILTER = $(1)$(warning PATH_FILTER is deprecated; used for $(1)) #--------------------------------------------------------------- # Names of installed items # # each list starts with the destination directory name(s) # to make sure it's there INSTALL_PROD = $(PRODNAME:%= $(INSTALL_BIN)/%) INSTALL_LIBS = $(LIBNAME:%=$(INSTALL_LIB)/%) INSTALL_MUNCHS = $(MUNCHNAME:%=$(INSTALL_BIN)/%) INSTALL_SHRLIBS = $(SHRLIBNAME:%=$(INSTALL_SHRLIB)/%) INSTALL_LOADABLE_SHRLIBS = $(LOADABLE_SHRLIBNAME:%=$(INSTALL_SHRLIB)/%) INSTALL_DLLSTUB_LIBS = $(DLLSTUB_LIBNAME:%=$(INSTALL_LIB)/%) INSTALL_TCLLIBS = $(TCLLIBNAME:%=$(INSTALL_TCLLIB)/%) INSTALL_TCLINDEX = $(TCLINDEX:%=$(INSTALL_TCLLIB)/%) INSTALL_SCRIPTS = $(SCRIPTS:%= $(INSTALL_BIN)/%) INSTALL_OBJS = $(OBJSNAME:%= $(INSTALL_BIN)/%) INSTALL_DOCS = $(DOCS:%= $(INSTALL_DOC)/%) INSTALL_HTMLS = $(HTMLS:%= $(INSTALL_HTML)/$(HTMLS_DIR)/%) INSTALL_TEMPLATE = $(addprefix $(INSTALL_TEMPLATES_SUBDIR)/, \ $(subst $(CONFIG),top/configure,$(TEMPLATES))) INSTALL_CONFIGS = $(CONFIGS:%= $(INSTALL_CONFIG)/%) INSTALL_BIN_INSTALLS = $(addprefix $(INSTALL_BIN)/,$(notdir $(BIN_INSTALLS))) INSTALL_LIB_INSTALLS = $(addprefix $(INSTALL_LIB)/,$(notdir $(LIB_INSTALLS))) #--------------------------------------------------------------- # Installed file permissions BIN_PERMISSIONS = 555 LIB_PERMISSIONS = 444 SHRLIB_PERMISSIONS = 555 INSTALL_PERMISSIONS = 444 #--------------------------------------------------------------- # # auto determine the directory paths that things are installed to # RULES: # 0) found in any one of several compiler specific paths # => install to $(INSTALL_INCLUDE)/compiler/$(CMPLR_CLASS) # 1) not found in (0) and found in any one of several OS specific paths # => install to $(INSTALL_INCLUDE)/os/$(OS_CLASS) # 2) not found in (1) and found in generic paths # => install to $(INSTALL_INCLUDE) # 3) not found in (1) or (2) then may be (not yet) computer generated # => install into $(INSTALL_INCLUDE)/os/$(OS_CLASS) and let # build rules work on vpath # # These rules guarantee that the users include from # no more than three directories # INSTALL_INC += $(foreach inc, $(INC), \ $(firstword \ $(CMPLR_INSTALL_INC) \ $(OS_INSTALL_INC) \ $(GENERIC_INSTALL_INC) \ $(GENERATED_INSTALL_INC) ) ) INSTALL_INC += $(addprefix $(INSTALL_INCLUDE)/os/$(OS_CLASS)/, $(INC_$(OS_CLASS)) ) # # Rule 0 # CMPLR_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/compiler/$(CMPLR_CLASS)/, $(INSTALL_INC_jjj) ) INSTALL_INC_jjj = $(foreach dir, $(CMPLR_SRC_DIRS), $(INSTALL_INC_iii) ) INSTALL_INC_iii = $(subst $(dir)/, , $(INSTALL_INC_hhh) ) INSTALL_INC_hhh = $(wildcard $(addsuffix /$(inc), $(dir)) ) # # Rule 1 # OS_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/os/$(OS_CLASS)/, $(INSTALL_INC_ggg) ) INSTALL_INC_ggg = $(foreach dir, $(OS_SRC_DIRS), $(INSTALL_INC_fff) ) INSTALL_INC_fff = $(subst $(dir)/, , $(INSTALL_INC_eee) ) INSTALL_INC_eee = $(wildcard $(addsuffix /$(inc), $(dir)) ) # # Rule 2 # GENERIC_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/, $(INSTALL_INC_ccc) ) INSTALL_INC_ccc = $(foreach dir, .. $(SRC_DIRS), $(INSTALL_INC_bbb) ) INSTALL_INC_bbb = $(subst $(dir)/, , $(INSTALL_INC_aaa) ) INSTALL_INC_aaa = $(wildcard $(addsuffix /$(inc), $(dir)) ) # # Rule 3 # GENERATED_INSTALL_INC = $(INSTALL_INCLUDE)/$(inc) COMMON_INC += $(filter $(COMMON_DIR)/%, $(foreach file, $(INC), \ $(firstword $(SOURCE_INC) $(COMMON_DIR)/$(file) ) ) ) SOURCE_INC = $(wildcard $(file) $(SOURCE_INC_bbb) ) SOURCE_INC_bbb = $(foreach dir, $(ALL_SRC_DIRS), $(SOURCE_INC_aaa) ) SOURCE_INC_aaa = $(addsuffix /$(file), $(dir) ) endif base-7.0.3.1/configure/CONFIG_DATABASE_MODULE0000664000577000060420000000242113557101274016554 0ustar anjaesctl#************************************************************************* # Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # Set EPICS_DATABASE if necessary ifndef EPICS_DATABASE EPICS_DATABASE = $(if $(BUILDING_DATABASE),$(INSTALL_LOCATION),$(EPICS_BASE)) # Paths to tools built here EPICS_DATABASE_HOST_BIN = $(EPICS_DATABASE)/bin/$(EPICS_HOST_ARCH) endif # Set location of locally-built tools MAKEBPT = $(EPICS_DATABASE_HOST_BIN)/makeBpt$(HOSTEXE) DBEXPAND = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdExpand.pl DBTORECORDTYPEH = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdToRecordtypeH.pl DBTOMENUH = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdToMenuH.pl DBDTOHTML = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdToHtml.pl REGISTERRECORDDEVICEDRIVER = $(PERL) $(EPICS_DATABASE_HOST_BIN)/registerRecordDeviceDriver.pl MSI3_15 = $(EPICS_DATABASE_HOST_BIN)/msi$(HOSTEXE) # Libraries needed to link a basic IOC EPICS_BASE_IOC_LIBS = dbRecStd dbCore ca Com base-7.0.3.1/configure/CONFIG_DATABASE_VERSION0000664000577000060420000000055713557101274016724 0ustar anjaesctl# Version number for the database APIs and shared library EPICS_DATABASE_MAJOR_VERSION = 3 EPICS_DATABASE_MINOR_VERSION = 17 EPICS_DATABASE_MAINTENANCE_VERSION = 5 # Development flag, set to zero for release versions EPICS_DATABASE_DEVELOPMENT_FLAG = 0 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 base-7.0.3.1/configure/CONFIG_ENV0000664000577000060420000000336513557101274015003 0ustar anjaesctl#************************************************************************* # 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. # 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. #************************************************************************* # Author: Andrew Johnson # Date: 20 April 1995 # # Experimental Physics and Industrial Control System (EPICS) # # CONFIG_ENV - EPICS Environment Parameter configuration file # # This file is read by the script base/src/libCom/env/bldEnvdata.pl # Variable definitions must take the form # VAR = VALUE # or # VAR = "Value containing spaces" # with each one on its own line. # Enclosing spaces and "" will be trimmed. # # Default environment settings # Channel Access: # Details are in the CA reference manual # (see CAref.html in this distribution) EPICS_CA_ADDR_LIST="" EPICS_CA_AUTO_ADDR_LIST=YES EPICS_CA_NAME_SERVERS="" EPICS_CA_CONN_TMO=30.0 EPICS_CA_REPEATER_PORT=5065 EPICS_CA_SERVER_PORT=5064 EPICS_CA_MAX_ARRAY_BYTES=16384 EPICS_CA_AUTO_ARRAY_BYTES=YES EPICS_CA_BEACON_PERIOD=15.0 EPICS_CA_MAX_SEARCH_PERIOD=300.0 EPICS_CA_MCAST_TTL=1 EPICS_CAS_BEACON_PERIOD= EPICS_CAS_BEACON_PORT= EPICS_CAS_AUTO_BEACON_ADDR_LIST="" EPICS_CAS_BEACON_ADDR_LIST="" EPICS_CAS_SERVER_PORT= EPICS_CAS_INTF_ADDR_LIST="" EPICS_CAS_IGNORE_ADDR_LIST="" # Servers to disable EPICS_IOC_IGNORE_SERVERS="" # Log Server: # EPICS_IOC_LOG_PORT Log server port number etc. EPICS_IOC_LOG_PORT=7004 # Other services: EPICS_CMD_PROTO_PORT= EPICS_AR_PORT=7002 base-7.0.3.1/configure/CONFIG_FILE_TYPE0000664000577000060420000000464613557101274015736 0ustar anjaesctl#************************************************************************* # Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* # # Macros and rules to create a new installation file type # # -------------------------------------------------------------- # Module developers can now define a new type of file, e.g. ABC, # so that files of type ABC will be installed into a directory # defined by INSTALL_ABC. This is done by creating a new CONFIG # file, e.g. CONFIG_ABC, with the following lines: # # FILE_TYPE += ABC # INSTALL_ABC = $(INSTALL_LOCATION)/abc # # The INSTALL_ABC directory should be be a subdirectory of # $(INSTALL_LOCATION). The file type ABC should be target # architecture independent (alh files, medm files, edm files). # # Files of type ABC are then installed into the INSTALL_ABC # directory by adding a line like the following to a Makefile. # # ABC += # # Rules necessary to create files of type ABC should be put in # a RULES_ABC file. Variables used by those rules should appear # in a CONFIG_ABC file. # # The module developer installs new CONFIG* or RULES* files # into the directory $(INSTALL_LOCATION)/cfg by including the # following Makefile line: # # CFG += CONFIG_ABC RULES_ABC # # CONFIG and RULES files in the $(INSTALL_LOCATION)/cfg directory # are included by the Base config files so their definitions and # rules are available for use by later src directory Makefiles in # the same module, or by other modules with a RELEASE line that # points to the TOP of the module providing these files. FILE_TYPE += ADL INSTALL_ADL = $(INSTALL_LOCATION)/adl FILE_TYPE += ALH INSTALL_ALH = $(INSTALL_LOCATION)/alh FILE_TYPE += CFG INSTALL_CFG = $(INSTALL_LOCATION)/cfg FILE_TYPE += EDL INSTALL_EDL = $(INSTALL_LOCATION)/edl FILE_TYPE += PERL_MODULES INSTALL_PERL_MODULES = $(INSTALL_LOCATION_LIB)/perl FILE_TYPE += PKGCONFIG INSTALL_PKGCONFIG = $(INSTALL_LOCATION_LIB)/pkgconfig INSTALLS_CFG = $(CFG:%= $(INSTALL_CFG)/%) DIRECTORY_TARGETS += $(foreach type, $(FILE_TYPE),$(INSTALL_$(type))) base-7.0.3.1/configure/CONFIG_LIBCOM_MODULE0000664000577000060420000000150013557101274016352 0ustar anjaesctl#************************************************************************* # Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # Set location of locally generated tools YACC = $(abspath $(EPICS_BASE)/bin/$(EPICS_HOST_ARCH))/antelope$(HOSTEXE) LEX = $(abspath $(EPICS_BASE)/bin/$(EPICS_HOST_ARCH))/e_flex$(HOSTEXE) \ -S$(EPICS_BASE)/include/flex.skel.static # Default stack size for osiThread OSITHREAD_USE_DEFAULT_STACK = NO OSITHREAD_DEFAULT_STACK_FLAGS_YES = -DOSITHREAD_USE_DEFAULT_STACK BASE_CPPFLAGS += $(OSITHREAD_DEFAULT_STACK_FLAGS_$(OSITHREAD_USE_DEFAULT_STACK)) base-7.0.3.1/configure/CONFIG_LIBCOM_VERSION0000664000577000060420000000054513557101274016522 0ustar anjaesctl# Version number for the libcom APIs and shared library EPICS_LIBCOM_MAJOR_VERSION = 3 EPICS_LIBCOM_MINOR_VERSION = 17 EPICS_LIBCOM_MAINTENANCE_VERSION = 6 # Development flag, set to zero for release versions EPICS_LIBCOM_DEVELOPMENT_FLAG = 0 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 base-7.0.3.1/configure/CONFIG_SITE0000664000577000060420000001707213557101274015117 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* # # CONFIG_SITE - Global site configuration file # # The host architecture performing the build, in the form # -[-] # # Currently Supporting: # cygwin-x86 (cygwin compiler used for host builds) # cygwin-x86_64 (cygwin compiler used for host builds) # darwin-ppc (PowerPC based Apple running OSX) # darwin-ppcx86 (Universal binaries for both CPUs) # darwin-x86 (Intel based Apple running OSX) # freebsd-x86 (GNU compiler used for host builds) # freebsd-x86_64 (GNU compiler used for host builds) # linux-arm (GNU compiler used for host builds) # linux-ppc (GNU compiler used for host builds) # linux-ppc64 (GNU compiler used for host builds) # linux-x86 (GNU compiler used for host builds) # linux-x86_64 (GNU compiler used for host builds) # solaris-sparc (Sun compiler used for host builds) # solaris-sparc-gnu (GNU compiler used for host builds) # solaris-sparc64 (Sun compiler used for host builds) # solaris-sparc64-gnu (GNU compiler used for host builds) # solaris-x86 (Sun compiler used for host builds) # solaris-x86-gnu (GNU compiler used for host builds) # solaris-x86_64 (Sun compiler used for host builds) # solaris-x86_64-gnu (GNU compiler used for host builds) # win32-x86 (MS Visual C++ compiler used for host builds) # win32-x86-mingw (MinGW compiler used for host builds) # win32-x86-static (MS Visual C++ compiler used for host builds) # windows-x64 (MS Visual C++ compiler used for host builds) # windows-x64-mingw (MinGW compiler used for host builds) # windows-x64-static (MS Visual C++ compiler used for host builds) # Debugging builds: # linux-arm-debug (GNU compiler used for host builds) # linux-x86-debug (GNU compiler with -g option for host builds) # linux-x86_64-debug (GNU compiler with -g option for host builds) # solaris-sparc-debug (sun compiler no optimization,-g for debugging info) # win32-x86-debug (MS Visual C++ compiler with debug option for host builds) # windows-x64-debug (MS Visual C++ compiler with debug option for host builds) # EPICS_HOST_ARCH is a required environment variable # Do not set EPICS_HOST_ARCH in this file. # Use base/startup files to set EPICS_HOST_ARCH or # provide EPICS_HOST_ARCH on the GNU make command line. # The cross-compiler architectures to build EPICS for # # Currently Supporting: # ios-arm (darwin-x86 host) # ios-386 (darwin-x86 host) # linux-386 (linux-x86 host) # linux-486 (linux-x86 host) # linux-586 (linux-x86 host) # linux-686 (linux-x86 host) # linux-arm (linux-x86 or -x86_64 host) # linux-arm_eb (linux-x86 host) # linux-arm_el (linux-x86 host) # linux-athlon (linux-x86 host) # linux-cris (Axis GNU crosscompiler on linux-x86 host) # linux-cris_v10 (Axis GNU crosscompiler on linux-x86 host) # linux-cris_v32 (Axis GNU crosscompiler on linux-x86 host) # linux-microblaze # linux-xscale_be # vxWorks-486 # vxWorks-68040 # vxWorks-68040lc # vxWorks-68060 # vxWorks-pentium # vxWorks-ppc32 (32-bit PowerPC CPUs with full FPU) # vxWorks-ppc32sf (32-bit PowerPC CPUs without FPU) # vxWorks-ppc603 # vxWorks-ppc603_long # vxWorks-ppc604 # vxWorks-ppc604_long # vxWorks-ppc604_altivec # vxWorks-mpc8540 # vxWorks-mpc8548 # RTEMS-at91rm9200ek # RTEMS-beatnik # RTEMS-gen68360 # RTEMS-mcp750 # RTEMS-mvme167 # RTEMS-mvme2100 # RTEMS-mvme2700 # RTEMS-mvme3100 # RTEMS-mvme5500 # RTEMS-pc386 # RTEMS-psim # RTEMS-uC5282 # win32-x86-mingw (linux-x86 or -x86_64 host) # # Which target architectures to cross-compile for. # Definitions in configure/os/CONFIG_SITE..Common # may override this setting. CROSS_COMPILER_TARGET_ARCHS= #CROSS_COMPILER_TARGET_ARCHS=vxWorks-ppc32 # If only some of your host architectures can compile the # above CROSS_COMPILER_TARGET_ARCHS specify those host # architectures here. If the combination is complicated, # set CROSS_COMPILER_TARGET_ARCHS in the appropriate # configure/os/CONFIG_SITE..Common files instead. CROSS_COMPILER_HOST_ARCHS= # The 'runtests', 'tapfiles' and 'junitfiles' make targets normally only run # self-tests for the EPICS_HOST_ARCH architecture. If the host can execute # the self-test programs for any other cross-built architectures such as # a -debug architecture, those architectures must be named in this variable: CROSS_COMPILER_RUNTEST_ARCHS= # Build shared libraries (DLLs on Windows). # Must be either YES or NO. Definitions in the target-specific # os/CONFIG.Common. and os/CONFIG_SITE.Common. files may # override this setting. On Windows only these combinations are valid: # SHARED_LIBRARIES = YES and STATIC_BUILD = NO # SHARED_LIBRARIES = NO and STATIC_BUILD = YES SHARED_LIBRARIES=YES # Build client objects statically. # Must be either YES or NO. STATIC_BUILD=NO # Host build optimization. # Must be either YES or NO. HOST_OPT=YES # Cross build optimization. # Must be either YES or NO. CROSS_OPT=YES # Generate verbose compiler warnings for host builds. # Must be either YES or NO. HOST_WARN=YES # Generate verbose compiler warnings for cross-compiled builds. # Must be either YES or NO. CROSS_WARN=YES # Installation directory, if you want Base to be installed into a # different location then uncomment and set this. #INSTALL_LOCATION= # Use POSIX thread priority scheduling (if available). # Must be either YES or NO USE_POSIX_THREAD_PRIORITY_SCHEDULING = YES # Site version number, if set will append '-' and this string to the # EPICS version number string that is reported by many tools. EPICS_SITE_VERSION = # For GNU compiler, use pipes rather than temporary files for # communication between the various stages of compilation. # Must be either YES or NO GCC_PIPE = NO # Set RPATH when linking executables and libraries. # Must be either YES, NO, or ORIGIN. If you set this to NO you must also provide a # way for Base executables to find their shared libraries when they are # run at build-time, e.g. set the LD_LIBRARY_PATH environment variable. # ORIGIN is a feature of the ELF executable format used by Linux, freebsd, and solaris. LINKER_USE_RPATH = YES # Only used when LINKER_USE_RPATH=ORIGIN # The build time root(s) of the relocatable tree (separate multiple w/ ':'). # Linking to libraries under any root directory will be relative. # Linking to libraries outside of this root will be absolute. # All root directories are considered to be the same. LINKER_ORIGIN_ROOT = $(INSTALL_LOCATION) # Overrides for the settings above may appear in a CONFIG_SITE.local file -include $(CONFIG)/CONFIG_SITE.local base-7.0.3.1/configure/CONFIG_SITE_ENV0000664000577000060420000000571713557101274015632 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # Author: Andrew Johnson # Date: 1 May 1995 # # Experimental Physics and Industrial Control System (EPICS) # # CONFIG_SITE_ENV - EPICS Environment Parameter Site configuration file # # This file is read by the script base/src/libCom/env/bldEnvData.pl # Variable definitions must take the form # VAR = VALUE # or # VAR = "Value containing spaces" # with each one on its own line. # Enclosing spaces and "" will be trimmed. # # Site-specific environment settings ## Time service: # EPICS_TZ # Local timezone rules for vxWorks and RTEMS. The value follows the Posix # TZ environment variable's Mm.n.d/h format (see the IBM link below for # details). If TZ hasn't already been set when the osdTime timeRegister() # C++ static constructor runs, this parameter will be copied into the TZ # environment variable. Once the OS clock has been synchronized to NTP the # routine tz2timezone() will be run to convert TZ into the TIMEZONE # variable format that VxWorks needs. # https://developer.ibm.com/articles/au-aix-posix/ # Japan Standard Time, no DST: #EPICS_TZ = "JST-9" # Central European (Summer) Time: #EPICS_TZ = "CET-1CEST,M3.5.0/2,M10.5.0/3" # Greenwich Mean/British Summer Time: #EPICS_TZ = "GMT0BST,M3.5.0/1,M10.5.0/2" # US Eastern Standard/Daylight Time: #EPICS_TZ = "EST5EDT,M3.2.0/2,M11.1.0/2" # US Central Standard/Daylight Time: EPICS_TZ = "CST6CDT,M3.2.0/2,M11.1.0/2" # US Mountain Standard/Daylight Time: #EPICS_TZ = "MST7MDT,M3.2.0/2,M11.1.0/2" # US Pacific Standard/Daylight Time: #EPICS_TZ = "PST8PDT,M3.2.0/2,M11.1.0/2" # US Hawaiian Standard Time, no DST: #EPICS_TZ = "HST10" # EPICS_TS_NTP_INET # NTP time server ip address for VxWorks and RTEMS. # IOC will use its boot host if this is not set. EPICS_TS_NTP_INET= # IOC Shell: # IOCSH_PS1 # Prompt string # IOCSH_HISTSIZE # Number of lines of command history to keep. # IOCSH_HISTEDIT_DISABLE # Prevents use of readline or equivalent if defined. IOCSH_PS1="epics> " IOCSH_HISTSIZE=50 IOCSH_HISTEDIT_DISABLE= # Log Server: # EPICS_IOC_LOG_INET # Log server ip addr. # EPICS_IOC_LOG_FILE_NAME # pathname to the log file. # EPICS_IOC_LOG_FILE_LIMIT # maximum log file size. # EPICS_IOC_LOG_FILE_COMMAND # A shell command string used to obtain a new # path name in response to SIGHUP - the new path name will # replace any path name supplied in EPICS_IOC_LOG_FILE_NAME EPICS_IOC_LOG_INET= EPICS_IOC_LOG_FILE_NAME= EPICS_IOC_LOG_FILE_COMMAND= EPICS_IOC_LOG_FILE_LIMIT=1000000 base-7.0.3.1/configure/Makefile0000664000577000060420000000176313557101274015043 0ustar anjaesctl#************************************************************************* # Copyright (c) 2002 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* TOP=.. include $(TOP)/configure/CONFIG # Bootstrap resolution: tools not installed yet TOOLS = $(TOP)/src/tools CONFIGS += $(subst ../,,$(wildcard ../CONFIG*)) CONFIGS += $(subst ../,,$(wildcard ../os/CONFIG*)) CONFIGS += $(subst ../,,$(wildcard ../RELEASE*)) CONFIGS += $(subst ../,,$(wildcard ../RULES*)) CFG += CONFIG_LIBCOM_MODULE CFG += CONFIG_LIBCOM_VERSION CFG += CONFIG_CA_MODULE CFG += CONFIG_CA_VERSION CFG += CONFIG_DATABASE_MODULE CFG += CONFIG_DATABASE_VERSION include $(TOP)/configure/RULES base-7.0.3.1/configure/RELEASE0000664000577000060420000000150413557101274014377 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* # # RELEASE: Define the location of external EPICS products # # The version of this file in Base should normally be empty. # # Define INSTALL_LOCATION in CONFIG_SITE # NB: Settings in RELEASE files can be overridden in files named # RELEASE.$(EPICS_HOST_ARCH).Common # RELEASE.Common.$(T_A) # RELEASE.$(EPICS_HOST_ARCH).$(T_A) base-7.0.3.1/configure/RULES0000664000577000060420000000113713557101274014213 0ustar anjaesctl#************************************************************************* # Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* ifndef T_A include $(CONFIG)/RULES_ARCHS else #T_A include $(CONFIG)/RULES_BUILD endif # T_A defined base-7.0.3.1/configure/RULES.Db0000664000577000060420000003763013557101274014546 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* # RULES.Db # Set db substitutions and template file suffixes SUBST_SUFFIX ?= .substitutions TEMPL_SUFFIX ?= .template #--------------------------------------------------------------- # vpath vpath %.pm $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD)) vpath %.pod $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD)) vpath %.dbd $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD)) vpath %.db $(USR_VPATH) $(SRC_DIRS) $(dir $(DB)) vpath %.vdb $(USR_VPATH) $(SRC_DIRS) $(dir $(DB)) vpath %$(SUBST_SUFFIX) $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR) vpath %$(TEMPL_SUFFIX) $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR) vpath bpt%.data $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR) vpath %.acf $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR) vpath %.acs $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR) #--------------------------------------------------------------- # dbflags dbdflags DBD_SEARCH_DIRS = . .. $(COMMON_DIR) $(SRC_DIRS) $(INSTALL_DBD) $(RELEASE_DBD_DIRS) DB_SEARCH_DIRS = . .. $(COMMON_DIR) $(SRC_DIRS) $(INSTALL_DB) $(RELEASE_DB_DIRS) DBDFLAGS = $(USR_DBDFLAGS) $(CMD_DBDFLAGS) $(addprefix -I,$(DBD_SEARCH_DIRS)) DBFLAGS = $($*_DBFLAGS) $(USR_DBFLAGS) $(CMD_DBFLAGS) $(addprefix -I,$(DB_SEARCH_DIRS)) REGRDDFLAGS = $(DBDFLAGS) $($*_REGRDDFLAGS) $(USR_REGRDDFLAGS) $(CMD_REGRDDFLAGS) #--------------------------------------------------------------- # Targets # --------------------------------------------------- # To allow os specific dbd files AND have the -j option work properly, CROSS_TARGET_OS_TYPES = $(sort $(foreach target, \ $(EPICS_HOST_ARCH) $(CROSS_COMPILER_TARGET_ARCHS), \ $(firstword $(subst -, ,$(target))))) DBD += $(foreach type, $(CROSS_TARGET_OS_TYPES), $(DBD_$(type))) # Users add os specific dbd files to a Makefile as follows # # DBD_vxWorks += abcVx.dbd # DBD_RTEMS += abcRTEMS.dbd # DBD_solaris += abcSolaris.dbd # # --------------------------------------------------- # DBD concatination files COMMON_DBDCATS += $(addprefix $(COMMON_DIR)/,$(DBDCAT)) DBDCAT_SOURCES += $(foreach file, $($*_DBD), $(DBDCAT_SOURCE) ) DBDCAT_SOURCE = $(firstword $(wildcard $(file) $(foreach dir, \ $(DBD_SEARCH_DIRS),$(addsuffix /$(file),$(dir)))) \ $(COMMON_DIR)/$(file)) DBDCAT_COMMAND = $(if $(DBDCAT_SOURCES),\ $(CAT) $(DBDCAT_SOURCES) > $(notdir $@),\ @echo "No input files for $(notdir $@)") INSTALL_DBDS += $(addprefix $(INSTALL_DBD)/,$(DBDCAT)) # --------------------------------------------------- DBDINC_NAME = $(patsubst %.h,%,$(patsubst %.dbd,%,$(DBDINC))) DBD += $(addsuffix .dbd,$(DBDINC_NAME)) INC += $(addsuffix .h,$(DBDINC_NAME)) INSTALL_DBDS += $(addprefix $(INSTALL_DBD)/,$(notdir $(DBD))) COMMON_DBDS += $(filter $(COMMON_DIR)/%, $(foreach file, $(DBD), \ $(firstword $(SOURCE_DBD) $(COMMON_DIR)/$(file) ) ) ) SOURCE_DBD = $(wildcard $(file) $(SOURCE_DBD_bbb) ) SOURCE_DBD_bbb = $(foreach dir, $(GENERIC_SRC_DIRS), $(SOURCE_DBD_aaa) ) SOURCE_DBD_aaa = $(addsuffix /$(file), $(dir) ) INSTALL_DBS += $(addprefix $(INSTALL_DB)/,$(notdir $(DB))) COMMON_DBS += $(filter $(COMMON_DIR)/%, $(foreach file, $(DB), \ $(firstword $(SOURCE_DB) $(COMMON_DIR)/$(file) ) ) ) SOURCE_DB = $(wildcard $(file) $(SOURCE_DB_bbb) ) SOURCE_DB_bbb = $(foreach dir, $(GENERIC_SRC_DIRS), $(SOURCE_DB_aaa) ) SOURCE_DB_aaa = $(addsuffix /$(file), $(dir) ) COMMONS = $(COMMON_DIR)/*.dbd $(COMMON_DIR)/*.db $(COMMON_DIR)/*.h \ $(COMMON_DIR)/*$(SUBST_SUFFIX) $(COMMON_DIR)/*$(TEMPL_SUFFIX) # Remove trailing numbers (to 99) on stem TEMPLATE1 = $(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%, \ $(patsubst %4,%,$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%, \ $(patsubst %8,%,$(patsubst %9,%,$*)))))))))) TEMPLATE2 = $(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%, \ $(patsubst %4,%,$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%, \ $(patsubst %8,%,$(patsubst %9,%,$(TEMPLATE1))))))))))) TEMPLATE3 = $(addsuffix $(TEMPL_SUFFIX),$(addprefix ../,$(TEMPLATE2))) TEMPLATE_FILENAME = $(firstword $(wildcard $($*_TEMPLATE) \ $(addprefix ../,$($*_TEMPLATE)) ../$*$(TEMPL_SUFFIX) $(TEMPLATE3) \ ../template)) INSTALL_DB_INSTALLS = $(addprefix $(INSTALL_DB)/,$(notdir $(DB_INSTALLS))) INSTALL_DBD_INSTALLS = $(addprefix $(INSTALL_DBD)/,$(notdir $(DBD_INSTALLS))) COMMONDEP_TARGET = $(COMMON_DIR)/$(basename $@) #--------------------------------------------------------------- # acf files # An access security configuration file, *.acf, can be created from # an *.acs file (has format of acf file plus #include "filename" lines) # flags for GNU compiler ACF_CPPFLAGS_YES = -undef -nostdinc ACF_CPPFLAGS = $(ACF_CPPFLAGS_$(GNU)) ACF_INCLUDES = -I. $(TARGET_INCLUDES) $(USR_INCLUDES)\ $(SRC_INCLUDES) -I$(INSTALL_DB) ACFDEPENDS_CMD = $(MKMF) -m $@ $(ACF_INCLUDES) $(COMMONDEP_TARGET) $< ACF_CMD = $(CPP) $(ACF_CPPFLAGS) $(ACF_INCLUDES) $< > $@ #--------------------------------------------------------------- # dependencies HINC += $(addsuffix .h,$(DBDINC_NAME)) COMMON_DBDINC += $(addprefix $(COMMON_DIR)/,$(HINC)) DBDDEPENDS_FILES += $(addsuffix $(DEP),$(HINC) \ $(patsubst $(COMMON_DIR)/%,%,$(COMMON_DBS)) \ $(patsubst $(COMMON_DIR)/%,%, \ $(filter-out $(COMMON_DIR)/bpt%.dbd,$(COMMON_DBDS)))) #--------------------------------------------------------------- ifndef T_A DEP = .d TEMPLATE3 += $(addsuffix $(TEMPL_SUFFIX), $(TEMPLATE2)) COMMON_DIR = . INSTALL_DBDS = INSTALL_DBS = COMMON_DBDS = $(DBD) COMMON_DBS = $(DB) COMMONS = $(DBD) $(DB) ACTIONS = inc ACTIONS += build ACTIONS += install ACTIONS += buildInstall ACTIONS += runtests tapfiles clean-tests test-results junitfiles actionArchTargets = $(foreach action, $(ACTIONS), \ $(foreach arch, $(BUILD_ARCHS), $(action)$(DIVIDER)$(arch))) cleanArchTargets = $(foreach arch, $(BUILD_ARCHS), clean$(DIVIDER)$(arch)) include $(CONFIG)/CONFIG_APP_INCLUDE all: install install: buildInstall buildInstall: build rebuild: clean install .PHONY: all $(ACTIONS) $(actionArchTargets) $(BUILD_ARCHS): install $(cleanArchTargets): clean .PHONY: $(BUILD_ARCHS) $(actionArchTargets) $(cleanArchTargets) else # T_A is defined ifeq ($(EPICS_HOST_ARCH),$(T_A)) host: install else host: endif .PHONY: host endif # T_A ifneq (,$(strip $(DBDDEPENDS_FILES))) -include $(DBDDEPENDS_FILES) endif #--------------------------------------------------------------- # build dependancies, clean rule inc: $(COMMON_INC) $(INSTALL_INC) build: $(COMMON_DBDS) $(COMMON_DBS) $(COMMON_DBDCATS) \ $(INSTALL_DBDS) $(INSTALL_DBS) \ $(DBDDEPENDS_FILES) $(TARGETS) \ $(INSTALL_DB_INSTALLS) $(INSTALL_DBD_INSTALLS) clean: db_clean db_clean: @$(RM) $(COMMONS) $(DBDDEPENDS_FILES) @$(RM) *_registerRecordDeviceDriver.cpp @$(RM) $(TARGETS) .PHONY: db_clean realclean: clean #--------------------------------------------------------------- # Dependency files %Record.h$(DEP): $(COMMON_DIR)/%Record.dbd @$(RM) $@ @$(DBTORECORDTYPEH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ %Record.h$(DEP): %Record.dbd @$(RM) $@ @$(DBTORECORDTYPEH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ %Record.h$(DEP): ../%Record.dbd @$(RM) $@ @$(DBTORECORDTYPEH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ menu%.h$(DEP): $(COMMON_DIR)/menu%.dbd @$(RM) $@ @$(DBTOMENUH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ menu%.h$(DEP): menu%.dbd @$(RM) $@ @$(DBTOMENUH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ menu%.h$(DEP): ../menu%.dbd @$(RM) $@ @$(DBTOMENUH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ %.dbd$(DEP): %.dbd.pod @$(RM) $@ @echo "$(COMMONDEP_TARGET): ../Makefile" > $@ %.dbd$(DEP): %Include.dbd @$(RM) $@ @$(DBEXPAND) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ %.dbd$(DEP): ../%Include.dbd @$(RM) $@ @$(DBEXPAND) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ %.dbd$(DEP): @$(RM) $@ @$(DBEXPAND) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $($*_DBD) > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ %.db$(DEP): %$(SUBST_SUFFIX) @$(RM) $@ $(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) -S$< $(TEMPLATE_FILENAME) > $@ %.db$(DEP): ../%$(SUBST_SUFFIX) @$(RM) $@ $(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) -S$< $(TEMPLATE_FILENAME) > $@ %.db$(DEP): %$(TEMPL_SUFFIX) @$(RM) $@ $(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) $< > $@ %.db$(DEP): ../%$(TEMPL_SUFFIX) @$(RM) $@ $(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) $< > $@ %.acf$(DEP): %.acs @$(RM) $@ @$(ACFDEPENDS_CMD) %.acf$(DEP): ../%.acs @$(RM) $@ @$(ACFDEPENDS_CMD) .PRECIOUS: %$(DEP) #--------------------------------------------------------------- # Substitution files # WARNING: CREATESUBSTITUTIONS script needs output dir on command line ifdef CREATESUBSTITUTIONS $(COMMON_DIR)/%$(SUBST_SUFFIX): $(ECHO) "Create substitutions" @$(RM) $@ $(CREATESUBSTITUTIONS) $@ endif $(INSTALL_DB)/%$(SUBST_SUFFIX): %$(SUBST_SUFFIX) $(ECHO) "Installing substitution file $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) $(INSTALL_DB)/%$(SUBST_SUFFIX): ../%$(SUBST_SUFFIX) $(ECHO) "Installing substitution file $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) .PRECIOUS: $(COMMON_DIR)/%$(SUBST_SUFFIX) #--------------------------------------------------------------- # Template files $(INSTALL_DB)/%$(TEMPL_SUFFIX): %$(TEMPL_SUFFIX) $(ECHO) "Installing template file $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) $(INSTALL_DB)/%$(TEMPL_SUFFIX): ../%$(TEMPL_SUFFIX) $(ECHO) "Installing template file $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) .PRECIOUS: $(COMMON_DIR)/%$(TEMPL_SUFFIX) #--------------------------------------------------------------- # INC files $(COMMON_DIR)/%Record.h: $(COMMON_DIR)/%Record.dbd @$(RM) $(notdir $@) $(DBTORECORDTYPEH) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%Record.h: %Record.dbd @$(RM) $(notdir $@) $(DBTORECORDTYPEH) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%Record.h: ../%Record.dbd @$(RM) $(notdir $@) $(DBTORECORDTYPEH) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ $(COMMON_DIR)/menu%.h: $(COMMON_DIR)/menu%.dbd @$(RM) $(notdir $@) $(DBTOMENUH) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ $(COMMON_DIR)/menu%.h: menu%.dbd @$(RM) $(notdir $@) $(DBTOMENUH) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ $(COMMON_DIR)/menu%.h: ../menu%.dbd @$(RM) $(notdir $@) $(DBTOMENUH) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ .PRECIOUS: $(COMMON_DIR)/%.h #--------------------------------------------------------------- # DBD files $(COMMON_DIR)/bpt%.dbd: bpt%.data @$(RM) $(notdir $@) $(MAKEBPT) $< $(notdir $@) @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%.dbd: %.dbd.pod @$(RM) $(notdir $@) $(PERL) $(TOOLS)/podRemove.pl -o $(notdir $@) $< @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%.dbd: %Include.dbd $(ECHO) "Expanding dbd file $<" @$(RM) $(notdir $@) $(DBEXPAND) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%.dbd: ../%Include.dbd $(ECHO) "Expanding dbd file $<" @$(RM) $(notdir $@) $(DBEXPAND) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ # Make DBDCAT file x depend on x_DBD source files define DBDCAT_template $$(COMMON_DIR)/$(1).dbd: ../Makefile $$(foreach file, $$($(1)_DBD),$$(DBDCAT_SOURCE) ) endef $(foreach name,$(subst .dbd,,$(DBDCAT)), $(eval $(call DBDCAT_template,$(name)))) $(COMMON_DBDCATS):$(COMMON_DIR)/%.dbd: $(ECHO) "Creating dbd file $(notdir $@)" @$(RM) $(notdir $@) $(DBDCAT_COMMAND) @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%.dbd: $(ECHO) "Creating dbd file $(notdir $@)" @$(RM) $(notdir $@) $(DBEXPAND) $(DBDFLAGS) -o $(notdir $@) $($*_DBD) @$(MV) $(notdir $@) $@ $(INSTALL_DBD)/%: $(COMMON_DIR)/% $(ECHO) "Installing created dbd file $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) $(INSTALL_DBD)/%: % $(ECHO) "Installing dbd file $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) $(INSTALL_DBD)/%: ../% $(ECHO) "Installing dbd file $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) define DBD_INSTALLS_template $$(INSTALL_DBD)/$$(notdir $(1)): $(1) $(ECHO) "Installing $$@" @$$(INSTALL) -d -m $$(INSTALL_PERMISSIONS) $$^ $$(INSTALL_DBD) endef $(foreach file, $(DBD_INSTALLS), $(eval $(call DBD_INSTALLS_template, $(file)))) .PRECIOUS: $(COMMON_DBDS) $(COMMON_DIR)/%.dbd #--------------------------------------------------------------- # HTML files $(COMMON_DIR)/%.html: %.dbd.pod @$(RM) $(notdir $@) $(DBDTOHTML) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%.html: %.pod @$(RM) $(notdir $@) $(PODTOHTML) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%.html: %.pm @$(RM) $(notdir $@) $(PODTOHTML) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%.html: ../%.pm @$(RM) $(notdir $@) $(PODTOHTML) -s -o $(notdir $@) $< @$(MKDIR) $(dir $@) @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%.html: ../%.pl @$(RM) $(notdir $@) $(PODTOHTML) -s -o $(notdir $@) $< @$(MV) $(notdir $@) $@ .PRECIOUS: $(COMMON_DIR)/%.html %.html #--------------------------------------------------------------- # DB files $(COMMON_DIR)/%.db: $(COMMON_DIR)/%.edf $(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $*.VAR $< @$(REPLACEVAR) < $*.VAR > $@ @$(RM) $*.VAR $(COMMON_DIR)/%.db: %$(SUBST_SUFFIX) $(ECHO) "Inflating database from $< $(TEMPLATE_FILENAME)" @$(RM) $(notdir $@) $(MSI3_15) $(DBFLAGS) -o $(notdir $@) -S$< $(TEMPLATE_FILENAME) @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%.db: ../%$(SUBST_SUFFIX) $(ECHO) "Inflating database from $< $(TEMPLATE_FILENAME)" @$(RM) $(notdir $@) $(MSI3_15) $(DBFLAGS) -o $(notdir $@) -S$< $(TEMPLATE_FILENAME) @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%.db: %$(TEMPL_SUFFIX) $(ECHO) "Inflating database from $<" @$(RM) $(notdir $@) $(MSI3_15) $(DBFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%.db: ../%$(TEMPL_SUFFIX) $(ECHO) "Inflating database from $<" @$(RM) $(notdir $@) $(MSI3_15) $(DBFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ $(COMMON_DIR)/%.acf: %.acs $(ECHO) "Creating acf file $@" @$(RM) $@ $(ACF_CMD) $(COMMON_DIR)/%.acf: ../%.acs $(ECHO) "Creating acf file $@" @$(RM) $@ $(ACF_CMD) .PRECIOUS: $(COMMON_DIR)/%.acf $(INSTALL_DB)/%: % $(ECHO) "Installing $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) $(INSTALL_DB)/%: ../% $(ECHO) "Installing $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) $(INSTALL_DB)/%.db: $(COMMON_DIR)/%.db $(ECHO) "Installing created db file $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) define DB_INSTALLS_template $$(INSTALL_DB)/$$(notdir $(1)): $(1) $(ECHO) "Installing $$@" @$$(INSTALL) -d -m $$(INSTALL_PERMISSIONS) $$^ $$(INSTALL_DB) endef $(foreach file, $(DB_INSTALLS), $(eval $(call DB_INSTALLS_template, $(file)))) .PRECIOUS: $(COMMON_DIR)/%.edf .PRECIOUS: $(COMMON_DBS) #--------------------------------------------------------------- # register record,device,driver support %_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd @$(RM) $@ $(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ $< $(basename $@) $(IOCS_APPL_TOP) %_registerRecordDeviceDriver.cpp: %.dbd @$(RM) $@ $(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ $< $(basename $@) $(IOCS_APPL_TOP) %_registerRecordDeviceDriver.cpp: ../%.dbd @$(RM) $@ $(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ $< $(basename $@) $(IOCS_APPL_TOP) .PRECIOUS: %_registerRecordDeviceDriver.cpp base-7.0.3.1/configure/RULES.ioc0000664000577000060420000000253113557101274014763 0ustar anjaesctl#************************************************************************* # Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* # RULES.ioc include $(CONFIG)/RULES_DIRS build$(DIVIDER)$(ARCH) build: buildInstall install$(DIVIDER)$(ARCH) install: buildInstall $(ARCH): buildInstall ifeq ($(filter $(ARCH),$(BUILD_ARCHS)),$(strip $(ARCH))) buildInstall$(DIVIDER)$(ARCH) buildInstall: $(TARGETS) clean$(DIVIDER)$(ARCH) clean: $(RM) cdCommands envPaths dllPath.bat relPaths.sh else buildInstall$(DIVIDER)$(ARCH) buildInstall: clean$(DIVIDER)$(ARCH) clean: endif cdCommands dllPath.bat relPaths.sh: \ $(wildcard $(TOP)/configure/RELEASE*) \ $(wildcard $(TOP)/configure/CONFIG_SITE*) | $(INSTALL_BIN) $(CONVERTRELEASE) -a $(ARCH) -t $(IOCS_APPL_TOP) $@ envPaths: $(wildcard $(TOP)/configure/RELEASE*) \ $(wildcard $(TOP)/configure/CONFIG_SITE*) | $(INSTALL_BIN) $(CONVERTRELEASE) -t $(IOCS_APPL_TOP) $@ realclean: $(RM) cdCommands envPaths dllPath.bat relPaths.sh base-7.0.3.1/configure/RULES_ARCHS0000664000577000060420000000517113557101274015135 0ustar anjaesctl#************************************************************************* # Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* all: install host: install$(DIVIDER)$(EPICS_HOST_ARCH) ACTIONS = inc ACTIONS += build ACTIONS += install ACTIONS += buildInstall ACTIONS += runtests tapfiles clean-tests test-results junitfiles actionArchTargets = $(foreach action, $(ACTIONS), \ $(addprefix $(action)$(DIVIDER), $(BUILD_ARCHS))) cleanArchTargets = $(addprefix clean$(DIVIDER), $(BUILD_ARCHS)) buildDirs = $(addprefix O., $(BUILD_ARCHS)) # Include /cfg/DIR_RULES* files from tops defined in RELEASE* files # Do this here so they can add ACTIONS # RELEASE_CFG_DIR_RULES = $(foreach top, $(RELEASE_TOPS), \ $(wildcard $($(top))/cfg/DIR_RULES*)) ifneq ($(RELEASE_CFG_DIR_RULES),) include $(RELEASE_CFG_DIR_RULES) endif # Create EPICS_HOST_ARCH dependancies for GNU make -j option. # Needed in dirs where EPICS_HOST_ARCH build creates a tool used in # cross arch builds CROSS_ARCHS += $(CROSS1) $(CROSS2) define DEP_template $(2) : $(EPICS_HOST_ARCH) $(1)$(DIVIDER)$(2) : $(1)$(DIVIDER)$(EPICS_HOST_ARCH) O.$(2) endef $(foreach action, $(ACTIONS), \ $(foreach arch, $(CROSS_ARCHS), \ $(eval $(call DEP_template,$(action),$(arch))))) # Allows rebuild to work with parallel builds option, -j. ifeq (rebuild,$(filter rebuild,$(MAKECMDGOALS))) $(buildDirs) O.Common : clean rebuild : install endif archPart = $(word 2, $(subst $(DIVIDER), ,$@)) actionPart = $(word 1, $(subst $(DIVIDER), ,$@)) $(actionArchTargets) : $(buildDirs) O.Common $(MAKE) -C O.$(archPart) -f ../Makefile TOP=$(TOP)/.. \ T_A=$(archPart) $(actionPart) $(BUILD_ARCHS) : % : O.% O.Common $(MAKE) -C O.$@ -f ../Makefile TOP=$(TOP)/.. T_A=$@ $(ACTIONS) : % : $(foreach arch, $(BUILD_ARCHS), %$(DIVIDER)$(arch)) $(buildDirs): $(PERL) $(TOOLS)/makeMakefile.pl $@ $(TOP)/.. O.Common: $(MKDIR) O.Common # Clean rules # clean: archsCommonClean archsCommonClean: $(RMDIR) $(buildDirs) O.Common archclean: $(RMDIR) $(buildDirs) $(cleanArchTargets): $(RMDIR) O.$(archPart) realclean: $(RMDIR) O.* .PHONY : $(actionArchTargets) .PHONY : $(cleanArchTargets) .PHONY : $(BUILD_ARCHS) rebuild archsCommonClean .PHONY : $(ACTIONS) clean realclean archclean host all include $(CONFIG)/RULES_COMMON base-7.0.3.1/configure/RULES_BUILD0000664000577000060420000003724013557101274015136 0ustar anjaesctl#************************************************************************* # Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* # RULES_BUILD # Rules for making things specified in a Makefile # # CWD is O.$(T_A), but most sources are elsewhere ifndef BASE_RULES_BUILD BASE_RULES_BUILD=1 vpath %.c $(USR_VPATH) $(ALL_SRC_DIRS) vpath %.cc $(USR_VPATH) $(ALL_SRC_DIRS) 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 %.skel.static $(USR_VPATH) $(ALL_SRC_DIRS) vpath %.y $(USR_VPATH) $(ALL_SRC_DIRS) vpath %.l $(USR_VPATH) $(ALL_SRC_DIRS) #--------------------------------------------------------------- include $(CONFIG)/CONFIG_ADDONS #--------------------------------------------------------------- # Set PROD, TESTPROD, OBJS, and LIBRARY SCRIPTS_HOST += $(PERL_SCRIPTS) # PERL_SCRIPTS are installed into existing $(INSTALL_BIN) for Host systems ifeq ($(findstring Host,$(VALID_BUILDS)),Host) LIBRARY += $(LIBRARY_HOST) LOADABLE_LIBRARY += $(LOADABLE_LIBRARY_HOST) OBJS += $(OBJS_HOST) PROD += $(PROD_HOST) SCRIPTS += $(SCRIPTS_HOST) TARGETS += $(TARGETS_HOST) TESTLIBRARY += $(TESTLIBRARY_HOST) TESTSCRIPTS += $(TESTSCRIPTS_HOST) TESTPROD += $(TESTPROD_HOST) endif ifeq ($(findstring Ioc,$(VALID_BUILDS)),Ioc) LIBRARY += $(LIBRARY_IOC) LOADABLE_LIBRARY += $(LOADABLE_LIBRARY_IOC) OBJS += $(OBJS_IOC) PROD += $(PROD_IOC) SCRIPTS += $(SCRIPTS_IOC) TARGETS += $(TARGETS_IOC) TESTLIBRARY += $(TESTLIBRARY_IOC) TESTSCRIPTS += $(TESTSCRIPTS_IOC) TESTPROD += $(TESTPROD_IOC) endif #--------------------------------------------------------------- ifdef TEMPLATES_DIR INSTALL_TEMPLATES_SUBDIR = $(INSTALL_TEMPLATES)/$(TEMPLATES_DIR) else INSTALL_TEMPLATES_SUBDIR = $(INSTALL_TEMPLATES) endif HTMLS_DIR ?= . #--------------------------------------------------------------- # First target all: install ifeq ($(EPICS_HOST_ARCH),$(T_A)) host: install else # Do nothing host: endif include $(CONFIG)/RULES_FILE_TYPE include $(CONFIG)/RULES.Db #--------------------------------------------------------------- # Include defines and rules for prod, library and test* targets #ifneq (,$(strip $(PROD) $(TESTPROD) $(LIBRARY) $(TESTLIBRARY) $(LOADABLE_LIBRARY) )) include $(CONFIG)/RULES_TARGET #endif #--------------------------------------------------------------- # Read dependency files ifneq (,$(strip $(HDEPENDS_FILES))) $(filter-out $(wildcard *$(DEP)), $(HDEPENDS_FILES)): | $(COMMON_INC) -include $(HDEPENDS_FILES) endif #--------------------------------------------------------------- # Products and Object libraries # PRODTARGETS += $(PRODNAME) $(MUNCHNAME) $(CTDT_SRCS) $(CTDT_OBJS) $(NMS) TESTPRODTARGETS += $(TESTPRODNAME) $(TESTMUNCHNAME) #--------------------------------------------------------------- # Test specifications and test result files # ifneq (,$(strip $(TESTS))) TARGETS += testspec endif # Enable testing if this host can run tests on the current target ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS))) RUNTESTS_ENABLED = YES TAPFILES += $(TESTSCRIPTS:.t=.tap) JUNITFILES += $(TAPFILES:.tap=.xml) endif #--------------------------------------------------------------- # Libraries # LIBTARGETS += $(LIBNAME) $(INSTALL_LIBS) $(TESTLIBNAME) \ $(SHRLIBNAME) $(INSTALL_SHRLIBS) $(TESTSHRLIBNAME) \ $(DLLSTUB_LIBNAME) $(INSTALL_DLLSTUB_LIBS) $(TESTDLLSTUB_LIBNAME) \ $(LOADABLE_SHRLIBNAME) $(INSTALL_LOADABLE_SHRLIBS) # Main targets install: buildInstall buildInstall: build # Allows rebuild to work with parallel builds option, -j. install: $(patsubst rebuild,clean,$(filter rebuild,$(MAKECMDGOALS))) rebuild: clean install build: inc build: $(OBJSNAME) $(LIBTARGETS) $(PRODTARGETS) $(TESTPRODTARGETS) \ $(TARGETS) $(TESTSCRIPTS) $(INSTALL_LIB_INSTALLS) inc: $(COMMON_INC) $(INSTALL_INC) $(INSTALL_CONFIGS) buildInstall: \ $(INSTALL_SCRIPTS) $(INSTALL_PROD) $(INSTALL_MUNCHS) \ $(INSTALL_TCLLIBS) $(INSTALL_TCLINDEX) \ $(INSTALL_HTMLS) $(INSTALL_DOCS) \ $(INSTALL_OBJS) \ $(INSTALL_TEMPLATE) \ $(INSTALL_BIN_INSTALLS) clean: build_clean build_clean: $(ECHO) "Cleaning" @$(RM) *.i *$(OBJ) *.a \ $(LIBNAME) $(TESTLIBNAME) $(SHRLIBNAME) $(TESTSHRLIBNAME) \ $(DLLSTUB_LIBNAME) $(TESTDLLSTUB_LIBNAME) \ $(LOADABLE_SHRLIBNAME) \ $(INC) $(TARGETS) $(TDS) $(CLEANS) \ *.out MakefileInclude *.manifest *.exp \ $(COMMON_INC) $(HDEPENDS_FILES) $(PRODTARGETS) $(TESTPRODTARGETS) \ $(TESTSCRIPTS) $(TAPFILES) $(JUNITFILES) ifdef RES @$(RM) *$(RES) endif # Sort mkdir targets to remove duplicates & make parents first $(DIRECTORY_TARGETS): $(MKDIR) $(sort $@) # Install LIB_INSTALLS libraries before linking executables $(TESTPRODNAME) $(PRODNAME): | $(INSTALL_LIB_INSTALLS) # Install built libraries too, unless Makefile says to wait ifneq ($(DELAY_INSTALL_LIBS),YES) $(TESTPRODNAME) $(PRODNAME): | $(INSTALL_LIBS) $(INSTALL_DLLSTUB_LIBS) endif # RELEASE file consistency checking checkRelease: +$(CONVERTRELEASE) checkRelease warnRelease: $(CONVERTRELEASE) checkRelease noCheckRelease: ifeq ($(EPICS_HOST_ARCH),$(T_A)) $(info Warning: RELEASE file consistency checks have been disabled) endif # $(FINAL_DIR) signals eventual install locations to makeRPath script $(TESTPRODNAME): FINAL_DIR=. $(PRODNAME): FINAL_DIR=$(INSTALL_BIN) $(TESTSHRLIBNAME): FINAL_DIR=. $(SHRLIBNAME): FINAL_DIR=$(INSTALL_SHRLIB) $(LOADABLE_SHRLIBNAME): FINAL_DIR=$(INSTALL_SHRLIB) #--------------------------------------------------------------- # The order of the following rules is # VERY IMPORTANT !!!! $(TESTPRODNAME) $(PRODNAME): $(PRODUCT_OBJS) $(PROD_RESS) $(PROD_DEPLIBS) $(TESTPRODNAME) $(PRODNAME): %$(EXE): | $(INSTALL_LIB) @$(RM) $@ $(LINK.cpp) $(MT_EXE_COMMAND) %_ctdt$(OBJ): %_ctdt.c @$(RM) $@ $(COMPILE.ctdt) $< %$(DEP):%.c @$(RM) $@ $(HDEPENDS.c) $< %$(DEP):%.cc @$(RM) $@ $(HDEPENDS.cpp) $< %$(DEP):%.cpp @$(RM) $@ $(HDEPENDS.cpp) $< # Cancel GNUMake's built-in rules, which don't have our _INC # dependencies so could get used in some circumstances (gdd) %.o: %.c %.o: %.cc %.o: %.cpp # Include files are order-only prerequisites for compilation: %$(OBJ): %.c | $(COMMON_INC) $(INSTALL_INC) @$(RM) $@ $(COMPILE.c) -c $< %$(OBJ): %.cc | $(COMMON_INC) $(INSTALL_INC) @$(RM) $@ $(COMPILE.cpp) -c $< %$(OBJ): %.cpp | $(COMMON_INC) $(INSTALL_INC) @$(RM) $@ $(COMPILE.cpp) -c $< # Windows resource compiler %$(RES): %.rc @$(RM) $@ $(RCCMD) YACCOPT ?= $($*_YACCOPT) # # rename the y.tab.h file only if we # are creating it # %.c: %.y @$(RM) $*.tab.c @$(RM) $*.tab.h $(YACC) -b$* $(YACCOPT) $< $(MV) $*.tab.c $*.c $(if $(findstring -d, $(YACCOPT)),$(MV) $*.tab.h $*.h,) # must be a separate rule since when not using '-d' the # prefix for .h will be different then .c %.h: %.c %.y %.c: %.l @$(RM) $@ $(LEX) $(LEXOPT) -o$@ $< #--------------------------------------------------------------- # Libraries, shared/DLL and stubs $(LIBNAME) $(TESTLIBNAME): $(LIBRARY_OBJS) $(filter-out $(DLLSTUB_LIBNAME) $(TESTDLLSTUB_LIBNAME), $(LIBNAME) $(TESTLIBNAME)): $(LIB_PREFIX)%$(LIB_SUFFIX): @$(RM) $@ $(ARCMD) ifneq ($(strip $(RANLIB)),) $(RANLIB) $@ endif # RANLIB $(SHRLIBNAME) $(DLLSTUB_LIBNAME) $(TESTSHRLIBNAME) $(TESTDLLSTUB_LIBNAME): \ $(LIBRARY_OBJS) $(LIBRARY_RESS) $(SHRLIB_DEPLIBS) # Stub library timestamps may be earlier than the DLL itself. # This order-only prerequisite resolves any related problems. # The $(LINK.shrlib) command must build both library files if # the target requires a separate stub library file. $(DLLSTUB_LIBNAME): | $(SHRLIBNAME); $(SHRLIBNAME): $(SHRLIB_PREFIX)%$(SHRLIB_SUFFIX): @$(RM) $@ $(LINK.shrlib) $(MT_DLL_COMMAND) $(TESTDLLSTUB_LIBNAME): | $(TESTSHRLIBNAME); $(TESTSHRLIBNAME): $(SHRLIB_PREFIX)%$(SHRLIB_SUFFIX): @$(RM) $@ $(LINK.shrlib) $(MT_DLL_COMMAND) $(LOADABLE_SHRLIBNAME): $(LIBRARY_OBJS) $(LIBRARY_RESS) $(SHRLIB_DEPLIBS) $(LOADABLE_SHRLIBNAME): $(LOADABLE_SHRLIB_PREFIX)%$(LOADABLE_SHRLIB_SUFFIX): @$(RM) $@ $(LINK.shrlib) $(MT_DLL_COMMAND) $(LIBNAME) $(SHRLIBNAME) $(LOADABLE_SHRLIBNAME): | $(INSTALL_LIB) $(INSTALL_LIB): @$(MKDIR) $@ #--------------------------------------------------------------- # C++ munching for VxWorks %.nm: %$(EXE) @$(RM) $@ $(NM) $< > $@ %.nm: %$(OBJ) @$(RM) $@ $(NM) $< > $@ %_ctdt.c: %.nm $(TOOLS)/munch.pl @$(RM) $@ $(PERL) $(TOOLS)/munch.pl -o $@ $< $(MUNCHNAME): %$(MUNCH_SUFFIX): $(MUNCH_DEPENDS) %$(EXE) @$(RM) $@ $(MUNCH_CMD) $(TESTMUNCHNAME): %$(MUNCH_SUFFIX): $(MUNCH_DEPENDS) %$(EXE) @$(RM) $@ $(MUNCH_CMD) #--------------------------------------------------------------- # GeSys modules for RTEMS $(MODNAME): %$(MODEXT): %$(EXE) @echo "Building module $@" @$(RM) $@ $(LINK.mod) #--------------------------------------------------------------- # Generate Perl include path module %ModuleDirs.pm: $(wildcard $(TOP)/configure/RELEASE*) @$(MKDIR) $(dir $@) $(CONVERTRELEASE) -T $(TOP) $@ #--------------------------------------------------------------- # Automated testing runtests: $(TESTSCRIPTS) ifdef RUNTESTS_ENABLED $(PERL) -MTest::Harness -e 'runtests @ARGV if @ARGV;' $^ endif testspec: $(TESTSCRIPTS) @$(RM) $@ @echo OS-class: $(OS_CLASS) > $@ @echo Target-arch: $(T_A) >> $@ $(if $^, @echo Tests: $^ >> $@) $(if $(TESTFILES), @echo Files: $(TESTFILES) >> $@) $(if $(TESTSPEC_$(OS_CLASS)), @echo "Harness: $(TESTSPEC_$(OS_CLASS))" >> $@) test-results: tapfiles ifneq ($(TAPFILES),) ifdef RUNTESTS_ENABLED $(PROVE) --failures --ext .tap --exec "$(CAT)" --color $(TAPFILES) endif CURRENT_TAPFILES := $(wildcard $(TAPFILES)) CURRENT_JUNITFILES := $(wildcard $(JUNITFILES)) endif clean-tests: ifneq ($(CURRENT_TAPFILES),) $(RM) $(CURRENT_TAPFILES) endif ifneq ($(CURRENT_JUNITFILES),) $(RM) $(CURRENT_JUNITFILES) endif tapfiles: $(TESTSCRIPTS) $(TAPFILES) junitfiles: $(JUNITFILES) # A .tap file is the output from running the associated test script %.tap: %.t ifdef RUNTESTS_ENABLED $(PERL) $< -tap > $@ endif %.xml: %.tap $(TAPTOJUNIT) --puretap --output $@ --input $< $* # If there's a perl test script (.plt) available, use it %.t: ../%.plt @$(RM) $@ $(EXPAND_TOOL) -t $(INSTALL_LOCATION) -a $(T_A) $< $@ # Test programs (.t files) must be written in Perl. # Generate a perl program to exec the real test binary. %.t: %$(EXE) $(TOOLS)/makeTestfile.pl @$(RM) $@ $(PERL) $(TOOLS)/makeTestfile.pl $(T_A) $(EPICS_HOST_ARCH) $@ $< #--------------------------------------------------------------- # Generate header with version number from VCS ifneq ($(GENVERSION),) $(COMMON_DIR)/$(GENVERSION): FORCE $(GENVERSIONHEADER) -t $(TOP) -N $(GENVERSIONMACRO) -V "$(GENVERSIONDEFAULT)" $@ endif #--------------------------------------------------------------- # Install rules for BIN_INSTALLS and LIB_INSTALLS define BIN_INSTALLS_template $$(INSTALL_BIN)/$$(notdir $(1)): $(1) $(ECHO) "Installing $$( build directory. # make PRINT.T_A PRINT_Var = $(@:PRINT.%=%) PRINT.%: @echo $(PRINT_Var) = '$($(PRINT_Var))' .PHONY: PRINT PRINT.% # Clean rules for recursively deleting editor backup files # and dependency (.d) files from CWD and below. cvsclean: $(PERL) $(CVSCLEAN) depclean: $(PERL) $(DEPCLEAN) .PHONY: cvsclean depclean # User specific rules # -include $(HOME)/configure/RULES_USER base-7.0.3.1/configure/RULES_DIRS0000664000577000060420000000670313557101274015040 0ustar anjaesctl#************************************************************************* # Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* ARCHS += $(BUILD_ARCHS) ACTIONS += inc build install buildInstall clean realclean archclean ACTIONS += runtests tapfiles clean-tests test-results junitfiles dirActionArchTargets = $(foreach dir, $(DIRS), \ $(foreach action, $(ACTIONS), \ $(foreach arch, $(ARCHS), \ $(dir)$(DIVIDER)$(action)$(DIVIDER)$(arch)))) dirArchTargets += $(foreach dir, $(DIRS), \ $(foreach arch, $(ARCHS), \ $(dir)$(DIVIDER)$(arch))) dirActionTargets += $(foreach dir, $(DIRS), \ $(foreach action, $(ACTIONS), \ $(dir)$(DIVIDER)$(action))) actionArchTargets = $(foreach action, $(ACTIONS), \ $(foreach arch, $(ARCHS), \ $(action)$(DIVIDER)$(arch))) all: install host: install$(DIVIDER)$(EPICS_HOST_ARCH) # Include /cfg/DIR_RULES* files from tops defined in RELEASE* files # Do this here so they can add ACTIONS # RELEASE_CFG_DIR_RULES = $(foreach top, $(RELEASE_TOPS), \ $(wildcard $($(top))/cfg/DIR_RULES*)) ifneq ($(RELEASE_CFG_DIR_RULES),) include $(RELEASE_CFG_DIR_RULES) endif # Allows rebuild to work with parallel builds option, -j. ifeq (rebuild,$(filter rebuild,$(MAKECMDGOALS))) $(foreach dir, $(DIRS), $(dir)$(DIVIDER)install): \ $(foreach dir, $(DIRS), $(dir)$(DIVIDER)clean) rebuild: $(foreach dir, $(DIRS), $(dir)$(DIVIDER)install) endif # Create directory dependancies lines for GNU make -j option # Only works with GNU make 3.81 or later (uses eval function) define DEP_template1 $(1): $$($(1)_DEPEND_DIRS) endef $(foreach dir, $(DIRS), \ $(eval $(call DEP_template1,$(dir)))) define DEP_template2 $(1)$$(DIVIDER)$(2) : $$(foreach ddir, $$($(1)_DEPEND_DIRS), \ $$(addsuffix $$(DIVIDER)$(2),$$(ddir))) endef $(foreach action, $(ACTIONS), \ $(foreach dir, $(DIRS), \ $(eval $(call DEP_template2,$(dir),$(action))))) define DEP_template3 $(1)$$(DIVIDER)$(2) : $$(foreach ddir, $$($(1)_DEPEND_DIRS), \ $$(addsuffix $$(DIVIDER)$(2),$$(ddir))) endef $(foreach arch, $(ARCHS), \ $(foreach dir, $(DIRS), \ $(eval $(call DEP_template3,$(dir),$(arch))))) define DEP_template4 $(1)$$(DIVIDER)$(2)$$(DIVIDER)$(3) : $$(foreach ddir, $$($(1)_DEPEND_DIRS), \ $$(addsuffix $$(DIVIDER)$(2)$$(DIVIDER)$(3),$$(ddir))) endef $(foreach arch, $(ARCHS), \ $(foreach action, $(ACTIONS), \ $(foreach dir, $(DIRS), \ $(eval $(call DEP_template4,$(dir),$(action),$(arch)))))) dirPart = $(join $(dir $@), $(word 1, $(subst $(DIVIDER), ,$(notdir $@)))) actionArchPart = $(join $(word 2, $(subst $(DIVIDER), ,$(notdir $@))), \ $(addprefix $(DIVIDER),$(word 3, $(subst $(DIVIDER), ,$(notdir $@))))) $(DIRS) $(dirActionTargets) $(dirArchTargets) $(dirActionArchTargets) : $(MAKE) -C $(dirPart) $(actionArchPart) $(ARCHS) $(ACTIONS) $(actionArchTargets) :%: \ $(foreach dir, $(DIRS), $(dir)$(DIVIDER)%) .PHONY: $(DIRS) all host rebuild .PHONY: $(ARCHS) $(ACTIONS) .PHONY: $(dirActionTargets) $(dirArchTargets) .PHONY: $(dirActionArchTargets) .PHONY: $(actionArchTargets) include $(CONFIG)/RULES_COMMON base-7.0.3.1/configure/RULES_EXPAND0000664000577000060420000000413313557101274015251 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* # RULES_EXPAND vpath %@ $(USR_VPATH) $(ALL_SRC_DIRS) #--------------------------------------------------------------- # Variable expansion # Default settings EXPAND_TOOL ?= $(PERL) $(TOOLS)/expandVars.pl EXPANDFLAGS += -t $(INSTALL_LOCATION) -a $(T_A) EXPANDFLAGS += $(addprefix -D ,$(EXPAND_VARS)) # The names of files to be expanded must end with '@' EXPANDED = $(EXPAND:%@=%) $(EXPANDED): %: %@ $(ECHO) "Expanding $< to $@" @$(RM) $@ @$(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@ clean: expand_clean expand_clean: @$(RM) $(EXPANDED) .PRECIOUS: $(EXPANDED) .PHONY: expand_clean #--------------------------------------------------------------- # Assemblies (files assembled from snippets) ASSEMBLE_TOOL ?= $(PERL) $(TOOLS)/assembleSnippets.pl define COMMON_ASSEMBLY_template $1_SNIPPETS += $$(foreach dir, .. $$(SRC_DIRS), \ $$(wildcard $$(dir)/$$($1_PATTERN))) $(COMMON_DIR)/$1: $$($1_SNIPPETS) $(ECHO) "Assembling common file $$@ from snippets" @$(RM) $1 $(ASSEMBLE_TOOL) -o $1 $$^ @$(MV) $1 $$@ endef $(foreach asy, $(COMMON_ASSEMBLIES), \ $(eval $(call COMMON_ASSEMBLY_template,$(strip $(asy))))) define ASSEMBLY_template $1_SNIPPETS += $$(foreach dir, .. $$(SRC_DIRS), \ $$(wildcard $$(dir)/$$($1_PATTERN))) $1: $$($1_SNIPPETS) $(ECHO) "Assembling file $$@ from snippets" @$(RM) $$@ $(ASSEMBLE_TOOL) -o $$@ $$^ endef $(foreach asy, $(ASSEMBLIES), \ $(eval $(call ASSEMBLY_template,$(strip $(asy))))) define ASSEMBLY_DEP_template $1$(DEP): @echo $1: > $$@ endef $(foreach asy, $(sort $(COMMON_ASSEMBLIES) $(ASSEMBLIES)), \ $(eval $(call ASSEMBLY_DEP_template,$(strip $(asy))))) base-7.0.3.1/configure/RULES_FILE_TYPE0000664000577000060420000000422313557101274015652 0ustar anjaesctl#************************************************************************* # Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* # Include /configure/RULES_BUILD from tops defined in RELEASE* files, # excluding EPICS_BASE # RELEASE_RULES_BUILDS = $(foreach top, \ $(filter-out EPICS_BASE, $(RELEASE_TOPS)), \ $(wildcard $($(top))/configure/RULES_BUILD)) ifneq ($(RELEASE_RULES_BUILDS),) include $(RELEASE_RULES_BUILDS) endif # Include /cfg/RULES* files from tops defined in RELEASE* files # RELEASE_CFG_RULES = $(foreach top, $(RELEASE_TOPS), \ $(wildcard $($(top))/cfg/RULES*)) ifneq ($(RELEASE_CFG_RULES),) include $(RELEASE_CFG_RULES) endif # If this is not BASE then include /configure/RULES_BUILD # ifeq ($(wildcard $(TOP)/configure/CONFIG_BASE_VERSION),) TOP_RULES_BUILDS = $(wildcard $(TOP)/configure/RULES_BUILD) ifneq ($(TOP_RULES_BUILDS),) include $(TOP_RULES_BUILDS) endif endif # Include our own $(INSTALL_CFG)/RULES* files # TOP_CFG_RULES = $(wildcard $(INSTALL_CFG)/RULES*) ifneq ($(TOP_CFG_RULES),) include $(TOP_CFG_RULES) endif # Rules to install each FILE_TYPE # define FILE_TYPE_template $(1) += $$(if $$(strip $$($(1)_$(OS_CLASS))), \ $$(subst -nil-,,$$($(1)_$(OS_CLASS))), \ $$($(1)_DEFAULT)) INSTALLS_$(1) = $$($(1):%=$$(INSTALL_$(1))/%) $$(INSTALL_$(1))/%: ../% $(ECHO) "Installing $(1) file $$@" @$$(INSTALL) -d -m $$(INSTALL_PERMISSIONS) $$< $$(dir $$@) $$(INSTALL_$(1))/%: % $(ECHO) "Installing $(1) file $$@" @$$(INSTALL) -d -m $$(INSTALL_PERMISSIONS) $$< $$(dir $$@) buildInstall: $$(INSTALLS_$(1)) endef $(foreach type, $(FILE_TYPE), \ $(eval $(call FILE_TYPE_template,$(strip $(type))))) # Cleaning FILE_TYPE files # clean: file_type_clean file_type_clean: @$(RM) $(foreach type, $(FILE_TYPE), $($(type))) .PHONY : file_type_clean base-7.0.3.1/configure/RULES_MODULES0000664000577000060420000000401513557101274015401 0ustar anjaesctl#************************************************************************* # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # Support modules can use these rules to build submodules too. # # The requirements to do so are: # 1. Create a file CONFIG_SITE.local in the same directory as the # Makefile, which defines these variables (the last one is empty): # PARENT_MODULE - The name submodules call their parent # INSTALL_LOCATION := $($(PARENT_MODULE)) # CONFIG_INSTALLS = # 2. The Makefile must set TOP and include $(TOP)/configure/CONFIG and # CONFIG_SITE.local # 3. Submodules are added to the SUBMODULES variable in the Makefile # 4. Dependencies between submodules must be set using # _DEPEND_DIRS = # 5. The Makefile must end by including $(TOP)/configure/RULES_MODULES # 6. Submodules must have a configure/RELEASE file that contains # -include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local # 7. Submodules must have a configure/CONFIG_SITE file that contains # -include $(TOP)/../CONFIG_SITE.local # Add checked-out submodules to DIRS DIRS += $(subst /Makefile,,$(wildcard $(addsuffix /Makefile, $(SUBMODULES)))) include $(CONFIG)/RULES_DIRS INSTALL_LOCATION_ABS := $(abspath $(INSTALL_LOCATION)) RELEASE_LOCAL := RELEASE.$(EPICS_HOST_ARCH).local # Ensure that RELEASE..local exists before doing anything else all host $(DIRS) $(ARCHS) $(ACTIONS) $(dirActionTargets) $(dirArchTargets) \ $(dirActionArchTargets) $(actionArchTargets): | $(RELEASE_LOCAL) # Convenience target RELEASE.host: $(RELEASE_LOCAL) $(RELEASE_LOCAL): Makefile CONFIG_SITE.local $(ECHO) Creating $@ with $(ECHO) " $(PARENT_MODULE) = $(INSTALL_LOCATION_ABS)" @echo $(PARENT_MODULE) = $(INSTALL_LOCATION_ABS)> $@ realclean: $(RM) $(wildcard RELEASE.*.local) .PHONY: RELEASE.host realclean base-7.0.3.1/configure/RULES_OCTAVE0000664000577000060420000000254713557101274015262 0ustar anjaesctl#************************************************************************* # Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* # Octave definitions and rules ifeq ($(findstring Host,$(VALID_BUILDS)),Host) FILE_TYPE += OCTAVE INSTALL_OCTAVE = $(INSTALL_LOCATION_LIB)/octave DIRECTORY_TARGETS += $(INSTALL_OCTAVE) ifdef T_A MKOCTFILE_FLAGS += --mex --verbose -DOCTAVE MKOCTFILE_FLAGS += $(RELEASE_INCLUDES) $(addprefix -L,$(SHRLIB_SEARCH_DIRS)) vpath %.mex $(USR_VPATH) $(ALL_SRC_DIRS) vpath %.m $(USR_VPATH) $(ALL_SRC_DIRS) define OCTAVES_template $(1) : $$($(1)_SRCS) endef $(foreach file, $(OCTAVES),$(eval $(call OCTAVES_template,$(strip $(file))))) clean: octave_clean #This clean works from O.* dirs. octave_clean: @$(RM) *.mex *.m .PHONY: octave_clean .PRECIOUS: *.m *.mex %.mex: mkoctfile $(MKOCTFILE_FLAGS) $($*_LIBS:%=-l%) $($*_SRCS) endif endif # Makefile usage: # OCTAVES += abc.mex def.mex # abc_SRCS = a1.c a2.c # abc_LIBS = ca Com # def_SRCS = a3.c a4.c # def_LIBS = ca Com base-7.0.3.1/configure/RULES_TARGET0000664000577000060420000001261713557101274015266 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* # RULES_TARGET define TARGET_template $(1)_$(2) += $$(if $$(strip $$($(1)_$(2)_$$(OS_CLASS))), \ $$(subst -nil-,,$$($(1)_$(2)_$$(OS_CLASS))), \ $$($(1)_$(2)_DEFAULT)) endef $(foreach type, SRCS RCS OBJS LDFLAGS LDOBJS SYS_LIBS , \ $(foreach target, $(PROD) $(TESTPROD) $(LIBRARY) $(TESTLIBRARY) $(LOADABLE_LIBRARY) , \ $(eval $(call TARGET_template,$(strip $(target)),$(type))))) #----------------------------------------------------------------------- # This define block requires GNU make 3.81 define PROD_template ifeq ($$(strip $$($(1)_OBJS) $$($(1)_SRCS) $$(PRODUCT_OBJS)),) $(1)_OBJS = $(1)$$(OBJ) endif endef $(foreach target, $(PROD) $(TESTPROD), \ $(eval $(call PROD_template,$(strip $(target))))) #----------------------------------------------------------------------- # These must be done before PROD2_template define TESTLIBRARY_template $(1)_DIR = . TESTBUILD_LIBRARY += $$(if $$(strip $$($(1)_OBJSNAME) $$(LIBRARY_OBJS)),$(1),) endef $(foreach target, $(TESTLIBRARY), \ $(eval $(call TESTLIBRARY_template,$(strip $(target))))) #----------------------------------------------------------------------- define TARGET2_template $(1)_LDLIBS += $$($(1)_LIBS) $(1)_LDLIBS += $$(if $$(strip $$($(1)_LIBS_$(OS_CLASS))), \ $$(subst -nil-,,$$($(1)_LIBS_$(OS_CLASS))), \ $$($(1)_LIBS_DEFAULT)) $(1)_RESS = $$(if $(RES),$$(addsuffix $(RES),$$(basename $$($(1)_RCS))),) $(1)_OBJSNAME = $$(addsuffix $(OBJ),$$(basename $$($(1)_OBJS) $$($(1)_SRCS) )) $(1)_DEPLIBS = $$(foreach lib, $$($(1)_LDLIBS), \ $$(firstword $$(wildcard \ $$(addsuffix /$(DLLSTUB_PREFIX)$$(lib)$(DLLSTUB_SUFFIX), \ $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS)) \ $$(addsuffix /$(SHRLIB_PREFIX)$$(lib)*$(SHRLIB_SUFFIX_BASE)*, \ $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS)) \ $$(addsuffix /$(LIB_PREFIX)$$(lib)$(LIB_SUFFIX), \ $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS)) \ ) $$(addsuffix /$(BUILDLIB_PREFIX)$$(lib)$(BUILDLIB_SUFFIX), \ $$(firstword $$($$(lib)_DIR) $(SHRLIB_SEARCH_DIRS))))) endef $(foreach target, $(PROD) $(TESTPROD) $(LIBRARY) $(TESTLIBRARY) $(LOADABLE_LIBRARY) , \ $(eval $(call TARGET2_template,$(strip $(target))))) #----------------------------------------------------------------------- define PROD2_template $(1)$$(EXE): $$($(1)_OBJSNAME) $$($(1)_RESS) $$($(1)_DEPLIBS) endef $(foreach target, $(PROD) $(TESTPROD), \ $(eval $(call PROD2_template,$(strip $(target))))) #----------------------------------------------------------------------- define LIBRARY_template $(1)_DLL_DEPLIBS=$$(foreach lib, $$($(1)_DLL_LIBS), \ $$(firstword $$(wildcard \ $$(addsuffix /$(DLLSTUB_PREFIX)$$(lib)$(DLLSTUB_SUFFIX), \ $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS)) \ $$(addsuffix /$(SHRLIB_PREFIX)$$(lib)*$(SHRLIB_SUFFIX_BASE)*, \ $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS)) \ $$(addsuffix /$(LIB_PREFIX)$$(lib)$(LIB_SUFFIX), \ $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS)) \ ) $$(addsuffix /$(BUILDLIB_PREFIX)$$(lib)$(BUILDLIB_SUFFIX), \ $$(firstword $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS))))) $$(LIB_PREFIX)$(1)$$(LIB_SUFFIX):$$($(1)_OBJSNAME) $$($(1)_RESS) $$(LIB_PREFIX)$(1)$$(LIB_SUFFIX):$$($(1)_DEPLIBS) ifeq ($$(SHARED_LIBRARIES),YES) ifdef SHRLIB_SUFFIX $$(SHRLIB_PREFIX)$(1)$$(SHRLIB_SUFFIX):$$($(1)_OBJSNAME) $$($(1)_RESS) $$(SHRLIB_PREFIX)$(1)$$(SHRLIB_SUFFIX):$$($(1)_DEPLIBS) $$(SHRLIB_PREFIX)$(1)$$(SHRLIB_SUFFIX):$$($(1)_DLL_DEPLIBS) endif endif endef $(foreach target, $(LIBRARY) $(TESTLIBRARY), \ $(eval $(call LIBRARY_template,$(strip $(target))))) #----------------------------------------------------------------------- define LIBRARY2_template BUILD_LIBRARY += $$(if $$(strip $$($(1)_OBJSNAME) $$(LIBRARY_OBJS)),$(1),) # Needed for -j parallel builds option ifeq ($$(SHARED_LIBRARIES),YES) ifdef SHRLIB_SUFFIX $$(INSTALL_LIB)/$$(DLLSTUB_PREFIX)$(1)$$(DLLSTUB_SUFFIX): \ $$(INSTALL_SHRLIB)/$$(SHRLIB_PREFIX)$(1)$$(SHRLIB_SUFFIX) endif endif endef $(foreach target, $(LIBRARY), \ $(eval $(call LIBRARY2_template,$(strip $(target))))) #----------------------------------------------------------------------- define LOADABLE_LIBRARY_template LOADABLE_BUILD_LIBRARY += $$(if $$(strip $$($(1)_OBJSNAME) $$(LIBRARY_OBJS)),$(1),) $(1)_DLL_DEPLIBS=$$(foreach lib, $$($(1)_DLL_LIBS),\ $$(firstword $$(wildcard $$(addsuffix /$$(LIB_PREFIX)$$(lib).\*,\ $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS)))\ $$(addsuffix /$$(LIB_PREFIX)$$(lib)$$(LIB_SUFFIX),\ $$(firstword $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS))))) $$(LOADABLE_SHRLIB_PREFIX)$(1)$$(LOADABLE_SHRLIB_SUFFIX):$$($(1)_OBJSNAME) $$($(1)_RESS) $$(LOADABLE_SHRLIB_PREFIX)$(1)$$(LOADABLE_SHRLIB_SUFFIX):$$($(1)_DEPLIBS) $$(LOADABLE_SHRLIB_PREFIX)$(1)$$(LOADABLE_SHRLIB_SUFFIX):$$($(1)_DLL_DEPLIBS) endef $(foreach target, $(LOADABLE_LIBRARY), \ $(eval $(call LOADABLE_LIBRARY_template,$(strip $(target))))) #----------------------------------------------------------------------- base-7.0.3.1/configure/RULES_TOP0000664000577000060420000001132713557101274014737 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* include $(CONFIG)/RULES_DIRS # Disable most top rules when installing into a parent's tree, indicated # by PARENT_MODULE being set in the modules/CONFIG_SITE.local file and # INSTALL_LOCATION pointing to the the same place as in the parent. ifeq ($(origin PARENT_MODULE),file) ifeq ($(INSTALL_LOCATION),$($(PARENT_MODULE))) DISABLE_TOP_RULES=YES endif endif ifndef DISABLE_TOP_RULES # # Rules for a regular application top directory # distclean: realclean cvsclean realuninstall realuninstall: uninstallDirs $(RMDIR) $(INSTALL_LOCATION_BIN) $(RMDIR) $(INSTALL_LOCATION_LIB) UNINSTALL_DIRS += $(INSTALL_DB) $(INSTALL_DBD) $(INSTALL_DOC) $(INSTALL_HTML) UNINSTALL_DIRS += $(INSTALL_INCLUDE) $(INSTALL_TEMPLATES) $(DIRECTORY_TARGETS) ifneq ($(INSTALL_LOCATION),$(TOP)) UNINSTALL_DIRS += $(INSTALL_CONFIG) endif uninstallDirs: $(RMDIR) $(UNINSTALL_DIRS) uninstall: archuninstall uninstallDirs archuninstall: $(addprefix uninstall$(DIVIDER),$(BUILD_ARCHS)) | cleandirs archPart = $(word 2, $(subst $(DIVIDER), ,$@)) uninstall$(DIVIDER)%: $(RMDIR) $(INSTALL_LOCATION_BIN)/$(archPart) $(RMDIR) $(INSTALL_LOCATION_LIB)/$(archPart) # Remove the bin and lib directories if they have no sub-directories # cleandirs: @$(NOP) ifeq ($(wildcard $(INSTALL_LOCATION_BIN)/*),) $(RMDIR) $(INSTALL_LOCATION_BIN) endif ifeq ($(wildcard $(INSTALL_LOCATION_LIB)/*),) $(RMDIR) $(INSTALL_LOCATION_LIB) endif else # # Using a disabled rule aborts # cleandirs distclean uninstall realuninstall archuninstall: $(error Target '$@' not available in a submodule) endif # DISABLE_TOP_RULES help: @echo "Usage: gnumake [options] [target] ..." @echo "Targets supported by all Makefiles:" @echo " all - Same as install (default rule)" @echo " inc - Installs header files" @echo " build - Builds and installs all targets" @echo " install - Builds and installs all targets" @echo " buildInstall - Same as install (deprecated)" @echo " clean - Removes the O. dirs created by running make" @echo " In O. dir, clean removes build created files" @echo " realclean - Removes ALL O. dirs" @echo " Cannot be used within an O. dir" @echo " rebuild - Same as clean install" @echo " archclean - Removes O. dirs but not O.Common dir" @echo " depclean - Removes .d files from all O. dirs." @echo " cvsclean - Removes backup files etc. from all dirs below" @echo " runtests - Run self-tests, summarize results immediately" @echo " tapfiles - Run self-tests, save to O./*.tap files" @echo " test-results - Summarize all O./*.tap files" @echo " clean-tests - Removes all O./*.tap files" @echo "\"Partial\" build targets supported by Makefiles:" @echo " host - Builds and installs $(EPICS_HOST_ARCH) only." @echo " inc$(DIVIDER) - Installs only header files." @echo " build$(DIVIDER) - Builds and installs only." @echo " install$(DIVIDER) - Builds and installs only." @echo " clean$(DIVIDER) - Cleans binaries in O. dirs only." @echo "Targets supported by top level Makefile:" ifndef DISABLE_TOP_RULES @echo " archuninstall - Remove bin & lib directories created by this hostarch." @echo " uninstall$(DIVIDER) - Remove bin & lib directories for only." @echo " uninstall - Remove install directories created by this hostarch." @echo " realuninstall - Removes ALL install dirs" @echo " distclean - Same as realclean cvsclean realuninstall." endif @echo " help - Prints this list of valid make targets " @echo "Object targets are supported by the O. level Makefile .e.g" @echo " xxxRecord.o" .PHONY: cleandirs distclean uninstall help .PHONY: realuninstall archuninstall uninstallDirs ifndef DISABLE_TOP_RULES # Include /cfg/TOP_RULES* files from tops defined in RELEASE* files # RELEASE_CFG_TOP_RULES = $(foreach top, $(RELEASE_TOPS), \ $(wildcard $($(top))/cfg/TOP_RULES*)) ifneq ($(RELEASE_CFG_TOP_RULES),) include $(RELEASE_CFG_TOP_RULES) endif endif base-7.0.3.1/configure/Sample.Makefile0000664000577000060420000001442213557101274016257 0ustar anjaesctl#************************************************************************* # 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. # 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. #************************************************************************* # Makefile for base/src/sample # # # Sample Makefile showing some possible entries # that are allowed using RULES_BUILD. # TOP = ../../.. include $(TOP)/configure/CONFIG # Add-on CPPFLAGS that are needed by this Makefile. # (If possible, all system specific flags should be # defined in configure/os/CONFIG.. # # These CPPFLAGS rules also apply to these Makefile-variables: # CPPFLAGS C preprocessor flags # CFLAGS C flags # CXXFLAGS C++ flags # LDFLAGS link flags # # This is used on all systems: USR_CPPFLAGS = -DVAR=value -Ddefine_for_all_systems # ..only for WIN32: USR_CPPFLAGS_WIN32 = -DVERSION='WIN32 port' # # -nil- is special: # if USR_CPPFLAGS_WIN32 was undefined or empty, .._DEFAULT would have # been used. # To indicate # "yes, there is a special USR_CPPFLAGS for WIN32, but it's empty" # you have to set it to -nil-: USR_CPPFLAGS_WIN32 = -nil- # .. for all other arch classes: USR_CPPFLAGS_DEFAULT = -DVERSION='generic Unix' # CPPFLAGS that are only used to compile a_file.c or a_file.cpp: # a_file_CPPFLAGS = -DIN_A_FILE a_file_CPPFLAGS_WIN32 = -DVERSION='WIN32 port' # --------------------------------------------------------- # general rule for all .c .cpp .h .hh files and scripts: # # In here you supply just the filename without '../' etc. # While building in an O.xxx subdir, the # sources are extracted from the # ../os/$(OS_CLASS) directory if it exists, or # ../os/default directory if it exists, or # .. directory # --------------------------------------------------------- # includes to install from this Makefile # # again: if INC_$(OS_CLASS) is defined, it is added to INC, # otherwise INC_DEFAULT (if defined) is added: # INC_DEFAULT = for_all_but_WIN32_or_vxWorks.h INC_WIN32 = only_for_WIN32.h INC_vxWorks = -nil- # vxWorks uses no special include INC = file.h # -------------------------------------------------------------------- # defining a library # -------------------------------------------------------------------- # # Contents of a library are specified via SRCS, LIB_SRCS, or .._SRCS. # From this the platform specific object names (.o, .obj, ...) # are derived automatically. # # Platform specific objects: # use .._OBJS_$(OS_CLASS) or .._OBJS_DEFAULT # # Platform specific files can also be put in # separate os/OS_CLASS directories! # # For almost every file the seach order is: # ./os/OS_CLASS # ./os/generic # . # So usually only LIB_SRCS should be sufficient! # SRCS files will be used for both LIBRARY and PROD SRCS = file_for_lib.c another_file.cpp SRCS_DEFAULT = posix.c SRCS_WIN32 = win32_special.c SRCS_Linux = -nil- # libname_SRCS = file_for_lib.c another_file.cpp libname_SRCS_DEFAULT = posix.c libname_SRCS_WIN32 = win32_special.c libname_SRCS_Linux = -nil- # # SRCS that are used for all libraries LIB_SRCS = file_for_lib.c another_file.cpp LIB_SRCS_DEFAULT = posix.c LIB_SRCS_WIN32 = win32_special.c LIB_SRCS_Linux = -nil- # Library to build: # lib$(LIBRARY).a or ..dll/..exp/..lib # LIBRARY=libname # # Host or Ioc platform specific library to build: # LIBRARY_IOC=libnameIoc LIBRARY_HOST=libnameHost # Library version SHRLIB_VERSION = # On WIN32 results in /version:$(SHRLIB_VERSION) link option # On Unix type hosts .$(SHRLIB_VERSION) is appended to library name # -------------------------------------------------------------------- # defining products (executable programs) # -------------------------------------------------------------------- # # if SRCS is undefined, it defaults to $(PROD).c SRCS=a.c b.c c.c # SRCS that are used for all PRODs # PROD_SRCS = ppp.c qqq.c # SRCS that are only used for PROD a_file # a_file_SRCS = aa.c bb.c # # EPICS libs needed to link PROD, TESTPROD and sharable library # # note that DLL_LIBS (the libraries needed to link a shareable # library) is created by default from the PROD/SYS libraries specified # below minus the name of the sharable library (LIBRARY) # # # ---------- libraries for a specific product pppp # for all systems pppp_LIBS = Com Ca # for most systems: pppp_LIBS_DEFAULT = mathlib pppp_LIBS_WIN32 = -nil- # ---------- libraries for all products # for all systems PROD_LIBS = Com Ca # for most systems: PROD_LIBS_DEFAULT = mathlib PROD_LIBS_WIN32 = -nil- # ---------- Libraries for all products and all libraries: # for all systems USR_LIBS = Xm Xt X11 Xm_DIR = $(MOTIF_LIB) Xt_DIR = $(X11_LIB) X11_DIR = $(X11_LIB) # for most systems USR_LIBS_DEFAULT = foolib USR_LIBS_WIN32 = -nil- foolib_DIR = $(FOO_LIB) # system libs needed to link PROD, TESTPROD and sharable library # # ---------- system libraries for all products # for all systems: PROD_SYS_LIBS = m # for most systems: PROD_SYS_LIBS_DEFAULT = foolib PROD_SYS_LIBS_WIN32 = -nil- # Product, # may be caRepeater.o -> caRepeater # or caRepeater.obj -> caRepeater.exe PROD = prod PROD_DEFAULT = product_for_rest PROD_WIN32 = product_only_for_WIN32 PROD_Linux = product_only_for_Linux PROD_solaris = product_only_for_solaris PROD_HOST = product_only_for_host_type_systems PROD_IOC = product_only_for_ioc_type_systems # Product version PROD_VERSION = # On WIN32 results in /version:$(SHRLIB_VERSION) link option # On Unix type hosts PROD_VERSION) is ignored # Scripts to install # # If there is both ../$(SCRIPTS) and ../$(OS_CLASS)/$(SCRIPTS), # the latter, system specific version will be installed! # SCRIPTS_DEFAULT = script_for_rest SCRIPTS_WIN32 = script_only_for_WIN32 SCRIPTS_Linux = script_only_for_Linux SCRIPTS = script # if you want to build products locally without installing: # TESTPROD = test # put all definitions before the following include line # put all rules after the following include line include $(TOP)/configure/RULES # EOF Makefile base-7.0.3.1/configure/os/CONFIG.Common.RTEMS0000664000577000060420000001355013557101274017031 0ustar anjaesctl# # This file contains definitions for RTEMS builds # # Author: W. Eric Norum # University of Saskatchewan # eric.norum@usask.ca # # Contains definitions common to all RTEMS targets # # This file is maintained by the build community. # Sites may override definitions in os/CONFIG_SITE.Common.RTEMS #------------------------------------------------------- # #------------------------------------------------------- # RTEMS tools are similar to UNIX tools -include $(CONFIG)/os/CONFIG.Common.UnixCommon GNU_TARGET_INCLUDE_DIR = unexport GCC_EXEC_PREFIX #-------------------------------------------------- # Get RTEMS_BASE definition -include $(CONFIG)/os/CONFIG_SITE.Common.RTEMS ifneq ($(CONFIG),$(TOP)/configure) -include $(TOP)/configure/CONFIG_SITE.Common.RTEMS endif #-------------------------------------------------- # Set RTEMS_BSP from T_A if not already done RTEMS_BSP ?= $(subst RTEMS-,,$(T_A)) #------------------------------------------------------- # Pick up the RTEMS tool/path definitions from the RTEMS BSP directory. include $(RTEMS_BASE)/$(RTEMS_TARGET_CPU)-rtems$(RTEMS_VERSION)/$(RTEMS_BSP)/Makefile.inc include $(RTEMS_CUSTOM) include $(CONFIG.CC) #------------------------------------------------------- # RTEMS cross-development tools CC = $(RTEMS_TOOLS)/bin/$(CC_FOR_TARGET) $(GCCSPECS) -fasm CCC = $(RTEMS_TOOLS)/bin/$(CXX) CPP = $(RTEMS_TOOLS)/bin/$(CC_FOR_TARGET) -x c -E AR = $(RTEMS_TOOLS)/bin/$(AR_FOR_TARGET) LD = $(RTEMS_TOOLS)/bin/$(LD_FOR_TARGET) -r RANLIB := $(RTEMS_TOOLS)/bin/$(RANLIB) #------------------------------------------------------- # Build types VALID_BUILDS = Ioc #-------------------------------------------------- # The RTEMS Makefiles redefine several macros, so we have to # reset them to the proper EPICS values, from CONFIG_COMMON CFLAGS = $($(BUILD_CLASS)_CFLAGS) $(POSIX_CFLAGS) $(OPT_CFLAGS)\ $(DEBUG_CFLAGS) $(PIPE_CFLAGS) $(WARN_CFLAGS) $(TARGET_CFLAGS)\ $(USR_CFLAGS) $(CMD_CFLAGS) $(ARCH_DEP_CFLAGS) $(CODE_CFLAGS)\ $(STATIC_CFLAGS) $(OP_SYS_CFLAGS) $(LIBRARY_SRC_CFLAGS) CXXFLAGS = $($(BUILD_CLASS)_CXXFLAGS) $(POSIX_CXXFLAGS) $(OPT_CXXFLAGS)\ $(DEBUG_CXXFLAGS) $(PIPE_CFLAGS) $(WARN_CXXFLAGS) $(TARGET_CXXFLAGS)\ $(USR_CXXFLAGS) $(CMD_CXXFLAGS) $(ARCH_DEP_CXXFLAGS) $(CODE_CXXFLAGS)\ $(STATIC_CXXCFLAGS) $(OP_SYS_CXXFLAGS) $(LIBRARY_SRC_CFLAGS) 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) LDLIBS = $(POSIX_LDLIBS) $(ARCH_DEP_LDLIBS) $(DEBUG_LDLIBS) $(OP_SYS_LDLIBS)\ $(GNU_LDLIBS_$(GNU)) CPPFLAGS += $($(BUILD_CLASS)_CPPFLAGS) $(POSIX_CPPFLAGS) $(OPT_CPPFLAGS)\ $(DEBUG_CPPFLAGS) $(WARN_CPPFLAGS) $(BASE_CPPFLAGS) $(TARGET_CPPFLAGS)\ $(USR_CPPFLAGS) $(CMD_CPPFLAGS) $(ARCH_DEP_CPPFLAGS) $(OP_SYS_CPPFLAGS)\ $(OP_SYS_INCLUDE_CPPFLAGS) $(CODE_CPPFLAGS) ECHO = @$(if $(findstring s,$(MFLAGS)),$(NOP),echo) #-------------------------------------------------- # Although RTEMS uses gcc, it wants to use gcc its own way CROSS_CPPFLAGS = CROSS_LDFLAGS = SHRLIB_CFLAGS = OPT_CFLAGS_YES = $(CFLAGS_OPTIMIZE_V) OPT_CXXFLAGS_YES = $(CFLAGS_OPTIMIZE_V) OPT_CFLAGS_NO = $(CFLAGS_DEBUG_V) OPT_CXXFLAGS_NO = $(CFLAGS_DEBUG_V) MODEXT=.obj #-------------------------------------------------- # operating system class (include/os/) OS_CLASS = RTEMS #-------------------------------------------------- # Operating system flags OP_SYS_LDLIBS += -lrtemsCom -lc -lrtemscpu -lCom -lnfs -lm OP_SYS_LDFLAGS += $(CPU_CFLAGS) -u Init \ $(PROJECT_RELEASE)/lib/no-dpmem.rel \ $(PROJECT_RELEASE)/lib/no-mp.rel \ $(PROJECT_RELEASE)/lib/no-part.rel \ $(PROJECT_RELEASE)/lib/no-signal.rel \ $(PROJECT_RELEASE)/lib/no-rtmon.rel MOD_SYS_LDFLAGS += $(CPU_CFLAGS) -Wl,-r -nostdlib # Do not link against libraries which are part of the Generic Image GESYS_LIBS += -lgcc GESYS_LIBS += -lc -lm -lrtemscpu -lrtemsbsp -lrtems++ -lbspExt GESYS_LIBS += -lcexp -ltecla_r -lspencer_regexp -lpmelf -lpmbfd GESYS_LIBS += -lnfs -ltelnetd -lrtems-gdb-stub # While not part of the Generic Image it provides symbols which # would conflict. GESYS_LIBS += -lrtemsCom #-------------------------------------------------- # Options for building GeSys loadable objects MODNAME_YES = $(PRODNAME:%$(EXE)=%$(MODEXT)) MODNAME += $(MODNAME_$(USE_GESYS)) PRODTARGETS += $(MODNAME) BIN_INSTALLS += $(MODNAME) # changes to LDFLAGS in CONFIG_COMMON and LINK.cpp in CONFIG.Common.UnixCommon # should be reflected here with the following exceptions # # replace OP_SYS_LDFLAGS with MOD_SYS_LDFLAGS # replace PROD_LDLIBS with MOD_LDLIBS # remove STATIC_LDFLAGS MOD_LDLIBS = $(filter-out $(GESYS_LIBS),$(PROD_LDLIBS)) MOD_LDFLAGS = $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) $(POSIX_LDFLAGS) \ $(ARCH_DEP_LDFLAGS) $(DEBUG_LDFLAGS) $(MOD_SYS_LDFLAGS) $($(BUILD_CLASS)_LDFLAGS)\ $(RUNTIME_LDFLAGS) $(CODE_LDFLAGS) LINK.mod = $(CCC) -o $@ $(PRODDIR_LDFLAGS) $(MOD_LDFLAGS) LINK.mod += $(PROD_LDFLAGS) $(PROD_LD_OBJS) $(PROD_LD_RESS) $(MOD_LDLIBS) #-------------------------------------------------- # Here munching means creating a bootable object binary ifdef MUNCH_SUFFIX MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) TESTMUNCHNAME = $(TESTPRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) endif #-------------------------------------------------- # RTEMS has neither shared libraries nor dynamic loading STATIC_BUILD=YES SHARED_LIBRARIES=NO CODE_CFLAGS = CODE_CXXFLAGS = #-------------------------------------------------- # Override the usual RTEMS verbosity from ar ARFLAGS = rc #-------------------------------------------------- # Command-line input support LDLIBS_LIBTECLA = -ltecla_r -lncurses LDLIBS_READLINE = -lreadline -lncurses #-------------------------------------------------- # Allow site overrides -include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).RTEMS base-7.0.3.1/configure/os/CONFIG.Common.RTEMS-at91rm9200ek0000664000577000060420000000054313557101274020775 0ustar anjaesctl# # CONFIG.Common.RTEMS-at91rm9200ek # Author: Ralf Hartmann # BESSY # Ralf.Hartmann@bessy.de # # Atmel AT91RM9200-EK evaluation kit # using the AT91RM9200 ARM9-based 32-bit RISC microcontroller # # All RTEMS targets use the same Makefile fragment # RTEMS_BSP = at91rm9200ek RTEMS_TARGET_CPU = arm include $(CONFIG)/os/CONFIG.Common.RTEMS base-7.0.3.1/configure/os/CONFIG.Common.RTEMS-beatnik0000664000577000060420000000157213557101274020445 0ustar anjaesctl# # CONFIG.Common.RTEMS-beatnik # Author: Dayle Kotturi # # All RTEMS targets use the same Makefile fragment # EXE = .elf RTEMS_BSP = beatnik RTEMS_TARGET_CPU = powerpc GNU_TARGET = powerpc-rtems ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL ARCH_DEP_CFLAGS += -DHAVE_MOTLOAD ARCH_DEP_CFLAGS += -DRTEMS_NETWORK_CONFIG_MBUF_SPACE=2048 ARCH_DEP_CFLAGS += -DRTEMS_NETWORK_CONFIG_CLUSTER_SPACE=5120 OP_SYS_LDLIBS += -lbspExt MUNCH_SUFFIX = .boot define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary $< $@ endef include $(CONFIG)/os/CONFIG.Common.RTEMS RTEMSSYMS=$(PRODNAME:%$(EXE)=%.sym) RTEMSIMGS=$(PRODNAME:%$(EXE)=%.bin) INSTALL_RTEMSSYMS=$(RTEMSSYMS:%=$(INSTALL_BIN)/%) INSTALL_RTEMSIMGS=$(RTEMSIMGS:%=$(INSTALL_BIN)/%) %.sym: %$(EXE) $(XSYMS) $^ $@ %.bin: %$(EXE) $(OBJCOPY) -Obinary $^ $@ #PRODTARGETS+=$(INSTALL_RTEMSSYMS) $(INSTALL_RTEMSIMGS) base-7.0.3.1/configure/os/CONFIG.Common.RTEMS-gen683600000664000577000060420000000034313557101274020203 0ustar anjaesctl# # Author: W. Eric Norum # Canadian Light Source # eric@cls.usask.ca # # All RTEMS targets use the same Makefile fragment # RTEMS_BSP = gen68360 RTEMS_TARGET_CPU = m68k include $(CONFIG)/os/CONFIG.Common.RTEMS base-7.0.3.1/configure/os/CONFIG.Common.RTEMS-mcp7500000664000577000060420000000034013557101274020033 0ustar anjaesctl# # Author: W. Eric Norum # Canadian Light Source # eric@cls.usask.ca # # All RTEMS targets use the same Makefile fragment # RTEMS_BSP = mcp750 RTEMS_TARGET_CPU = ppc include $(CONFIG)/os/CONFIG.Common.RTEMS base-7.0.3.1/configure/os/CONFIG.Common.RTEMS-mvme1670000664000577000060420000000034213557101274020224 0ustar anjaesctl# # Author: W. Eric Norum # Canadian Light Source # eric@cls.usask.ca # # All RTEMS targets use the same Makefile fragment # RTEMS_BSP = mvme167 RTEMS_TARGET_CPU = m68k include $(CONFIG)/os/CONFIG.Common.RTEMS base-7.0.3.1/configure/os/CONFIG.Common.RTEMS-mvme21000000664000577000060420000000127013557101274020272 0ustar anjaesctl# # CONFIG.Common.RTEMS-mvme2100 # Author: W. Eric Norum # # All RTEMS targets use the same Makefile fragment # EXE = .elf RTEMS_BSP = mvme2100 RTEMS_TARGET_CPU = powerpc GNU_TARGET = powerpc-rtems ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL ARCH_DEP_CFLAGS += -DHAVE_PPCBUG OP_SYS_LDLIBS += -lbspExt MUNCH_SUFFIX = .boot define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< rtems gzip -f9 rtems $(RTEMS_TOOLS)/bin/$(LD_FOR_TARGET) -o $@ \ $(PROJECT_RELEASE)/lib/bootloader.o \ --just-symbols=$< \ -b binary rtems.gz \ -T $(PROJECT_RELEASE)/lib/ppcboot.lds \ -Map $<.map rm -f rtems.gz endef include $(CONFIG)/os/CONFIG.Common.RTEMS base-7.0.3.1/configure/os/CONFIG.Common.RTEMS-mvme27000000664000577000060420000000111513557101274020276 0ustar anjaesctl# # Author: Matt Rippa # RTEMS_BSP = mvme2700 RTEMS_TARGET_CPU = powerpc ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL ARCH_DEP_CFLAGS += -DHAVE_PPCBUG ARCH_DEP_CFLAGS += -DNVRAM_INDIRECT MUNCH_SUFFIX = .boot define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< rtems gzip -f9 rtems $(RTEMS_TOOLS)/bin/$(LD_FOR_TARGET) -o $@ \ $(PROJECT_RELEASE)/lib/bootloader.o \ --just-symbols=$< \ -b binary rtems.gz \ -T $(PROJECT_RELEASE)/lib/ppcboot.lds \ -Map $<.map rm -f rtems.gz endef OP_SYS_LDLIBS += -lbspExt include $(CONFIG)/os/CONFIG.Common.RTEMS base-7.0.3.1/configure/os/CONFIG.Common.RTEMS-mvme31000000664000577000060420000000156413557101274020301 0ustar anjaesctl# # CONFIG.Common.RTEMS-mvme3100 # Author: W. Eric Norum # # All RTEMS targets use the same Makefile fragment # EXE = .elf RTEMS_BSP = mvme3100 RTEMS_TARGET_CPU = powerpc GNU_TARGET = powerpc-rtems ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL ARCH_DEP_CFLAGS += -DHAVE_MOTLOAD ARCH_DEP_CFLAGS += -DRTEMS_NETWORK_CONFIG_MBUF_SPACE=2048 ARCH_DEP_CFLAGS += -DRTEMS_NETWORK_CONFIG_CLUSTER_SPACE=5120 OP_SYS_LDLIBS += -lbspExt MUNCH_SUFFIX = .boot define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary $< $@ endef include $(CONFIG)/os/CONFIG.Common.RTEMS RTEMSSYMS=$(PRODNAME:%$(EXE)=%.sym) RTEMSIMGS=$(PRODNAME:%$(EXE)=%.bin) INSTALL_RTEMSSYMS=$(RTEMSSYMS:%=$(INSTALL_BIN)/%) INSTALL_RTEMSIMGS=$(RTEMSIMGS:%=$(INSTALL_BIN)/%) %.sym: %$(EXE) $(XSYMS) $^ $@ %.bin: %$(EXE) $(OBJCOPY) -Obinary $^ $@ #PRODTARGETS+=$(INSTALL_RTEMSSYMS) $(INSTALL_RTEMSIMGS) base-7.0.3.1/configure/os/CONFIG.Common.RTEMS-mvme55000000664000577000060420000000122213557101274020276 0ustar anjaesctl# # CONFIG.Common.RTEMS-mvme5500 # Author: W. Eric Norum # # All RTEMS targets use the same Makefile fragment # EXE = .elf RTEMS_BSP = mvme5500 RTEMS_TARGET_CPU = powerpc GNU_TARGET = powerpc-rtems ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL ARCH_DEP_CFLAGS += -DHAVE_MOTLOAD ARCH_DEP_CFLAGS += -DRTEMS_NETWORK_CONFIG_MBUF_SPACE=2048 ARCH_DEP_CFLAGS += -DRTEMS_NETWORK_CONFIG_CLUSTER_SPACE=5120 ARCH_DEP_CFLAGS += -DBSP_NVRAM_BASE_ADDR=0xf1110000 OP_SYS_LDLIBS += -lbspExt MUNCH_SUFFIX = .boot define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary $< $@ endef OP_SYS_LDLIBS += -lbspExt include $(CONFIG)/os/CONFIG.Common.RTEMS base-7.0.3.1/configure/os/CONFIG.Common.RTEMS-pc3860000664000577000060420000000105713557101274017671 0ustar anjaesctl# # Author: W. Eric Norum # Canadian Light Source # eric@cls.usask.ca # # All RTEMS targets use the same Makefile fragment # RTEMS_BSP = pc386 RTEMS_TARGET_CPU = i386 MUNCH_SUFFIX = .boot define MUNCH_CMD $(RM) $*.bin $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< $*.bin $(BIN2BOOT) $@ 0x00097E00 \ $(PROJECT_RELEASE)/lib/start16.bin 0x00097C00 0 $*.bin 0x00100000 0 endef include $(CONFIG)/os/CONFIG.Common.RTEMS # # Put text segment where it will work with etherboot # OP_SYS_LDFLAGS += -Wl,-Ttext,0x100000 base-7.0.3.1/configure/os/CONFIG.Common.RTEMS-pc386-qemu0000664000577000060420000000050013557101274020626 0ustar anjaesctl# CONFIG.Common.RTEMS-pc386-qemu # # Definitions for the RTEMS-pc386-qemu target # Site-specific overrides go in CONFIG_SITE.Common.RTEMS-pc386-qemu # #------------------------------------------------------- # Include definitions from RTEMS-pc386 include $(CONFIG)/os/CONFIG.Common.RTEMS-pc386 RTEMS_QEMU_FIXUPS = YES base-7.0.3.1/configure/os/CONFIG.Common.RTEMS-psim0000664000577000060420000000034513557101274017775 0ustar anjaesctl# # Author: W. Eric Norum # University of Saskatchewan # eric.norum@usask.ca # # All RTEMS targets use the same Makefile fragment # RTEMS_BSP = psim RTEMS_TARGET_CPU = ppc include $(CONFIG)/os/CONFIG.Common.RTEMS base-7.0.3.1/configure/os/CONFIG.Common.RTEMS-uC52820000664000577000060420000000057613557101274017723 0ustar anjaesctl# # Author: W. Eric Norum # Canadian Light Source # eric@cls.usask.ca # # All RTEMS targets use the same Makefile fragment # RTEMS_BSP = uC5282 RTEMS_TARGET_CPU = m68k ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL MUNCH_SUFFIX = .boot define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< $@ endef include $(CONFIG)/os/CONFIG.Common.RTEMS base-7.0.3.1/configure/os/CONFIG.Common.UnixCommon0000664000577000060420000001023513557101274020230 0ustar anjaesctl# CONFIG.Common.UnixCommon # # Contains definitions common to all Unix target archs # # This file is maintained by the build community. # Sites may override definitions in CONFIG_SITE.Common.UnixCommon # or CONFIG_SITE..UnixCommon #------------------------------------------------------- # Unix valid build types VALID_BUILDS = Host Ioc #------------------------------------------------------- # Unix prefix and suffix definitions EXE = OBJ = .o #Library prefix and suffixes LIB_PREFIX = lib LIB_SUFFIX = .a SHRLIB_SUFFIX_BASE = .so SHRLIB_SUFFIX = $(SHRLIB_SUFFIX_BASE)$(addprefix .,$(SHRLIB_VERSION)) LOADABLE_SHRLIB_SUFFIX = $(SHRLIB_SUFFIX_BASE)$(addprefix .,$(LOADABLE_SHRLIB_VERSION)) LOADABLE_SHRLIB_PREFIX = lib #------------------------------------------------------- # names of libraries to build # -> lib.a LIBNAME = $(BUILD_LIBRARY:%=$(LIB_PREFIX)%$(LIB_SUFFIX)) TESTLIBNAME = $(TESTBUILD_LIBRARY:%=$(LIB_PREFIX)%$(LIB_SUFFIX)) # -> lib.so. SHRLIBNAME_YES = $(BUILD_LIBRARY:%=$(SHRLIB_PREFIX)%$(SHRLIB_SUFFIX)) TESTSHRLIBNAME_YES = $(TESTBUILD_LIBRARY:%=$(SHRLIB_PREFIX)%$(SHRLIB_SUFFIX)) LOADABLE_SHRLIBNAME = $(LOADABLE_BUILD_LIBRARY:%=$(LOADABLE_SHRLIB_PREFIX)%$(LOADABLE_SHRLIB_SUFFIX)) #------------------------------------------------------- # shrlib: SHRLIB_DEPLIBS, SHRLIB_LDLIBS and SHRLIBDIR_LDFLAGS definitions # SHRLIB_LIBS deprecated LIB_LIBS += $(SHRLIB_LIBS) SHRLIB_DEPLIBS = $(foreach lib, $(LIB_LIBS) $(USR_LIBS), \ $(firstword $(wildcard \ $(addsuffix /$(DLLSTUB_PREFIX)$(lib)$(DLLSTUB_SUFFIX), \ $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \ $(addsuffix /$(SHRLIB_PREFIX)$(lib)*$(SHRLIB_SUFFIX_BASE)*, \ $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \ $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \ ) $(addsuffix /$(BUILDLIB_PREFIX)$(lib)$(BUILDLIB_SUFFIX), \ $(if $(filter $(lib),$(TESTLIBRARY)),.,$(INSTALL_LIB))))) SHRLIB_LDLIBS = $(addprefix -l, $($*_LDLIBS) $(LIB_LIBS) $(USR_LIBS)) \ $(STATIC_LDLIBS) \ $(addprefix -l, $($*_SYS_LIBS) $(LIB_SYS_LIBS) $(USR_SYS_LIBS)) \ $(LDLIBS) SHRLIB_DEPLIB_DIRS = $(foreach word, \ $(sort $(INSTALL_LIB)/ $(dir $($*_DEPLIBS) $(SHRLIB_DEPLIBS))), \ $(shell $(FULLPATHNAME) $(word))) SHRLIBDIR_LDFLAGS += $(SHRLIB_DEPLIB_DIRS:%=-L%) #------------------------------------------------------- # Prod: PROD_DEPLIBS, PROD_LDLIBS and PRODDIR_LDFLAGS definitions PROD_DEPLIBS = $(foreach lib, $(PROD_LIBS) $(USR_LIBS), \ $(firstword $(wildcard \ $(addsuffix /$(DLLSTUB_PREFIX)$(lib)$(DLLSTUB_SUFFIX), \ $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \ $(addsuffix /$(SHRLIB_PREFIX)$(lib)*$(SHRLIB_SUFFIX_BASE)*, \ $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \ $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \ ) $(addsuffix /$(BUILDLIB_PREFIX)$(lib)$(BUILDLIB_SUFFIX), \ $(if $(filter $(lib),$(TESTLIBRARY)),.,$(INSTALL_LIB))))) PROD_LDLIBS = $(addprefix -l, $($*_LDLIBS) $(PROD_LIBS) $(USR_LIBS)) \ $(STATIC_LDLIBS) \ $(addprefix -l, $($*_SYS_LIBS) $(PROD_SYS_LIBS) $(USR_SYS_LIBS)) LDLIBS_STATIC_YES = LDLIBS LDLIBS_SHARED_NO = LDLIBS PROD_LDLIBS += $($(firstword $(LDLIBS_STATIC_$(STATIC_BUILD)) \ $(LDLIBS_SHARED_$(SHARED_LIBRARIES)))) PROD_DEPLIB_DIRS = $(foreach word, \ $(sort $(INSTALL_LIB)/ $(dir $($*_DEPLIBS) $(PROD_DEPLIBS))), \ $(shell $(FULLPATHNAME) $(word))) PRODDIR_LDFLAGS += $(PROD_DEPLIB_DIRS:%=-L%) #-------------------------------------------------- # Link definitions LINK.cpp = $(CCC) -o $@ $(STATIC_LDFLAGS) $(PRODDIR_LDFLAGS) $(LDFLAGS) LINK.cpp += $(PROD_LDFLAGS) $(PROD_LD_OBJS) $(PROD_LD_RESS) $(PROD_LDLIBS) LINK.shrlib = $(CCC) -o $@ $(TARGET_LIB_LDFLAGS) $(SHRLIBDIR_LDFLAGS) $(LDFLAGS) LINK.shrlib += $(LIB_LDFLAGS) $(LIBRARY_LD_OBJS) $(LIBRARY_LD_RESS) $(SHRLIB_LDLIBS) #-------------------------------------------------- # Operating system definitions OP_SYS_CPPFLAGS += -DUNIX OP_SYS_LDLIBS += -lm #-------------------------------------------------- # Allow site overrides -include $(CONFIG)/os/CONFIG_SITE.Common.UnixCommon -include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).UnixCommon base-7.0.3.1/configure/os/CONFIG.Common.cygwin-x860000664000577000060420000000576013557101274020066 0ustar anjaesctl# CONFIG.Common.cygwin-x86 # # This file is maintained by the build community. # # Definitions for cygwin-x86 target builds # Sites may override these definitions in CONFIG_SITE.Common.cygwin-x86 #------------------------------------------------------- # Include definitions common to all Unix targets include $(CONFIG)/os/CONFIG.Common.UnixCommon OS_CLASS = cygwin32 ARCH_CLASS = x86 # Link libraries controlled by COMMANDLINE_LIBRARY # The Cygwin version 1.7.15 needs readline and ncursesw, # older ones may need readline and curses. LDLIBS_READLINE_NCURSESW = -lreadline -lncursesw LDLIBS_READLINE_CURSES = -lreadline -lcurses LDLIBS_READLINE = -lreadline POSIX_CPPFLAGS = -D_POSIX_THREADS -D_POSIX_TIMERS POSIX_LDLIBS += -lpthread ARCH_DEP_CFLAGS += -m32 ARCH_DEP_LDFLAGS += -m32 # 32-bit compiler defines _X86_ 1 # Compiler defines __CYGWIN__ 1 # 32-bit compiler defines __CYGWIN32__ 1 # Compiler defines __unix__ 1 # Compiler defines __unix 1 # Compiler defines unix 1 # This macro now deprecated, use __CYGWIN__ in the future OP_SYS_CPPFLAGS += -DCYGWIN32 EXE = .exe # Use .o for static object files, .obj for shared library object files OBJ_NO = .o OBJ_YES = .obj OBJ = $(OBJ_$(SHARED_LIBRARIES)) COMPILE.c += $(if $(filter %$(OBJ),$@),-o $@) COMPILE.cpp += $(if $(filter %$(OBJ),$@),-o $@) HDEPENDS_ARCHFLAGS = -MT $*$(OBJ) BUILD_DLL_CFLAGS_YES = -DEPICS_BUILD_DLL BUILD_DLL_CFLAGS_NO = BUILD_DLL_CFLAGS = $(BUILD_DLL_CFLAGS_$(SHARED_LIBRARIES)) STATIC_CFLAGS_YES = $(BUILD_DLL_CFLAGS) STATIC_CFLAGS_NO = $(BUILD_DLL_CFLAGS) -DEPICS_CALL_DLL STATIC_CXXFLAGS_YES = $(BUILD_DLL_CFLAGS) STATIC_CXXFLAGS_NO = $(BUILD_DLL_CFLAGS) -DEPICS_CALL_DLL # Adjust names of libraries to build # SHRLIB_PREFIX = SHRLIB_SUFFIX_BASE = .dll SHRLIB_SUFFIX = $(SHRLIB_SUFFIX_BASE) SHRLIBNAME_YES = $(BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX)) TESTSHRLIBNAME_YES = $(TESTBUILD_LIBRARY:%=%$(SHRLIB_SUFFIX_BASE)) LOADABLE_SHRLIBNAME = $(LOADABLE_BUILD_LIBRARY:%=%$(LOADABLE_SHRLIB_SUFFIX)) # # When SHARED_LIBRARIES is YES we are building a DLL link library # When SHARED_LIBRARIES is NO we are building an object library # LIB_PREFIX_NO = LIB_SUFFIX_NO = .lib LIB_PREFIX_YES = lib LIB_SUFFIX_YES = .dll.a LIB_PREFIX = $(LIB_PREFIX_$(SHARED_LIBRARIES)) LIB_SUFFIX = $(LIB_SUFFIX_$(SHARED_LIBRARIES)) DLLSTUB_PREFIX = lib DLLSTUB_SUFFIX = .dll.a DLLSTUB_LIBNAME_YES = $(BUILD_LIBRARY:%=$(LIB_PREFIX)%$(LIB_SUFFIX)) DLLSTUB_LIBNAME = $(DLLSTUB_LIBNAME_$(SHARED_LIBRARIES)) TESTDLLSTUB_LIBNAME_YES = $(TESTBUILD_LIBRARY:%=$(LIB_PREFIX)%$(LIB_SUFFIX)) TESTDLLSTUB_LIBNAME = $(TESTDLLSTUB_LIBNAME_$(SHARED_LIBRARIES)) TESTLIBNAME_NO = $(TESTBUILD_LIBRARY:%=$(LIB_PREFIX)%$(LIB_SUFFIX)) TESTLIBNAME = $(TESTLIBNAME_$(SHARED_LIBRARIES)) # dll install location INSTALL_SHRLIB = $(INSTALL_BIN) # Cygwin supports the sunrpc package in versions before 1.7. # Cygwin supports the tirpc (Transport Independent RPC) package in versions 1.7 and later. # uname -r return a string like "1.76(0230/5/3)" CYGWIN_RPC_LIB= $(if $(findstring 1.5,$(shell uname -r)),rpc,tirpc) base-7.0.3.1/configure/os/CONFIG.Common.cygwin-x86_640000664000577000060420000000054613557101274020374 0ustar anjaesctl# CONFIG.Common.cygwin-x86_64 # # This file is maintained by the build community. # # Definitions for cygwin-x86_64 target builds # Sites may override these definitions in CONFIG_SITE.Common.cygwin-x86_64 #------------------------------------------------------- include $(CONFIG)/os/CONFIG.Common.cygwin-x86 ARCH_DEP_CFLAGS = -m64 ARCH_DEP_LDFLAGS = -m64 base-7.0.3.1/configure/os/CONFIG.Common.darwin-ppc0000664000577000060420000000071213557101274020177 0ustar anjaesctl# CONFIG.Common.darwin-ppc # # This file is maintained by the build community. # # Definitions for darwin-ppc target builds # Sites may override these definitions in CONFIG_SITE.Common.darwin-ppc #------------------------------------------------------- # # To build universal binaries, configure ARCH_CLASS # in the file CONFIG_SITE.Common.darwin-ppc # Include definitions common to all Darwin targets include $(CONFIG)/os/CONFIG.darwinCommon.darwinCommon base-7.0.3.1/configure/os/CONFIG.Common.darwin-ppcx860000664000577000060420000000074713557101274020555 0ustar anjaesctl# CONFIG.Common.darwin-ppcx86 # # This file is maintained by the build community. # # Definitions for Darwin universal PowerPC + x86 target builds # Sites may override these definitions in CONFIG_SITE.Common.darwin-ppcx86 #------------------------------------------------------- # # To build universal binaries, configure ARCH_CLASS # in the file CONFIG_SITE.Common.darwin-ppcx86 # Include definitions common to all Darwin targets include $(CONFIG)/os/CONFIG.darwinCommon.darwinCommon base-7.0.3.1/configure/os/CONFIG.Common.darwin-x860000664000577000060420000000071213557101274020042 0ustar anjaesctl# CONFIG.Common.darwin-x86 # # This file is maintained by the build community. # # Definitions for darwin-x86 target builds # Sites may override these definitions in CONFIG_SITE.Common.darwin-x86 #------------------------------------------------------- # # To build universal binaries, configure ARCH_CLASS # in the file CONFIG_SITE.Common.darwin-x86 # Include definitions common to all Darwin targets include $(CONFIG)/os/CONFIG.darwinCommon.darwinCommon base-7.0.3.1/configure/os/CONFIG.Common.freebsd-x860000664000577000060420000000157313557101274020176 0ustar anjaesctl# # This file is maintained by the build community. # # Definitions for freebsd-x86 target builds # Sites may override these definitions in CONFIG_SITE.Common.freebsd-x86 #------------------------------------------------------- # Include definitions common to all freebsd targets include $(CONFIG)/os/CONFIG.Common.freebsdCommon ARCH_CLASS = x86 ARCH_DEP_CPPFLAGS += -D_X86_ ifeq ($(BUILD_CLASS),CROSS) ifeq ($(EPICS_HOST_ARCH),freebsd-x86) # Added for 386,486,... cross builds CMPLR_PREFIX= CROSS_INCLUDES= CROSS_LDFLAGS= # Use -w not -Wall #WARN_CFLAGS_YES = -w #WARN_CXXFLAGS_YES = -w -include $(CONFIG)/os/CONFIG_SITE.Common.freebsd-x86 -include $(CONFIG)/os/CONFIG.freebsd-x86.freebsd-x86 -include $(CONFIG)/os/CONFIG_SITE.freebsd-x86.freebsd-x86 else GNU_TARGET=i586-pc-freebsd-gnu CMPLR_SUFFIX= CMPLR_PREFIX=$(addsuffix -,$(GNU_TARGET)) endif endif base-7.0.3.1/configure/os/CONFIG.Common.freebsd-x86_640000664000577000060420000000163113557101274020502 0ustar anjaesctl# # This file is maintained by the build community. # # Definitions for freebsd-x86_64 target builds # Sites may override these definitions in CONFIG_SITE.Common.freebsd-x86_64 #------------------------------------------------------- # Include definitions common to all freebsd targets include $(CONFIG)/os/CONFIG.Common.freebsdCommon ARCH_CLASS = x86_64 ARCH_DEP_CPPFLAGS += -D_X86_64_ ifeq ($(BUILD_CLASS),CROSS) ifeq ($(EPICS_HOST_ARCH),freebsd-x86_64) # Added for 386,486,... cross builds CMPLR_PREFIX= CROSS_INCLUDES= CROSS_LDFLAGS= # Use -w not -Wall #WARN_CFLAGS_YES = -w #WARN_CXXFLAGS_YES = -w -include $(CONFIG)/os/CONFIG_SITE.Common.freebsd-x86_64 -include $(CONFIG)/os/CONFIG.freebsd-x86_64.freebsd-x86_64 -include $(CONFIG)/os/CONFIG_SITE.freebsd-x86_64.freebsd-x86_64 else GNU_TARGET=i586-pc-freebsd-gnu CMPLR_SUFFIX= CMPLR_PREFIX=$(addsuffix -,$(GNU_TARGET)) endif endif base-7.0.3.1/configure/os/CONFIG.Common.freebsdCommon0000664000577000060420000000232013557101274020713 0ustar anjaesctl# # This file is maintained by the build community. # # Definitions for freebsd target builds # Sites may override these definitions in CONFIG_SITE.Common.freebsdCommon #------------------------------------------------------- # Include definitions common to all Unix targets include $(CONFIG)/os/CONFIG.Common.UnixCommon OS_CLASS = freebsd CODE_CPPFLAGS = -D_REENTRANT POSIX_CPPFLAGS = -D_POSIX_THREADS POSIX_LDLIBS = -lpthread # -D_BSD_SOURCE for gethostname() in unistd.h as needed by cacChannelIO.cpp. OP_SYS_CPPFLAGS += -D_BSD_SOURCE OP_SYS_CPPFLAGS += -Dfreebsd # Set runtime path for shared libraries SHRLIBDIR_RPATH_LDFLAGS_YES += $(SHRLIB_DEPLIB_DIRS:%=-Wl,-rpath,%) SHRLIBDIR_LDFLAGS += $(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) # Set runtime path for products PRODDIR_RPATH_LDFLAGS_YES += $(PROD_DEPLIB_DIRS:%=-Wl,-rpath,%) PRODDIR_LDFLAGS += $(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) # Definitions used when COMMANDLINE_LIBRARY is READLINE LDLIBS_READLINE = -lreadline -lcurses GNU_LDLIBS_YES = -lgcc_pic #-------------------------------------------------- # Allow site overrides -include $(CONFIG)/os/CONFIG_SITE.Common.freebsdCommon -include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).freebsdCommon base-7.0.3.1/configure/os/CONFIG.Common.ios-arm0000664000577000060420000000122413557101274017501 0ustar anjaesctl# CONFIG.Common.ios-arm # # This file is maintained by the build community. # # Definitions for ios-arm target builds # Sites may override these definitions in CONFIG_SITE.Common.ios-arm # or CONFIG_SITE..ios-arm #------------------------------------------------------- IOS_PLATFORM = iPhoneOS OP_SYS_CFLAGS += -fno-inline-functions OP_SYS_CFLAGS += -miphoneos-version-min=$(IOS_DEPLOYMENT_TARGET) OP_SYS_LDFLAGS += -miphoneos-version-min=$(IOS_DEPLOYMENT_TARGET) # iOS optimization flags for arm architecture OPT_CFLAGS_YES = -O2 OPT_CXXFLAGS_YES = -O2 # Include definitions common to all iphone targets include $(CONFIG)/os/CONFIG.Common.iosCommon base-7.0.3.1/configure/os/CONFIG.Common.ios-x860000664000577000060420000000104013557101274017343 0ustar anjaesctl# CONFIG.Common.ios-x86 # # This file is maintained by the build community. # # Definitions for ios-x86 target builds # Sites may override these definitions in CONFIG_SITE.Common.ios-x86 # or CONFIG_SITE..ios-x86 #------------------------------------------------------- IOS_PLATFORM = iPhoneSimulator OP_SYS_CFLAGS += -mios-simulator-version-min=$(IOS_DEPLOYMENT_TARGET) OP_SYS_LDFLAGS += -mios-simulator-version-min=$(IOS_DEPLOYMENT_TARGET) # Include definitions common to all iOS targets include $(CONFIG)/os/CONFIG.Common.iosCommon base-7.0.3.1/configure/os/CONFIG.Common.iosCommon0000664000577000060420000000724113557101274020102 0ustar anjaesctl# CONFIG.Common.iosCommon # # This file is maintained by the build community. # # Definitions for all Apple iOS builds # Sites may override these definitions in CONFIG_SITE.Common.iosCommon # or CONFIG_SITE..iosCommon #------------------------------------------------------- # Include definitions common to all Unix targets include $(CONFIG)/os/CONFIG.Common.UnixCommon # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon #------------------------------------------------------- # Valid build types VALID_BUILDS = Ioc #------------------------------------------------------- # operating system class (include/os/) OS_CLASS = iOS #-------------------------------------------------- # GNU and SDK directories GNU_DIR = $(PLATFORM_DIR)/Developer/usr SDK_DIR = $(PLATFORM_DIR)/Developer/SDKs/$(IOS_PLATFORM).sdk #------------------------------------------------------- # Build architecture flags # ARCH_CLASS must contain a list of CPU architectures which must be # valid arguments to the -arch options for the cc and ld commands. # ARCH_CLASS is defined in a CONFIG_SITE file which is not loaded # until after this file. # ARCH_DEP_FLAGS = $(addprefix -arch ,$(ARCH_CLASS)) ARCH_DEP_CFLAGS += $(ARCH_DEP_FLAGS) ARCH_DEP_LDFLAGS += $(ARCH_DEP_FLAGS) #-------------------------------------------------- # Operating system 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: CC_GNU = gcc CCC_GNU = g++ CMPLR_CLASS_GNU = gcc CC_LLVM_GNU = llvm-gcc CCC_LLVM_GNU = llvm-g++ CMPLR_CLASS_LLVM_GNU = gcc CC_CLANG = clang CCC_CLANG = clang++ CMPLR_CLASS_CLANG = clang CMPLR_CLASS = $(CMPLR_CLASS_$(COMPILER)) # Convert the iOS platform to lowercase for passing to xcrun's sdk parameter XCRUN_SDK_BASE = $(shell echo $(IOS_PLATFORM) | tr A-Z a-z) #------------------------------------------------------- # Linker flags GNU_LDLIBS_YES = OP_SYS_LDFLAGS += -dynamic -Z -L$(SDK_DIR)/usr/lib -L$(SDK_DIR)/usr/lib/system #------------------------------------------------------- # Shared libraries SHRLIB_VERSION = $(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION) SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined suppress \ -install_name $(shell $(FULLPATHNAME) $(INSTALL_LIB))/$@ \ -compatibility_version $(EPICS_VERSION).$(EPICS_REVISION) \ -current_version $(SHRLIB_VERSION) SHRLIB_SUFFIX_BASE = .dylib SHRLIB_SUFFIX = .$(SHRLIB_VERSION)$(SHRLIB_SUFFIX_BASE) LOADABLE_SHRLIB_LDFLAGS = -bundle -flat_namespace -undefined suppress #-------------------------------------------------- # code flags CODE_CFLAGS = -fno-common -Wno-unused-value CODE_CXXFLAGS = -fno-common # # Add support for Objective-C source # vpath %.m $(USR_VPATH) $(ALL_SRC_DIRS) %.o: %.m $(COMPILE.c) -c $< #-------------------------------------------------- # Header dependency file generation # HDEPENDS_METHOD = MKMF #-------------------------------------------------- # Allow site overrides -include $(CONFIG)/os/CONFIG_SITE.Common.iosCommon -include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).iosCommon #-------------------------------------------------- # Find the Xcode programs for the selected SDK CC := $(shell xcrun -sdk $(XCRUN_SDK_BASE) -find $(CC_$(COMPILER))) CCC := $(shell xcrun -sdk $(XCRUN_SDK_BASE) -find $(CCC_$(COMPILER))) AR := $(shell xcrun -sdk $(XCRUN_SDK_BASE) -find ar) -rc LD := $(shell xcrun -sdk $(XCRUN_SDK_BASE) -find ld) -r RANLIB := $(shell xcrun -sdk $(XCRUN_SDK_BASE) -find ranlib) base-7.0.3.1/configure/os/CONFIG.Common.linux-3860000664000577000060420000000120613557101274017607 0ustar anjaesctl# CONFIG.Common.linux-386 # # This file is maintained by the build community. # # Definitions for linux-386 target builds # Sites may override these definitions in CONFIG_SITE.Common.linux-386 #------------------------------------------------------- # Include definitions common to all linux x86 targets include $(CONFIG)/os/CONFIG.Common.linux-x86 ARCH_DEP_CFLAGS = -march=i386 ifeq ($(BUILD_CLASS),CROSS) VALID_BUILDS = Ioc endif # If your crosscompiler name has a GNU target prefix like -gcc, # e.g. i386-pc-linux-gnu-gcc, put a GNU_TARGET definition in # CONFIG_SITE..linux-386 file, e.g. GNU_TARGET=i386-pc-linux-gnu base-7.0.3.1/configure/os/CONFIG.Common.linux-4860000664000577000060420000000112313557101274017606 0ustar anjaesctl# CONFIG.Common.linux-486 # # Definitions for linux-486 target builds # Sites may override these definitions in CONFIG_SITE.Common.linux-486 #------------------------------------------------------- # Include definitions common to all linux x86 targets include $(CONFIG)/os/CONFIG.Common.linux-x86 ARCH_DEP_CFLAGS = -march=i486 ifeq ($(BUILD_CLASS),CROSS) VALID_BUILDS = Ioc endif # If your crosscompiler name has a GNU target prefix like -gcc, # e.g. i486-pc-linux-gnu-gcc, put a GNU_TARGET definition in # CONFIG_SITE..linux-486 file, e.g. GNU_TARGET=i486-pc-linux-gnu base-7.0.3.1/configure/os/CONFIG.Common.linux-5860000664000577000060420000000116413557101274017614 0ustar anjaesctl# CONFIG.Common.linux-586 # # Definitions for linux-586 target builds # Sites may override these definitions in CONFIG_SITE.Common.linux-586 #------------------------------------------------------- # Include definitions common to all linux x86 targets include $(CONFIG)/os/CONFIG.Common.linux-x86 # i586 is equivalent to pentium ARCH_DEP_CFLAGS = -march=i586 ifeq ($(BUILD_CLASS),CROSS) VALID_BUILDS = Ioc endif # If your crosscompiler name has a GNU target prefix like -gcc, # e.g. i586-pc-linux-gnu-gcc, put a GNU_TARGET definition in # CONFIG_SITE..linux-586 file, e.g. GNU_TARGET=i586-pc-linux-gnu base-7.0.3.1/configure/os/CONFIG.Common.linux-6860000664000577000060420000000116513557101274017616 0ustar anjaesctl# CONFIG.Common.linux-686 # # Definitions for linux-686 target builds # Sites may override these definitions in CONFIG_SITE.Common.linux-686 #------------------------------------------------------- # Include definitions common to all linux x86 targets include $(CONFIG)/os/CONFIG.Common.linux-x86 # i686 is euivalent to pentiumpro ARCH_DEP_CFLAGS = -march=i686 ifeq ($(BUILD_CLASS),CROSS) VALID_BUILDS = Ioc endif # If your crosscompiler name has a GNU target prefix like -gcc, # e.g. i686-pc-linux-gnu-gcc, put a GNU_TARGET definition in # CONFIG_SITE..linux-686 file, e.g. GNU_TARGET=i686-pc-linux-gnu base-7.0.3.1/configure/os/CONFIG.Common.linux-arm0000664000577000060420000000045613557101274020054 0ustar anjaesctl# CONFIG.Common.linux-arm # # Definitions for linux-arm target builds # Override these settings in CONFIG_SITE.Common.linux-arm #------------------------------------------------------- # Include definitions common to all Linux targets include $(CONFIG)/os/CONFIG.Common.linuxCommon ARCH_CLASS = arm base-7.0.3.1/configure/os/CONFIG.Common.linux-arm-debug0000664000577000060420000000050713557101274021135 0ustar anjaesctl# CONFIG.Common.linux-arm-debug # # Definitions for linux-arm with debug compiler flags # Override these settings in CONFIG_SITE.Common.linux-arm-debug #------------------------------------------------------- # Include definitions common to all linux-arm target archs include $(CONFIG)/os/CONFIG.Common.linux-arm HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.Common.linux-arm_eb0000664000577000060420000000050113557101274020511 0ustar anjaesctl# CONFIG.Common.linux-arm_eb # # Definitions for linux-arm_eb (big endian) target builds # Sites may override these definitions in CONFIG_SITE.Common.linux-arm_eb #------------------------------------------------------- # Include definitions common to all Linux-arm targets include $(CONFIG)/os/CONFIG.Common.linux-arm base-7.0.3.1/configure/os/CONFIG.Common.linux-arm_el0000664000577000060420000000050413557101274020526 0ustar anjaesctl# CONFIG.Common.linux-arm_el # # Definitions for linux-arm_el (little endian) target builds # Sites may override these definitions in CONFIG_SITE.Common.linux-arm_el #------------------------------------------------------- # Include definitions common to all linux-arm targets include $(CONFIG)/os/CONFIG.Common.linux-arm base-7.0.3.1/configure/os/CONFIG.Common.linux-athlon0000664000577000060420000000116613557101274020561 0ustar anjaesctl# CONFIG.Common.linux-athlon # # Definitions for linux-athlon target builds # Sites may override these definitions in CONFIG_SITE.Common.linux-athlon #------------------------------------------------------- # Include definitions common to all linux x86 targets include $(CONFIG)/os/CONFIG.Common.linux-x86 ARCH_DEP_CFLAGS += -march=athlon-mp -mfpmath=sse ifeq ($(BUILD_CLASS),CROSS) VALID_BUILDS = Ioc endif # If your crosscompiler name has a GNU target prefix like -gcc, # e.g. athlon-pc-linux-gnu-gcc, put a GNU_TARGET definition in # CONFIG_SITE..linux-athlon file, e.g. GNU_TARGET=athlon-pc-linux-gnu base-7.0.3.1/configure/os/CONFIG.Common.linux-cris0000664000577000060420000000337313557101274020236 0ustar anjaesctl# CONFIG.Common.linux-cris # # Author: Peter Zumbruch # GSI # P.Zumbruch@gsi.de # # Definitions for linux-cris target builds # Sites may override these definitions in CONFIG_SITE.Common.linux-cris #------------------------------------------------------- # Include definitions common to all linux targets include $(CONFIG)/os/CONFIG.Common.linuxCommon ARCH_CLASS = cris ifeq ($(BUILD_CLASS),CROSS) GNU_TARGET = cris-axis-linux-gnu # prefix of compiler tools CMPLR_SUFFIX = CMPLR_PREFIX = $(addsuffix -,$(GNU_TARGET)) # CROSS_TOP_DIR # usually AXIS_TOP_DIR is defined via # the init_env script of the SDK provided by Axis # ## AXIS_TOP_DIR defined? Make missing mandatory variable visible AXIS_TOP_DIR?=UNDEFINED_ENV__AXIS_TOP_DIR AXIS_SDK_DIR?=$(AXIS_TOP_DIR) # CROSS_INCLUDES AXIS_SDK_TARGET_INCLUDE_DIR = $(AXIS_SDK_DIR)/target/$(GNU_TARGET)/include AXIS_SDK_TARGET_INCLUDE_DIR +=$(AXIS_SDK_DIR)/target/$(GNU_TARGET)/usr/include CROSS_INCLUDES = $(addprefix -isystem ,$(AXIS_SDK_TARGET_INCLUDE_DIR)) # CROSS_LDFLAGS AXIS_SDK_TARGET_LIB_DIR = $(AXIS_SDK_DIR)/target/$(GNU_TARGET)/lib AXIS_SDK_TARGET_LIB_DIR += $(AXIS_SDK_DIR)/target/$(GNU_TARGET)/usr/lib CROSS_LDFLAGS = $(addprefix -L,$(AXIS_SDK_TARGET_LIB_DIR)) -include $(CONFIG)/os/CONFIG_SITE.Common.linux-cris ifeq ($(EPICS_HOST_ARCH), linux-x86) -include $(CONFIG)/os/CONFIG.linux-x86.linux-cris -include $(CONFIG)/os/CONFIG_SITE.linux-x86.linux-cris endif endif SHARED_LIBRARIES=NO STATIC_BUILD=YES ARCH_DEP_CFLAGS += -mno-mul-bug-workaround OP_SYS_CFLAGS += -mlinux ARCH_DEP_CPPFLAGS += -D_cris_ -mlinux #uncomment CRIS_COMPILER_DEBUG for debugging cris-compiled code #CRIS_COMPILER_DEBUG base-7.0.3.1/configure/os/CONFIG.Common.linux-microblaze0000664000577000060420000000123713557101274021422 0ustar anjaesctl# CONFIG.Common.linux-microblaze # # This file is maintained by the build community. # # Definitions for Xilinx MicroBlaze FPGA Soft Core Processor target builds. # This target has been tested with the Xilinx Spartan 6 MicroBlaze. # Site-specific overrides of these definitions should be made in the file # CONFIG_SITE.Common.linux-microblaze #------------------------------------------------------- # Include definitions common to all Linux targets include $(CONFIG)/os/CONFIG.Common.linuxCommon ARCH_CLASS = microblaze ifeq ($(BUILD_CLASS),CROSS) VALID_BUILDS = Ioc GNU_TARGET = microblazeel-unknown-linux-gnu CMPLR_PREFIX = $(addsuffix -,$(GNU_TARGET)) endif base-7.0.3.1/configure/os/CONFIG.Common.linux-ppc0000664000577000060420000000053013557101274020050 0ustar anjaesctl# CONFIG.Common.linux-ppc # # Definitions for linux-ppc target builds # Sites may override these definitions in CONFIG_SITE.Common.linux-ppc #------------------------------------------------------- # Include definitions common to all Unix targets include $(CONFIG)/os/CONFIG.Common.linuxCommon ARCH_CLASS = ppc ARCH_DEP_CPPFLAGS += -D_ppc_ base-7.0.3.1/configure/os/CONFIG.Common.linux-ppc640000664000577000060420000000062513557101274020227 0ustar anjaesctl# CONFIG.Common.linux-ppc64 # # Definitions for linux-ppc64 target builds # Sites may override these definitions in CONFIG_SITE.Common.linux-ppc64 #------------------------------------------------------- # Include definitions common to all linux targets include $(CONFIG)/os/CONFIG.Common.linuxCommon ARCH_CLASS = ppc64 ARCH_DEP_CPPFLAGS += -D_ppc_64_ ARCH_DEP_CFLAGS += -m64 ARCH_DEP_LDFLAGS += -m64 base-7.0.3.1/configure/os/CONFIG.Common.linux-x860000664000577000060420000000117213557101274017716 0ustar anjaesctl# CONFIG.Common.linux-x86 # # Definitions for linux-x86 target builds # Sites may override these definitions in CONFIG_SITE.Common.linux-x86 #------------------------------------------------------- # Include definitions common to all linux targets include $(CONFIG)/os/CONFIG.Common.linuxCommon ARCH_CLASS = x86 ARCH_DEP_CFLAGS = $(GNU_TUNE_CFLAGS) ARCH_DEP_CPPFLAGS += -D_X86_ OP_SYS_CFLAGS += -m32 OP_SYS_LDFLAGS += -m32 # If your crosscompiler name has a GNU target prefix like -gcc, # e.g. x86-redhat-linux-gcc, put a GNU_TARGET definition in # CONFIG_SITE..linux-x86 file, e.g. GNU_TARGET=x86-redhat-linux base-7.0.3.1/configure/os/CONFIG.Common.linux-x86-debug0000664000577000060420000000052513557101274021003 0ustar anjaesctl# CONFIG.Common.linux-x86-debug # # Definitions for linux-x86 with debug compiler flags # Sites may override these definitions in CONFIG_SITE.Common.linux-x86-debug #------------------------------------------------------- # Include definitions common to all linux-x86 target archs include $(CONFIG)/os/CONFIG.Common.linux-x86 HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.Common.linux-x86_640000664000577000060420000000122213557101274020223 0ustar anjaesctl# CONFIG.Common.linux-x86_64 # # Definitions for linux-x86_64 target builds # Sites may override these definitions in CONFIG_SITE.Common.linux-x86_64 #------------------------------------------------------- # Include definitions common to all linux targets include $(CONFIG)/os/CONFIG.Common.linuxCommon ARCH_CLASS = x86_64 ARCH_DEP_CFLAGS = $(GNU_TUNE_CFLAGS) ARCH_DEP_CPPFLAGS += -D_X86_64_ OP_SYS_CFLAGS += -m64 OP_SYS_LDFLAGS += -m64 # If your crosscompiler name has a GNU target prefix like -gcc, # e.g. x86_64-redhat-linux-gcc, put a GNU_TARGET definition in # CONFIG_SITE..linux-x86_64 file, e.g. GNU_TARGET=x86_64-redhat-linux base-7.0.3.1/configure/os/CONFIG.Common.linux-x86_64-debug0000664000577000060420000000054413557101274021315 0ustar anjaesctl# CONFIG.Common.linux-x86_64-debug # # Definitions for linux-x86_64 with debug compiler flags # Sites may override these definitions in CONFIG_SITE.Common.linux-x86_64-debug #------------------------------------------------------- # Include definitions common to all linux-x86_64 target archs include $(CONFIG)/os/CONFIG.Common.linux-x86_64 HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.Common.linux-xscale_be0000664000577000060420000000171513557101274021221 0ustar anjaesctl# CONFIG.Common.linux-xscale_be # # Definitions for linux-xscale_be (big-endian) target builds. # This target has been tested with the MOXA UC-7408-LX Plus. # Site-specific overrides of these definitions should be made in the file # CONFIG_SITE.Common.linux-xscale_be #------------------------------------------------------- # Include definitions common to all Linux targets include $(CONFIG)/os/CONFIG.Common.linuxCommon ARCH_CLASS = xscale ifeq ($(BUILD_CLASS),CROSS) VALID_BUILDS = Ioc GNU_TARGET = xscale_be CMPLR_PREFIX = $(GNU_TARGET:%=%-) # Configure for readline if requested OP_SYS_INCLUDES += $(READLINE_DIR:%=-I%/include) READLINE_LDFLAGS = $(READLINE_DIR:%=-L%/lib) RUNTIME_LDFLAGS_READLINE_YES_NO = $(READLINE_DIR:%=-Wl,-rpath,%/lib) RUNTIME_LDFLAGS += \ $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH)_$(STATIC_BUILD)) SHRLIBDIR_LDFLAGS += $(READLINE_LDFLAGS) PRODDIR_LDFLAGS += $(READLINE_LDFLAGS) endif base-7.0.3.1/configure/os/CONFIG.Common.linuxCommon0000664000577000060420000000354213557101274020447 0ustar anjaesctl# CONFIG.Common.linuxCommon # # Definitions for linux target builds # Sites may override these definitions in CONFIG_SITE.Common.linuxCommon #------------------------------------------------------- # Include definitions common to all Unix targets include $(CONFIG)/os/CONFIG.Common.UnixCommon OS_CLASS = Linux # Define _GNU_SOURCE and _DEFAULT_SOURCE for maximum portability POSIX_CPPFLAGS = -D_GNU_SOURCE -D_DEFAULT_SOURCE POSIX_LDLIBS = -lpthread OP_SYS_CPPFLAGS += -Dlinux OP_SYS_LDLIBS += -lrt -ldl # Use -rdynamic to maximize symbols available for stacktrace OP_SYS_LDFLAGS += -rdynamic # Linker flags for static & shared-library builds STATIC_LDFLAGS_YES= -Wl,-Bstatic STATIC_LDFLAGS_NO= STATIC_LDLIBS_YES= -Wl,-Bdynamic # Set runtime path for shared libraries if LINKER_USE_RPATH=YES SHRLIBDIR_RPATH_LDFLAGS_YES = $(SHRLIB_DEPLIB_DIRS:%=-Wl,-rpath,%) SHRLIBDIR_RPATH_LDFLAGS_ORIGIN = $(shell $(MAKERPATH) -O '\$$ORIGIN' -F $(FINAL_DIR) -R $(LINKER_ORIGIN_ROOT) $(SHRLIB_DEPLIB_DIRS)) SHRLIBDIR_LDFLAGS += \ $(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) # Set runtime path for products if LINKER_USE_RPATH=YES PRODDIR_RPATH_LDFLAGS_YES = $(PROD_DEPLIB_DIRS:%=-Wl,-rpath,%) PRODDIR_RPATH_LDFLAGS_ORIGIN = $(shell $(MAKERPATH) -O '\$$ORIGIN' -F $(FINAL_DIR) -R $(LINKER_ORIGIN_ROOT) $(PROD_DEPLIB_DIRS)) PRODDIR_LDFLAGS += \ $(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) # Link libraries controlled by COMMANDLINE_LIBRARY # The newest Linux versions only need readline, older ones need both # readline and ncurses, and the oldest need readline and curses LDLIBS_READLINE = -lreadline LDLIBS_READLINE_NCURSES = -lreadline -lncurses LDLIBS_READLINE_CURSES = -lreadline -lcurses #-------------------------------------------------- # Allow site overrides -include $(CONFIG)/os/CONFIG_SITE.Common.linuxCommon -include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).linuxCommon base-7.0.3.1/configure/os/CONFIG.Common.solaris-sparc0000664000577000060420000000405313557101274020717 0ustar anjaesctl# CONFIG.Common.solaris-sparc # # Definitions for solaris-sparc target archs # Sites may override these definitions in CONFIG_SITE.Common.solaris-sparc #------------------------------------------------------- # Include definitions common to all Unix target archs include $(CONFIG)/os/CONFIG.Common.UnixCommon OS_CLASS = solaris ARCH_CLASS = sparc CODE_CPPFLAGS = -D__EXTENSIONS__ COMPILER_CPPFLAGS += -mt COMPILER_LDFLAGS += -mt SOLARIS_VERSION = $(subst 5.,,$(shell uname -r)) POSIX_CFLAGS = -xc99 -D_POSIX_C_SOURCE=200112L POSIX_LDLIBS += -lposix4 -lpthread OP_SYS_CPPFLAGS += -DSOLARIS=$(SOLARIS_VERSION) $(COMPILER_CPPFLAGS) OP_SYS_LDFLAGS += $(COMPILER_LDFLAGS) # Set runtime path for shared libraries SHRLIBDIR_RPATH_LDFLAGS_YES += $(SHRLIB_DEPLIB_DIRS:%=-R%) SHRLIBDIR_LDFLAGS += $(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) # Set runtime path for products PRODDIR_RPATH_LDFLAGS_YES += $(PROD_DEPLIB_DIRS:%=-R%) PRODDIR_LDFLAGS += $(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) GNU_TARGET=sparc-sun-solaris2 STLPORT_CFLAGS_YES= -library=stlport4 STLPORT_CFLAGS_NO= STLPORT_LDLIBS_YES = STLPORT_LDLIBS_NO = -lCstd # can be overridden in CONFIG_SITE.Common.solaris-sparc USE_STLPORT=NO OP_SYS_CFLAGS+=$(STLPORT_CFLAGS_$(USE_STLPORT)) OP_SYS_LDFLAGS+=$(STLPORT_CFLAGS_$(USE_STLPORT)) OP_SYS_LDLIBS += $(STLPORT_LDLIBS_$(USE_STLPORT)) # OS libraries used when generating shared libraries or static binaries OP_SYS_LDLIBS += -lsocket -lnsl OP_SYS_LDLIBS_8 += -ldl -lCrun -lc OP_SYS_LDLIBS_9 += -ldl -lumem -lCrun -lc OP_SYS_LDLIBS_10 += -lumem -lCrun -lc OP_SYS_LDLIBS += $(OP_SYS_LDLIBS_$(SOLARIS_VERSION)) # Definitions used when COMMANDLINE_LIBRARY is READLINE READLINE_DIR = $(GNU_DIR) INCLUDES_READLINE = -I$(READLINE_DIR)/include RUNTIME_LDFLAGS_READLINE_YES += -R$(READLINE_DIR)/lib RUNTIME_LDFLAGS_READLINE += $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH)) LDFLAGS_READLINE += -L$(READLINE_DIR)/lib LDLIBS_READLINE = -lreadline -lcurses # Use archive if there is a problem with the readline shared library #LDLIBS_READLINE = -Bstatic -lreadline -Bdynamic -lcurses base-7.0.3.1/configure/os/CONFIG.Common.solaris-sparc-debug0000664000577000060420000000063613557101274022006 0ustar anjaesctl# CONFIG.Common.solaris-sparc-debug # # Definitions for solaris-sparc with debug compiler flags # Sites may override these definitions in CONFIG_SITE.Common.solaris-sparc-debug #------------------------------------------------------- # Include definitions common to all solaris-sparc target archs include $(CONFIG)/os/CONFIG.Common.solaris-sparc # Removes -O optimization and adds -g compile option HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.Common.solaris-sparc-gnu0000664000577000060420000000103613557101274021504 0ustar anjaesctl# CONFIG.Common.solaris-sparc-gnu # # Definitions for solaris-sparc gnu compiler target archs # Sites may override these definitions in CONFIG_SITE.Common.solaris-sparc-gnu #------------------------------------------------------- # Include definitions common to all solaris-sparc target archs include $(CONFIG)/os/CONFIG.Common.solaris-sparc COMPILER_CPPFLAGS = -D_REENTRANT POSIX_CFLAGS = -std=gnu99 -D_POSIX_C_SOURCE=200112L STLPORT_LDLIBS_NO = OP_SYS_LDLIBS_8 = -ldl OP_SYS_LDLIBS_9 = -ldl OP_SYS_LDLIBS_10 = OP_SYS_LDLIBS_11 = -lc base-7.0.3.1/configure/os/CONFIG.Common.solaris-sparc640000664000577000060420000000060613557101274021071 0ustar anjaesctl# CONFIG.Common.solaris-sparc64 # # Definitions for solaris-sparc64 compiler target archs # Sites may override these definitions in CONFIG_SITE.Common.solaris-sparc64 #------------------------------------------------------- # Include definitions common to all solaris-sparc target archs include $(CONFIG)/os/CONFIG.Common.solaris-sparc ARCH_DEP_CFLAGS += -m64 ARCH_DEP_LDFLAGS += -m64 base-7.0.3.1/configure/os/CONFIG.Common.solaris-sparc64-gnu0000664000577000060420000000075113557101274021661 0ustar anjaesctl# CONFIG.Common.solaris-sparc64-gnu # # Definitions for solaris-sparc64 gnu compiler target archs # Sites may override these definitions in CONFIG_SITE.Common.solaris-sparc64-gnu #------------------------------------------------------- # Include definitions common to all solaris-sparc-gnu target archs include $(CONFIG)/os/CONFIG.Common.solaris-sparc-gnu ARCH_DEP_CFLAGS += -mcpu=v9 -m64 ARCH_DEP_LDFLAGS += -mcpu=v9 -m64 ARCH_DEP_LDFLAGS += -L$(GNU_LIB)/sparcv9 -R$(GNU_LIB)/sparcv9 base-7.0.3.1/configure/os/CONFIG.Common.solaris-x860000664000577000060420000000376113557101274020241 0ustar anjaesctl# CONFIG.Common.solaris-x86 # # Definitions for solaris-x86 target archs # Sites may override these definitions in CONFIG_SITE.Common.solaris-x86 #------------------------------------------------------- # Include definitions common to all Unix target archs include $(CONFIG)/os/CONFIG.Common.UnixCommon OS_CLASS = solaris ARCH_CLASS = x86 CODE_CPPFLAGS = -D__EXTENSIONS__ COMPILER_CPPFLAGS += -mt COMPILER_LDFLAGS += -mt SOLARIS_VERSION = $(subst 5.,,$(shell uname -r)) POSIX_CFLAGS = -xc99 -D_POSIX_C_SOURCE=200112L POSIX_LDLIBS += -lposix4 -lpthread OP_SYS_CPPFLAGS += -DSOLARIS=$(SOLARIS_VERSION) $(COMPILER_CPPFLAGS) OP_SYS_LDFLAGS += $(COMPILER_LDFLAGS) ARCH_DEP_CPPFLAGS = -D_X86_ # Set runtime path for shared libraries SHRLIBDIR_RPATH_LDFLAGS_YES += $(SHRLIB_DEPLIB_DIRS:%=-R%) SHRLIBDIR_LDFLAGS += $(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) # Set runtime path for products PRODDIR_RPATH_LDFLAGS_YES += $(PROD_DEPLIB_DIRS:%=-R%) PRODDIR_LDFLAGS += $(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) GNU_TARGET=x86-sun-solaris2 STLPORT_CFLAGS_YES= -library=stlport4 STLPORT_CFLAGS_NO= STLPORT_LDLIBS_YES = STLPORT_LDLIBS_NO = -lCstd # can be overridden from ...SITE USE_STLPORT=NO OP_SYS_CFLAGS+=$(STLPORT_CFLAGS_$(USE_STLPORT)) OP_SYS_LDFLAGS+=$(STLPORT_CFLAGS_$(USE_STLPORT)) OP_SYS_LDLIBS += -lsocket -lnsl OP_SYS_LDLIBS_8 += -ldl -lCrun -lc OP_SYS_LDLIBS_9 += -ldl -lCrun -lc OP_SYS_LDLIBS_10 += -lCrun -lc OP_SYS_LDLIBS_11 += -lCrun -lc OP_SYS_LDLIBS += $(OP_SYS_LDLIBS_$(SOLARIS_VERSION)) OP_SYS_LDLIBS += $(STLPORT_LDLIBS_$(USE_STLPORT)) # Definitions used when COMMANDLINE_LIBRARY is READLINE READLINE_DIR = $(GNU_DIR) INCLUDES_READLINE = -I$(READLINE_DIR)/include RUNTIME_LDFLAGS_READLINE_YES += -R$(READLINE_DIR)/lib RUNTIME_LDFLAGS_READLINE += $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH)) LDFLAGS_READLINE += -L$(READLINE_DIR)/lib LDLIBS_READLINE = -lreadline -lcurses # Use archive if there is a problem with the readline shared library #LDLIBS_READLINE = -Bstatic -lreadline -Bdynamic -lcurses base-7.0.3.1/configure/os/CONFIG.Common.solaris-x86-gnu0000664000577000060420000000104013557101274021014 0ustar anjaesctl# CONFIG.Common.solaris-x86-gnu # # Definitions for solaris-x86 gnu compiler target archs # Sites may override these definitions in CONFIG_SITE.Common.solaris-x86-gnu #------------------------------------------------------- # Include definitions common to all solaris-x86 target archs include $(CONFIG)/os/CONFIG.Common.solaris-x86 COMPILER_CPPFLAGS = -D_REENTRANT POSIX_CFLAGS = -std=gnu99 -D_POSIX_C_SOURCE=200112L STLPORT_LDLIBS_NO = OP_SYS_LDLIBS_8 = -ldl -lc OP_SYS_LDLIBS_9 = -ldl -lc OP_SYS_LDLIBS_10 = -lc OP_SYS_LDLIBS_11 = -lc base-7.0.3.1/configure/os/CONFIG.Common.solaris-x86_640000664000577000060420000000057713557101274020554 0ustar anjaesctl# CONFIG.Common.solaris-x86_64 # # Definitions for solaris-x86_64 compiler target archs # Sites may override these definitions in CONFIG_SITE.Common.solaris-x86_64 #------------------------------------------------------- # Include definitions common to all solaris-x86 target archs include $(CONFIG)/os/CONFIG.Common.solaris-x86 ARCH_DEP_CFLAGS += -m64 ARCH_DEP_LDFLAGS += -m64 base-7.0.3.1/configure/os/CONFIG.Common.solaris-x86_64-gnu0000664000577000060420000000071413557101274021334 0ustar anjaesctl# CONFIG.Common.solaris-x86_64-gnu # # Definitions for solaris-x86_64 gnu compiler target archs # Sites may override these definitions in CONFIG_SITE.Common.solaris-x86_64-gnu #------------------------------------------------------- # Include definitions common to all solaris-x86-gnu target archs include $(CONFIG)/os/CONFIG.Common.solaris-x86-gnu ARCH_DEP_CFLAGS += -m64 ARCH_DEP_LDFLAGS += -m64 #ARCH_DEP_LDFLAGS += -L$(GNU_LIB)/amd64 -R$(GNU_LIB)/amd64 base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-4860000664000577000060420000000113213557101274020132 0ustar anjaesctl# CONFIG.Common.vxWorks-486 # # Definitions for vxWorks-486 target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-486 #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorksCommon # Vx GNU cross compiler suffix CMPLR_SUFFIX = pentium ARCH_CLASS = x86 ARCH_DEP_CPPFLAGS = -DCPU=I80486 -D_X86_ ARCH_DEP_CFLAGS = -mtune=i486 -march=i486 ARCH_DEP_CFLAGS += -fno-zero-initialized-in-bss -fno-defer-pop # Allow site overrides -include $(CONFIG)/os/CONFIG_SITE.Common.vxWorks-486 base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-486-debug0000664000577000060420000000052013557101274021216 0ustar anjaesctl# CONFIG.Common.vxWorks-486-debug # # Definitions for vxWorks-486-debug target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-486-debug #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorks-486 CROSS_OPT = NO base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-680400000664000577000060420000000102113557101274020267 0ustar anjaesctl# CONFIG.Common.vxWorks-68040 # # Definitions for vxWorks-68040 target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-68040 #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorksCommon # Vx GNU cross compiler suffix CMPLR_SUFFIX = 68k ARCH_CLASS = 68k # Architecture specific build flags ARCH_DEP_CPPFLAGS = -DCPU=MC68040 ARCH_DEP_CFLAGS = -m68040 OPT_CFLAGS_YES = -O0 GNU_TARGET = m68k-wrs-vxworks base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-68040-debug0000664000577000060420000000053013557101274021357 0ustar anjaesctl# CONFIG.Common.vxWorks-68040-debug # # Definitions for vxWorks-68040-debug target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-68040-debug #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorks-68040 CROSS_OPT = NO base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-68040lc0000664000577000060420000000104613557101274020615 0ustar anjaesctl# CONFIG.Common.vxWorks-68040lc # # Definitions for vxWorks-68040lc target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-68040lc #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorksCommon # Vx GNU cross compiler suffix CMPLR_SUFFIX = 68k ARCH_CLASS = 68k # Architecture specific build flags ARCH_DEP_CPPFLAGS = -DCPU=MC68LC040 ARCH_DEP_CFLAGS = -m68040 -msoft-float OPT_CFLAGS_YES = -O0 GNU_TARGET = m68k-wrs-vxworks base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-68040lc-debug0000664000577000060420000000054013557101274021677 0ustar anjaesctl# CONFIG.Common.vxWorks-68040lc-debug # # Definitions for vxWorks-68040lc-debug target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-68040lc-debug #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorks-68040lc CROSS_OPT = NO base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-680600000664000577000060420000000103013557101274020271 0ustar anjaesctl# CONFIG.Common.vxWorks-68060 # # Definitions for vxWorks-68060 target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-68060 #------------------------------------------------------- # Include definitions common to all vxWorks target archs include $(CONFIG)/os/CONFIG.Common.vxWorksCommon # Vx GNU cross compiler suffix CMPLR_SUFFIX = 68k ARCH_CLASS = 68k # Architecture specific build flags ARCH_DEP_CPPFLAGS = -DCPU=MC68060 ARCH_DEP_CFLAGS = -m68040 OPT_CFLAGS_YES = -O0 GNU_TARGET = m68k-wrs-vxworks base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-68060-debug0000664000577000060420000000053013557101274021361 0ustar anjaesctl# CONFIG.Common.vxWorks-68060-debug # # Definitions for vxWorks-68060-debug target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-68060-debug #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorks-68060 CROSS_OPT = NO base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-mpc85400000664000577000060420000000122113557101274020710 0ustar anjaesctl# CONFIG.Common.vxWorks-mpc8540 # # Definitions for vxWorks-mpc8540 target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-mpc8540 #------------------------------------------------------- # Include definitions common to all vxWorks target archs include $(CONFIG)/os/CONFIG.Common.vxWorksCommon GNU_DIR_5 = $(WIND_BASE)/gnu/3.3/$(WIND_HOST_TYPE) # Vx GNU cross compiler suffix CMPLR_SUFFIX = ppc ARCH_CLASS = ppc # Architecture specific build flags ARCH_DEP_CPPFLAGS = -DCPU=PPC85XX ARCH_DEP_CFLAGS = -mcpu=8540 -msoft-float -mspe=no -mabi=no-spe ARCH_DEP_CFLAGS += -mstrict-align -mlongcall GNU_TARGET = powerpc-wrs-vxworks base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-mpc8540-debug0000664000577000060420000000054013557101274021777 0ustar anjaesctl# CONFIG.Common.vxWorks-mpc8540-debug # # Definitions for vxWorks-mpc8540-debug target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-mpc8540-debug #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorks-mpc8540 CROSS_OPT = NO base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-mpc85480000664000577000060420000000122213557101274020721 0ustar anjaesctl# CONFIG.Common.vxWorks-mpc8548 # # Definitions for vxWorks-mpc8548 target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-mpc8548 #------------------------------------------------------- # Include definitions common to all vxWorks target archs include $(CONFIG)/os/CONFIG.Common.vxWorksCommon # Vx GNU cross compiler suffix CMPLR_SUFFIX = ppc ARCH_CLASS = ppc # Architecture specific build flags ARCH_DEP_CPPFLAGS = -DCPU=PPC32 ARCH_DEP_CFLAGS = -DCPU_VARIANT=_ppc85XX_e500v2 ARCH_DEP_CFLAGS += -mlongcall # This flag isn't present in early vxWorks 6.x versions #ARCH_DEP_CFLAGS += -te500v2 GNU_TARGET = powerpc-wrs-vxworks base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-mpc8548-debug0000664000577000060420000000053413557101274022012 0ustar anjaesctl# CONFIG.Common.vxWorks-mpc8548-debug # # Definitions for vxWorks-mpc8548-debug targets. # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-mpc8548-debug #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorks-mpc8548 CROSS_OPT = NO base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-pentium0000664000577000060420000000116713557101274021302 0ustar anjaesctl# CONFIG.Common.vxWorks-pentium # # Definitions for vxWorks-pentium target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-pentium #------------------------------------------------------- # Include definitions common to all vxWorks target archs include $(CONFIG)/os/CONFIG.Common.vxWorksCommon # Vx GNU cross compiler suffix CMPLR_SUFFIX = pentium ARCH_CLASS = x86 ARCH_DEP_CPPFLAGS = -DCPU=PENTIUM -D_X86_ ARCH_DEP_CFLAGS = -mtune=pentium -march=pentium ARCH_DEP_CFLAGS += -fno-zero-initialized-in-bss -fno-defer-pop # Allow site overrides -include $(CONFIG)/os/CONFIG_SITE.Common.vxWorks-pentium base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-pentium-debug0000664000577000060420000000054013557101274022360 0ustar anjaesctl# CONFIG.Common.vxWorks-pentium-debug # # Definitions for vxWorks-pentium-debug target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-pentium-debug #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorks-pentium CROSS_OPT = NO base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-ppc320000664000577000060420000000110113557101274020534 0ustar anjaesctl# # Definitions for vxWorks-ppc603 targets with >32MB of RAM # Site-specific overrides go in CONFIG_SITE.Common.vxWorks-ppc603_long # #------------------------------------------------------- # Include definitions common to all vxWorks target archs include $(CONFIG)/os/CONFIG.Common.vxWorksCommon # Vx GNU cross compiler suffix CMPLR_SUFFIX = ppc ARCH_CLASS = ppc # Architecture specific build flags ARCH_DEP_CPPFLAGS = -DCPU=PPC32 ARCH_DEP_CFLAGS = -mstrict-align GNU_TARGET = powerpc-wrs-vxworks # Tell compiler to generate long branches ARCH_DEP_CFLAGS += -mlongcall base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-ppc32-debug0000664000577000060420000000053013557101274021625 0ustar anjaesctl# CONFIG.Common.vxWorks-ppc32-debug # # Definitions for vxWorks-ppc32-debug target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-ppc32-debug #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc32 CROSS_OPT = NO base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-ppc32sf0000664000577000060420000000052613557101274021077 0ustar anjaesctl# # Definitions for vxWorks-ppc32sf targets # Site-specific overrides go in CONFIG_SITE.Common.vxWorks-ppc32sf # #------------------------------------------------------- # Inherit the settings from vxWorks-ppc32 include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc32 # Tell compiler to use software floating-point ARCH_DEP_CFLAGS += -msoft-float base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-ppc32sf-debug0000664000577000060420000000054013557101274022157 0ustar anjaesctl# CONFIG.Common.vxWorks-ppc32sf-debug # # Definitions for vxWorks-ppc32sf-debug target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-ppc32sf-debug #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc32sf CROSS_OPT = NO base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-ppc6030000664000577000060420000000100713557101274020625 0ustar anjaesctl# # Definitions for vxWorks-ppc603 targets: PPC603 and PMC8240 CPUs # Site-specific overrides go in CONFIG_SITE.Common.vxWorks-ppc603 # #------------------------------------------------------- # Include definitions common to all vxWorks target archs include $(CONFIG)/os/CONFIG.Common.vxWorksCommon # Vx GNU cross compiler suffix CMPLR_SUFFIX = ppc ARCH_CLASS = ppc # Architecture specific build flags ARCH_DEP_CPPFLAGS = -DCPU=PPC603 ARCH_DEP_CFLAGS = -mcpu=603 -mstrict-align GNU_TARGET = powerpc-wrs-vxworks base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-ppc603-debug0000664000577000060420000000053413557101274021715 0ustar anjaesctl# CONFIG.Common.vxWorks-ppc603-debug # # Definitions for vxWorks-ppc603-debug target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-ppc603-debug #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc603 CROSS_OPT = NO base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-ppc603_long0000664000577000060420000000054613557101274021653 0ustar anjaesctl# # Definitions for vxWorks-ppc603 targets with >32MB of RAM # Site-specific overrides go in CONFIG_SITE.Common.vxWorks-ppc603_long # #------------------------------------------------------- # Inherit the settings from vxWorks-ppc603 include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc603 # Tell compiler to generate long branches ARCH_DEP_CFLAGS += -mlongcall base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-ppc603_long-debug0000664000577000060420000000056013557101274022733 0ustar anjaesctl# CONFIG.Common.vxWorks-ppc603_long-debug # # Definitions for vxWorks-ppc603_long-debug target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-ppc603_long-debug #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc603_long CROSS_OPT = NO base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-ppc6040000664000577000060420000000133013557101274020625 0ustar anjaesctl# # Definitions for vxWorks-ppc604 targets: PPC604, MPC7xx and MPC74xx CPUs # Site-specific overrides go in CONFIG_SITE.Common.vxWorks-ppc604 # #------------------------------------------------------- # Include definitions common to all vxWorks target archs include $(CONFIG)/os/CONFIG.Common.vxWorksCommon # Vx GNU cross compiler suffix CMPLR_SUFFIX = ppc ARCH_CLASS = ppc # Architecture specific build flags ARCH_DEP_CPPFLAGS = -DCPU=PPC604 ARCH_DEP_CFLAGS_2 = -mcpu=604 -mstrict-align -mno-implicit-fp ARCH_DEP_CFLAGS_3 = -mcpu=604 -mstrict-align -mno-implicit-fp ARCH_DEP_CFLAGS_4 = -mcpu=604 -mstrict-align -fno-implicit-fp ARCH_DEP_CFLAGS = $(ARCH_DEP_CFLAGS_$(VX_GNU_MAJOR_VERSION)) GNU_TARGET = powerpc-wrs-vxworks base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-ppc604-debug0000664000577000060420000000053413557101274021716 0ustar anjaesctl# CONFIG.Common.vxWorks-ppc604-debug # # Definitions for vxWorks-ppc604-debug target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-ppc604-debug #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc604 CROSS_OPT = NO base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-ppc604_altivec0000664000577000060420000000133613557101274022342 0ustar anjaesctl# # Definitions for vxWorks-ppc604 targets with an Altivec and >32MB of RAM. # Site-specific overrides go in CONFIG_SITE.Common.vxWorks-ppc604_altivec # #------------------------------------------------------- # Inherit the settings from vxWorks-ppc604_long include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc604_long defval = $(if $(strip $(1),),$(1),$(2)) # Tell compiler to include altivec support; not available in 5.4 ALTIVEC_CFLAG_5.4 = THIS_VERSION_OF_VXWORKS DOES_NOT_SUPPORT_ALTIVEC ALTIVEC_CFLAG_5.5 = -fvec -DALTIVEC ALTIVEC_CFLAG_6.0 = -fvec -DALTIVEC # From 6.1 onwards the compiler option changed ALTIVEC_CFLAG = -maltivec -DALTIVEC ARCH_DEP_CFLAGS += $(call defval,$(ALTIVEC_CFLAG_$(VXWORKS_VERSION)),$(ALTIVEC_CFLAG)) base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-ppc604_altivec-debug0000664000577000060420000000057413557101274023431 0ustar anjaesctl# CONFIG.Common.vxWorks-ppc604_altivec-debug # # Definitions for vxWorks-ppc604_altivec-debug target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-ppc604_altivec-debug #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc604_altivec CROSS_OPT = NO base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-ppc604_long0000664000577000060420000000054613557101274021654 0ustar anjaesctl# # Definitions for vxWorks-ppc604 targets with >32MB of RAM # Site-specific overrides go in CONFIG_SITE.Common.vxWorks-ppc604_long # #------------------------------------------------------- # Inherit the settings from vxWorks-ppc604 include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc604 # Tell compiler to generate long branches ARCH_DEP_CFLAGS += -mlongcall base-7.0.3.1/configure/os/CONFIG.Common.vxWorks-ppc604_long-debug0000664000577000060420000000056013557101274022734 0ustar anjaesctl# CONFIG.Common.vxWorks-ppc604_long-debug # # Definitions for vxWorks-ppc604_long-debug target archs # Sites may override these definitions in CONFIG_SITE.Common.vxWorks-ppc604_long-debug #------------------------------------------------------- # Include definitions common to all vxWorks archs include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc604_long CROSS_OPT = NO base-7.0.3.1/configure/os/CONFIG.Common.vxWorksCommon0000664000577000060420000001420413557101274020770 0ustar anjaesctl# CONFIG.Common.vxWorksCommon # # Definitions for vxWorks target archs # Override these definitions in CONFIG_SITE.Common.vxWorksCommon # or CONFIG_SITE..vxWorksCommon #------------------------------------------------------- # Vx valid build types VALID_BUILDS = Ioc #-------------------------------------------------- # operating system class (include/os/) OS_CLASS = vxWorks #------------------------------------------------------- # Prefix and suffix definitions EXE = OBJ = .o LIB_PREFIX = lib LIB_SUFFIX = .a MUNCH_SUFFIX = .munch #------------------------------------------------------- # Compiler definitions CMPLR_PREFIX= CC = $(GNU_BIN)/$(CMPLR_PREFIX)cc$(CMPLR_SUFFIX) CCC = $(GNU_BIN)/$(CMPLR_PREFIX)cc$(CMPLR_SUFFIX) #------------------------------------------------------- # Library definitions LIBNAME = $(BUILD_LIBRARY:%=$(LIB_PREFIX)%$(LIB_SUFFIX)) TESTLIBNAME = $(TESTBUILD_LIBRARY:%=$(LIB_PREFIX)%$(LIB_SUFFIX)) #-------------------------------------------------- # Prod: DEPLIBS, LDFLAGS, and LDLIBS definitions PROD_DEPLIBS = $(foreach lib,$(PROD_LIBS) $(USR_LIBS), \ $(firstword $(wildcard \ $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS))) \ $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ $(if $(filter $(lib),$(TESTLIBRARY)),.,$(INSTALL_LIB))))) PROD_LDLIBS = $(addprefix -l,$($*_LDLIBS) $(PROD_LIBS) $(USR_LIBS) \ $($*_SYS_LIBS) $(PROD_SYS_LIBS) $(USR_SYS_LIBS)) PROD_DEPLIB_DIRS = $(dir $($*_DEPLIBS)) $(dir $(PROD_DEPLIBS)) PRODDIR_LDFLAGS += $(sort $(PROD_DEPLIB_DIRS:%=-L%)) #------------------------------------------------------- # Prod definitions MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) CTDT_SRCS = $(PRODNAME:%$(EXE)=%_ctdt.c) CTDT_OBJS = $(PRODNAME:%$(EXE)=%_ctdt$(OBJ)) NMS = $(PRODNAME:%$(EXE)=%.nm) MUNCH_DEPENDS = %_ctdt$(OBJ) #-------------------------------------------------- # vxWorks version numbers VXWORKS_MAJOR_VERSION = $(basename $(basename $(VXWORKS_VERSION))) # These are needed for vxWorks 6.x; the GNU toolset version number # is in the path to the compiler tools: VX_GNU_VERSION_6.0 = 3.3.2 VX_GNU_VERSION_6.1 = 3.3.2 VX_GNU_VERSION_6.2 = 3.3.2 VX_GNU_VERSION_6.3 = 3.4.4 VX_GNU_VERSION_6.4 = 3.4.4 VX_GNU_VERSION_6.5 = 3.4.4 VX_GNU_VERSION_6.6 = 4.1.2 VX_GNU_VERSION_6.7 = 4.1.2 VX_GNU_VERSION_6.8 = 4.1.2 VX_GNU_VERSION_6.9 = 4.3.3 VX_GNU_VERSION = $(VX_GNU_VERSION_$(VXWORKS_VERSION)) VX_GNU_MAJOR_VERSION = $(basename $(basename $(VX_GNU_VERSION))) #-------------------------------------------------- # Fix old Linux WIND_HOST_TYPE ifeq ($(WIND_HOST_TYPE),x86-linux) WIND_HOST_TYPE = x86-linux2 endif #-------------------------------------------------- # vxWorks directory definitions VX_DIR = $(WIND_BASE)/vxworks-$(VXWORKS_VERSION) GNU_TARGET_INCLUDE_DIR = $(VX_DIR)/target/h $(VX_DIR)/target/h/wrn/coreip #-------------------------------------------------- # vxWorks GNU directories GNU_DIR = $(WIND_BASE)/gnu/$(VX_GNU_VERSION)-vxworks-$(VXWORKS_VERSION)/$(WIND_HOST_TYPE) #-------------------------------------------------- # This finds nm on any supported VxWorks version NMPROG = $(CMPLR_PREFIX)nm$(CMPLR_SUFFIX)$(HOSTEXE) NM = $(firstword $(wildcard $(WIND_BASE)/*/$(WIND_HOST_TYPE)/bin/$(NMPROG))) #-------------------------------------------------- # A linker script is essential for munching from vxWorks 6.6 onwards # (i.e. with versions that use gcc 4.1.2 or later). MUNCH_LDFLAGS_6 = -T $(VX_DIR)/target/h/tool/gnu/ldscripts/link.OUT MUNCH_LDFLAGS = $(MUNCH_LDFLAGS_$(VXWORKS_MAJOR_VERSION)) #-------------------------------------------------- # These are required by some of the Wind River tools export WIND_BASE export WIND_HOME = $(WIND_BASE) export WIND_HOST_TYPE export TOOL_FAMILY = GNU #-------------------------------------------------- # Operating system flags OP_SYS_CPPFLAGS += -DvxWorks=vxWorks OP_SYS_CFLAGS += -fno-builtin # Fix for vxWorks headers that use macros defined in vxWorks.h but # which don't actually include vxWorks.h themselves, for example the # target/h/sys/stat.h file which uses ULONG. This also stops dbDefs.h # from defining the OFFSET macro, which generates lots of warnings. OP_SYS_INCLUDE_CPPFLAGS += -include $(VX_DIR)/target/h/vxWorks.h #-------------------------------------------------- # Optimization: Officially vxWorks only supports -O2 or less. OPT_CFLAGS_YES = -O2 OPT_CXXFLAGS_YES = -O2 #-------------------------------------------------- # code flags CODE_CFLAGS = # # For vxWorks versions before 6.3 we need this g++ compiler flag CODE_CXXFLAGS_6.0 = -fno-implicit-templates CODE_CXXFLAGS_6.1 = -fno-implicit-templates CODE_CXXFLAGS_6.2 = -fno-implicit-templates CODE_CXXFLAGS_6 = $(CODE_CXXFLAGS_$(VXWORKS_VERSION)) CODE_CXXFLAGS = $(CODE_CXXFLAGS_$(VXWORKS_MAJOR_VERSION)) #-------------------------------------------------- # no shared libs for vxWorks SHRLIB_CFLAGS = SHRLIB_LDFLAGS = #-------------------------------------------------- # Don't use gcc 2.x for dependency generation HDEPENDS_METHOD_2 = MKMF HDEPENDS_METHOD = $(firstword $(HDEPENDS_METHOD_$(VX_GNU_MAJOR_VERSION)) COMP) #-------------------------------------------------- # osithead use default stack, YES or NO override OSITHREAD_USE_DEFAULT_STACK = NO #-------------------------------------------------- # Link definitions CROSS_LDFLAGS = # LINK.cpp = $(LD) -o $@ $(STATIC_LDFLAGS) $(PRODDIR_LDFLAGS) $(LDFLAGS) LINK.cpp += $(PROD_LDFLAGS) $(PROD_LD_OBJS) $(PROD_LD_RESS) $(PROD_LDLIBS) #-------------------------------------------------- # Definitions for compile of *_ctdt.c file CFLAGS_ctdt = $(filter-out -pedantic,$(CFLAGS)) -fdollars-in-identifiers COMPILE.ctdt = $(CC) -c $(CPPFLAGS) $(CFLAGS_ctdt) $(INCLUDES) $(SOURCE_FLAG) #-------------------------------------------------- # C preprocessor command VXCPPFLAGS = $(filter-out $(OP_SYS_INCLUDE_CPPFLAGS),$(CPPFLAGS)) PREPROCESS.cpp = $(CPP) $(VXCPPFLAGS) $(INCLUDES) $< > $@ #-------------------------------------------------- # Use LEDLIB for command-line editing COMMANDLINE_LIBRARY = LEDLIB #-------------------------------------------------- # Allow site overrides -include $(CONFIG)/os/CONFIG_SITE.Common.vxWorksCommon -include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).vxWorksCommon base-7.0.3.1/configure/os/CONFIG.Common.win32-x86-mingw0000664000577000060420000000517213557101274020644 0ustar anjaesctl# CONFIG.Common.win32-x86-mingw # # Definitions for win32-x86-mingw target builds # Sites may override these definitions in CONFIG_SITE.Common.win32-x86-mingw #------------------------------------------------------- # Include definitions common to all Unix targets include $(CONFIG)/os/CONFIG.Common.UnixCommon OS_CLASS = WIN32 ARCH_CLASS = x86 POSIX = NO # Definitions used when COMMANDLINE_LIBRARY is READLINE LDLIBS_READLINE = -lreadline -lcurses ARCH_DEP_CFLAGS += -m32 ARCH_DEP_LDFLAGS += -m32 # Compiler defines _X86_ 1 # Compiler defines __MSVCRT__ 1 # Compiler defines __MINGW32__ 1 # Compiler defines __WIN32 1 # Compiler defines __WINNT 1 # Compiler defines __WINNT__ 1 # Compiler defines __WIN32__ 1 # Compiler defines _WIN32 1 # Compiler defines WIN32 1 # Compiler defines WINNT 1 # Compiler does not define __unix __unix__ unix # Override for -DUNIX from CONFIG.Common.UnixCommon OP_SYS_CPPFLAGS = -D_MINGW EXE = .exe RES = .coff # Use .o for static object files, .obj for shared library object files OBJ_NO = .o OBJ_YES = .obj OBJ = $(OBJ_$(SHARED_LIBRARIES)) COMPILE.c += $(if $(filter %$(OBJ),$@),-o $@) COMPILE.cpp += $(if $(filter %$(OBJ),$@),-o $@) HDEPENDS_ARCHFLAGS = -MT $*$(OBJ) BUILD_DLL_CFLAGS_YES = -DEPICS_BUILD_DLL BUILD_DLL_CFLAGS_NO = BUILD_DLL_CFLAGS = $(BUILD_DLL_CFLAGS_$(SHARED_LIBRARIES)) STATIC_CFLAGS_YES = $(BUILD_DLL_CFLAGS) STATIC_CFLAGS_NO = $(BUILD_DLL_CFLAGS) -DEPICS_CALL_DLL STATIC_CXXFLAGS_YES = $(BUILD_DLL_CFLAGS) STATIC_CXXFLAGS_NO = $(BUILD_DLL_CFLAGS) -DEPICS_CALL_DLL # Adjust the names of the libraries to build # # When SHARED_LIBRARIES is YES we build a DLL and its stub library # SHRLIB_PREFIX = SHRLIB_SUFFIX_BASE = .dll SHRLIB_SUFFIX = $(SHRLIB_SUFFIX_BASE) SHRLIBNAME_YES = $(BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX)) TESTSHRLIBNAME_YES = $(TESTBUILD_LIBRARY:%=%$(SHRLIB_SUFFIX_BASE)) LOADABLE_SHRLIB_PREFIX = LOADABLE_SHRLIB_SUFFIX = $(SHRLIB_SUFFIX_BASE) LOADABLE_SHRLIBNAME = $(LOADABLE_BUILD_LIBRARY:%=%$(LOADABLE_SHRLIB_SUFFIX)) DLLSTUB_PREFIX = lib DLLSTUB_SUFFIX = .dll.a DLLSTUB_LIBNAME_YES = $(BUILD_LIBRARY:%=$(DLLSTUB_PREFIX)%$(DLLSTUB_SUFFIX)) DLLSTUB_LIBNAME = $(DLLSTUB_LIBNAME_$(SHARED_LIBRARIES)) TESTDLLSTUB_LIBNAME_YES = $(TESTBUILD_LIBRARY:%=$(DLLSTUB_PREFIX)%$(DLLSTUB_SUFFIX)) TESTDLLSTUB_LIBNAME = $(TESTDLLSTUB_LIBNAME_$(SHARED_LIBRARIES)) # When SHARED_LIBRARIES is NO we build a static archive library # LIB_PREFIX = LIB_SUFFIX = .lib LIBNAME_NO = $(BUILD_LIBRARY:%=%$(LIB_SUFFIX)) LIBNAME = $(LIBNAME_$(SHARED_LIBRARIES)) TESTLIBNAME_NO = $(TESTBUILD_LIBRARY:%=%$(LIB_SUFFIX)) TESTLIBNAME = $(TESTLIBNAME_$(SHARED_LIBRARIES)) # dll install location INSTALL_SHRLIB = $(INSTALL_BIN) base-7.0.3.1/configure/os/CONFIG.Common.windows-x64-mingw0000664000577000060420000000052413557101274021364 0ustar anjaesctl# CONFIG.Common.windows-x64-mingw # # Definitions for windows-x64-mingw target builds # Sites may override these definitions in CONFIG_SITE.Common.windows-x64-mingw #------------------------------------------------------- include $(CONFIG)/os/CONFIG.Common.win32-x86-mingw ARCH_CLASS = x64 ARCH_DEP_CFLAGS = -m64 ARCH_DEP_LDFLAGS = -m64 base-7.0.3.1/configure/os/CONFIG.UnixCommon.Common0000664000577000060420000000055613557101274020235 0ustar anjaesctl# CONFIG.UnixCommon.Common # # Definitions common to unix hosts # Sites may override these definitions in CONFIG_SITE.UnixCommon.Common #------------------------------------------------------- # Unix command definitions CP = cp MV = mv RM = rm -f MKDIR = mkdir -p RMDIR = rm -rf CAT = cat # Allow site overrides -include $(CONFIG)/os/CONFIG_SITE.UnixCommon.Common base-7.0.3.1/configure/os/CONFIG.cygwin-x86.Common0000664000577000060420000000073013557101274020056 0ustar anjaesctl# CONFIG.cygwin-x86.Common # # Definitions for cygwin-x86 host archs # Sites may override these definitions in CONFIG_SITE.cygwin-x86.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common WIND_HOST_TYPE = x86-win32 OSITHREAD_USE_DEFAULT_STACK = NO HOSTEXE=.exe # Needed to find dlls for base installed build tools (antelope,eflex,...) PATH := $(EPICS_BASE_BIN):$(PATH) base-7.0.3.1/configure/os/CONFIG.cygwin-x86.cygwin-x860000664000577000060420000000143613557101274020555 0ustar anjaesctl# CONFIG.cygwin-x86.cygwin-x86 # # Definitions for cygwin-x86 host - cygwin-x86 target builds # Sites may override these definitions in CONFIG_SITE.cygwin-x86.cygwin-x86 #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon # cygwin's gcc, g++, ar, ld, and ranlib must be in user's path CC = gcc CCC = g++ AR = ar -rc LD = ld -r RANLIB = ranlib RES=.coff RCCMD = windres $(INCLUDES) $< $@ # No -fPIC avoids "-fPIC ignored for target (all code is position independent)" SHRLIB_CFLAGS = SHRLIB_LDFLAGS = -shared -Wl,--out-implib,$(LIB_PREFIX)$*$(LIB_SUFFIX) LOADABLE_SHRLIB_LDFLAGS = -shared -Wl,--out-implib,$(LIB_PREFIX)$*$(LIB_SUFFIX) # Override linking with gcc library from CONFIG.gnuCommon GNU_LDLIBS_YES = base-7.0.3.1/configure/os/CONFIG.cygwin-x86.cygwin-x86-debug0000664000577000060420000000074613557101274021644 0ustar anjaesctl# CONFIG.cygwin-x86.cygwin-x86-debug # # Definitions for cygwin-x86 host - cygwin-x86-debug target build # Sites may override these definitions in CONFIG_SITE.cygwin-x86.cygwin-x86-debug #------------------------------------------------------- -include $(CONFIG)/os/CONFIG.Common.cygwin-x86 -include $(CONFIG)/os/CONFIG.cygwin-x86.cygwin-x86 -include $(CONFIG)/os/CONFIG_SITE.Common.cygwin-x86 -include $(CONFIG)/os/CONFIG_SITE.cygwin-x86.cygwin-x86 BUILD_CLASS = HOST HOST_OPT = NO base-7.0.3.1/configure/os/CONFIG.cygwin-x86.linux-arm0000664000577000060420000000222413557101274020542 0ustar anjaesctl# CONFIG.cygwin-x86.linux-arm # # Definitions for cywgin-x86 host - linux-arm target builds # Override these settings in CONFIG_SITE.cygwin-x86.linux-arm #------------------------------------------------------- VALID_BUILDS = Ioc GNU_TARGET = arm-linux # prefix of compiler tools CMPLR_SUFFIX = CMPLR_PREFIX = $(addsuffix -,$(GNU_TARGET)) # Provide a link-time path for shared libraries SHRLIBDIR_RPATH_LDFLAGS_YES += $(SHRLIB_DEPLIB_DIRS:%=-Wl,-rpath-link,%) SHRLIBDIR_LDFLAGS += $(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) # Provide a link-time path for products PRODDIR_RPATH_LDFLAGS_YES += $(PROD_DEPLIB_DIRS:%=-Wl,-rpath-link,%) PRODDIR_LDFLAGS += $(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) # Provide a link-time path for readline RUNTIME_LDFLAGS_READLINE_YES = -Wl,-rpath-link,$(GNU_DIR)/lib RUNTIME_LDFLAGS_READLINE = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH)) RUNTIME_LDFLAGS_READLINE_CURSES = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH)) RUNTIME_LDFLAGS_READLINE_NCURSES = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH)) # Library flags STATIC_LDFLAGS_YES= -Wl,-Bstatic STATIC_LDFLAGS_NO= STATIC_LDLIBS_YES= -Wl,-Bdynamic STATIC_LDLIBS_NO= base-7.0.3.1/configure/os/CONFIG.cygwin-x86_64.Common0000664000577000060420000000037613557101274020375 0ustar anjaesctl# CONFIG.cygwin-x86_64.Common # # Definitions for cygwin-x86_64 host archs # Sites may override these definitions in CONFIG_SITE.cygwin-x86_64.Common #------------------------------------------------------- include $(CONFIG)/os/CONFIG.cygwin-x86.Common base-7.0.3.1/configure/os/CONFIG.cygwin-x86_64.cygwin-x86_640000664000577000060420000000052413557101274021374 0ustar anjaesctl# CONFIG.cygwin-x86_64.cygwin-x86_64 # # Definitions for cygwin-x86_64 host - cygwin-x86_64 target builds # Sites may override these definitions in CONFIG_SITE.cygwin-x86_64.cygwin-x86_64 #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/os/CONFIG.cygwin-x86.cygwin-x86 base-7.0.3.1/configure/os/CONFIG.cygwin-x86_64.linux-arm0000664000577000060420000000041213557101274021050 0ustar anjaesctl# CONFIG.cygwin-x86_64.linux-arm # # Definitions for cygwin-x86_64 host - linux-arkm targets # Override these settings in CONFIG_SITE.cygwin-x86_64.linux-arm #------------------------------------------------------- include $(CONFIG)/os/CONFIG.cygwin-x86.linux-arm base-7.0.3.1/configure/os/CONFIG.darwin-ppc.Common0000664000577000060420000000044013557101274020175 0ustar anjaesctl# CONFIG.darwin-ppc.Common # # Definitions for darwin-ppc host builds # Sites may override these definitions in CONFIG_SITE.darwin-ppc.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common base-7.0.3.1/configure/os/CONFIG.darwin-ppc.darwin-ppc-debug0000664000577000060420000000077513557101274022110 0ustar anjaesctl# CONFIG.darwin-ppc.darwin-ppc-debug # # Definitions for darwin-ppc host - darwin-ppc-debug target build with debug compiler flags # Sites may override these definitions in CONFIG_SITE.darwin-ppc.darwin-ppc-debug #------------------------------------------------------- -include $(CONFIG)/os/CONFIG.Common.darwin-ppc -include $(CONFIG)/os/CONFIG.darwin-ppc.darwin-ppc -include $(CONFIG)/os/CONFIG_SITE.Common.darwin-ppc -include $(CONFIG)/os/CONFIG_SITE.darwin-ppc.darwin-ppc BUILD_CLASS=HOST HOST_OPT = NO base-7.0.3.1/configure/os/CONFIG.darwin-ppcx86.Common0000664000577000060420000000047213557101274020550 0ustar anjaesctl# CONFIG.darwin-ppcx86.Common # # Definitions for Darwin universal PowerPC + x86 host builds # Sites may override these definitions in CONFIG_SITE.darwin-ppcx86.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common base-7.0.3.1/configure/os/CONFIG.darwin-x86.Common0000664000577000060420000000044013557101274020040 0ustar anjaesctl# CONFIG.darwin-x86.Common # # Definitions for darwin-x86 host builds # Sites may override these definitions in CONFIG_SITE.darwin-x86.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common base-7.0.3.1/configure/os/CONFIG.darwin-x86.darwin-x86-debug0000664000577000060420000000077613557101274021617 0ustar anjaesctl# CONFIG.darwin-x86.darwin-x86-debug # # Definitions for darwin-x86 host - darwin-x86-debug target build with debug compiler flags # Sites may override these definitions in CONFIG_SITE.darwin-x86.darwin-x86-debug #------------------------------------------------------- -include $(CONFIG)/os/CONFIG.Common.darwin-x86 -include $(CONFIG)/os/CONFIG.darwin-x86.darwin-x86 -include $(CONFIG)/os/CONFIG_SITE.Common.darwin-x86 -include $(CONFIG)/os/CONFIG_SITE.darwin-x86.darwin-x86 BUILD_CLASS=HOST HOST_OPT = NO base-7.0.3.1/configure/os/CONFIG.darwinCommon.darwinCommon0000664000577000060420000000440513557101274022000 0ustar anjaesctl# CONFIG.darwinCommon.darwinCommon # # Common definitions for darwin builds # Sites may override these definitions in CONFIG_SITE.darwinCommon.darwinCommon #------------------------------------------------------- # Include definitions common to all Unix targets include $(CONFIG)/os/CONFIG.Common.UnixCommon # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon # # Set OS-specific information # OS_CLASS = Darwin # # Build architecture flags # For Darwin, ARCH_CLASS may be empty, or may contain a list of CPU # architectures which must be valid arguments to the -arch options # for the cc and ld commands. # ARCH_CLASS is defined in a CONFIG_SITE file which is not loaded # until after this file. # ARCH_DEP_FLAGS = $(addprefix -arch ,$(ARCH_CLASS)) ARCH_DEP_CFLAGS += $(ARCH_DEP_FLAGS) ARCH_DEP_LDFLAGS += $(ARCH_DEP_FLAGS) # # Special flags for Darwin # No common blocks (as required when using shared libraries) # OP_SYS_CFLAGS += -fno-common # # Darwin os definition # OP_SYS_CPPFLAGS += -Ddarwin # # Always compile in debugging symbol table information # OPT_CFLAGS_YES += -g OPT_CXXFLAGS_YES += -g # # Libraries for command-line editing. # LDLIBS_READLINE = -lreadline # # Command-line input support # COMMANDLINE_LIBRARY=READLINE GNU_DIR = /usr # Apple soft-links these compilers to clang/clang++ CMPLR_CLASS = clang CC = cc CCC = c++ GNU = NO # # Darwin shared libraries # SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined dynamic_lookup \ -install_name $(shell $(FULLPATHNAME) $(INSTALL_LIB))/$@ \ $(addprefix -compatibility_version , $(SHRLIB_VERSION)) \ $(addprefix -current_version , $(SHRLIB_VERSION)) SHRLIB_SUFFIX_BASE = .dylib SHRLIB_SUFFIX = $(addprefix ., $(SHRLIB_VERSION))$(SHRLIB_SUFFIX_BASE) LOADABLE_SHRLIB_LDFLAGS = -bundle -undefined dynamic_lookup # # Position-independent code is the default on Darwin. # CODE_CFLAGS = CODE_CXXFLAGS = # # Add support for Objective-C source # vpath %.m $(USR_VPATH) $(ALL_SRC_DIRS) %.o: %.m $(COMPILE.c) -c $< # # Header dependency file generation # HDEPENDS_METHOD = MKMF #-------------------------------------------------- # Allow site overrides -include $(CONFIG)/os/CONFIG_SITE.darwinCommon.darwinCommon -include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).darwinCommon base-7.0.3.1/configure/os/CONFIG.freebsd-x86.Common0000664000577000060420000000040313557101274020165 0ustar anjaesctl# # Definitions for freebsd host builds # Sites may override these definitions in CONFIG_SITE.freebsd-x86.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common base-7.0.3.1/configure/os/CONFIG.freebsd-x86.freebsd-x860000664000577000060420000000057713557101274021006 0ustar anjaesctl# # Definitions for freebsd-x86 host - freebsd-x86 target builds # Sites may override these definitions in CONFIG_SITE.freebsd-x86.freebsd-x86 #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon STATIC_LDFLAGS_YES= -Wl,-Bstatic STATIC_LDFLAGS_NO= STATIC_LDLIBS_YES= -Wl,-Bdynamic STATIC_LDLIBS_NO= base-7.0.3.1/configure/os/CONFIG.freebsd-x86_64.Common0000664000577000060420000000041013557101274020474 0ustar anjaesctl# # Definitions for freebsd host builds # Sites may override these definitions in CONFIG_SITE.freebsd-x86_64.Common #------------------------------------------------------- # Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common base-7.0.3.1/configure/os/CONFIG.freebsd-x86_64.freebsd-x86_640000664000577000060420000000061313557101274021617 0ustar anjaesctl# # 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 #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon STATIC_LDFLAGS_YES= -Wl,-Bstatic STATIC_LDFLAGS_NO= STATIC_LDLIBS_YES= -Wl,-Bdynamic STATIC_LDLIBS_NO= base-7.0.3.1/configure/os/CONFIG.linux-386.Common0000664000577000060420000000047213557101274017613 0ustar anjaesctl# CONFIG.linux-386.Common # # Definitions for linux-386 host builds # Sites may override these definitions in CONFIG_SITE.linux-386.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common WIND_HOST_TYPE = x86-linux2 base-7.0.3.1/configure/os/CONFIG.linux-386.linux-3860000664000577000060420000000047013557101274020036 0ustar anjaesctl# CONFIG.linux-386.linux-386 # # Definitions for linux-386 host - linux-386 target builds # Sites may override these definitions in CONFIG_SITE.linux-386.linux-386 #------------------------------------------------------- # Include linux-x86 compiler definitions include $(CONFIG)/os/CONFIG.linux-x86.linux-x86 base-7.0.3.1/configure/os/CONFIG.linux-486.Common0000664000577000060420000000047213557101274017614 0ustar anjaesctl# CONFIG.linux-486.Common # # Definitions for linux-486 host builds # Sites may override these definitions in CONFIG_SITE.linux-486.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common WIND_HOST_TYPE = x86-linux2 base-7.0.3.1/configure/os/CONFIG.linux-486.linux-4860000664000577000060420000000047113557101274020041 0ustar anjaesctl# CONFIG.linux-486.linux-486 # # Definitions for linux-486 host - linux-486 target builds # Sites may override these definitions in CONFIG_SITE.linux-486.linux-486 #------------------------------------------------------- # Include linux-x86 compiler definitions include $(CONFIG)/os/CONFIG.linux-x86.linux-x86 base-7.0.3.1/configure/os/CONFIG.linux-586.Common0000664000577000060420000000047213557101274017615 0ustar anjaesctl# CONFIG.linux-586.Common # # Definitions for linux-586 host builds # Sites may override these definitions in CONFIG_SITE.linux-586.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common WIND_HOST_TYPE = x86-linux2 base-7.0.3.1/configure/os/CONFIG.linux-586.linux-5860000664000577000060420000000047113557101274020043 0ustar anjaesctl# CONFIG.linux-586.linux-586 # # Definitions for linux-586 host - linux-586 target builds # Sites may override these definitions in CONFIG_SITE.linux-586.linux-586 #------------------------------------------------------- # Include linux-x86 compiler definitions include $(CONFIG)/os/CONFIG.linux-x86.linux-x86 base-7.0.3.1/configure/os/CONFIG.linux-686.Common0000664000577000060420000000047213557101274017616 0ustar anjaesctl# CONFIG.linux-686.Common # # Definitions for linux-686 host builds # Sites may override these definitions in CONFIG_SITE.linux-686.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common WIND_HOST_TYPE = x86-linux2 base-7.0.3.1/configure/os/CONFIG.linux-686.linux-6860000664000577000060420000000047113557101274020045 0ustar anjaesctl# CONFIG.linux-686.linux-686 # # Definitions for linux-686 host - linux-686 target builds # Sites may override these definitions in CONFIG_SITE.linux-686.linux-686 #------------------------------------------------------- # Include linux-x86 compiler definitions include $(CONFIG)/os/CONFIG.linux-x86.linux-x86 base-7.0.3.1/configure/os/CONFIG.linux-arm-debug.Common0000664000577000060420000000044713557101274021140 0ustar anjaesctl# CONFIG.linux-arm-debug.Common # # Definitions for linux-arm-debug host builds # Override these settings in CONFIG_SITE.linux-arm-debug.Common #------------------------------------------------------- #Include definitions common to linux-arm hosts include $(CONFIG)/os/CONFIG.linux-arm.Common base-7.0.3.1/configure/os/CONFIG.linux-arm-debug.linux-arm-debug0000664000577000060420000000053113557101274022702 0ustar anjaesctl# CONFIG.linux-arm-debug.linux-arm-debug # # Definitions for linux-arm-debug host and target build # Override these settings in CONFIG_SITE.linux-arm-debug.linux-arm-debug #------------------------------------------------------- include $(CONFIG)/os/CONFIG.linux-arm.linux-arm # Removes -O optimization and adds -g compile option HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.linux-arm.Common0000664000577000060420000000043513557101274020051 0ustar anjaesctl# CONFIG.linux-arm.Common # # Definitions for linux-arm host builds # Sites may override these definitions in CONFIG_SITE.linux-arm.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common base-7.0.3.1/configure/os/CONFIG.linux-arm.linux-arm0000664000577000060420000000043213557101274020532 0ustar anjaesctl# CONFIG.linux-arm.linux-arm # # Definitions for native linux-arm builds # Sites may override these definitions in CONFIG_SITE.linux-arm.linux-arm #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon base-7.0.3.1/configure/os/CONFIG.linux-arm.linux-arm-debug0000664000577000060420000000063213557101274021620 0ustar anjaesctl# CONFIG.linux-arm.linux-arm-debug # # Definitions for linux-arm host - linux-arm-debug target build # Override these settings in CONFIG_SITE.linux-arm.linux-arm-debug #------------------------------------------------------- -include $(CONFIG)/os/CONFIG.linux-arm.linux-arm -include $(CONFIG)/os/CONFIG_SITE.Common.linux-arm -include $(CONFIG)/os/CONFIG_SITE.linux-arm.linux-arm BUILD_CLASS=HOST HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.linux-ppc.Common0000664000577000060420000000043613557101274020055 0ustar anjaesctl# CONFIG.linux-ppc.Common # # Definitions for linux-ppc host builds # Sites may override these definitions in CONFIG_SITE.linux-ppc.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common base-7.0.3.1/configure/os/CONFIG.linux-ppc.linux-ppc0000664000577000060420000000045513557101274020545 0ustar anjaesctl# CONFIG.linux-ppc.linux-ppc # # Definitions for linux-ppc host - linux-ppc target builds # Sites may override these definitions in CONFIG_SITE.linux-ppc.linux-ppc #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon base-7.0.3.1/configure/os/CONFIG.linux-ppc64.Common0000664000577000060420000000044413557101274020226 0ustar anjaesctl# CONFIG.linux-ppc64.Common # # Definitions for linux-ppc64 host builds # Sites may override these definitions in CONFIG_SITE.linux-ppc64.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common base-7.0.3.1/configure/os/CONFIG.linux-ppc64.linux-ppc640000664000577000060420000000050613557101274021066 0ustar anjaesctl# CONFIG.linux-ppc64.linux-ppc64 # # Definitions for linux-ppc64 host - linux-ppc64 target builds # Sites may override these definitions in CONFIG_SITE.linux-ppc64.linux-ppc64 #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/os/CONFIG.linux-x86.linux-x86 base-7.0.3.1/configure/os/CONFIG.linux-x86-debug.Common0000664000577000060420000000057613557101274021011 0ustar anjaesctl# CONFIG.linux-x86-debug.Common # # Definitions for linux-x86 debug with debug compiler flags # Sites may override these definitions in CONFIG_SITE.linux-x86-debug.Common #------------------------------------------------------- #Include definitions common to linux-x86 hosts include $(CONFIG)/os/CONFIG.linux-x86.Common # Make all crosscompiler builds debug builds #CROSS_OPT=NO base-7.0.3.1/configure/os/CONFIG.linux-x86-debug.linux-x86-debug0000664000577000060420000000060213557101274022415 0ustar anjaesctl# CONFIG.linux-x86-debug.linux-x86-debug # # Definitions for linux-x86 host - linux-x86 target build with debug compiler flags # Sites may override these definitions in CONFIG_SITE.linux-x86-debug.linux-x86-debug #------------------------------------------------------- include $(CONFIG)/os/CONFIG.linux-x86.linux-x86 # Removes -O optimization and adds -g compile option HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.linux-x86.Common0000664000577000060420000000047213557101274017720 0ustar anjaesctl# CONFIG.linux-x86.Common # # Definitions for linux-x86 host builds # Sites may override these definitions in CONFIG_SITE.linux-x86.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common WIND_HOST_TYPE = x86-linux2 base-7.0.3.1/configure/os/CONFIG.linux-x86.linux-arm0000664000577000060420000000151413557101274020402 0ustar anjaesctl# CONFIG.linux-x86.linux-arm # # Definitions for linux-x86 host - linux-arm target builds # Override these settings in CONFIG_SITE.linux-x86.linux-arm #------------------------------------------------------- VALID_BUILDS = Ioc GNU_TARGET = arm-linux # prefix of compiler tools CMPLR_SUFFIX = CMPLR_PREFIX = $(addsuffix -,$(GNU_TARGET)) # Provide a link-time path for readline if needed OP_SYS_INCLUDES += $(READLINE_DIR:%=-I%/include) READLINE_LDFLAGS = $(READLINE_DIR:%=-L%/lib) RUNTIME_LDFLAGS_READLINE_YES_NO = $(READLINE_DIR:%=-Wl,-rpath,%/lib) RUNTIME_LDFLAGS += \ $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH)_$(STATIC_BUILD)) SHRLIBDIR_LDFLAGS += $(READLINE_LDFLAGS) PRODDIR_LDFLAGS += $(READLINE_LDFLAGS) # Library flags STATIC_LDFLAGS_YES= -Wl,-Bstatic STATIC_LDFLAGS_NO= STATIC_LDLIBS_YES= -Wl,-Bdynamic STATIC_LDLIBS_NO= base-7.0.3.1/configure/os/CONFIG.linux-x86.linux-arm-debug0000664000577000060420000000050013557101274021460 0ustar anjaesctl# CONFIG.linux-x86.linux-arm-debug # # Definitions for linux-x86 host - linux-arm-debug target builds # Override these settings in CONFIG_SITE.linux-x86.linux-arm-debug #------------------------------------------------------- # Include definitions for linux-arm targets include $(CONFIG)/os/CONFIG.linux-x86.linux-arm base-7.0.3.1/configure/os/CONFIG.linux-x86.linux-arm_eb0000664000577000060420000000052113557101274021045 0ustar anjaesctl# CONFIG.linux-x86.linux-arm_eb # # Definitions for linux-x86 host - linux-arm_eb (big endian) target builds # Sites may override these definitions in CONFIG_SITE.linux-x86.linux-arm_eb #------------------------------------------------------- # Include definitions for linux-arm targets include $(CONFIG)/os/CONFIG.linux-x86.linux-arm base-7.0.3.1/configure/os/CONFIG.linux-x86.linux-arm_el0000664000577000060420000000052413557101274021062 0ustar anjaesctl# CONFIG.linux-x86.linux-arm_el # # Definitions for linux-x86 host - linux-arm_el (little endian) target builds # Sites may override these definitions in CONFIG_SITE.linux-x86.linux-arm_el #------------------------------------------------------- # Include definitions for linux-arm targets include $(CONFIG)/os/CONFIG.linux-x86.linux-arm base-7.0.3.1/configure/os/CONFIG.linux-x86.linux-cris0000664000577000060420000000137013557101274020563 0ustar anjaesctl# CONFIG.linux-x86.linux-cris # # Author: Peter Zumbruch # GSI # P.Zumbruch@gsi.de # # Definitions for linux-x86 host - linux-cris target builds # Sites may override these definitions in CONFIG_SITE.linux-x86.linux-cris #------------------------------------------------------- GNU_DIR = $(CRIS_CROSS_COMPILER) #STATIC_... STATIC_LDFLAGS_YES= -Wl,-Bstatic ## debian-gcc Bug#438641 GNU_LDLIBS_YES = STATIC_LDFLAGS_YES += -static-libgcc # if not in debug mode strip all symbols ifndef CRIS_COMPILER_DEBUG STATIC_LDFLAGS_YES += -Wl,--strip-all endif ifeq ($(GNU),YES) STATIC_LDFLAGS_NO = -lgcc else STATIC_LDFLAGS_NO = endif STATIC_LDLIBS_YES= STATIC_LDLIBS_NO= OPT_CXXFLAGS_YES = -Os ifeq ($(STATIC_BUILD), YES) $(shell echo yes) endif base-7.0.3.1/configure/os/CONFIG.linux-x86.linux-cris_v100000664000577000060420000000114013557101274021244 0ustar anjaesctl# CONFIG.linux-x86.linux-cris_v10 # # Author: Peter Zumbruch # GSI # P.Zumbruch@gsi.de # # Definitions for linux-x86 host - linux-cris_v10 target builds # Sites may override these definitions in CONFIG_SITE.linux-x86.linux-cris_v10 #------------------------------------------------------- # Include definitions common to all linux-cris targets include $(CONFIG)/os/CONFIG.Common.linux-cris GNU_TARGET = cris-axis-linux-gnu ARCH_DEP_CFLAGS += -march=v10 # if you are using different places for cris_v10 cris_v32 # you have to define for each architecture # AXIS_SDK_DIR= base-7.0.3.1/configure/os/CONFIG.linux-x86.linux-cris_v320000664000577000060420000000114313557101274021253 0ustar anjaesctl# CONFIG.linux-x86.linux-cris_v32 # # Author: Peter Zumbruch # GSI # P.Zumbruch@gsi.de # # Definitions for linux-x86 host - linux-cris_v32 target builds # Sites may override these definitions in CONFIG_SITE.linux-x86.linux-cris_v32 #------------------------------------------------------- # Include definitions common to all linux-cris targets include $(CONFIG)/os/CONFIG.Common.linux-cris GNU_TARGET = crisv32-axis-linux-gnu ARCH_DEP_CFLAGS += -march=v32 # if you are using different places for cris_v10 cris_v32 # you have to define for each architecture # AXIS_SDK_DIR= base-7.0.3.1/configure/os/CONFIG.linux-x86.linux-x860000664000577000060420000000072413557101274020252 0ustar anjaesctl# CONFIG.linux-x86.linux-x86 # # Definitions for linux-x86 host - linux-x86 target builds # Sites may override these definitions in CONFIG_SITE.linux-x86.linux-x86 #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon STATIC_LDFLAGS_YES= -Wl,-Bstatic STATIC_LDFLAGS_NO= STATIC_LDLIBS_YES= -Wl,-Bdynamic STATIC_LDLIBS_NO= SHRLIB_LDFLAGS += -Wl,-h$@ LOADABLE_SHRLIB_LDFLAGS += -Wl,-h$@ base-7.0.3.1/configure/os/CONFIG.linux-x86.linux-x86-debug0000664000577000060420000000070313557101274021333 0ustar anjaesctl# CONFIG.linux-x86.linux-x86-debug # # Definitions for linux-x86 host - linux-x86-debug target build with debug compiler flags # Sites may override these definitions in CONFIG_SITE.linux-x86.linux-x86-debug #------------------------------------------------------- -include $(CONFIG)/os/CONFIG.linux-x86.linux-x86 #-include $(CONFIG)/os/CONFIG_SITE.Common.linux-x86 #-include $(CONFIG)/os/CONFIG_SITE.linux-x86.linux-x86 BUILD_CLASS=HOST HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.linux-x86.win32-x86-mingw0000664000577000060420000000142613557101274021174 0ustar anjaesctl# CONFIG.linux-x86.win32-x86-mingw # # Definitions for linux-x86 host win32-x86-mingw target builds # Override these definitions in CONFIG_SITE.linux-x86.win32-x86-mingw #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon # Add resource compiler RCCMD = $(GNU_BIN)/$(CMPLR_PREFIX)windres$(CMPLR_SUFFIX) $(INCLUDES) $< $@ # Remove -fPIC flags, add out-implib SHRLIB_CFLAGS = SHRLIB_LDFLAGS = -shared \ -Wl,--out-implib,$(DLLSTUB_PREFIX)$*$(DLLSTUB_SUFFIX) LOADABLE_SHRLIB_LDFLAGS = -shared \ -Wl,--out-implib,$(DLLSTUB_PREFIX)$*$(DLLSTUB_SUFFIX) # Don't link with gcc library GNU_LDLIBS_YES = # Link with system libraries OP_SYS_LDLIBS = -lws2_32 -ladvapi32 -luser32 -lkernel32 -lwinmm -ldbghelp base-7.0.3.1/configure/os/CONFIG.linux-x86.windows-x64-mingw0000664000577000060420000000136313557101274021720 0ustar anjaesctl# CONFIG.linux-x86.windows-x64-mingw # # Definitions for linux-x86 host windows-x64-mingw target builds # Override these definitions in CONFIG_SITE.linux-x86.windows-x64-mingw #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon # Add resource compiler RCCMD = $(GNU_BIN)/$(CMPLR_PREFIX)windres$(CMPLR_SUFFIX) $(INCLUDES) $< $@ # Remove -fPIC flags, add out-implib SHRLIB_CFLAGS = SHRLIB_LDFLAGS = -shared \ -Wl,--out-implib,$(DLLSTUB_PREFIX)$*$(DLLSTUB_SUFFIX) LOADABLE_SHRLIB_LDFLAGS = -shared \ -Wl,--out-implib,$(DLLSTUB_PREFIX)$*$(DLLSTUB_SUFFIX) # No need to explicitly link with gcc library GNU_LDLIBS_YES = # Link with winsock2 OP_SYS_LDLIBS = -lws2_32 base-7.0.3.1/configure/os/CONFIG.linux-x86_64-debug.Common0000664000577000060420000000061513557101274021314 0ustar anjaesctl# CONFIG.linux-x86_64-debug.Common # # Definitions for linux-x86_64 debug with debug compiler flags # Sites may override these definitions in CONFIG_SITE.linux-x86_64-debug.Common #------------------------------------------------------- #Include definitions common to linux-x86_64 hosts include $(CONFIG)/os/CONFIG.linux-x86_64.Common # Make all crosscompiler builds debug builds #CROSS_OPT=NO base-7.0.3.1/configure/os/CONFIG.linux-x86_64-debug.linux-x86_64-debug0000664000577000060420000000063213557101274023242 0ustar anjaesctl# CONFIG.linux-x86_64-debug.linux-x86_64-debug # # Definitions for linux-x86_64 host - linux-x86_64 target build with debug compiler flags # Sites may override these definitions in CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug #------------------------------------------------------- include $(CONFIG)/os/CONFIG.linux-x86_64.linux-x86_64 # Removes -O optimization and adds -g compile option HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.linux-x86_64.Common0000664000577000060420000000050313557101274020224 0ustar anjaesctl# CONFIG.linux-x86_64.Common # # Definitions for linux-x86_64 host builds # Sites may override these definitions in CONFIG_SITE.linux-x86_64.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common WIND_HOST_TYPE = x86-linux2 base-7.0.3.1/configure/os/CONFIG.linux-x86_64.linux-arm0000664000577000060420000000043013557101274020707 0ustar anjaesctl# CONFIG.linux-x86_64.linux-arm # # Definitions for linux-x86_64 host - linux-arm target builds # Sites may override these definitions in CONFIG_SITE.linux-x86_64.linux-arm #------------------------------------------------------- include $(CONFIG)/os/CONFIG.linux-x86.linux-arm base-7.0.3.1/configure/os/CONFIG.linux-x86_64.linux-arm-debug0000664000577000060420000000043513557101274022000 0ustar anjaesctl# CONFIG.linux-x86_64.linux-arm-debug # # Definitions for linux-x86_64 host - linux-arm-debug target builds # Override these settings in CONFIG_SITE.linux-x86_64.linux-arm-debug #------------------------------------------------------- include $(CONFIG)/os/CONFIG.linux-x86.linux-arm base-7.0.3.1/configure/os/CONFIG.linux-x86_64.linux-x86_640000664000577000060420000000051413557101274021071 0ustar anjaesctl# CONFIG.linux-x86_64.linux-x86_64 # # Definitions for linux-x86_64 host - linux-x86_64 target builds # Sites may override these definitions in CONFIG_SITE.linux-x86_64.linux-x86_64 #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/os/CONFIG.linux-x86.linux-x86 base-7.0.3.1/configure/os/CONFIG.linux-x86_64.linux-x86_64-debug0000664000577000060420000000074413557101274022162 0ustar anjaesctl# CONFIG.linux-x86_64.linux-x86_64-debug # # Definitions for linux-x86_64 host - linux-x86_64-debug target build with debug compiler flags # Sites may override these definitions in CONFIG_SITE.linux-x86_64.linux-x86_64-debug #------------------------------------------------------- -include $(CONFIG)/os/CONFIG.linux-x86_64.linux-x86_64 -include $(CONFIG)/os/CONFIG_SITE.Common.linux-x86_64 -include $(CONFIG)/os/CONFIG_SITE.linux-x86_64.linux-x86_64 BUILD_CLASS=HOST HOST_OPT = NO base-7.0.3.1/configure/os/CONFIG.linux-x86_64.win32-x86-mingw0000664000577000060420000000052613557101274021505 0ustar anjaesctl# CONFIG.linux-x86_64.win32-x86-mingw # # Definitions for linux-x86_64 host win32-x86-mingw target builds # Override these definitions in CONFIG_SITE.linux-x86_64.win32-x86-mingw #------------------------------------------------------- # Settings as for the linux-x86 host architecture include $(CONFIG)/os/CONFIG.linux-x86.win32-x86-mingw base-7.0.3.1/configure/os/CONFIG.linux-x86_64.windows-x64-mingw0000664000577000060420000000053613557101274022232 0ustar anjaesctl# CONFIG.linux-x86_64.windows-x64-mingw # # Definitions for linux-x86_64 host windows-x64-mingw target builds # Override these definitions in CONFIG_SITE.linux-x86_64.windows-x64-mingw #------------------------------------------------------- # Settings as for the linux-x86 host architecture include $(CONFIG)/os/CONFIG.linux-x86.windows-x64-mingw base-7.0.3.1/configure/os/CONFIG.solaris-sparc-debug.Common0000664000577000060420000000052613557101274022004 0ustar anjaesctl# CONFIG.solaris-sparc-debug.Common # # Definitions for solaris-sparc debug with debug compiler flags # Sites may override these definitions in CONFIG_SITE.solaris-sparc-debug.Common #------------------------------------------------------- #Include definitions common to solaris-sparc hosts include $(CONFIG)/os/CONFIG.solaris-sparc.Common base-7.0.3.1/configure/os/CONFIG.solaris-sparc-debug.solaris-sparc-debug0000664000577000060420000000072713557101274024425 0ustar anjaesctl# CONFIG.solaris-sparc-debug.solaris-sparc-debug # # Definitions for solaris-sparc host - solaris-sparc target build with debug compiler flags # Sites may override these definitions in CONFIG_SITE.solaris-sparc-debug.solaris-sparc-debug #------------------------------------------------------- include $(CONFIG)/os/CONFIG_SITE.Common.solaris-sparc include $(CONFIG)/os/CONFIG.solaris-sparc.solaris-sparc # Removes -O optimization and adds -g compile option HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.solaris-sparc-gnu.Common0000664000577000060420000000051313557101274021503 0ustar anjaesctl# CONFIG.solaris-sparc-gnu.Common # # Definitions for solaris-sparc gnu compiler host builds # Sites may override these definitions in CONFIG_SITE.solaris-sparc-gnu.Common #------------------------------------------------------- #Include definitions common to solaris-sparc hosts include $(CONFIG)/os/CONFIG.solaris-sparc.Common base-7.0.3.1/configure/os/CONFIG.solaris-sparc-gnu.solaris-sparc-gnu0000664000577000060420000000124713557101274023631 0ustar anjaesctl# CONFIG.solaris-sparc-gnu.solaris-sparc-gnu # # Definitions for solaris-sparc gnu compiler host - solaris-sparc gnu compiler target builds # Sites may override these definitions in CONFIG_SITE.solaris-sparc-gnu.solaris-sparc-gnu #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon AR = ar -rc RANLIB= LD = ld -r STATIC_LDFLAGS_YES= -Wl,-Bstatic STATIC_LDFLAGS_NO= STATIC_LDLIBS_YES= -Wl,-Bdynamic STATIC_LDLIBS_NO= OP_SYS_LDFLAGS += -z ignore -z combreloc -z lazyload SHRLIB_LDFLAGS += -Wl,-z,defs -Wl,-z,text -Wl,-h,$@ LOADABLE_SHRLIB_LDFLAGS += -Wl,-z,text -Wl,-h,$@ GNU_LDLIBS_YES += -lc base-7.0.3.1/configure/os/CONFIG.solaris-sparc.Common0000664000577000060420000000051113557101274020712 0ustar anjaesctl# CONFIG.solaris-sparc.Common # # Definitions for solaris-sparc host archs # Sites may override these definitions in CONFIG_SITE.solaris-sparc.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common WIND_HOST_TYPE = sun4-solaris2 base-7.0.3.1/configure/os/CONFIG.solaris-sparc.solaris-sparc0000664000577000060420000000051313557101274022246 0ustar anjaesctl# CONFIG.solaris-sparc.solaris-sparc # # Definitions for solaris-sparc host - solaris-sparc target build # Sites may override these definitions in CONFIG_SITE.solaris-sparc.solaris-sparc #------------------------------------------------------- include $(CONFIG)/os/CONFIG.solarisCommon.solarisCommon SHRLIB_CFLAGS = -xcode=pic32 base-7.0.3.1/configure/os/CONFIG.solaris-sparc.solaris-sparc-debug0000664000577000060420000000075613557101274023343 0ustar anjaesctl# CONFIG.solaris-sparc.solaris-sparc-debug # # Definitions for solaris-sparc host - solaris-sparc-debug target build with debug compiler flags # Sites may override these definitions in CONFIG_SITE.solaris-sparc.solaris-sparc-debug #------------------------------------------------------- -include $(CONFIG)/os/CONFIG.solaris-sparc.solaris-sparc -include $(CONFIG)/os/CONFIG_SITE.Common.solaris-sparc -include $(CONFIG)/os/CONFIG_SITE.solaris-sparc.solaris-sparc BUILD_CLASS=HOST HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.solaris-sparc.solaris-sparc-gnu0000664000577000060420000000055013557101274023036 0ustar anjaesctl# CONFIG.solaris-sparc.solaris-sparc-gnu # # Definitions for solaris-sparc host - solaris-sparc-gnu target build with gnu compiler # Sites may override these definitions in CONFIG_SITE.solaris-sparc.solaris-sparc-gnu #------------------------------------------------------- include $(CONFIG)/os/CONFIG.solaris-sparc-gnu.solaris-sparc-gnu BUILD_CLASS = HOST base-7.0.3.1/configure/os/CONFIG.solaris-sparc.solaris-sparc640000664000577000060420000000054713557101274022427 0ustar anjaesctl# CONFIG.solaris-sparc.solaris-sparc64 # # Definitions for solaris-sparc host - solaris-sparc64 target build with Sun compiler # Sites may override these definitions in CONFIG_SITE.solaris-sparc.solaris-sparc64 #------------------------------------------------------- include $(CONFIG)/os/CONFIG.solaris-sparc64.solaris-sparc64 BUILD_CLASS = HOST GNU = NO base-7.0.3.1/configure/os/CONFIG.solaris-sparc.solaris-sparc64-gnu0000664000577000060420000000056213557101274023213 0ustar anjaesctl# CONFIG.solaris-sparc.solaris-sparc64-gnu # # Definitions for solaris-sparc host - solaris-sparc64-gnu target build with gnu compiler # Sites may override these definitions in CONFIG_SITE.solaris-sparc.solaris-sparc64-gnu #------------------------------------------------------- include $(CONFIG)/os/CONFIG.solaris-sparc64-gnu.solaris-sparc64-gnu BUILD_CLASS = HOST base-7.0.3.1/configure/os/CONFIG.solaris-sparc64-gnu.Common0000664000577000060420000000053113557101274021655 0ustar anjaesctl# CONFIG.solaris-sparc64-gnu.Common # # Definitions for solaris-sparc64 gnu compiler host builds # Sites may override these definitions in CONFIG_SITE.solaris-sparc64-gnu.Common #------------------------------------------------------- #Include definitions common to solaris-sparc-gnu hosts include $(CONFIG)/os/CONFIG.solaris-sparc-gnu.Common base-7.0.3.1/configure/os/CONFIG.solaris-sparc64-gnu.solaris-sparc64-gnu0000664000577000060420000000063413557101274024154 0ustar anjaesctl# CONFIG.solaris-sparc64-gnu.solaris-sparc64-gnu # # Definitions for solaris-sparc64 gnu compiler host - solaris-sparc64 gnu compiler target builds # Sites may override these definitions in CONFIG_SITE.solaris-sparc64-gnu.solaris-sparc64-gnu #------------------------------------------------------- # Include common solaris-sparc-gnu definitions include $(CONFIG)/os/CONFIG.solaris-sparc-gnu.solaris-sparc-gnu base-7.0.3.1/configure/os/CONFIG.solaris-sparc64.Common0000664000577000060420000000051113557101274021064 0ustar anjaesctl# CONFIG.solaris-sparc64.Common # # Definitions for solaris-sparc64 Sun compiler host builds # Sites may override these definitions in CONFIG_SITE.solaris-sparc64.Common #------------------------------------------------------- #Include definitions common to solaris-sparc hosts include $(CONFIG)/os/CONFIG.solaris-sparc.Common base-7.0.3.1/configure/os/CONFIG.solaris-sparc64.solaris-sparc640000664000577000060420000000056613557101274022602 0ustar anjaesctl# CONFIG.solaris-sparc64.solaris-sparc64 # # Definitions for solaris-sparc64 compiler host - solaris-sparc64 compiler target builds # Sites may override these definitions in CONFIG_SITE.solaris-sparc64.solaris-sparc64 #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/os/CONFIG.solaris-sparc.solaris-sparc base-7.0.3.1/configure/os/CONFIG.solaris-sparc64.solaris-sparc64-debug0000664000577000060420000000076013557101274023662 0ustar anjaesctl# CONFIG.solaris-sparc64.solaris-sparc64-debug # # Definitions for solaris-sparc64 host - solaris-sparc64-debug target build with debug compiler flags # Sites may override these definitions in CONFIG_SITE.solaris-sparc64.solaris-sparc64-debug #------------------------------------------------------- include $(CONFIG)/os/CONFIG.Common.solaris-sparc64 include $(CONFIG)/os/CONFIG.solaris-sparc64.solaris-sparc64 BUILD_CLASS=HOST # Removes -O optimization and adds -g compile option HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.solaris-x86-gnu.Common0000664000577000060420000000050113557101274021015 0ustar anjaesctl# CONFIG.solaris-x86-gnu.Common # # Definitions for solaris-x86 gnu compiler host builds # Sites may override these definitions in CONFIG_SITE.solaris-x86-gnu.Common #------------------------------------------------------- #Include definitions common to solaris-x86 hosts include $(CONFIG)/os/CONFIG.solaris-x86.Common base-7.0.3.1/configure/os/CONFIG.solaris-x86-gnu.solaris-x86-gnu0000664000577000060420000000077213557101274022465 0ustar anjaesctl# CONFIG.solaris-x86-gnu.solaris-x86-gnu # # Definitions for solaris-x86 gnu compiler host - solaris-x86 gnu compiler target builds # Sites may override these definitions in CONFIG_SITE.solaris-x86-gnu.solaris-x86-gnu #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon AR = ar -rc RANLIB= LD = ld -r SHRLIB_LDFLAGS += -h $@ -z defs LOADABLE_SHRLIB_LDFLAGS += -h $@ OP_SYS_LDFLAGS += -z ignore -z combreloc -z lazyload base-7.0.3.1/configure/os/CONFIG.solaris-x86.Common0000664000577000060420000000044413557101274020234 0ustar anjaesctl# CONFIG.solaris-x86.Common # # Definitions for solaris-x86 host builds # Sites may override these definitions in CONFIG_SITE.solaris-x86.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common base-7.0.3.1/configure/os/CONFIG.solaris-x86.solaris-x860000664000577000060420000000047113557101274021103 0ustar anjaesctl# CONFIG.solaris-x86.solaris-x86 # # Definitions for solaris-x86 host - solaris-x86 target builds # Sites may override these definitions in CONFIG_SITE.solaris-x86.solaris-x86 #------------------------------------------------------- include $(CONFIG)/os/CONFIG.solarisCommon.solarisCommon SHRLIB_CFLAGS = -KPIC base-7.0.3.1/configure/os/CONFIG.solaris-x86.solaris-x86-debug0000664000577000060420000000107513557101274022170 0ustar anjaesctl# CONFIG.solaris-x86.solaris-x86-debug # # Definitions for solaris-x86 host - solaris-x86-debug target build with debug compiler flags # Sites may override these definitions in CONFIG_SITE.solaris-x86.solaris-x86-debug #------------------------------------------------------- -include $(CONFIG)/os/CONFIG.Common.solaris-x86 -include $(CONFIG)/os/CONFIG.solaris-x86.solaris-x86 -include $(CONFIG)/os/CONFIG_SITE.Common.solaris-x86 -include $(CONFIG)/os/CONFIG_SITE.solaris-x86.solaris-x86 BUILD_CLASS=HOST # Removes -O optimization and adds -g compile option HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.solaris-x86.solaris-x86_640000664000577000060420000000053413557101274021414 0ustar anjaesctl# CONFIG.solaris-x86.solaris-x86_64 # # Definitions for solaris-x86 host - solaris-x86_64 target build with Sun compiler # Sites may override these definitions in CONFIG_SITE.solaris-x86.solaris-x86_64 #------------------------------------------------------- include $(CONFIG)/os/CONFIG.solaris-x86_64.solaris-x86_64 BUILD_CLASS = HOST GNU = NO base-7.0.3.1/configure/os/CONFIG.solaris-x86_64-gnu.Common0000664000577000060420000000052313557101274021332 0ustar anjaesctl# CONFIG.solaris-x86_64-gnu.Common # # Definitions for solaris-x86_64 gnu compiler host builds # Sites may override these definitions in CONFIG_SITE.solaris-sparc64-gnu.Common #------------------------------------------------------- #Include definitions common to solaris-x86-gnu hosts include $(CONFIG)/os/CONFIG.solaris-x86-gnu.Common base-7.0.3.1/configure/os/CONFIG.solaris-x86_64-gnu.solaris-x86_64-gnu0000664000577000060420000000062113557101274023300 0ustar anjaesctl# CONFIG.solaris-x86_64-gnu.solaris-x86_64-gnu # # Definitions for solaris-x86_64 gnu compiler host - solaris-sx86_64 gnu compiler target builds # Sites may override these definitions in CONFIG_SITE.solaris-x86_64-gnu.solaris-x86_64-gnu #------------------------------------------------------- # Include common solaris-x86-gnu definitions include $(CONFIG)/os/CONFIG.solaris-x86-gnu.solaris-x86-gnu base-7.0.3.1/configure/os/CONFIG.solaris-x86_64.Common0000664000577000060420000000050413557101274020542 0ustar anjaesctl# CONFIG.solaris-x86_64.Common # # Definitions for solaris-x86_64 Sun compiler host builds # Sites may override these definitions in CONFIG_SITE.solaris-x86_64.Common #------------------------------------------------------- #Include definitions common to solaris-sparc hosts include $(CONFIG)/os/CONFIG.solaris-x86.Common base-7.0.3.1/configure/os/CONFIG.solaris-x86_64.solaris-x86_640000664000577000060420000000056413557101274021730 0ustar anjaesctl# CONFIG.solaris-x86_64.solaris-x86_64 # # Definitions for solaris-x86_64 Sun compiler host - solaris-x86_64 Sun compiler target builds # Sites may override these definitions in CONFIG_SITE.solaris-x86_64.solaris-x86_64 #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/os/CONFIG.solaris-x86.solaris-x86 base-7.0.3.1/configure/os/CONFIG.solaris-x86_64.solaris-x86_64-debug0000664000577000060420000000074713557101274023017 0ustar anjaesctl# CONFIG.solaris-x86_64.solaris-x86_64-debug # # Definitions for solaris-x86_64 host - solaris-x86_64-debug target build with debug compiler flags # Sites may override these definitions in CONFIG_SITE.solaris-x86_64.solaris-x86_64-debug #------------------------------------------------------- include $(CONFIG)/os/CONFIG.Common.solaris-x86_64 include $(CONFIG)/os/CONFIG.solaris-x86_64.solaris-x86_64 BUILD_CLASS=HOST # Removes -O optimization and adds -g compile option HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.solarisCommon.solarisCommon0000664000577000060420000000325413557101274022361 0ustar anjaesctl# CONFIG.solarisCommon.solarisCommon # # Definitions for solaris host - solaris target build # Sites may override these definitions in CONFIG_SITE.solarisCommon.solarisCommon #------------------------------------------------------- CMPLR_CLASS = solStudio SPARCWORKS = /opt/SUNWspro GNU = NO # SPARCWORKS path is set in a CONFIG_SITE file CC = $(SPARCWORKS)/bin/cc CCC = $(SPARCWORKS)/bin/CC CPP = $(CC) -E -Qn RANLIB = # required by sun's C++ compiler AR = $(CCC) -xar -o LD = ld -r #Prepare the object code for profiling with prof. (YES or NO) PROFILE=NO #Prepare the object code for profiling with gprof. (YES or NO) GPROF=NO # Configure OS vendor C compiler PROF_CFLAGS_YES = -p GPROF_CFLAGS_YES = -xpg CODE_CFLAGS = $(PROF_CFLAGS_$(PROFILE)) $(GPROF_CFLAGS_$(GPROF)) WARN_CFLAGS_YES = WARN_CFLAGS_NO = -w OPT_CFLAGS_YES = -xO4 OPT_CFLAGS_NO = -g # Configure OS vendor C++ compiler PROF_CXXFLAGS_YES = -p GPROF_CXXFLAGS_YES = -xpg CODE_CXXFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) WARN_CXXFLAGS_YES = +w WARN_CXXFLAGS_NO = OPT_CXXFLAGS_YES = -O OPT_CXXFLAGS_NO = -g CODE_LDFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) STATIC_LDFLAGS_YES= -Bstatic STATIC_LDFLAGS_NO= STATIC_LDLIBS_YES= -Bdynamic STATIC_LDLIBS_NO= SHRLIB_LDFLAGS = -z defs -G -h $@ -z text LOADABLE_SHRLIB_LDFLAGS = -G -h $@ -z text OP_SYS_LDFLAGS += -z ignore -z combreloc -z lazyload # Header dependency file generation command HDEPENDS_METHOD = COMP HDEPENDS_COMPFLAGS = -xM1 > $@ #-------------------------------------------------- # Allow site overrides -include $(CONFIG)/os/CONFIG_SITE.solarisCommon.solarisCommon -include $(CONFIG)/os/CONFIG_SITE.(EPICS_HOST_ARCH).solarisCommon base-7.0.3.1/configure/os/CONFIG.win32-x86-debug.Common0000664000577000060420000000045013557101274020603 0ustar anjaesctl# CONFIG.win32-x86-debug.Common # # Definitions for win32-x86-debug host arch # Override these definitions in CONFIG_SITE.win32-x86-debug.Common #------------------------------------------------------- #Include definitions common to win32-x86 hosts include $(CONFIG)/os/CONFIG.win32-x86.Common base-7.0.3.1/configure/os/CONFIG.win32-x86-debug.win32-x86-debug0000664000577000060420000000067313557101274022033 0ustar anjaesctl# CONFIG.win32-x86-debug.win32-x86-debug # # Definitions for win32-x86-debug host - win32-x86-debug target build # Override these definitions in CONFIG_SITE.win32-x86-debug.win32-x86-debug #------------------------------------------------------- #Include definitions common to win32-x86 builds include $(CONFIG)/os/CONFIG.win32-x86.win32-x86 -include $(CONFIG)/os/CONFIG_SITE.win32-x86.win32-x86 # Override CONFIG_SITE settings: HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.win32-x86-mingw.Common0000664000577000060420000000136713557101274020646 0ustar anjaesctl# CONFIG.win32-x86-mingw.Common # # Definitions for win32-x86-mingw host archs # Sites may override these definitions in CONFIG_SITE.win32-x86-mingw.Common #------------------------------------------------------- #Include definitions common to unix hosts include $(CONFIG)/os/CONFIG.UnixCommon.Common CP = $(PERL) -MExtUtils::Command -e cp MV = $(PERL) -MExtUtils::Command -e mv RM = $(PERL) -MExtUtils::Command -e rm_f MKDIR = $(PERL) -MExtUtils::Command -e mkpath RMDIR = $(PERL) -MExtUtils::Command -e rm_rf NOP = $(PERL) -e '' CAT = $(PERL) -MExtUtils::Command -e cat WIND_HOST_TYPE = x86-win32 OSITHREAD_USE_DEFAULT_STACK = NO HOSTEXE=.exe # Needed to find dlls for base installed build tools (antelope,eflex,...) PATH := $(EPICS_BASE_BIN):$(PATH) base-7.0.3.1/configure/os/CONFIG.win32-x86-mingw.win32-x86-mingw0000664000577000060420000000177513557101274022125 0ustar anjaesctl# CONFIG.win32-x86-mingw.win32-x86-mingw # # Definitions for win32-x86-mingw host - win32-x86-mingw target builds # Sites may override these definitions in CONFIG_SITE.win32-x86-mingw.win32-x86-mingw #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon # Undo various things set by CONFIG.gnuCommon CMPLR_PREFIX = # Remove $(GNU_BIN)/ path CC = $(CMPLR_PREFIX)gcc CCC = $(CMPLR_PREFIX)g++ AR = $(CMPLR_PREFIX)ar -rc LD = $(CMPLR_PREFIX)ld -r RANLIB = $(CMPLR_PREFIX)ranlib # Add resource compiler RCCMD = $(CMPLR_PREFIX)windres $(INCLUDES) $< $@ # Remove -fPIC flags, add out-implib SHRLIB_CFLAGS = SHRLIB_LDFLAGS = -shared \ -Wl,--out-implib,$(DLLSTUB_PREFIX)$*$(DLLSTUB_SUFFIX) LOADABLE_SHRLIB_LDFLAGS = -shared \ -Wl,--out-implib,$(DLLSTUB_PREFIX)$*$(DLLSTUB_SUFFIX) # Don't link with gcc library GNU_LDLIBS_YES = # Link with system libraries OP_SYS_LDLIBS = -lws2_32 -ladvapi32 -luser32 -lkernel32 -lwinmm -ldbghelp base-7.0.3.1/configure/os/CONFIG.win32-x86-mingw.win32-x86-mingw-debug0000664000577000060420000000106313557101274023177 0ustar anjaesctl# CONFIG.win32-x86-mingw.win32-x86-mingw-debug # # Definitions for win32-x86-mingw compiler host - win32-x86-mingw debug compiler target builds # Sites may override these definitions in CONFIG_SITE.win32-x86-mingw.win32-x86-mingw-debug #------------------------------------------------------- -include $(CONFIG)/os/CONFIG.Common.win32-x86-mingw -include $(CONFIG)/os/CONFIG.win32-x86-mingw.win32-x86-mingw -include $(CONFIG)/os/CONFIG_SITE.Common.win32-x86-mingw -include $(CONFIG)/os/CONFIG_SITE.win32-x86-mingw.win32-x86-mingw BUILD_CLASS = HOST HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.win32-x86-static.Common0000664000577000060420000000041013557101274021000 0ustar anjaesctl# CONFIG.win32-x86-static.Common # # Definitions for win32-x86-static host archs # Sites may override these definitions in CONFIG_SITE.win32-x86-static.Common #------------------------------------------------------- include $(CONFIG)/os/CONFIG.win32-x86.Common base-7.0.3.1/configure/os/CONFIG.win32-x86-static.win32-x86-static0000664000577000060420000000073613557101274022435 0ustar anjaesctl# CONFIG.win32-x86-static.win32-x86.static # # Definitions for win32-x86-static host - win32-x86-static target build # Override these definitions in CONFIG_SITE.win32-x86-static.win32-x86-static #------------------------------------------------------- #Include definitions common to win32-x86 builds include $(CONFIG)/os/CONFIG.win32-x86.win32-x86 -include $(CONFIG)/os/CONFIG_SITE.win32-x86.win32-x86 # Override CONFIG_SITE settings: SHARED_LIBRARIES = NO STATIC_BUILD = YES base-7.0.3.1/configure/os/CONFIG.win32-x86.Common0000664000577000060420000000155613557101274017527 0ustar anjaesctl# CONFIG.win32-x86.Common # # Definitions for win32-x86 host archs # Override these definitions in CONFIG_SITE.win32-x86.Common #------------------------------------------------------- CP = $(PERL) -MExtUtils::Command -e cp MV = $(PERL) -MExtUtils::Command -e mv RM = $(PERL) -MExtUtils::Command -e rm_f MKDIR = $(PERL) -MExtUtils::Command -e mkpath RMDIR = $(PERL) -MExtUtils::Command -e rm_rf NOP = $(PERL) -e '' CAT = $(PERL) -MExtUtils::Command -e cat WIND_HOST_TYPE = x86-win32 OSITHREAD_USE_DEFAULT_STACK = NO HOSTEXE=.exe # Does not work if using cygwin make # because HOME is always defined ifndef HOME HOME = $(HOMEDRIVE)$(HOMEPATH) endif # Needed to find dlls for base installed build tools (antelope,eflex,...) PATH := $(EPICS_BASE_BIN):$(PATH) # Silence the tr1 namespace deprecation warnings USR_CXXFLAGS_WIN32 += -D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING base-7.0.3.1/configure/os/CONFIG.win32-x86.win32-x860000664000577000060420000002313413557101274017660 0ustar anjaesctl# CONFIG.win32-x86.win32-x86 # # Definitions for win32-x86 host - win32-x86 target build # Override these definitions in CONFIG_SITE.win32-x86.win32-x86 #------------------------------------------------------- # Win32 valid build types and include directory suffixes VALID_BUILDS = Host Ioc CMPLR_CLASS = msvc OPT_WHOLE_PROGRAM = YES #------------------------------------------------------- WINLINK = link RCCMD = rc -l 0x409 $(INCLUDES) -fo $@ $< ARCMD = lib -nologo -verbose -out:$@ $(LIB_OPT_LDFLAGS) $(LIBRARY_LD_OBJS) # # Configure OS vendor C compiler CC = cl # Override CONFIG.gnuCommon settings for cross builds. GNU = NO HDEPENDS_METHOD = MKMF # Compiler flags for C files (C++ is below) # # -W display warnings at level d # -W4 is for maximum (lint type) warnings # -W3 is for production quality warnings # -W2 displays significant warnings # -W1 is the default and shows severe warnings only # -w Set warning C to be shown at level WARN_CFLAGS_YES = -W3 WARN_CFLAGS_NO = -W1 # # -Ox maximum optimizations # -GL whole program optimization # -Oy- re-enable creation of frame pointers OPT_CFLAGS_YES_YES = -Ox -GL -Oy- OPT_CFLAGS_YES_NO = -Ox -Oy- OPT_CFLAGS_YES = $(OPT_CFLAGS_YES_$(OPT_WHOLE_PROGRAM)) # # -Zi generate program database for debugging information # -RTCsu enable run-time error checks OPT_CFLAGS_NO = -Zi -RTCsu # specify object file name and location OBJ_CFLAG = -Fo # # the following options are required when # vis c++ compiles the code (and includes # the header files) # # -MT static multithreaded C RTL # -MTd static multithreaded C RTL (debug version) # -MD multithreaded C RTL in DLL # -MDd multithreaded C RTL in DLL (debug version) BUILD_DLL_CFLAGS_NO = BUILD_DLL_CFLAGS_YES = -DEPICS_BUILD_DLL BUILD_DLL_CFLAGS = $(BUILD_DLL_CFLAGS_$(SHARED_LIBRARIES)) VISC_CFLAGS_DEBUG_NO = d VISC_CFLAGS_DEBUG_YES = VISC_CFLAGS_DEBUG = $(VISC_CFLAGS_DEBUG_$(HOST_OPT)) STATIC_CFLAGS_YES= -MT$(VISC_CFLAGS_DEBUG) $(BUILD_DLL_CFLAGS) STATIC_CFLAGS_NO= -MD$(VISC_CFLAGS_DEBUG) $(BUILD_DLL_CFLAGS) -DEPICS_CALL_DLL # OS vendor c preprocessor CPP = cl -nologo -C -E # Configure OS vendor C++ compiler # # -EHsc - generate code for exceptions # -GR - generate code for run time type identification # CCC = cl -EHsc -GR # Other compiler flags, used for CPP, C and C++ # # -FC - Show absolute path of source file in diagnostics # -D__STDC__=0 gives us both: # 1) define STDC for code (pretend ANSI conformance) # 2) set it to 0 to use MS C "extensions" (open for _open etc.) # because MS uses: if __STDC__ ... disable many nice things # CODE_CPPFLAGS += -nologo -FC -D__STDC__=0 CODE_CPPFLAGS += -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE # Compiler flags for C++ files # # -W disable warnings from levels > n # -w set warning m to level n # -w44355 "'this' used in the base initializer list" # -w44344 "behavior change: use of explicit template arguments results in ..." # -w44251 "class needs to have dll-interface to be used by clients of ..." WARN_CXXFLAGS_YES = -W3 -w44355 -w44344 -w44251 WARN_CXXFLAGS_NO = -W1 # # -Ox maximum optimizations # -GL whole program optimization # -Oy- re-enable creation of frame pointers OPT_CXXFLAGS_YES_YES = -Ox -GL -Oy- OPT_CXXFLAGS_YES_NO = -Ox -Oy- OPT_CXXFLAGS_YES = $(OPT_CXXFLAGS_YES_$(OPT_WHOLE_PROGRAM)) # # -Zi generate program database for debugging information # -RTCsu enable run-time error checks OPT_CXXFLAGS_NO = -RTCsu -Zi # specify object file name and location OBJ_CXXFLAG = -Fo # # the following options are required when # vis c++ compiles the code (and includes # the header files) # # -MT static multithreaded C RTL # -MTd static multithreaded C RTL (debug version) # -MD multithreaded C RTL in DLL # -MDd multithreaded C RTL in DLL (debug version) STATIC_CXXFLAGS_YES= -MT$(VISC_CFLAGS_DEBUG) $(BUILD_DLL_CFLAGS) STATIC_CXXFLAGS_NO= -MD$(VISC_CFLAGS_DEBUG) $(BUILD_DLL_CFLAGS) -DEPICS_CALL_DLL STATIC_LDLIBS_YES=ws2_32.lib advapi32.lib user32.lib kernel32.lib winmm.lib dbghelp.lib STATIC_LDLIBS_NO= STATIC_LDFLAGS= RANLIB= # # option needed for parallel builds with Visual Studio 2013 onward # VS2012 and above have VisualStudioVersion, so just need to exclude 2012 (11.0) # -FS Force Synchronous PDB Writes # ifneq ($(VisualStudioVersion),) ifneq ($(VisualStudioVersion),11.0) OPT_CXXFLAGS_NO += -FS OPT_CFLAGS_NO += -FS endif endif # # add -profile here to run the ms profiler # -LTCG whole program optimization # -incremental:no full linking # -fixed:no generate relocatable code # -version:. - only 2 components allowed, 0-65535 each # -debug generate debugging info LINK_OPT_FLAGS_WHOLE_YES = -LTCG LINK_OPT_FLAGS_YES = $(LINK_OPT_FLAGS_WHOLE_$(OPT_WHOLE_PROGRAM)) LINK_OPT_FLAGS_YES += -incremental:no -opt:ref LINK_OPT_FLAGS_YES += -release $(PROD_VERSION:%=-version:%) LINK_OPT_FLAGS_NO = -debug -incremental:no -fixed:no OPT_LDFLAGS = $(LINK_OPT_FLAGS_$(HOST_OPT)) LIB_OPT_FLAGS_YES = $(LINK_OPT_FLAGS_WHOLE_$(OPT_WHOLE_PROGRAM)) LIB_OPT_LDFLAGS = $(LIB_OPT_FLAGS_$(HOST_OPT)) ARCH_DEP_CFLAGS= SHRLIB_CFLAGS= OS_CLASS=WIN32 POSIX=NO # ifdef WIN32 looks better that ifeq ($(OS_CLASS),WIN32) ?? WIN32=1 EXE=.exe OBJ=.obj RES=.res # MS Visual C++ doesn't recognize *.cc as a C++ source file, # so C++ compiles get the flag -TP COMPILER_CXXFLAGS = -TP # Operating system flags OP_SYS_CFLAGS = OP_SYS_CXXFLAGS = $(COMPILER_CXXFLAGS) # Files and flags needed to link DLLs (used in RULES_BUILD) WIN32_DLLFLAGS = -subsystem:windows -dll $(OPT_LDFLAGS) \ $(USR_LDFLAGS) $(CMD_LDFLAGS) $(TARGET_LDFLAGS) $(LIB_LDFLAGS) # Specify dll .def file only if it exists DLL_DEF_FLAG = $(addprefix -def:,$(wildcard ../$(addsuffix .def,$*))) # A WIN32 dll has three parts: # x.dll: the real dll (SHRLIBNAME) # x.lib: what you link to progs that use the dll (DLLSTUB_LIBNAME) # x.exp: what you need to build the dll (in no variable) LINK.shrlib = $(WINLINK) -nologo $(WIN32_DLLFLAGS) -out:$@ \ -implib:$(@:%$(SHRLIB_SUFFIX)=%$(LIB_SUFFIX)) \ $(DLL_DEF_FLAG) $(LIBRARY_LD_OBJS) $(LIBRARY_LD_RESS) $(SHRLIB_LDLIBS) # Adjust names of libraries to build SHRLIB_SUFFIX_BASE = .dll SHRLIB_SUFFIX = $(SHRLIB_SUFFIX_BASE) SHRLIBNAME_YES = $(BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX)) LOADABLE_SHRLIBNAME = $(LOADABLE_BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX)) TESTSHRLIBNAME_YES = $(TESTBUILD_LIBRARY:%=%$(SHRLIB_SUFFIX_BASE)) # When SHARED_LIBRARIES is YES we are building a DLL shared library. # When SHARED_LIBRARIES is NO we are building an object library DLLSTUB_SUFFIX = .lib DLLSTUB_LIBNAME_YES = $(BUILD_LIBRARY:%=%.lib) DLLSTUB_LIBNAME = $(DLLSTUB_LIBNAME_$(SHARED_LIBRARIES)) TESTDLLSTUB_LIBNAME_YES = $(TESTBUILD_LIBRARY:%=%.lib) TESTDLLSTUB_LIBNAME = $(TESTDLLSTUB_LIBNAME_$(SHARED_LIBRARIES)) LIB_PREFIX= LIB_SUFFIX=.lib LIBNAME_NO = $(BUILD_LIBRARY:%=%.lib) LIBNAME = $(LIBNAME_$(SHARED_LIBRARIES)) TESTLIBNAME_NO = $(TESTBUILD_LIBRARY:%=%.lib) TESTLIBNAME = $(TESTLIBNAME_$(SHARED_LIBRARIES)) # dll install location INSTALL_SHRLIB = $(INSTALL_BIN) #-------------------------------------------------- # Products dependancy definitions PROD_DEPLIBS = $(foreach lib, $(PROD_LIBS) $(USR_LIBS), \ $(firstword $(wildcard \ $(addsuffix /$(DLLSTUB_PREFIX)$(lib)$(DLLSTUB_SUFFIX), \ $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \ $(addsuffix /$(SHRLIB_PREFIX)$(lib)*$(SHRLIB_SUFFIX_BASE)*, \ $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \ $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \ ) $(addsuffix /$(BUILDLIB_PREFIX)$(lib)$(BUILDLIB_SUFFIX), \ $(if $(filter $(lib),$(TESTLIBRARY)),.,$(INSTALL_LIB))))) PROD_LDLIBS += $($*_DEPLIBS) $(PROD_DEPLIBS) PROD_LDLIBS += $(addsuffix .lib, \ $($*_SYS_LIBS) $(PROD_SYS_LIBS) $(USR_SYS_LIBS)) LDLIBS_STATIC_YES = LDLIBS LDLIBS_SHARED_NO = LDLIBS PROD_LDLIBS += $(STATIC_LDLIBS) \ $($(firstword $(LDLIBS_STATIC_$(STATIC_BUILD)) \ $(LDLIBS_SHARED_$(SHARED_LIBRARIES)))) #-------------------------------------------------- # Libraries dependancy definitions # libs that we need to link the DLL with # (it isnt necessary to rebuild the dll if these change) SHRLIB_DEPLIBS = $(foreach lib, $(LIB_LIBS) $(USR_LIBS), \ $(firstword $(wildcard \ $(addsuffix /$(DLLSTUB_PREFIX)$(lib)$(DLLSTUB_SUFFIX), \ $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \ $(addsuffix /$(SHRLIB_PREFIX)$(lib)*$(SHRLIB_SUFFIX_BASE)*, \ $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \ $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS)) \ ) $(addsuffix /$(BUILDLIB_PREFIX)$(lib)$(BUILDLIB_SUFFIX), \ $(if $(filter $(lib),$(TESTLIBRARY)),.,$(INSTALL_LIB))))) SHRLIB_LDLIBS += $($*_DLL_DEPLIBS) $($*_DEPLIBS) $(SHRLIB_DEPLIBS) SHRLIB_LDLIBS += $(addsuffix .lib, \ $($*_SYS_DLL_LIBS) \ $($*_SYS_LIBS) $(LIB_SYS_LIBS) $(USR_SYS_LIBS) ) #-------------------------------------------------- # Linker definition LINK.cpp = $(WINLINK) -nologo $(STATIC_LDFLAGS) $(LDFLAGS) $(PROD_LDFLAGS) \ -out:$@ $(PROD_LD_OBJS) $(PROD_LD_RESS) $(PROD_LDLIBS) #-------------------------------------------------- # UseManifestTool.pl checks MS Visual c++ compiler version number to # decide whether or not to use the Manifest Tool command to embed the # linker created .manifest file into a library or product target. # useManifestTool.pl returns 0(don't use) or 1(use). # MT.exe = mt.exe -nologo -manifest $@.manifest MT_DLL_COMMAND1 = $(MT.exe) "-outputresource:$@;\#2" MT_EXE_COMMAND_YES = MT_EXE_COMMAND_NO = $(MT.exe) "-outputresource:$@;\#1" MT_EXE_COMMAND1 = $(MT_EXE_COMMAND_$(STATIC_BUILD)) MT_DLL_COMMAND = $(MT_DLL_COMMAND$(shell $(PERL) $(TOOLS)/useManifestTool.pl)) MT_EXE_COMMAND = $(MT_EXE_COMMAND$(shell $(PERL) $(TOOLS)/useManifestTool.pl)) base-7.0.3.1/configure/os/CONFIG.win32-x86.win32-x86-debug0000664000577000060420000000074713557101274020751 0ustar anjaesctl# CONFIG.win32-x86.win32-x86-debug # # Definitions for win32-x86 host - win32-x86-debug target build # Override these definitions in CONFIG_SITE.win32-x86.win32-x86-debug #------------------------------------------------------- #Include definitions common to win32-x86 builds include $(CONFIG)/os/CONFIG.win32-x86.win32-x86 -include $(CONFIG)/os/CONFIG_SITE.win32-x86.win32-x86 # Override CONFIG.CrossCommon settings: BUILD_CLASS = HOST # Override CONFIG_SITE settings: HOST_OPT = NO base-7.0.3.1/configure/os/CONFIG.win32-x86.win32-x86-static0000664000577000060420000000100513557101274021136 0ustar anjaesctl# CONFIG.win32-x86.win32-x86-static # # Definitions for win32-x86 host - win32-x86-static target build # Override these definitions in CONFIG_SITE.win32-x86.win32-x86-static #------------------------------------------------------- #Include definitions common to win32-x86 builds include $(CONFIG)/os/CONFIG.win32-x86.win32-x86 -include $(CONFIG)/os/CONFIG_SITE.win32-x86.win32-x86 # Override CONFIG.CrossCommon settings: BUILD_CLASS = HOST # Override CONFIG_SITE settings: SHARED_LIBRARIES = NO STATIC_BUILD = YES base-7.0.3.1/configure/os/CONFIG.windows-x64-debug.Common0000664000577000060420000000046213557101274021332 0ustar anjaesctl# CONFIG.windows-x64-debug.Common # # Definitions for windows-x64-debug host arch # Override these definitions in CONFIG_SITE.windows-x64-debug.Common #------------------------------------------------------- #Include definitions common to windows-x64 hosts include $(CONFIG)/os/CONFIG.windows-x64.Common base-7.0.3.1/configure/os/CONFIG.windows-x64-debug.windows-x64-debug0000664000577000060420000000054113557101274023275 0ustar anjaesctl# CONFIG.windows-x64-debug.windows-x64-debug # # Definitions for windows-x64 debug compiler host - windows-x64 debug compiler target builds # Sites may override these definitions in CONFIG_SITE.windows-x64-debug.windows-x64-debug #------------------------------------------------------- include $(CONFIG)/os/CONFIG.windows-x64.windows-x64 HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.windows-x64-mingw.Common0000664000577000060420000000042013557101274021357 0ustar anjaesctl# CONFIG.windows-x64-mingw.Common # # Definitions for windows-x64-mingw host archs # Sites may override these definitions in CONFIG_SITE.windows-x64-mingw.Common #------------------------------------------------------- include $(CONFIG)/os/CONFIG.win32-x86-mingw.Common base-7.0.3.1/configure/os/CONFIG.windows-x64-mingw.windows-x64-mingw0000664000577000060420000000053213557101274023363 0ustar anjaesctl# CONFIG.windows-x64-mingw.windows-x64-mingw # # Definitions for windows-x64-mingw target archs # Sites may override these definitions in CONFIG_SITE.windows-x64-mingw.windows-x64-mingw #------------------------------------------------------- # Include common gnu compiler definitions include $(CONFIG)/os/CONFIG.win32-x86-mingw.win32-x86-mingw base-7.0.3.1/configure/os/CONFIG.windows-x64-static.Common0000664000577000060420000000046613557101274021537 0ustar anjaesctl# CONFIG.windows-x64-static.Common # # Definitions for windows-x64-static host archs # Override these definitions in CONFIG_SITE.windows-x64-static.Common #------------------------------------------------------- #Include definitions common to windows-x64 hosts include $(CONFIG)/os/CONFIG.windows-x64.Common base-7.0.3.1/configure/os/CONFIG.windows-x64-static.windows-x64-static0000664000577000060420000000067113557101274023703 0ustar anjaesctl# CONFIG.windows-x64-static.windows-x64-static # # Definitions for windows-x64-static host - windows-x64-static target build # Override these definitions in CONFIG_SITE.windows-x64-static.windows-x64-static #------------------------------------------------------- #Include definitions common to windows-x64 builds include $(CONFIG)/os/CONFIG.windows-x64.windows-x64 # Override CONFIG_SITE settings: SHARED_LIBRARIES = NO STATIC_BUILD= YES base-7.0.3.1/configure/os/CONFIG.windows-x64.Common0000664000577000060420000000043413557101274020245 0ustar anjaesctl# CONFIG.windows-x64.Common # # Definitions for windows-x64 host arch # Override these definitions in CONFIG_SITE.windows-x64.Common #------------------------------------------------------- #Include definitions common to win32-x86 hosts include $(CONFIG)/os/CONFIG.win32-x86.Common base-7.0.3.1/configure/os/CONFIG.windows-x64.windows-x640000664000577000060420000000071313557101274021126 0ustar anjaesctl# CONFIG.windows-x64.windows-x64 # # Definitions for windows-x64 host - windows-x64 target build # Override these definitions in CONFIG_SITE.windows-x64.windows-x64 #------------------------------------------------------- #Include definitions common to win32-x86 builds include $(CONFIG)/os/CONFIG.win32-x86.win32-x86 -include $(CONFIG)/os/CONFIG_SITE.win32-x86.win32-x86 OPT_LDFLAGS += -MACHINE:X64 # -MACHINE:X64 # -MACHINE:IA64 (Itanium) # -MACHINE:X86 base-7.0.3.1/configure/os/CONFIG.windows-x64.windows-x64-debug0000664000577000060420000000070113557101274022207 0ustar anjaesctl# CONFIG.windows-x64.windows-x64-debug # # Definitions for windows-x64 host - windows-x64-debug target build # Override these definitions in CONFIG_SITE.windows-x64.windows-x64-debug #------------------------------------------------------- #Include definitions common to windows-x64 builds include $(CONFIG)/os/CONFIG.windows-x64.windows-x64 # Override CONFIG.CrossCommon settings: BUILD_CLASS = HOST # Override CONFIG_SITE settings: HOST_OPT=NO base-7.0.3.1/configure/os/CONFIG.windows-x64.windows-x64-static0000664000577000060420000000074113557101274022414 0ustar anjaesctl# CONFIG.windows-x64.windows-x64-static # # Definitions for windows-x64 host - windows-x64-static target build # Override these definitions in CONFIG_SITE.windows-x64.windows-x64-static #------------------------------------------------------- #Include definitions common to windows-x64 builds include $(CONFIG)/os/CONFIG.windows-x64.windows-x64 # Override CONFIG.CrossCommon settings: BUILD_CLASS = HOST # Override CONFIG_SITE settings: SHARED_LIBRARIES = NO STATIC_BUILD = YES base-7.0.3.1/configure/os/CONFIG_SITE.Common.RTEMS0000664000577000060420000000165413557101274017657 0ustar anjaesctl# CONFIG_SITE.Common.RTEMS # # Site-specific information for all RTEMS targets #------------------------------------------------------- # Where to find RTEMS # # APS: RTEMS_VERSION = 4.10.2 RTEMS_BASE = /usr/local/vw/rtems/rtems-$(RTEMS_VERSION) # Cross-compile toolchain in $(RTEMS_TOOLS)/bin # RTEMS_TOOLS = $(RTEMS_BASE) # Link Generic System loadable objects instead of full executable. # # A GeSys object is similar to a shared library. It can be (un)loaded # at runtime by the Generic System loader which is available as a # patch against RTEMS. USE_GESYS = NO # If you're using neither BOOTP/DHCP nor FLASH to pick up your IOC # network configuration you must uncomment and specify your Internet # Domain Name here # #OP_SYS_CFLAGS += -DRTEMS_NETWORK_CONFIG_DNS_DOMAINNAME= # Select the command-line-input library to use # COMMANDLINE_LIBRARY = EPICS #COMMANDLINE_LIBRARY = LIBTECLA #COMMANDLINE_LIBRARY = READLINE base-7.0.3.1/configure/os/CONFIG_SITE.Common.RTEMS-pc386-qemu0000664000577000060420000000047413557101274021464 0ustar anjaesctl# CONFIG_SITE.Common.RTEMS-pc386-qemu # # Site-specific overrides for the RTEMS-pc386-qemu target # # If you're building this architecture you _probably_ want to # run the tests for it under QEMU, but if not you can turn # them off here by commenting out this line: CROSS_COMPILER_RUNTEST_ARCHS += RTEMS-pc386-qemu base-7.0.3.1/configure/os/CONFIG_SITE.Common.cygwin-x860000664000577000060420000000117713557101274020710 0ustar anjaesctl# CONFIG_SITE.Common.cygwin-x86 # # Site Specific definitions for cygwin-x86 target # Depending on your version of Cygwin you'll want one of the following # lines to enable command-line editing and history in iocsh. If you're # not sure which, start with the top one and work downwards until the # build doesn't fail to link the readline library. If none of them work, # comment them all out to build without readline support. # Needs -lncursesw (Cygwin 1.7): COMMANDLINE_LIBRARY = READLINE_NCURSESW # Needs -lcurses (older versions) #COMMANDLINE_LIBRARY = READLINE_CURSES # No other libraries needed #COMMANDLINE_LIBRARY = READLINE base-7.0.3.1/configure/os/CONFIG_SITE.Common.cygwin-x86_640000664000577000060420000000126313557101274021215 0ustar anjaesctl# CONFIG_SITE.Common.cygwin-x86_64 # # Site Specific definitions for cygwin-x86_64 target # Only the local epics system manager should modify this file # If readline is installed uncomment the following line # to add command-line editing and history support #COMMANDLINE_LIBRARY = READLINE # Uncomment the following line if readline has problems #LDLIBS_READLINE = -lreadline -lcurses # 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 base-7.0.3.1/configure/os/CONFIG_SITE.Common.darwin-ppc0000664000577000060420000000047413557101274021030 0ustar anjaesctl# CONFIG_SITE.Common.darwin-ppc # # Site override definitions for darwin-ppc target builds #------------------------------------------------------- # Select which CPU architectures to include in your universal binaries: # ppc # ppc64 - Not tested ARCH_CLASS = ppc #ARCH_CLASS = ppc64 #ARCH_CLASS = ppc ppc64 base-7.0.3.1/configure/os/CONFIG_SITE.Common.darwin-ppcx860000664000577000060420000000112713557101274021372 0ustar anjaesctl# CONFIG_SITE.Common.darwin-ppcx86 # # Site override definitions for darwin-ppcx86 target builds #---------------------------------------------------------- # Select which CPU architectures to include in your universal binaries: # ppc # i386 # ppc64 - Not tested # x86_64 - Needs MacOS 10.4 with Universal SDK, or 10.5 or later. ARCH_CLASS = ppc i386 #ARCH_CLASS = ppc x86_64 #ARCH_CLASS = ppc i386 x86_64 #ARCH_CLASS = ppc64 i386 #ARCH_CLASS = ppc64 x86_64 #ARCH_CLASS = ppc64 i386 x86_64 #ARCH_CLASS = ppc ppc64 i386 #ARCH_CLASS = ppc ppc64 x86_64 #ARCH_CLASS = ppc ppc64 i386 x86_64 base-7.0.3.1/configure/os/CONFIG_SITE.Common.darwin-x860000664000577000060420000000205513557101274020670 0ustar anjaesctl# CONFIG_SITE.Common.darwin-x86 # # Site override definitions for darwin-x86 target builds #------------------------------------------------------- # Select which CPU architecture(s) to include in your MacOS binaries: # i386, x86_64, or both (fat binaries). #ARCH_CLASS = i386 ARCH_CLASS = x86_64 #ARCH_CLASS = i386 x86_64 # # Uncomment the following 3 lines to build with Apple's GCC instead of CLANG. # #CMPLR_CLASS = gcc #CC = gcc #CCC = g++ #GNU = YES # To use MacPorts GCC uncomment (and modify if necessary) the following: #GNU_DIR = /opt/local #CMPLR_CLASS = gcc #CC = $(GNU_BIN)/gcc -m64 #CCC = $(GNU_BIN)/g++ -m64 #GNU = YES # If you see this or similar errors while building in the src/cap5 directory # gcc: error: unrecognized option '-no-cpp-precomp' # the problem is due to the ccflags configuration that your version of Perl # was built with. You can replace the Cap5_CFLAGS setting in the Makefile # with a hand-edited set of flags for building that Perl library, or ignore # this problem if you don't need to use Channel Access from Perl. base-7.0.3.1/configure/os/CONFIG_SITE.Common.ios-arm0000664000577000060420000000207313557101274020330 0ustar anjaesctl# CONFIG_SITE.Common.ios-arm # # Site-specific settings for ios-arm target builds #------------------------------------------------------- # Which ARM instruction set(s) to generate code for: # Most iOS devices can run programs compiled for older # instruction sets, although the newer instructions are # more efficient. # # Apple's compilers can build for multiple architectures, # generating a Universal binary. This is larger and takes # longer to compile, but runs efficiently on all devices. # # Xcode 4.5 dropped support for the ARMv6. # # arm64 devices: iPhone 5S, 6 and 6 Plus, iPad Air Gen 1 and 2, # iPad Mini Gen 2 and 3 # armv7s devices: iPhone 5 and 5C, iPad Gen 4 # armv7 devices: iPhone 3GS, 4 and 4S, iPod Touch Gen 3 to 5 # iPad Gen 1 to 3, iPad Mini, Apple TV Gen 2 and 3 # armv6 devices: iPhone 1 and 3G, iPod Touch Gen 1 and 2 #ARCH_CLASS = arm64 #ARCH_CLASS = armv7s arm64 ARCH_CLASS = armv7 armv7s arm64 #ARCH_CLASS = armv7 armv7s #ARCH_CLASS = armv7 #ARCH_CLASS = armv6 armv7 #ARCH_CLASS = armv6 base-7.0.3.1/configure/os/CONFIG_SITE.Common.ios-x860000664000577000060420000000106213557101274020173 0ustar anjaesctl# CONFIG_SITE.Common.ios-x86 # # Site-specific settings for ios-x86 target builds #------------------------------------------------------- # Which x86 instruction set(s) to generate code for: # The iPhone Simulator now supports both 32-bit and 64-bit # instruction sets since the iPhone 6 uses a 64-bit CPU. # # Apple's compilers can build for multiple architectures, # generating a Universal binary. This is larger and takes # longer to compile, but runs efficiently on all devices. #ARCH_CLASS = i386 ARCH_CLASS = i386 x86_64 #ARCH_CLASS = x86_64 base-7.0.3.1/configure/os/CONFIG_SITE.Common.iosCommon0000664000577000060420000000234113557101274020722 0ustar anjaesctl# CONFIG_SITE.Common.iosCommon # # Site-specific settings for Apple iOS builds #------------------------------------------------------- # Minimum version of iOS the executables must run on. # Earlier versions may work, if XCode supports them. #IOS_DEPLOYMENT_TARGET = 5.0 #IOS_DEPLOYMENT_TARGET = 5.1 #IOS_DEPLOYMENT_TARGET = 6.0 #IOS_DEPLOYMENT_TARGET = 6.1 #IOS_DEPLOYMENT_TARGET = 7.0 #IOS_DEPLOYMENT_TARGET = 7.1 IOS_DEPLOYMENT_TARGET = 8.0 #IOS_DEPLOYMENT_TARGET = 8.1 # Older versions of Xcode may require this SDK_DIR definition #SDK_DIR = $(PLATFORM_DIR)/Developer/SDKs/$(IOS_PLATFORM)$(IOS_DEPLOYMENT_TARGET).sdk # Which compiler to use: # CLANG is required for Xcode 5.0 and later # LLVM_GNU uses the llvm-gcc and llvm-g++ compilers # GNU is needed for older versions of Xcode COMPILER = CLANG #COMPILER = LLVM_GNU #COMPILER = GNU # Most sites will want to build shared libraries (which is the # default), but if you get an error from ld while building libCom, # try uncommenting this, which is needed for some compiler versions: #SHARED_LIBRARIES = NO # Get platform path from OS, these are usually correct: XCODE_PATH := $(shell xcode-select -print-path) PLATFORM_DIR = $(XCODE_PATH)/Platforms/$(IOS_PLATFORM).platform base-7.0.3.1/configure/os/CONFIG_SITE.Common.linux-arm0000664000577000060420000000262313557101274020676 0ustar anjaesctl# CONFIG_SITE.Common.linux-arm # # Site-specific settings for the linux-arm target # NOTE: In most cases if SHARED_LIBRARIES is set to YES the # shared libraries will be found automatically. However if the .so # files are installed at a different path to their compile-time path # then in order to be found at runtime do one of these: # a) LD_LIBRARY_PATH must include the full absolute pathname to # $(INSTALL_LOCATION)/lib/$(EPICS_HOST_ARCH) when invoking base # executables. # b) Add the runtime path to SHRLIB_DEPLIB_DIRS and PROD_DEPLIB_DIRS, which # will add the named directory to the list contained in the executables. # c) Add the runtime path to /etc/ld.so.conf and run ldconfig # to inform the system of the shared library location. # Use GNU Readline if the header file is installed COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \ $(firstword $(READLINE_DIR) $(GNU_DIR))/include/readline/readline.h), \ READLINE, EPICS)) # If libreadline needs additional libraries to be linked with it, try # uncommenting each of the lines below in turn, starting with the top # one and working downwards, until the build succeeds. Do a 'make rebuild' # from the top of the Base tree after changing this setting. # Needs -lncurses: #COMMANDLINE_LIBRARY = READLINE_NCURSES # Needs -lcurses: #COMMANDLINE_LIBRARY = READLINE_CURSES # Readline is broken or you don't want use it: #COMMANDLINE_LIBRARY = EPICS base-7.0.3.1/configure/os/CONFIG_SITE.Common.linux-cris0000664000577000060420000000256413557101274021063 0ustar anjaesctl# CONFIG_SITE.Common.linux-cris # # Site-specific settings for the linux-cris target # NOTE: In most cases if SHARED_LIBRARIES is set to YES the # shared libraries will be found automatically. However if the .so # files are installed at a different path to their compile-time path # then in order to be found at runtime do one of these: # a) LD_LIBRARY_PATH must include the full absolute pathname to # $(INSTALL_LOCATION)/lib/$(EPICS_HOST_ARCH) when invoking base # executables. # b) Add the runtime path to SHRLIB_DEPLIB_DIRS and PROD_DEPLIB_DIRS, which # will add the named directory to the list contained in the executables. # c) Add the runtime path to /etc/ld.so.conf and run ldconfig # to inform the system of the shared library location. # Use GNU Readline if the header file is installed COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \ $(GNU_DIR)/include/readline/readline.h), READLINE, EPICS)) # If libreadline needs additional libraries to be linked with it, try # uncommenting each of the lines below in turn, starting with the top # one and working downwards, until the build succeeds. Do a 'make rebuild' # from the top of the Base tree after changing this setting. # Needs -lncurses: #COMMANDLINE_LIBRARY = READLINE_NCURSES # Needs -lcurses: #COMMANDLINE_LIBRARY = READLINE_CURSES # Readline is broken or you don't want use it: #COMMANDLINE_LIBRARY = EPICS base-7.0.3.1/configure/os/CONFIG_SITE.Common.linux-microblaze0000664000577000060420000000212313557101274022241 0ustar anjaesctl# CONFIG_SITE.Common.linux-microblaze # # Site-specific settings for the linux-microblaze target # The gnu tools for cross compiling for MicroBlaze (little endian) # on Linux can be downloaded from the Xilinx git server: # git clone git://git.xilinx.com/xldk/microblaze_v2.0_le.git # # The result contains a .tgz file with the tool-chain in it. # Set GNU_DIR to point to the un-tarred tool-chain: GNU_DIR = /usr/local/vw/microblaze-2.0/microblazeel-unknown-linux-gnu # Use GNU Readline if the header file is installed COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \ $(GNU_DIR)/include/readline/readline.h), READLINE, EPICS)) # If libreadline needs additional libraries to be linked with it, try # uncommenting each of the lines below in turn, starting with the top # one and working downwards, until the build succeeds. Do a 'make rebuild' # from the top of the Base tree after changing this setting. # Needs -lncurses: #COMMANDLINE_LIBRARY = READLINE_NCURSES # Needs -lcurses: #COMMANDLINE_LIBRARY = READLINE_CURSES # Readline is broken or you don't want use it: #COMMANDLINE_LIBRARY = EPICS base-7.0.3.1/configure/os/CONFIG_SITE.Common.linux-x860000664000577000060420000000360613557101274020546 0ustar anjaesctl# CONFIG_SITE.Common.linux-x86 # # Site-specific settings for the linux-x86 target # NOTE: In most cases if SHARED_LIBRARIES is set to YES the # shared libraries will be found automatically. However if the .so # files are installed at a different path to their compile-time path # then in order to be found at runtime do one of these: # a) LD_LIBRARY_PATH must include the full absolute pathname to # $(INSTALL_LOCATION)/lib/$(EPICS_HOST_ARCH) when invoking base # executables. # b) Add the runtime path to SHRLIB_DEPLIB_DIRS and PROD_DEPLIB_DIRS, which # will add the named directory to the list contained in the executables. # c) Add the runtime path to /etc/ld.so.conf and run ldconfig # to inform the system of the shared library location. # Use GNU Readline if the header file is installed COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \ $(GNU_DIR)/include/readline/readline.h), READLINE, EPICS)) # If libreadline needs additional libraries to be linked with it, try # uncommenting each of the lines below in turn, starting with the top # one and working downwards, until the build succeeds. Do a 'make rebuild' # from the top of the Base tree after changing this setting. # Needs -lncurses (RHEL 5 etc.): #COMMANDLINE_LIBRARY = READLINE_NCURSES # Needs -lcurses (older versions) #COMMANDLINE_LIBRARY = READLINE_CURSES # Readline is broken or you don't want use it: #COMMANDLINE_LIBRARY = EPICS # Permit access to 64-bit file-systems OP_SYS_CFLAGS += -D_FILE_OFFSET_BITS=64 # Uncomment the followings lines to build with CLANG instead of GCC. # #GNU = NO #CMPLR_CLASS = clang #CC = clang #CCC = clang++ # Tune GNU compiler output for a specific 32-bit cpu-type # (e.g. generic, native, i386, i686, pentium2/3/4, prescott, k6, athlon etc.) GNU_TUNE_CFLAGS = -mtune=generic # Developers using a suitable compiler may enable its address sanitizer: #ENABLE_ASAN = YES base-7.0.3.1/configure/os/CONFIG_SITE.Common.linux-x86_640000664000577000060420000000347413557101274021062 0ustar anjaesctl# CONFIG_SITE.Common.linux-x86_64 # # Site-specific settings for the linux-x86_64 target # NOTE: In most cases if SHARED_LIBRARIES is set to YES the # shared libraries will be found automatically. However if the .so # files are installed at a different path to their compile-time path # then in order to be found at runtime do one of these: # a) LD_LIBRARY_PATH must include the full absolute pathname to # $(INSTALL_LOCATION)/lib/$(EPICS_HOST_ARCH) when invoking base # executables. # b) Add the runtime path to SHRLIB_DEPLIB_DIRS and PROD_DEPLIB_DIRS, which # will add the named directory to the list contained in the executables. # c) Add the runtime path to /etc/ld.so.conf and run ldconfig # to inform the system of the shared library location. # Use GNU Readline if the header file is installed COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \ $(GNU_DIR)/include/readline/readline.h), READLINE, EPICS)) # If libreadline needs additional libraries to be linked with it, try # uncommenting each of the lines below in turn, starting with the top # one and working downwards, until the build succeeds. Do a 'make rebuild' # from the top of the Base tree after changing this setting. # Needs -lncurses (RHEL 5 etc.): #COMMANDLINE_LIBRARY = READLINE_NCURSES # Needs -lcurses (older versions) #COMMANDLINE_LIBRARY = READLINE_CURSES # Readline is broken or you don't want use it: #COMMANDLINE_LIBRARY = EPICS # Uncomment the followings lines to build with CLANG instead of GCC. # #GNU = NO #CMPLR_CLASS = clang #CC = clang #CCC = clang++ # Tune GNU compiler output for a specific 64-bit cpu-type # (e.g. generic, native, core2, nocona, k8, opteron, athlon64, barcelona etc.) GNU_TUNE_CFLAGS = -mtune=generic # Developers using a suitable compiler may enable its address sanitizer: #ENABLE_ASAN = YES base-7.0.3.1/configure/os/CONFIG_SITE.Common.linux-xscale_be0000664000577000060420000000137413557101274022046 0ustar anjaesctl# CONFIG_SITE.Common.linux-xscale_be # # Site-specific settings for the linux-xscale_be target # Use GNU Readline if the header file is installed COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \ $(firstword $(READLINE_DIR) $(GNU_DIR))/include/readline/readline.h), \ READLINE, EPICS)) # If libreadline needs additional libraries to be linked with it, try # uncommenting each of the lines below in turn, starting with the top # one and working downwards, until the build succeeds. Do a 'make rebuild' # from the top of the Base tree after changing this setting. # Needs -lncurses: #COMMANDLINE_LIBRARY = READLINE_NCURSES # Needs -lcurses: #COMMANDLINE_LIBRARY = READLINE_CURSES # Readline is broken or you don't want use it: #COMMANDLINE_LIBRARY = EPICS base-7.0.3.1/configure/os/CONFIG_SITE.Common.linuxCommon0000664000577000060420000000060113557101274021264 0ustar anjaesctl# CONFIG_SITE.Common.linuxCommon # # Site-specific settings for all linux targets # 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 base-7.0.3.1/configure/os/CONFIG_SITE.Common.solaris-sparc0000664000577000060420000000102413557101274021536 0ustar anjaesctl# CONFIG_SITE.Common.solaris-sparc # # Site Specific definitions for solaris-sparc target # Only the local epics system manager should modify this file # location of the Solaris Studio (was SunPro) compilers SPARCWORKS = /opt/SUNWspro #SPARCWORKS = /opt/solarisstudio12.3 # If readline is installed, uncomment the following macro definition # to use it for command-line editing and history support #COMMANDLINE_LIBRARY = READLINE # Use stLport library instead of default Cstd library # Must be either YES or NO #USE_STLPORT=YES base-7.0.3.1/configure/os/CONFIG_SITE.Common.solaris-sparc-gnu0000664000577000060420000000056513557101274022336 0ustar anjaesctl# CONFIG_SITE.Common.solaris-sparc-gnu # # Site Specific definitions for solaris-sparc-gnu target # Only the local epics system manager should modify this file # Include definitions common to all solaris-sparc-gnu target archs include $(CONFIG)/os/CONFIG_SITE.Common.solaris-sparc # solaris 10 default location #GNU_DIR=/usr/sfw # APS site override GNU_DIR = /usr/local base-7.0.3.1/configure/os/CONFIG_SITE.Common.solaris-sparc640000664000577000060420000000046113557101274021714 0ustar anjaesctl# CONFIG_SITE.Common.solaris-sparc64 # # Site Specific definitions for solaris-sparc64 target # Only the local epics system manager should modify this file # Include definitions common to all solaris-sparc64 target archs include $(CONFIG)/os/CONFIG_SITE.Common.solaris-sparc COMMANDLINE_LIBRARY = EPICS base-7.0.3.1/configure/os/CONFIG_SITE.Common.solaris-sparc64-gnu0000664000577000060420000000047713557101274022512 0ustar anjaesctl# CONFIG_SITE.Common.solaris-sparc64-gnu # # Site Specific definitions for solaris-sparc64-gnu target # Only the local epics system manager should modify this file # Include definitions common to all solaris-sparc-gnu target archs include $(CONFIG)/os/CONFIG_SITE.Common.solaris-sparc-gnu COMMANDLINE_LIBRARY = EPICS base-7.0.3.1/configure/os/CONFIG_SITE.Common.solaris-x860000664000577000060420000000041713557101274021060 0ustar anjaesctl# CONFIG_SITE.Common.solaris-x86 # # Site Specific definitions for solaris-x86 targets # Only the local epics system manager should modify this file # location of the Solaris Studio (was SunPro) compilers SPARCWORKS = /opt/SUNWspro #SPARCWORKS = /opt/solarisstudio12.3 base-7.0.3.1/configure/os/CONFIG_SITE.Common.solaris-x86-gnu0000664000577000060420000000036713557101274021653 0ustar anjaesctl# CONFIG_SITE.Common.solaris-x86-gnu # # Site Specific definitions for solaris-x86-gnu target # Only the local epics system manager should modify this file # solaris 10 default location #GNU_DIR=/usr/sfw # APS site override GNU_DIR = /usr/local base-7.0.3.1/configure/os/CONFIG_SITE.Common.solaris-x86_640000664000577000060420000000045213557101274021370 0ustar anjaesctl# CONFIG_SITE.Common.solaris-x86_64 # # Site Specific definitions for solaris-x86_64 target # Only the local epics system manager should modify this file # Include definitions common to all solaris-x86 target archs -include $(CONFIG)/os/CONFIG_SITE.Common.solaris-x86 COMMANDLINE_LIBRARY = EPICS base-7.0.3.1/configure/os/CONFIG_SITE.Common.solaris-x86_64-gnu0000664000577000060420000000047313557101274022162 0ustar anjaesctl# CONFIG_SITE.Common.solaris-x86_64-gnu # # Site Specific definitions for solaris-x86_64-gnu target # Only the local epics system manager should modify this file # Include definitions common to all solaris-sparc-gnu target archs include $(CONFIG)/os/CONFIG_SITE.Common.solaris-x86-gnu COMMANDLINE_LIBRARY = EPICS base-7.0.3.1/configure/os/CONFIG_SITE.Common.vxWorks-mpc85400000664000577000060420000000026613557101274021544 0ustar anjaesctl# # Site Specific definitions for the vxWorks-mpc8540 target # # Only the local epics system manager should modify this file #------------------------------------------------------- base-7.0.3.1/configure/os/CONFIG_SITE.Common.vxWorks-mpc85480000664000577000060420000000026613557101274021554 0ustar anjaesctl# # Site Specific definitions for the vxWorks-mpc8548 target # # Only the local epics system manager should modify this file #------------------------------------------------------- base-7.0.3.1/configure/os/CONFIG_SITE.Common.vxWorks-ppc6030000664000577000060420000000026613557101274021457 0ustar anjaesctl# # Site Specific definitions for the vxWorks-ppc603 target # # Only the local epics system manager should modify this file #------------------------------------------------------- base-7.0.3.1/configure/os/CONFIG_SITE.Common.vxWorks-ppc603_long0000664000577000060420000000043713557101274022476 0ustar anjaesctl# # Site Specific definitions for the vxWorks-ppc603_long target # # Only the local epics system manager should modify this file #------------------------------------------------------- # Inherit the settings from vxWorks-ppc603 -include $(CONFIG)/os/CONFIG_SITE.Common.vxWorks-ppc603 base-7.0.3.1/configure/os/CONFIG_SITE.Common.vxWorks-ppc6040000664000577000060420000000026613557101274021460 0ustar anjaesctl# # Site Specific definitions for the vxWorks-ppc604 target # # Only the local epics system manager should modify this file #------------------------------------------------------- base-7.0.3.1/configure/os/CONFIG_SITE.Common.vxWorks-ppc604_altivec0000664000577000060420000000045413557101274023166 0ustar anjaesctl# # Site Specific definitions for the vxWorks-ppc604_altivec target # # Only the local epics system manager should modify this file #------------------------------------------------------- # Inherit the settings from vxWorks-ppc604_long -include $(CONFIG)/os/CONFIG_SITE.Common.vxWorks-ppc604_long base-7.0.3.1/configure/os/CONFIG_SITE.Common.vxWorks-ppc604_long0000664000577000060420000000043713557101274022477 0ustar anjaesctl# # Site Specific definitions for the vxWorks-ppc604_long target # # Only the local epics system manager should modify this file #------------------------------------------------------- # Inherit the settings from vxWorks-ppc604 -include $(CONFIG)/os/CONFIG_SITE.Common.vxWorks-ppc604 base-7.0.3.1/configure/os/CONFIG_SITE.Common.vxWorksCommon0000664000577000060420000000146013557101274021614 0ustar anjaesctl# CONFIG_SITE.Common.vxWorksCommon # # Site specific definitions for vxWorks target builds. # Compiler options can vary with the vxWorks version number, so we # need to know that. Do not include any third-level digits. # Note: vxWorks 5.4.x and 5.5.x (Tornado 2.x) are not supported. # VxWorks 6.0 through 6.5 use older, untested versions of GCC. #VXWORKS_VERSION = 6.6 #VXWORKS_VERSION = 6.7 #VXWORKS_VERSION = 6.8 VXWORKS_VERSION = 6.9 # Sites may override the following path for a particular host # architecture by adding it to an appropriate # CONFIG_SITE.$(EPICS_HOST_ARCH).vxWorksCommon file. # WIND_BASE is where you installed the Wind River software. #WIND_BASE = /usr/local/vw/tornado22-$(ARCH_CLASS) WIND_BASE = /usr/local/vw/vxWorks-$(VXWORKS_VERSION) #WIND_BASE = /ade/vxWorks/$(VXWORKS_VERSION) base-7.0.3.1/configure/os/CONFIG_SITE.Common.win32-x86-mingw0000664000577000060420000000035413557101274021465 0ustar anjaesctl# CONFIG_SITE.Common.win32-x86-mingw # # Site Specific definitions for win32-x86-mingw target # If readline is available uncomment the following line # to enable command-line editing and history support #COMMANDLINE_LIBRARY = READLINE base-7.0.3.1/configure/os/CONFIG_SITE.Common.win32-x86-static0000664000577000060420000000050013557101274021624 0ustar anjaesctl# CONFIG_SITE.Common.win32-x86-static # # Site-specific settings for the win32-x86-static target # Whole-program optimization doesn't work with Visual Studio 2010 when # building static binaries. Newer versions of Visual Studio than 2010 # may work though, comment out or set this to YES to try. OPT_WHOLE_PROGRAM = NO base-7.0.3.1/configure/os/CONFIG_SITE.Common.windows-x64-static0000664000577000060420000000050413557101274022354 0ustar anjaesctl# CONFIG_SITE.Common.windows-x64-static # # Site-specific settings for the windows-x64-static target # Whole-program optimization doesn't work with Visual Studio 2010 when # building static binaries. Newer versions of Visual Studio than 2010 # may work though, comment out or set this to YES to try. OPT_WHOLE_PROGRAM = NO base-7.0.3.1/configure/os/CONFIG_SITE.cygwin-x86.Common0000664000577000060420000000027613557101274020707 0ustar anjaesctl# CONFIG_SITE.cygwin-x86.Common # # Site override definitions for cygwin-x86 host builds #------------------------------------------------------- #CROSS_COMPILER_TARGET_ARCHS = linux-arm base-7.0.3.1/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x860000664000577000060420000000036513557101274021401 0ustar anjaesctl# CONFIG_SITE.cygwin-x86.cygwin-x86 # # Site override definitions for cygwin-x86 host - cygwin-x86 target builds #------------------------------------------------------- # GNU_DIR used when COMMANDLINE_LIBRARY is READLINE #GNU_DIR=C:/cygwin base-7.0.3.1/configure/os/CONFIG_SITE.cygwin-x86.linux-arm0000664000577000060420000000050013557101274021361 0ustar anjaesctl# CONFIG_SITE.cygwin-x86.linux-arm # # Site specific definitions for cygwin-x86 host - linux-arm target builds #------------------------------------------------------- # Tools install path GNU_DIR = /usr/local/arm-linux # GNU crosscompiler target name GNU_TARGET = arm-linux STATIC_BUILD = YES SHARED_LIBRARIES = NO base-7.0.3.1/configure/os/CONFIG_SITE.cygwin-x86_64.linux-arm0000664000577000060420000000050613557101274021700 0ustar anjaesctl# CONFIG_SITE.cygwin-x86_64.linux-arm # # Site specific definitions for cygwin-x86_64 host - linux-arm target builds #------------------------------------------------------- # Tools install path GNU_DIR = /usr/local/arm-linux # GNU crosscompiler target name GNU_TARGET = arm-linux STATIC_BUILD = YES SHARED_LIBRARIES = NO base-7.0.3.1/configure/os/CONFIG_SITE.darwin-ppc.Common0000664000577000060420000000022213557101274021017 0ustar anjaesctl# CONFIG_SITE.darwin-ppc.Common # # Site override definitions for darwin-ppc host builds #------------------------------------------------------- base-7.0.3.1/configure/os/CONFIG_SITE.darwin-ppcx86.Common0000664000577000060420000000023013557101274021364 0ustar anjaesctl# CONFIG_SITE.darwin-ppcx86.Common # # Site override definitions for darwin-ppcx86 host builds #------------------------------------------------------- base-7.0.3.1/configure/os/CONFIG_SITE.darwin-x86.Common0000664000577000060420000000044613557101274020672 0ustar anjaesctl# CONFIG_SITE.darwin-x86.Common # # Site override definitions for darwin-x86 host builds #------------------------------------------------------- # Uncomment the following line to cross-compile the # iOS device (arm) and simulator (x86) binaries #CROSS_COMPILER_TARGET_ARCHS = ios-arm ios-x86 base-7.0.3.1/configure/os/CONFIG_SITE.darwinCommon.darwinCommon0000664000577000060420000000161313557101274022622 0ustar anjaesctl# CONFIG_SITE.darwinCommon.darwinCommon # # Site specific definitions for darwin builds #------------------------------------------------------- # Note the dir/firstword/wildcard functions below are used # to avoid warnings about missing directories. # Mix-and-match of different package systems is probably not advisable, # but you can try that if you like... # Uncomment these definitions when using Homebrew packages: #OP_SYS_INCLUDES += -I/usr/local/include #OP_SYS_LDFLAGS += $(addprefix -L,$(dir $(firstword $(wildcard /usr/local/lib/*)))) # Uncomment these definitions when using DarwinPorts packages: #OP_SYS_INCLUDES += -I/opt/local/include #OP_SYS_LDFLAGS += $(addprefix -L,$(dir $(firstword $(wildcard /opt/local/lib/*)))) # Uncomment these definitions when using Fink packages: #OP_SYS_INCLUDES += -I/sw/include #OP_SYS_LDFLAGS += $(addprefix -L,$(dir $(firstword $(wildcard /sw/lib/*)))) base-7.0.3.1/configure/os/CONFIG_SITE.linux-arm-debug.linux-arm-debug0000664000577000060420000000047613557101274023536 0ustar anjaesctl# CONFIG_SITE.linux-arm-debug.linux-arm-debug # # Site specific overrides for linux-arm-debug host and target builds #------------------------------------------------------- #Prepares the object code to collect data for profiling with prof. #PROFILE=YES #Compiles for profiling with the gprof profiler. #GPROF=YES base-7.0.3.1/configure/os/CONFIG_SITE.linux-arm.linux-arm0000664000577000060420000000022713557101274021360 0ustar anjaesctl# CONFIG_SITE.linux-arm.linux-arm # # Site specific definitions for native linux-arm builds #------------------------------------------------------- base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86-debug.linux-x86-debug0000664000577000060420000000047613557101274023252 0ustar anjaesctl# CONFIG_SITE.linux-x86-debug.linux-x86-debug # # Site specific overrides for linux-x86-debug host and target builds #------------------------------------------------------- #Prepares the object code to collect data for profiling with prof. #PROFILE=YES #Compiles for profiling with the gprof profiler. #GPROF=YES base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.Common0000664000577000060420000000055513557101274020546 0ustar anjaesctl# CONFIG_SITE.linux-x86.Common # # Site override definitions for linux-x86 host builds #------------------------------------------------------- # JBA test override values #CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 solaris-sparc #CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 #CROSS_COMPILER_TARGET_ARCHS = RTEMS-mvme2100 RTEMS-pc386 # RTEMS-mvme5500 RTEMS-mvme167 base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.RTEMS0000664000577000060420000000030013557101274020174 0ustar anjaesctl# # Site-specific information for all RTEMS targets # #------------------------------------------------------- # Needed by gcc export LD_LIBRARY_PATH := $(LD_LIBRARY_PATH):$(RTEMS_BASE)/lib base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.UnixCommon0000664000577000060420000000015613557101274021407 0ustar anjaesctl# # # Site Specific Configuration Information # Only the local epics system manager should modify this file base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.linux-arm0000664000577000060420000000251613557101274021231 0ustar anjaesctl# CONFIG_SITE.linux-x86.linux-arm # # Site specific definitions for linux-x86 host - linux-arm target builds #------------------------------------------------------- # Set GNU crosscompiler target name GNU_TARGET = arm-xilinx-linux-gnueabi # Set GNU tools install path # Examples are installations at the APS: #GNU_DIR = /usr/local/vw/zynq-2011.09 GNU_DIR = /usr/local/vw/zynq-2016.1/gnu/arm/lin #GNU_DIR = /usr/local/Xilinx/SDK/2016.3/gnu/arm/lin #GNU_DIR = /APSshare/XilinxSDK/2015.4/gnu/arm/lin # If cross-building shared libraries and the paths on the target machine are # different than on the build host, you should uncomment the lines below to # disable putting runtime library paths in products and shared libraries. # You will also need to provide another way for programs to find their shared # libraries at runtime, such as by setting LD_LIBRARY_PATH or by using # mechanisms related to /etc/ld.so.conf #SHRLIBDIR_RPATH_LDFLAGS_YES_NO = #PRODDIR_RPATH_LDFLAGS_YES_NO = # Note: It may be simpler to just set STATIC_BUILD=YES here and not # try to use shared libraries at all in these circumstances. # To use libreadline, point this to its install prefix #READLINE_DIR = $(GNU_DIR) #READLINE_DIR = /tools/cross/linux-x86.linux-arm/readline # See CONFIG_SITE.Common.linux-arm for other COMMANDLINE_LIBRARY values #COMMANDLINE_LIBRARY = READLINE base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.linux-arm-debug0000664000577000060420000000040713557101274022312 0ustar anjaesctl# CONFIG_SITE.linux-x86.linux-arm-debug # # Site specific settings for linux-x86 host - linux-arm-debug target builds #------------------------------------------------------- # Inherit settings from linux-arm include $(CONFIG)/os/CONFIG_SITE.linux-x86.linux-arm base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.linux-arm_eb0000664000577000060420000000071413557101274021675 0ustar anjaesctl# CONFIG_SITE.linux-x86.linux-arm_eb # # Site specific definitions for linux-x86 host - linux-arm_eb target builds #------------------------------------------------------- # Include definitions for linux-arm targets include $(CONFIG)/os/CONFIG_SITE.linux-x86.linux-arm # Path to the GNU toolset for linux-arm_eb (big endian) target #GNU_DIR = /local/anj/cross-arm/gcc-3.4.5-glibc-2.3.6/armeb-linux # GNU crosscompiler target name #GNU_TARGET = armeb-linux base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.linux-arm_el0000664000577000060420000000071713557101274021712 0ustar anjaesctl# CONFIG_SITE.linux-x86.linux-arm_el # # Site specific definitions for linux-x86 host - linux-arm_el target builds #------------------------------------------------------- # Include definitions for linux-arm targets include $(CONFIG)/os/CONFIG_SITE.linux-x86.linux-arm # Path to the GNU toolset for linux-arm_el (little endian) target #GNU_DIR = /local/anj/cross-arm/gcc-3.4.5-glibc-2.3.6/armel-linux # GNU crosscompiler target name #GNU_TARGET = armel-linux base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.linux-cris0000664000577000060420000000066413557101274021414 0ustar anjaesctl# CONFIG_SITE.linux-x86.linux-cris # # Author: Peter Zumbruch # GSI # P.Zumbruch@gsi.de # # Site specific definitions for linux-x86 host - linux-cris target builds #------------------------------------------------------- # define site specific location of cris cross compiler's gnu directory # but without bin sub directory, this will be added automatically. CRIS_CROSS_COMPILER ?= UNDEFINED_ENV__CRIS_CROSS_COMPILER base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.linux-x860000664000577000060420000000025013557101274021070 0ustar anjaesctl# CONFIG_SITE.linux-x86.linux-x86 # # Site specific definitions for linux-x86 host - linux-x86 target builds #------------------------------------------------------- base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.linux-xscale_be0000664000577000060420000000057313557101274022400 0ustar anjaesctl# CONFIG_SITE.linux-x86.linux-xscale_be # # Site specific definitions for linux-x86 host - linux-xscale_be targets #------------------------------------------------------- # Set GNU_DIR to point to directory containing the tool-chain GNU_DIR = /usr/local/vw/xscale_be # If readline is available, configure it READLINE_DIR = $(GNU_DIR)/target/usr COMMANDLINE_LIBRARY = READLINE base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.solaris-sparc0000664000577000060420000000033213557101274022071 0ustar anjaesctl# CONFIG_SITE.linux-x86.solaris-sparc # # Site specific definitions for linux-x86 host - solaris-sparc target builds #------------------------------------------------------- #GNU_DIR = /home/phoebus/JBA/gnu-solaris2 base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.vxWorks-680400000664000577000060420000000035613557101274021457 0ustar anjaesctl# CONFIG_SITE.linux-x86.vxWorks-68040 # # Site specific definitions for linux-x86 host - vxWorks-68040 target builds # Only the local epics system manager should modify this file #------------------------------------------------------- base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.vxWorks-ppc6030000664000577000060420000000027713557101274022013 0ustar anjaesctl# # Site-specific definitions for linux-x86 builds of vxWorks-ppc603 # # Only the local epics system manager should modify this file #------------------------------------------------------- base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.vxWorks-ppc603_long0000664000577000060420000000044613557101274023030 0ustar anjaesctl# # Site-specific definitions for linux-x86 builds of vxWorks-ppc603_long # # Only the local epics system manager should modify this file #------------------------------------------------------- # Inherit settings from vxWorks-ppc603 -include $(CONFIG)/os/CONFIG_SITE.linux-x86.vxWorks-ppc603 base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.vxWorksCommon0000664000577000060420000000032113557101274022141 0ustar anjaesctl# CONFIG_SITE.linux-x86.vxWorksCommon # # This file is maintained by the build community. # # Definitions for linux-x86 host - vxWorks target builds #------------------------------------------------------- base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw0000664000577000060420000000142013557101274022012 0ustar anjaesctl# CONFIG_SITE.linux-x86.win32-x86-mingw # # Configuration for linux-x86 host win32-x86-mingw target builds #------------------------------------------------------- # Early versions of the MinGW cross-build tools can only build # static (non-DLL) libraries. RHEL's cross-build of gcc 4.4.6 # needs these uncommented, cross-gcc 4.6.3 from Ubuntu does not: #SHARED_LIBRARIES = NO #STATIC_BUILD = YES # The cross-build tools are in $(GNU_DIR)/bin # Default is /usr #GNU_DIR = /usr/local # Different distribution cross-build packages use different prefixes: # Ubuntu, RHEL7: CMPLR_PREFIX = i686-w64-mingw32- # RHEL6: #CMPLR_PREFIX = i686-pc-mingw32- # Debian? #CMPLR_PREFIX = i586-mingw32msvc- # Use static compiler-support libraries OP_SYS_LDFLAGS += -static-libgcc -static-libstdc++ base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86.windows-x64-mingw0000664000577000060420000000134513557101274022544 0ustar anjaesctl# CONFIG_SITE.linux-x86.windows-x64-mingw # # Configuration for linux-x86 host windows-x64-mingw target builds #------------------------------------------------------- # Early versions of the MinGW cross-build tools can only build # static (non-DLL) libraries. For example RHEL's cross-gcc 4.4.6 # needs these uncommented, cross-gcc 4.6.3 for Ubuntu does not: #SHARED_LIBRARIES = NO #STATIC_BUILD = YES # The cross-build tools are in $(GNU_DIR)/bin # Default is /usr #GNU_DIR = /usr/local # Different distribution cross-build packages use different prefixes: # Ubuntu: #CMPLR_PREFIX = i686-w64-mingw32- # RHEL: CMPLR_PREFIX = x86_64-w64-mingw32- # Use static compiler-support libraries OP_SYS_LDFLAGS += -static-libgcc -static-libstdc++ base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug0000664000577000060420000000050113557101274024061 0ustar anjaesctl# CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug # # Site specific overrides for linux-x86_64 host and target builds #------------------------------------------------------- #Prepares the object code to collect data for profiling with prof. #PROFILE=YES #Compiles for profiling with the gprof profiler. #GPROF=YES base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86_64.Common0000664000577000060420000000045613557101274021057 0ustar anjaesctl# CONFIG_SITE.linux-x86_64.Common # # Site override definitions for linux-x86_64 host builds #------------------------------------------------------- #CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 solaris-sparc #CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 #CROSS_COMPILER_TARGET_ARCHS = RTEMS-mvme2100 base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86_64.UnixCommon0000664000577000060420000000022713557101274021717 0ustar anjaesctl# CONFIG_SITE.linux-x86_64.UnixCommon # # Site Specific configure override definitions # Only the local epics system manager should modify this file base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86_64.linux-arm0000664000577000060420000000040013557101274021530 0ustar anjaesctl# CONFIG_SITE.linux-x86_64.linux-arm # # Site specific settings for linux-x86_64 host - linux-arm target builds #------------------------------------------------------- # Inherit setting from linux-x86 include $(CONFIG)/os/CONFIG_SITE.linux-x86.linux-arm base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86_64.linux-arm-debug0000664000577000060420000000041513557101274022622 0ustar anjaesctl# CONFIG_SITE.linux-x86_64.linux-arm-debug # # Site specific settings for linux-x86_64 host - linux-arm-debug target builds #------------------------------------------------------- # Inherit settings from linux-arm include $(CONFIG)/os/CONFIG_SITE.linux-x86.linux-arm base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_640000664000577000060420000000026413557101274021717 0ustar anjaesctl# CONFIG_SITE.linux-x86_64.linux-x86_64 # # Site specific definitions for linux-x86_64 host - linux-x86_64 target builds #------------------------------------------------------- base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86_64.linux-xscale_be0000664000577000060420000000041313557101274022702 0ustar anjaesctl# CONFIG_SITE.linux-x86_64.linux-xscale_be # # Site specific settings for linux-x86_64 host - linux-xscale_be target #------------------------------------------------------- # Inherit setting from linux-x86 include $(CONFIG)/os/CONFIG_SITE.linux-x86.linux-xscale_be base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86_64.vxWorks-680400000664000577000060420000000036413557101274021767 0ustar anjaesctl# CONFIG_SITE.linux-x86_64.vxWorks-68040 # # Site specific definitions for linux-x86_64 host - vxWorks-68040 target builds # Only the local epics system manager should modify this file #------------------------------------------------------- base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86_64.vxWorks-ppc6030000664000577000060420000000030113557101274022310 0ustar anjaesctl# # Site-specific definitions for linux-x86_64 builds of vxWorks-ppc603 # # Only the local epics system manager should modify this file #------------------------------------------------------- base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86_64.vxWorks-ppc603_long0000664000577000060420000000045113557101274023335 0ustar anjaesctl# # Site-specific definitions for linux-x86 builds of vxWorks-ppc603_long # # Only the local epics system manager should modify this file #------------------------------------------------------- # Inherit settings from vxWorks-ppc603 -include $(CONFIG)/os/CONFIG_SITE.linux-x86_64.vxWorks-ppc603 base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86_64.win32-x86-mingw0000664000577000060420000000042613557101274022330 0ustar anjaesctl# CONFIG_SITE.linux-x86_64.win32-x86-mingw # # Configuration for linux-x86_64 host win32-x86-mingw target builds #------------------------------------------------------- # Inherit from the linux-x86 host architecture include $(CONFIG)/os/CONFIG_SITE.linux-x86.win32-x86-mingw base-7.0.3.1/configure/os/CONFIG_SITE.linux-x86_64.windows-x64-mingw0000664000577000060420000000043413557101274023053 0ustar anjaesctl# CONFIG_SITE.linux-x86_64.windows-x64-mingw # # Configuration for linux-x86_64 host windows-x64-mingw target builds #------------------------------------------------------- # Inherit from the linux-x86 host architecture include $(CONFIG)/os/CONFIG_SITE.linux-x86.windows-x64-mingw base-7.0.3.1/configure/os/CONFIG_SITE.solaris-sparc-debug.solaris-sparc-debug0000664000577000060420000000030613557101274025242 0ustar anjaesctlinclude $(CONFIG)/os/CONFIG_SITE.Common.solaris-sparc #Prepares the object code to collect data for profiling with prof. #PROFILE=YES #Compiles for profiling with the gprof profiler. #GPROF=YES base-7.0.3.1/configure/os/CONFIG_SITE.solaris-sparc.Common0000664000577000060420000000024713557101274021544 0ustar anjaesctl# CONFIG_SITE.solaris-sparc.Common # # Site specific override definitions for solaris-sparc host builds # Only the local epics system manager should modify this file base-7.0.3.1/configure/os/CONFIG_SITE.solaris-sparc.solaris-sparc-debug0000664000577000060420000000030613557101274024156 0ustar anjaesctlinclude $(CONFIG)/os/CONFIG_SITE.Common.solaris-sparc #Prepares the object code to collect data for profiling with prof. #PROFILE=YES #Compiles for profiling with the gprof profiler. #GPROF=YES base-7.0.3.1/configure/os/CONFIG_SITE.win32-x86-mingw.win32-x86-mingw0000664000577000060420000000042313557101274022736 0ustar anjaesctl# CONFIG_SITE.win32-x86-mingw.win32-x86-mingw # # Site Specific definitions for win32-x86-mingw target # The MinGW bin directory must be in your path. # Set the compiler prefix for your MinGW installation #CMPLR_PREFIX = i686-w64-mingw32- #CMPLR_PREFIX = i586-mingw32msvc- base-7.0.3.1/configure/os/CONFIG_SITE.win32-x86.Common0000664000577000060420000000043513557101274020346 0ustar anjaesctl# CONFIG_SITE.win32-x86.Common # # Site specific definitions for win32-x86 host # Only the local epics system manager should modify this file # jba test overrides #CROSS_COMPILER_TARGET_ARCHS=vxWorks-486 #CROSS_COMPILER_TARGET_ARCHS+=vxWorks-68040 #INSTALL_LOCATION = G:/testInstall base-7.0.3.1/configure/os/CONFIG_SITE.windows-x64-mingw.windows-x64-mingw0000664000577000060420000000037013557101274024207 0ustar anjaesctl# CONFIG_SITE.windows-x64-mingw.windows-x64-mingw # # Site Specific definitions for windows-x64-mingw target # Only the local epics system manager should modify this file # Prefix for mingw compiler from cygwin #CMPLR_PREFIX = x86_64-w64-mingw32- base-7.0.3.1/documentation/KnownProblems.html0000664000577000060420000000175513557101274017762 0ustar anjaesctl 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:

base-7.0.3.1/documentation/README.cris0000664000577000060420000000376213557101274016113 0ustar anjaesctlcross compiling EPICS and building IOC Applications for cris architectures (linux-cris_v10, linux-cris_v32) ====================================================================== Please mail questions, comments, corrections, etc. ... to P.Zumbruch@gsi.de November 2007 Tools needed ------------ o Axis SDK - Overview: http://developer.axis.com/wiki/doku.php?id=axis:sdk - Download: http://www.axis.com/products/dev_sdk/download_dist.php - Install HOWTO: http://developer.axis.com/wiki/doku.php?id=axis:software_distribution_install_howto o Axis GNU gcc release for cross compiling - Download: http://www.axis.com/products/dev_sdk/download_compiler.php - Install HOWTO: http://developer.axis.com/wiki/doku.php?id=axis:compiler_install Environment ----------- o CRIS_CROSS_COMPILER - path to top directory of cris cross compiler, where binaries are in sub directory bin/ - if not set, the make process will stop at place UNDEFINED_ENV__CRIS_CROSS_COMPILER o AXIS_TOP_DIR?=UNDEFINED_ENV__AXIS_TOP_DIR - path to axis SDK top directory - if not set compile and link commands will contain references to UNDEFINED_ENV__AXIS_TOP_DIR - to set the necessary variables, execute . ./init_env in the top directory of the SDK provided here. o CRIS_COMPILER_DEBUG - if defined symbols won't be stripped, resulting in comparably large files Building -------- o Edit the CONFIG_SITE files - CONFIG_SITE.linux-x86.Common: for CROSS_COMPILER_TARGET_ARCHS += linux-cris_v10 for CROSS_COMPILER_TARGET_ARCHS += linux-cris_v32 - optionally CONFIG_SITE.linux-x86.linux-cris for setting CRIS_CROSS_COMPILER - optionally create CONFIG_SITE.linux-x86.linux-cris_v10 - optionally create CONFIG_SITE.linux-x86.linux-cris_v32 o "make". Shared Libraries ---------------- Generating shared libraries is not supported. Please feel free to contact me if you encounter serious problems. Peter base-7.0.3.1/documentation/README.darwin.html0000664000577000060420000001263413557101274017400 0ustar anjaesctl Installation notes for EPICS on Mac OS X (Darwin)

Building EPICS base

  • To build base:
    1. Set the EPICS_HOST_ARCH environment variable to darwin-ppc, darwin-x86 or darwin-ppcx86. The scripts in the base/startup directory can automate this. For example, here's part of my Bash login script (~/.bash_login):
      #
      # EPICS
      #
      EPICS_BASE="${HOME}/src/EPICS/base"
      EPICS_EXTENSIONS="${HOME}/src/EPICS/extensions"
      . "${EPICS_BASE}"/startup/unix.sh
      
    2. cd to the EPICS base top-level source directory.
    3. Uncomment the appropriate line in the relevent EPICS_BASE/configure/os/CONFIG_SITE.Common.darwin-xxx file for your EPICS_HOST_ARCH value. Newer versions of OS X (e.g. Snow Leopard) may include only 64 bit versions of some OS libraries, so should only have the x86_64 ARCH_CLASS.
    4. Run make.
  • As distributed, EPICS on Mac OS X uses the readline command line input routines. IOC applications are more pleasant to interact with if either the readline or libtecla library is used. The easiest way to get either or both of these libraries on to your system is to download and install them using the either the DarwinPorts distribution or the Fink package manager. If you don't want to install the readline library, set the COMMANDLINE_LIBRARY variable in one of the CONFIG_SITE files to EPICS.

    Information on DarwinPorts is available from the DarwinPorts project page. DarwinPorts binary packages are available from here.

    Fink may be downloaded from the Source Forge.

  • If broadcasts are not seen locally, try adding "localhost" (127.0.0.1) to the EPICS_CA_ADDR_LIST.

Building EPICS extensions

Many extensions build and run properly on OS X. To build and run medm, first obtain the X11 run-time and developer packages from Apple and the OpenMotif3 package from Fink.

Objective-C and AppleScript

Code written in Objective-C can be included in host or IOC applications. Here are a couple of short Objective-C examples which can be used to send AppleScript events to other applications on the OS X machine.

/*
 * exampleAppleScriptRecord.m 
 *
 * Simple Objective-C/AppleScript subroutine record
 *
 * To use this record in an application:
 *
 * 1) Make the following changes to the application Makefile:
 *    - Add exampleAppleScriptRecord.m to the application SRCS.
 *    - Add -framework Foundation to the application LDFLAGS.
 * 2) Add the following line to the application database description:
 *      registrar(registerExampleAppleScript)
 * 3) Add a record to the application database:
 *      record(sub,"setVolume")
 *      {
 *          field(SNAM,"exampleAppleScriptProcess")
 *      }
 */
#import <Foundation/Foundation.h>
#include <registryFunction.h>
#include <subRecord.h>
#include <alarm.h>
#include <errlog.h>
#include <recGbl.h>
#include <epicsExport.h>

/*
 * Shim between EPICS and NSAppleScript class.
 */
static long
exampleAppleScriptProcess(struct subRecord *psub)
 {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSDictionary *err;
    NSAppleScript *nsa;
    
    nsa = [[NSAppleScript alloc] initWithSource:[NSString stringWithFormat:
                @"tell application \"Finder\" to set volume %g\n", psub->a]];
    if ([nsa executeAndReturnError:&err] == nil) {
        errlogPrintf("Failed to run AppleScript: %s\n",
                        [[err objectForKey:NSAppleScriptErrorMessage] cString]);
        recGblSetSevr(psub, SOFT_ALARM, INVALID_ALARM);
    }
    [nsa release];
    [pool release];
    return 0;
}

static registryFunctionRef subRef[] = {
    {"exampleAppleScriptProcess",(REGISTRYFUNCTION)exampleAppleScriptProcess}
};

static void registerExampleAppleScript(void)
{
    registryFunctionRefAdd(subRef,NELEMENTS(subRef));
}
epicsExportRegistrar(registerExampleAppleScript);


==============================================================================
/*
 * runAppleScript.m 
 *
 * Simple Objective-C/AppleScript shim to allow EPICS application to
 * send arbitrary AppleScript messages to other applications.
 *
 * To use this subroutine in an application make the following
 * changes to the application Makefile:
 * - Add runAppleScript.m to the application SRCS.
 * - Add -framework Foundation to the application LDFLAGS.
 */
#import <Foundation/Foundation.h>
#include <errlog.h>

int
runAppleScript(const char *format, ...)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSString *script;
    NSMutableDictionary *err;
    NSAppleScript *nsa;
    va_list args;
    int ret = 0;
    
    va_start(args, format);
    script = [[NSString alloc] initWithFormat:
                            [NSString stringWithCString:format] arguments:args];
    va_end(args);
    err = [NSMutableDictionary dictionaryWithCapacity:10];
    nsa = [[NSAppleScript alloc] initWithSource:script];
    if ([nsa executeAndReturnError:&err] == nil) {
        errlogPrintf("Failed to run AppleScript: %s\n",
                        [[err objectForKey:NSAppleScriptErrorMessage] cString]);
        ret = -1;
    }
    [script release];
    [nsa release];
    [pool release];
    return ret;
}
base-7.0.3.1/documentation/README.md0000664000577000060420000003757413557101274015563 0ustar anjaesctl# Installation Instructions ## EPICS Base Release 7.0.3.1 ----- ### Table of Contents - [What is EPICS base?](#0_0_1) - [What is new in this release?](#0_0_2) - [Copyright](#0_0_3) - [Supported platforms](#0_0_4) - [Supported compilers](#0_0_5) - [Software requirements](#0_0_6) - [Host system storage requirements](#0_0_7) - [Documentation](#0_0_8) - [Directory Structure](#0_0_10) - [Build related components](#0_0_11) - [Building EPICS base (Unix and Win32)](#0_0_12) - [Example application and extension](#0_0_13) - [Multiple host platforms](#0_0_14) ----- ### What is EPICS base? The Experimental Physics and Industrial Control Systems (EPICS) is an extensible set of software components and tools with which application developers can create a control system. This control system can be used to control accelerators, detectors, telescopes, or other scientific experimental equipment. EPICS base is the set of core software, i.e. the components of EPICS without which EPICS would not function. EPICS base allows an arbitrary number of target systems, IOCs (input/output controllers), and host systems, OPIs (operator interfaces) of various types. ### What is new in this release? Please check the `RELEASE_NOTES` file in the distribution for description of changes and release migration details. ### Copyright Please review the LICENSE file included in the distribution for legal terms of usage. ### Supported platforms The list of platforms supported by this version of EPICS base is given in the `configure/CONFIG_SITE` file. If you are trying to build EPICS Base on an unlisted host or for a different target machine you must have the proper host/target cross compiler and header files, and you will have to create and add the appropriate new configure files to the base/configure/os/directory. You can start by copying existing configuration files in the configure/os directory and then make changes for your new platforms. ### Supported compilers This version of EPICS base has been built and tested using the host vendor's C and C++ compilers, as well as the GNU gcc and g++ compilers. The GNU cross-compilers work for all cross-compiled targets. You may need the C and C++ compilers to be in your search path to do EPICS builds; check the definitions of CC and CCC in `base/configure/os/CONFIG..` if you have problems. ### Software requirements **GNU make** You must use GNU make, gnumake, for any EPICS builds. Set your path so that a gnumake version 3.81 or later is available. **Perl** You must have Perl version 5.8.1 or later installed. The EPICS configuration files do not specify the perl full pathname, so the perl 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. **Target systems** EPICS supports IOCs running on embedded platforms such as VxWorks and RTEMS built using a cross-compiler, and also supports soft IOCs running as processes on the host platform. **vxWorks** You must have vxWorks 6.8 or later 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 absolute path to and the version number of the vxWorks installation must be set in the `base/configure/os/CONFIG_SITE.Common.vxWorksCommon` file or in one of its target-specific overrides. Consult the [vxWorks 6.x](https://epics.anl.gov/base/vxWorks6.php) EPICS web pages about and the vxWorks documentation for information about configuring your vxWorks operating system for use with EPICS. **RTEMS** For RTEMS targets, you need RTEMS core and toolset version 4.9.x or 4.10.x (4.11 or 5.x are not yet supported). **GNU readline or Tecla library** GNU readline and Tecla libraries can be used by the IOC shell to provide command line editing and command line history recall and edit. GNU readline (or Tecla library) must be installed on your target system when `COMMANDLINE_LIBRARY` is set to READLINE (or TECLA) for that target. EPICS (EPICS shell) is the default specified in `CONFIG_COMMON`. A READLINE override is defined for linux-x86 in the EPICS distribution. Comment out `COMMANDLINE_LIBRARY=READLINE` in `configure/os/CONFIG_SITE.Common.linux-x86` if readline is not installed on linux-x86. Command-line editing and history will then be those supplied by the os. On vxWorks the ledLib command-line input library is used instead. ### Host system storage requirements The compressed tar file is approximately 1.6 MB in size. The distribution source tree takes up approximately 12 MB. Each host target will need around 40 MB for build files, and each cross-compiled target around 20 MB. ### Documentation EPICS documentation is available through the [EPICS website](https://epics.anl.gov/) at Argonne. Release specific documentation can also be found in the base/documentation directory of the distribution. ### Directory Structure #### Distribution directory structure: ``` base Root directory of the base distribution base/configure Operating system independent build config files base/configure/os Operating system dependent build config files base/documentation Distribution documentation base/src Source code in various subdirectories base/startup Scripts for setting up path and environment ``` #### Install directories created by the build: ``` bin Installed scripts and executables in subdirs cfg Installed build configuration files db Installed data bases dbd Installed data base definitions doc Installed documentation files html Installed html documentation include Installed header files include/os Installed os specific header files in subdirs include/compiler Installed compiler-specific header files lib Installed libraries in arch subdirectories lib/perl Installed perl modules templates Installed templates ``` ### Build related components #### base/documentation directory - contains setup, build, and install documents ``` README.md Instructions for setup and building epics base README.darwin.html Installation notes for Mac OS X (Darwin) RELEASE_NOTES.html Notes on release changes KnownProblems.html List of known problems and workarounds ``` #### base/startup directory - contains scripts to set environment and path ``` EpicsHostArch Shell script to set EPICS_HOST_ARCH env variable unix.csh C shell script to set path and env variables unix.sh Bourne shell script to set path and env variables win32.bat Bat file example to configure win32-x86 target windows.bat Bat file example to configure windows-x64 target ``` #### base/configure directory - contains build definitions and rules ``` CONFIG Includes configure files and allows variable overrides CONFIG.CrossCommon Cross build definitions CONFIG.gnuCommon Gnu compiler build definitions for all archs CONFIG_ADDONS Definitions for and DEFAULT options CONFIG_APP_INCLUDE CONFIG_BASE EPICS base tool and location definitions CONFIG_BASE_VERSION Definitions for EPICS base version number CONFIG_COMMON Definitions common to all builds CONFIG_ENV Definitions of EPICS environment variables CONFIG_FILE_TYPE CONFIG_SITE Site specific make definitions CONFIG_SITE_ENV Site defaults for EPICS environment variables MAKEFILE Installs CONFIG* RULES* creates RELEASE Location of external products RULES Includes appropriate rules file RULES.Db Rules for database and database definition files RULES.ioc Rules for application iocBoot/ioc* directory RULES_ARCHS Definitions and rules for building architectures RULES_BUILD Build and install rules and definitions RULES_DIRS Definitions and rules for building subdirectories RULES_EXPAND RULES_FILE_TYPE RULES_TARGET RULES_TOP Rules specific to a dir (uninstall and tar) Sample.Makefile Sample makefile with comments ``` #### base/configure/os directory - contains os-arch specific definitions ``` CONFIG.. Specific host-target build definitions CONFIG.Common. Specific target definitions for all hosts CONFIG..Common Specific host definitions for all targets CONFIG.UnixCommon.Common Definitions for Unix hosts and all targets CONFIG.Common.UnixCommon Definitions for Unix targets and all hosts CONFIG.Common.vxWorksCommon Specific host definitions for all vx targets CONFIG_SITE.. Site specific host-target definitions CONFIG_SITE.Common. Site specific target defs for all hosts CONFIG_SITE..Common Site specific host defs for all targets ``` ### Building EPICS base (Unix and Win32) #### Unpack file Unzip and untar the distribution file. Use WinZip on Windows systems. #### Set environment variables Files in the base/startup directory have been provided to help set required path and other environment variables. * `EPICS_HOST_ARCH` Before you can build or use EPICS R3.15, the environment variable `EPICS_HOST_ARCH` must be defined. A perl script EpicsHostArch.pl in the base/startup directory has been provided to help set `EPICS_HOST_ARCH.` You should have `EPICS_HOST_ARCH` set to your host operating system followed by a dash and then your host architecture, e.g. solaris-sparc. If you are not using the OS vendor's c/c++ compiler for host builds, you will need another dash followed by the alternate compiler name (e.g. "-gnu" for GNU c/c++ compilers on a solaris host or "-mingw" for MinGW c/c++ compilers on a WIN32 host). See `configure/CONFIG_SITE` for a list of supported `EPICS_HOST_ARCH` values. * `PERLLIB` On WIN32, some versions of Perl require that the environment variable PERLLIB be set to <perl directory location>. * `PATH` As already mentioned, you must have the perl executable and you may need C and C++ compilers in your search path. For building base you also must have echo in your search path. For Unix host builds you also need ln, cpp, cp, rm, mv, and mkdir in your search path and /bin/chmod must exist. On some Unix systems you may also need ar and ranlib in your path, and the C compiler may require as and ld in your path. On solaris systems you need uname in your path. * `LD_LIBRARY_PATH` R3.15 shared libraries and executables normally contain the full path to any libraries they require. However, if you move the EPICS files or directories from their build-time location then in order for the shared libraries to be found at runtime `LD_LIBRARY_PATH` must include the full pathname to `$(INSTALL_LOCATION)/lib/$(EPICS_HOST_ARCH)` when invoking executables, or some equivalent OS-specific mechanism (such as /etc/ld.so.conf on Linux) must be used. Shared libraries are now built by default on all Unix type hosts. #### Do site-specific build configuration **Site configuration** To configure EPICS, you may want to modify the default definitions in the following files: ``` configure/CONFIG_SITE Build choices. Specify target archs. configure/CONFIG_SITE_ENV Environment variable defaults configure/RELEASE TORNADO2 full path location ``` **Host configuration** To configure each host system, you may override the default definitions by adding a new file in the configure/os directory with override definitions. The new file should have the same name as the distribution file to be overridden except with CONFIG in the name changed to `CONFIG_SITE`. ``` configure/os/CONFIG.. Host build settings configure/os/CONFIG..Common Host common build settings ``` **Target configuration** To configure each target system, you may override the default definitions by adding a new file in the configure/os directory with override definitions. The new file should have the same name as the distribution file to be overridden except with CONFIG in the name replaced by `CONFIG_SITE`. This step is necessary even if the host system is the only target system. ``` configure/os/CONFIG.Common. Target common settings configure/os/CONFIG.. Host-target settings ``` #### Build EPICS base After configuring the build you should be able to build EPICS base by issuing the following commands in the distribution's root directory (base): ``` gnumake clean uninstall gnumake ``` The command "gnumake clean uninstall" will remove all files and directories generated by a previous build. The command "gnumake" will build and install everything for the configured host and targets. It is recommended that you do a "gnumake clean uninstall" at the root directory of an EPICS directory structure before each complete rebuild to ensure that all components will be rebuilt. ### Example application and extension A perl tool, makeBaseApp.pl is included in the distribution file. This script will create a sample application that can be built and then executed to try out this release of base. Instructions for building and executing the 3.15 example application can be found in the section "Example Application" of Chapter 2, "Getting Started", in the "IOC Application Developer's Guide" for this release. The "Example IOC Application" section briefly explains how to create and build an example application in a user created <top> directory. It also explains how to run the example application on a vxWorks ioc or as a process on the host system. By running the example application as a host-based IOC, you will be able to quickly implement a complete EPICS system and be able to run channel access clients on the host system. A perl script, makeBaseExt.pl, is included in the distribution file. This script will create a sample extension that can be built and executed. The makeBaseApp.pl and makeBaseExt.pl scripts are installed into the install location `bin/` directory during the base build. ### Multiple host platforms You can build using a single EPICS directory structure on multiple host systems and for multiple cross target systems. The intermediate and binary files generated by the build will be created in separate subdirectories and installed into the appropriate separate host/target install directories. EPICS executables and perl scripts are installed into the `$(INSTALL_LOCATION)/bin/` directories. Libraries are installed into $`(INSTALL_LOCATION)/lib/`. The default definition for `$(INSTALL_LOCATION)` is `$(TOP)` which is the root directory in the distribution directory structure, base. Created object files are stored in `O.` source subdirectories, This allows objects for multiple cross target architectures to be maintained at the same time. To build EPICS base for a specific host/target combination you must have the proper host/target C/C++ cross compiler and target header files and the base/configure/os directory must have the appropriate configure files. base-7.0.3.1/documentation/RELEASE_NOTES.md0000664000577000060420000024525713557101274016655 0ustar anjaesctl# 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 from commits to new releases in an older Base series are not described at the top of this file but have entries that appear lower down, under the series to which they were originally committed.** Thus it is important to read more than just the first section to understand everything that has changed in each release. The external PVA submodules each have their own separate set of release notes which should also be read to understand what has changed since an earlier release. # EPICS Release 7.0.3.1 **IMPORTANT NOTE:** *Some record types in this release will not be compatible with device support binaries compiled against earlier versions of those record types, because importing the record documentation from the EPICS Wiki [as described below](#imported-record-reference-documentation-from-wiki) also modified the order of some of the fields in the record definitions.* As long as all support modules and IOCs are rebuilt from source after updating them to use this release of EPICS Base, these changes should not have any affect. ### logClient reliability On supported targets (Linux, Mac, Windows) logClient will attempt to avoid dropping undelivered log messages when the connection to the log server is closed/reset. ### Timers and delays use monotonic clock Many internal timers and delay calculations use a monotonic clock epicsTimeGetMonotonic() instead of the realtime epicsTimeGetCurrent(). This is intended to make IOCs less susceptible to jumps in system time. ### Iocsh `on error ...` A new statement is added to enable IOC shell commands to signal error conditions, and for scripts to respond. This first is through the new function ```C int iocshSetError(int err); ``` A script may be prefixed with eg. "on error break" to stop at the failed command. ```sh on error continue | break | wait [value] | halt ``` A suggested form for IOC shell commands is: ```C static void doSomethingCallFunc(const iocshArgBuf *args) { iocshSetError(doSomething(...)); /* return 0 == success */ } ``` ### Relocatable Builds Allows built trees to be copied or moved without invalidating RPATH entires. The `LINKER_USE_RPATH` Makefile variable (see `configure/CONFIG_SITE`) may be set to `YES`, `NO`, and a new third option `ORIGIN`. This is limited to targets using the ELF executable format (eg. Linux). When `LINKER_USE_RPATH=ORIGIN`, the variable `LINKER_ORIGIN_ROOT` is set to one of the parents of the build directory. Any libraries being linked to which are found under this root will have a relative RPATH entry. Other libraries continue to result in absolute RPATH entries. An effect of this might change a support library from being linked with `-Wl,-rpath /build/epics-base/lib/linux-x86` to being linked with `-Wl,-rpath \$ORIGIN/../../../epics-base/lib/linux-x86` if the support module directory is `/build/mymodule` and `LINKER_ORIGIN_ROOT=/build`. 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 Previously, setting `STATIC_BUILD=NO` implied `LINKER_USE_RPATH=NO`. This is no longer the case. Setting `LINKER_USE_RPATH=YES` will always emit RPATH entries. This was found to be helpful when linking against some 3rd party libraries which are only available as shared objects. ### Channel Access Security: Check Hostname Against DNS Host names given in a `HAG` entry of an IOC's Access Security Configuration File (ACF) have to date been compared against the hostname provided by the CA client at connection time, which may or may not be the actual name of that client. This allows rogue clients to pretend to be a different host, and the IOC would believe them. An option is now available to cause an IOC to ask its operating system to look up the IP address of any hostnames listed in its ACF (which will normally be done using the DNS or the `/etc/hosts` file). The IOC will then compare the resulting IP address against the client's actual IP address when checking access permissions at connection time. This name resolution is performed at ACF file load time, which has a few consequences: 1. If the DNS is slow when the names are resolved this will delay the process of loading the ACF file. 2. If a host name cannot be resolved the IOC will proceed, but this host name will never be matched. 3. Any changes in the hostname to IP address mapping will not be picked up by the IOC unless and until the ACF file gets reloaded. Optionally, IP addresses may be added instead of, or in addition to, host names in the ACF file. This feature can be enabled before `iocInit` with ``` var("asCheckClientIP",1) ``` or with the VxWorks target shell use ```C asCheckClientIP = 1 ``` ### New and modified epicsThread APIs #### `epicsThreadCreateOpt()` A new routine `epicsThreadCreateOpt()` is an alternative to `epicsThreadCreate()` which takes some arguments via a structure (`struct epicsThreadOpts`) to allow for future extensions. ```C typedef struct epicsThreadOpts { unsigned int priority; unsigned int stackSize; unsigned int joinable; } epicsThreadOpts; #define EPICS_THREAD_OPTS_INIT { \ epicsThreadPriorityLow, epicsThreadStackMedium, 0} epicsThreadId epicsThreadCreateOpt(const char * name, EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts); ``` The final `opts` parameter may be `NULL` to use the default values of thread priority (low) and stack size (medium). Callers wishing to provide alternative settings for these thread options or to create a joinable thread (see below) should create and pass in an `epicsThreadOpts` structure as shown below. Always initialize one of these structures using the `EPICS_THREAD_OPTS_INIT` macro to ensure that any additional fields that get added in the future are set to their default values. ```C void startitup(void) { epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; epicsThreadId tid; opts.priority = epicsThreadPriorityMedium; tid = epicsThreadCreateOpt("my thread", &threadMain, NULL, &opts); } ``` C or C++ Code that also needs to build on earlier versions of Base can use `#ifdef EPICS_THREAD_OPTS_INIT` to determine whether the `epicsThreadCreateOpt()` API is available on this Base version. #### Thread stack sizes The `stackSize` member of the `epicsThreadOpts` structure and the equivalent parameters to the `epicsThreadCreate()` and `epicsThreadMustCreate()` routines can now be passed either one of the `epicsThreadStackSizeClass` enum values or a value returned from the `epicsThreadGetStackSize()` routine. #### `epicsThreadMustJoin()` If the new `joinable` flag of an `epicsThreadOpts` structure is non-zero (the default value is zero), the new API routine `epicsThreadMustJoin()` *must* be called with the thread's `epicsThreadId` when/after the thread exits, to free up thread resources. This function will block until the thread's main function has returned, allowing the parent to wait for its child thread. The child's `epicsThreadId` will no longer be valid and should not be used after the `epicsThreadMustJoin()` routine returns. A thread that was originally created with its joinable flag set may itself call `epicsThreadMustJoin()`, passing in its own epicsThreadId. This marks the thread as no longer being joinable, so it will then free the thread resources itself when its main function returns. The `epicsThreadId` of a thread that is not joinable gets invalidated as soon as its main function returns. ### Non-VME RTEMS targets now define pdevLibVME Previously IOC executables that made calls to devLib routines would fail to link when built for some non-VME based RTEMS targets, which would have to be explicitly filtered out by sites that build Base for those targets. [This fix](https://bugs.launchpad.net/epics-base/+bug/1841692) makes that no longer necessary, all RTEMS targets should now link although the IOC won't be able to be used with the VME I/O on those systems (that we don't have VMEbus I/O support for in libCom). # EPICS Release 7.0.3 ### `epicsTimeGetCurrent()` optimization Add a fast path to epicsTimeGetCurrent() and related calls in the common case where only the default OS current time provider is registered. This path does not take the global mutex guarding the time providers list, potentially reducing lock contention. ### dbEvent tweak Queue size The size of the queue used by dbEvent to push monitor updates has been slightly increased based on `DBR_TIME_DOUBLE` to better fill an ethernet frame. This may result in slightly fewer, but larger frames being sent. ### mbbo/mbbiDirect number of bits as precision Report NOBT as "precision" through the dbAccess API. This is not accessible through CA, but is planned to be used through QSRV. # EPICS Release 7.0.2.2 ### Build System changes * The GNUmake build targets `cvsclean` and `depclean` are now available from any directory; previously they were only available from application top directories. * The approach that EPICS Base uses for building submodules inside the parent module looks useful for support modules too. The rules for building submodules have been modified and extracted into a new `RULES_MODULES` file, so a support module will be able to use them too without having to copy them into its own `modules/Makefile`. There are some specific requirements that support modules and their submodules must follow, which are described as comments in the new `base/configure/RULES_MODULES` file itself. ### `EPICS_BASE_VERSION` Update Policy change In the past, a build of EPICS using sources checked out from the repository branch between official releases would have shown the version number of the previous release, followed by a -DEV suffix, for example 7.0.2.1-DEV. The policy that controls when the number gets updated has been changed, and now immediately after a release has been tagged the version number will be updated to the next patch release version, plus the -DEV suffix as before. Thus following 7.0.2.2 the version number will show as 7.0.2.3-DEV. This does not require the next official release to be numbered 7.0.2.3 though, it could become 7.0.3 or even 7.1.0 if the changes incorporated into it are more substantial than bug fixes. ### Drop `CLOCK_MONOTONIC_RAW` from posix/osdMonotonic.c Turns out this is ~10x slower to query than `CLOCK_MONOTONIC`. # EPICS Release 7.0.2.1 ### Linking shared libraries on macOS The linker flag `-flat_namespace` has been restored for creating shared libraries, although not for loadable libraries (bundles). This was required for building using the latest versions of Apple XCode. ### Fix `DB_LINK` loop breaking A regression was introduced in 7.0.2 which caused record chains with loops to be incorrectly broken. Processing should be skipped when a `DB_LINK` with Process Passive (PP) closes a loop to a synchronous record. Instead in 7.0.2 the targeted record would be processed if processing began with a remote action (or some other caller of `dbPutField()`). This would result in the loop running a second time. The loop would be broken on the second iteration. [See lp: #1809570](https://bugs.launchpad.net/epics-base/+bug/1809570) ### Old dbStaticLib APIs removed Support for some obsolete dbStaticLib Database Configuration Tool (DCT) APIs was removed some time ago, but vestiges of them still remained. The following routines and macros and have now finally been removed: * `int dbGetFieldType(DBENTRY *pdbentry)` * `int dbGetLinkType(DBENTRY *pdbentry)` * `DCT_STRING` * `DCT_INTEGER` * `DCT_REAL` * `DCT_MENU` * `DCT_MENUFORM` * `DCT_INLINK` * `DCT_OUTLINK` * `DCT_FWDLINK` * `DCT_NOACCESS` * `DCT_LINK_CONSTANT` * `DCT_LINK_FORM` * `DCT_LINK_PV` ### Fix for `dbhcr` before `iocInit` The `dbhcr` command used to work before `iocInit` as well as afterwards. It displays all records that have hardware addresses (`VME_IO`, `CAMAC_IO`, `GPIB_IO`, `INST_IO` etc.) but stopped working if run before iocInit due to the rewrite of the link address parser code in dbStaticLib. This release fixes that issue, although in some cases the output may be slightly different than it used to be. # EPICS Release 7.0.2 ### Launchpad Bugs The list of tracked bugs fixed in this release can be found on the [Launchpad Milestone page for EPICS Base 7.0.2](https://launchpad.net/epics-base/+milestone/7.0.2). ### Git Branches Recombined The four separate Git branches `core/master`, `libcom/master`, `ca/master` and `database/master` have been recombined into one branch called `7.0`. Keeping these as 4 separate branches in the same repository made it impossible to create merge requests that contained changes in more than one of these modules. The layout of the source files has not changed at all however, so the source code for libcom, ca and the database are still found separately under the module subdirectory. # EPICS Release 7.0.1.1 ### Changed SIML failure behavior A failure when fetching the simulation mode through `SIML` will not put the record into INVALID alarm state anymore. Instead, as long as the record's current alarm severity (`SEVR`)is `NO_ALARM`, its alarm status (`STAT`) will be set to `LINK_ALARM` without increasing the severity. This allows clients to get some notification of a failing or bad `SIML` link without otherwise affecting record processing. ### `dbVerify()` has been restored to dbStaticLib This routine was removed in Base-3.16.1 but has been reimplemented in this release by special request. Note that the error message strings that it returns when verification fails have changed, but are still designed for display to the user. ### Simulation mode improvements Records that support simulation mode have two new fields, `SSCN` (Simulation Scan Mode) and `SDLY` (Simulation Delay). `SSCN` is a menu field that provides an alternate value for the `SCAN` field to be used while the record is in simulation mode. This is especially useful for I/O scanned records, for which simulation mode was not working at all. Setting `SDLY` to a positive value makes the record process asynchronously in simulation mode, with the second stage processing happening after the specified time (in seconds). ### Extend the dbServer API with init/run/pause/stop methods This change permits IOCs to be built that omit the CA server (RSRV) by removing its registrar entry which is now provided in the new `rsrv.dbd` file. Other server layers can be built into the IOC (alongside RSRV or in place of it) by registering them in a similar manner. The dbServer API is documented with Doxygen comments in the header file. Specific IOC server layers can be disabled at runtime by adding their name to the environment variable `EPICS_IOC_IGNORE_SERVERS` (separated by spaces if more than one should be ignored). ### Grand source-code reorganization EPICS 7.0.1 contains the IOC Database, RSRV server and the Channel Access client code from EPICS Base 3.16.1 along with all the original record types and soft device support, but GDD and the Portable Channel Access Server have been unbundled and are now available separately. In their place we have brought in the more recently written EPICS V4 C++ libraries (collectively referred to as the PVA modules). The directory tree for EPICS is somewhat larger as a result, and the original structure of the Base directories has been split into 4 separate Git repositories. External modules should build against this new structure with little or no changes needed, except that some allowance may be needed for the merging of the V4 modules. There should be rather more description and documantation of these changes than is currently available, but as developers we generally much prefer to write code than documentation. Send questions to the tech-talk mailing list and we'll be happy to try and answer them! ## Changes between 3.16.1 and 3.16.2 The list of tracked bugs fixed in this release can be found on the [Launchpad Milestone page for EPICS Base 3.16.2](https://launchpad.net/epics-base/+milestone/3.16.2). ### Status reporting for the callback and scanOnce task queues Two new iocsh commands and some associated underlying APIs have been added to show the state of the queues that feed the three callback tasks and the scanOnce task, including a high-water mark which can optionally be reset. The new iocsh commands are `callbackQueueShow` and `scanOnceQueueShow`; both take an optional integer argument which must be non-zero to reset the high-water mark. ### Support for event codes greater than or equal to `NUM_TIME_EVENTS` Event numbers greater than or equal to `NUM_TIME_EVENTS` are now allowed if supported by the registered event time provider, which must provide its own advancing timestamp validation for such events. Time events numbered 0 through `(NUM_TIME_EVENTS-1)` are still validated by code in epicsGeneralTime.c that checks for advancing timestamps and enforces that restriction. ### Type-safe Device and Driver Support Tables Type-safe versions of the device and driver support structures `dset` and `drvet` have been added to the devSup.h and drvSup.h headers respectively. The original structure definitions have not been changed so existing support modules will still build normally, but older modules can be modified and new code written to be compatible with both. The old structure definitions will be replaced by the new ones if the macros `USE_TYPED_DSET` and/or `USE_TYPED_DRVET` are defined when the appropriate header is included. The best place to define these is in the Makefile, as with the `USE_TYPED_RSET` macro that was introduced in Base-3.16.1 and described below. See the comments in devSup.h for a brief usage example, or look at [this commit](https://github.com/epics-modules/ipac/commit/a7e0ff4089b9aa39108bc8569e95ba7fcf07cee9) to the ipac module to see a module conversion. A helper function `DBLINK* dbGetDevLink(dbCommon *prec)` has also been added to devSup.h which fetches a pointer to the INP or OUT field of the record. ### RTEMS build configuration update, running tests under QEMU This release includes the ability to run the EPICS unit tests built for a special version of the RTEMS-pc386 target architecture on systems that have an appropriate QEMU emulator installed (`qemu-system-i386`). It is also now possible to create sub-architectures of RTEMS targets, whereas previously the EPICS target architecture name had to be `RTEMS-$(RTEMS_BSP)`. The new target `RTEMS-pc386-qemu` builds binaries that can be run in the `qemu-system-i386` PC System emulator. This target is a derivative of the original `RTEMS-pc386` target but with additional software to build an in- memory file-system, and some minor modifications to allow the unit tests to work properly under QEMU. When this target is enabled, building any of the make targets that cause the built-in self-tests to be run (such as `make runtests`) will also run the tests for RTEMS using QEMU. To allow the new 3-component RTEMS target name, the EPICS build system for RTEMS was modified to allow a `configure/os/CONFIG.Common.` file to set the `RTEMS_BSP` variable to inform the build what RTEMS BSP to use. Previously this was inferred from the value of the `T_A` make variable, but that prevents having multiple EPICS targets that build against the same BSP. All the included RTEMS target configuration files have been updated; build configuration files for out-of-tree RTEMS targets will continue to work as the original rules are used to set `RTEMS_BSP` if it hasn't been set when needed. ### Link type enhancements This release adds three new link types: "state", "debug" and "trace". The "state" link type gets and puts boolean values from/to the dbState library that was added in the 3.15.1 release. The "debug" link type sets the `jlink::debug` flag in its child link, while the "trace" link type also causes the arguments and return values for all calls to the child link's jlif and lset routines to be printed on stdout. The debug flag can no longer be set using an info tag. The addition of the "trace" link type has allowed over 200 lines of conditional diagnostic printf() calls to be removed from the other link types. The "calc" link type can now be used for output links as well as input links. This allows modification of the output value and even combining it with values from other input links. See the separate JSON Link types document for details. A new `start_child()` method was added to the end of the jlif interface table. The `lset` methods have now been properly documented in the dbLink.h header file using Doxygen annotations, although we do not run Doxygen on the source tree yet to generate API documentation. Link types that utilize child links must now indicate whether the child will be used for input, output or forward linking by the return value from its `parse_start_map()` method. The `jlif_key_result` enum now contains 3 values `jlif_key_child_inlink`, `jlif_key_child_outlink` and `jlif_key_child_fwdlink` instead of the single `jlif_key_child_link` that was previously used for this. ### GNUmake targets for debugging Some additional build rules have been added to help debug configuration problems with the build system. Run `make show-makefiles` to get a sorted list of all the files that the build system includes when building in the current directory. A new pattern rule for `PRINT.%` can be used to show the value of any GNUmake variable for the current build directory (make sure you are in the right directory though, many variables are only set when inside the `O.` build directory). For example `make PRINT.T_A` will display the build target architecture name from inside a `O.` directory but the variable will be empty from an application top or src directory. `make PRINT.EPICS_BASE` will show the path to Base from any EPICS application directory though. ### Propagate PUTF across Asynchronous record processing The IOC contains a mechanism involving the PUTF and RPRO fields of each record to ensure that if a record is busy when it receives a put to one of its fields, the record will be processed again to ensure that the new field value has been correctly acted on. Until now that mechanism only worked if the put was to the asynchronous record itself, so puts that were chained from some other record via a DB link did not cause reprocessing. In this release the mechanism has been extended to propagate the PUTF state across DB links until all downstream records have been reprocessed. Some additional information about the record state can be shown by setting the TPRO field of an upstream record, and even more trace data is displayed if the debugging variable `dbAccessDebugPUTF` is set in addition to TPRO. ### Finding info fields A new iocsh command `dbli` lists the info fields defined in the database, and can take a glob pattern to limit output to specific info names. The newly added dbStaticLib function `dbNextMatchingInfo()` iterates through the info fields defined in the current record, and is used to implement the new command. ### Output from `dbpr` command enhanced The "DataBase Print Record" command `dbpr` now generates slightly better output, with more field types having their own display methods. This release also includes additional protection against buffer overflows while printing long links in `dbpr`, and corrects the output of long strings from the `dbgf` command. ### Record types mbbiDirect and mbboDirect upgraded to 32 bit The VAL fields and related fields of these records are now `DBF_LONG`. (Not `DBF_ULONG` in order to prevent Channel Access from promoting them to `DBF_DOUBLE`.) Additional bit fields `B10`...`B1F` have been added. Device support that accesses `VAL` or the bit fields directly (most don't) and aims for compatibility with old and new versions of these records should use at least 32 bit integer types to avoid bit loss. The number of bit fields can be calculated using `8 * sizeof(prec->val)` which is correct in both versions. ### Restore use of ledlib for VxWorks command editing The epicsReadline refactoring work described below unfortunately disabled the VxWorks implementation of the osdReadline.c API that uses ledlib for command editing and history. This functionality has now been restored, see Launchpad [bug #1741578](https://bugs.launchpad.net/bugs/1741578). ### Constant link types Constant links can now hold 64-bit integer values, either as scalars or arrays. Only base 10 is supported by the JSON parser though, the JSON standard doesn't allow for hexadecimal numbers. ### Upgraded the YAJL JSON Library The third-party YAJL library that has been included in libCom for several years has been upgraded to version 2.1.0 and several bugs fixed. This has an updated API, requiring any code that uses it to parse its own JSON files to be modified to match. The changes are mainly that it uses `size_t` instead `unsigned int` for string lengths, but it also uses `long long` instead of `long` for JSON integer values, which was the main motivation for the upgrade. The self-tests that YAJL comes with have been imported and are now run as an EPICS Unit Test program, and the JSON syntax accepted by the parser was extended to permit trailing commas in both arrays and maps. The difference between the old and new YAJL APIs can be detected at compile time by looking for the macro `EPICS_YAJL_VERSION` which is defined in the `yajl_common.h` header file along with a brief description of the API changes. ### Timestamp support for the calc link type A new optional parameter can be given when specifying a calc JSON link. The `time` parameter is a string containing a single letter `A..L` that selects one of the input links to be used for the timestamp of calculation if requested. The timestamp will be fetched atomically with the value from the chosen input link (providing that input link type supports the readLocked() method). ### Silence errors from puts to constant link types A soft channel output record with the OUT link unset uses the CONSTANT link type. The new link type code was causing some soft channel device supports to return an error status from the write method of that link type, which would cause a `ca_put()` operation to such a record to generate an exception. This has been silenced by giving the constant link types a dummy putValue method. A new test program has been added to prevent regressions of this behaviour. ### RSRV expanding large buffer causes crash In the 3.16.1 release a crash can occur in the IOC's RSRV server when a large array is made even larger; the previous array buffer was not being released correctly. See Launchpad [bug #1706703](https://bugs.launchpad.net/epics-base/+bug/1706703). ## Changes made between 3.16.0.1 and 3.16.1 ### IOC Database Support for 64-bit integers The IOC now supports the 64-bit integer field types `DBF_INT64` and `DBF_UINT64`, and there are new record types `int64in` and `int64out` derived from the `longin` and `longout` types respectively that use the `DBF_INT64` data type for their VAL and related fields. The usual range of Soft Channel device support are included for these new record types. All internal IOC APIs such as dbAccess can handle the new field types and their associated request values `DBR_INT64` and `DBR_UINT64`, which are implemented using the `epicsInt64` and `epicsUInt64` typedef's from the `epicsTypes.h` header. The waveform record type has been updated to support these new field types. **All waveform device support layers must be updated to recognize the new type enumeration values**, which had to be inserted before the `FLOAT` value in the enum `dbfType` and in `menuFtype`. C or C++ code can detect at compile-time whether this version of base provides 64-bit support by checking for the presence of the `DBR_INT64` macro as follows (Note that `DBF_INT64` is an enum tag and not a preprocessor macro): ``` #ifdef DBR_INT64 /* Code where Base has INT64 support */ #else /* Code for older versions */ #endif ``` If the code uses the old `db_access.h` types (probably because it's calling Channel Access APIs) then it will have to test against the EPICS version number instead, like this: ``` #include #ifndef VERSION_INT # define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P)) #endif #ifndef EPICS_VERSION_INT # define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL) #endif #if EPICS_VERSION_INT >= VERSION_INT(3,16,1,0) /* Code where Base has INT64 support */ #else /* Code for older versions */ #endif ``` 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 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 the IOC. Additional 64-bit support will be provided in later release. For instance the JSON parser for the new Link Support feature only handles integers up to 32 bits wide, so constant array initializer values cannot hold larger values in this release. ### Add `EPICS_CA_MCAST_TTL` A new environment parameter `EPICS_CA_MCAST_TTL` is used to set the Time To Live (TTL) value of any IP multi-cast CA search or beacon packets sent. ### `EPICS_CA_MAX_ARRAY_BYTES` is optional A new environment parameter `EPICS_CA_AUTO_ARRAY_BYTES` is now used by libca and RSRV (CA clients and the IOC CA server). The default is equivalent to setting `EPICS_CA_AUTO_ARRAY_BYTES=YES` which removes the need to set `EPICS_CA_MAX_ARRAY_BYTES` and always attempts to allocate sufficiently large network buffers to transfer large arrays properly over the network. In this case the value of the `EPICS_CA_MAX_ARRAY_BYTES` parameter is ignored. Explicitly setting `EPICS_CA_AUTO_ARRAY_BYTES=NO` will continue to honor the buffer setting in `EPICS_CA_AUTO_ARRAY_BYTES` as in previous releases. The default setting for `EPICS_CA_AUTO_ARRAY_BYTES` can be changed by adding the line ```makefile EPICS_CA_AUTO_ARRAY_BYTES=NO ``` to the `configure/CONFIG_SITE_ENV` file before building Base. Sites that wish to override this only for specific IOC architectures can create new files for each architecture named `configure/os/CONFIG_SITE_ENV.` with the above setting in before building Base. The configuration can also be explicitly changed by setting the environment variable in the IOC's startup script, anywhere above the `iocInit` line. The PCAS server (used by the PV Gateway and other CA servers) now always behaves as if `EPICS_CA_AUTO_ARRAY_BYTES` is set to `YES` (it ignores the configuration parameter and environment variable). ### Channel Access "modernization" Drop support for CA clients advertising protocol versions less than 4. This effects clients from Base older than 3.12.0-beta1. Newer clients will continue to be able to connect to older servers. Older clients will be ignored by newer servers. This allows removal of UDP echo and similar protocol features which are not compatible with secure protocol design practice. ### Lookup-tables using the subArrray record The subArray record can now be used as a lookup-table from a constant array specified in its INP field. For example: ``` record(subArray, "powers-of-2") { field(FTVL, "LONG") field(MALM, 12) field(INP, [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048]) field(INDX, 0) field(NELM, 1) } ``` The INDX field selects which power of 2 to set the VAL field to. In previous releases the INP field would have to have been pointed to a separate waveform record that was initialized with the array values somehow at initialization time. ### Synchronized Timestamps with TSEL=-2 Most Soft Channel input device support routines have supported fetching the timestamp through the INP link along with the input data. However before now there was no guarantee that the timestamp provided by a CA link came from the same update as the data, since the two were read from the CA input buffer at separate times without maintaining a lock on that buffer in between. This shortcoming could be fixed as a result of the new link support code, which allows code using a link to pass a subroutine to the link type which will be run with the link locked. The subroutine may make multiple requests for metadata from the link, but must not block. ### Extensible Link Types A major new feature introduced with this release of EPICS Base is an Extensible Link Type mechanism, also known as Link Support or JSON Link Types. This addition permits new kinds of link I/O to be added to an IOC in a similar manner to the other extension points already supported (e.g. record, device and driver support). A new link type must implement two related APIs, one for parsing the JSON string which provides the link address and the other which implements the link operations that get called at run-time to perform I/O. The link type is built into the IOC by providing a new `link` entry in a DBD file. #### New Link Types Added This release contains two new JSON link types, `const` and `calc`: * The `const` link type is almost equivalent to the old CONSTANT link type with the updates described below to accept arrays and strings, except that there is no need to wrap a scalar string constant inside array brackets since a constant string will never be confused with a PV name. * The `calc` link type allows CALC expressions to be used to combine values from other JSON links to produce its value. Until additional JSON link types are created though, the `calc` link type has little practical utility as it can currently only fetch inputs from other `calc` links or from `const` links. ``` field(INP, {calc:{expr:"A+B+1", args:[5, # A {const:6}] # B } } ) ``` The new link types are documented in a separate document that gets generated at build time and installed as `html/links.html`. #### Device Support Addressing using `JSON_LINK` The API to allow device support to use JSON addresses is currently incomplete; developers are advised not to try creating device support that specifies a `JSON_LINK` address type. #### Support Routine Modifications for Extensible Link Types For link fields in external record types and soft device support to be able to use the new link types properly, various changes are required to utilize the new Link Support API as defined in the dbLink.h header file and outlined below. The existing built-in Database and Channel Access link types have been altered to implement the link APIs, so will work properly after these conversions: * Make all calls to `recGblInitConstantLink()` unconditional on the link type, i.e. change this code: ```C if (prec->siml.type == CONSTANT) { recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); } ``` into this: ```C recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); ``` Note that `recGblInitConstantLink()` still returns TRUE if the field was successfully initialized from the link (implying the link is constant). This change will work properly with all Base releases currently in use. * Code that needs to identify a constant link should be modified to use the new routine `dbLinkIsConstant()` instead, which returns TRUE for constant or undefined links, FALSE for links whose `dbGetLink()` routine may return different values on different calls. For example this: ```C if (prec->dol.type != CONSTANT) ``` should become this: ```C if (!dbLinkIsConstant(&prec->dol)) ``` When the converted software is also required to build against older versions of Base, this macro definition may be useful: ```C #define dbLinkIsConstant(lnk) ((lnk)->type == CONSTANT) ``` * Any code that calls dbCa routines directly, or that explicitly checks if a link has been resolved as a CA link using code such as ```C if (prec->inp.type == CA_LINK) ``` will still compile and run, but will only work properly with the old CA link type. To operate with the new extensible link types such code must be modified to use the new generic routines defined in dbLink.h and should never attempt to examine or modify data inside the link. After conversion the above line would probably become: ```C if (dbLinkIsVolatile(&prec->inp)) ``` A volatile link is one like a Channel Access link which may disconnect and reconnect without notice at runtime. Database links and constant links are not volatile; unless their link address is changed they will always remain in the same state they started in. For compatibility when building against older versions of Base, this macro definition may be useful: ```C #define dbLinkIsVolatile(lnk) ((lnk)->type == CA_LINK) ``` * The current connection state of a volatile link can be found using the routine `dbIsLinkConnected()` which will only return TRUE for a volatile link that is currently connected. Code using the older dbCa API returning this information used to look like this: ```C stat = dbCaIsLinkConnected(plink); ``` which should become: ```C stat = dbIsLinkConnected(plink); ``` Similar changes should be made for calls to the other dbCa routines. * A full example can be found by looking at the changes to the calcout record type, which has been modified in this release to use the new dbLink generic API. ### Constant Link Values Previously a constant link (i.e. a link that did not point to another PV, either locally or over Channel Access) was only able to provide a single numeric value to a record initialization; any string given in a link field that was not recognized as a number was treated as a PV name. In this release, constant links can be expressed using JSON array syntax and may provide array initialization of values containing integers, doubles or strings. An array containing a single string value can also be used to initialize scalar strings, so the stringin, stringout, lsi (long string input), lso (long string output), printf, waveform, subArray and aai (analog array input) record types and/or their soft device supports have been modified to support this. Some examples of constant array and string initialized records are: ``` record(stringin, "const:string") { field(INP, ["Not-a-PV-name"]) } record(waveform, "const:longs") { field(FTVL, LONG) field(NELM, 10) field(INP, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) } record(aai, "const:doubles") { field(FTVL, DOUBLE) field(NELM, 10) field(INP, [0, 1, 1.6e-19, 2.718, 3.141593]) } record(aSub, "select") { field(FTA, STRING) field(NOA, 4) field(INPA, ["Zero", "One", "Two", "Three"]) field(FTB, SHORT) field(NOB, 1) field(FTVA, STRING) field(NOVA, 1) field(SNAM, "select_asub") } ``` Reminder: Link initialization with constant values normally only occurs at record initialization time. The calcout and printf record types are the only exceptions in the Base record types to this rule, so it is generally not useful to change a const link value after iocInit. ### Database Parsing of "Relaxed JSON" Values A database file can now provide a "relaxed JSON" value for a database field value or an info tag. Only a few field types can currently accept such values, but the capability is now available for use in other places in the future. When writing to a JSON-capable field at run-time however, only strictly compliant JSON may be used (the dbStaticLib parser rewrites relaxed JSON values into strict JSON before passing them to the datase for interpretation, where the strict rules must be followed). "Relaxed JSON" was developed to maximize compatibility with the previous database parser rules and reduce the number of double-quotes that would be needed for strict JSON syntax. The parser does accept strict JSON too though, which should be used when machine-generating database files. The differences are: * Strings containing only the characters `a-z A-Z 0-9 _ - + .` do not have to be enclosed in double-quote characters. * The above rule applies to map keys as well as to regular string values. * The JSON keywords `null`, `true` and `false` (all lower-case) will be recognized as keywords, so they must be quoted to use any of these single words as a string. * Comments may be used, introduced as usual by the `#` character and extending to the end of the line. A JSON field or info value is only enclosed in quotes when the value being provided is a single string, and even here the quotes can be omitted in some cases as described above. The following shows both correct and incorrect excerpts from a database file: ``` record(ai, math:pi) { field(INP, {const: 3.14159265358979}) # Correct field(SIOL, "{const: 3.142857}") # Wrong info(autosave, { # White-space and comments are allowed fields:[DESC, SIMM], pass0:[VAL] }) # Correct } ``` Note that the record, field and info-tag names do *not* accept JSON values, so they follows the older bareword rules for quoting where the colon `:` and several additional characters are legal in a bareword string. Only the value (after the comma) is parsed as JSON. The autosave module has not been modified to accept JSON syntax, the above is only an example of how JSON might be used. ### Echoless comments in iocsh The way comments are parsed by the iocsh interpreter has changed. The interpreter can be selectively disabled from echoing comments coming from a script by starting those lines with `#-` rather than just `#`. ### Typed record support methods The table of record support functions (rset methods for short) no longer has entries of type `RECSUPFUN` (which says: any number and type of arguments). Instead, rset methods are now typed by default. The `RECSUPFUN` typedef has been deprecated and casts to it as well as using the untyped `struct rset` will create compilation warnings. Existing code (e.g. external record supports) will generate such warnings when compiled against this version of Base, but it will work without changes. For a conversion period, the new typed rset definitions are activated by defining `USE_TYPED_RSET`, preferably by setting `USR_CPPFLAGS += -DUSE_TYPED_RSET` inside a Makefile. After activating the new typed rset in this way and making the following changes, the result should still compile and work properly against older versions of Base. The first parameter of `init_record` and `process` has been changed to `struct dbCommon *`. Record types that use `void*` here should be changed to use `struct dbCommon*`, and cast the argument to their own `xxxRecord *`. When compiled against this release, compiler warnings about incompatible types for the method pointers should be taken seriously. When compiled against older versions of base, such warnings are unavoidable. Record types written in C++ need to take more drastic measures because of the stricter type checking in C++. To remain compatible with older versions of base you will need to use something like: ``` #include "epicsVersion.h" #ifdef VERSION_INT # if EPICS_VERSION_INT < VERSION_INT(3,16,0,2) # define RECSUPFUN_CAST (RECSUPFUN) # else # define RECSUPFUN_CAST # endif #else # define RECSUPFUN_CAST (RECSUPFUN) #endif ``` and then replace `(RECSUPFUN)` with `RECSUPFUN_CAST` when initializing the rset. Further changes might also be needed, e.g. to adapt `const`-ness of method parameters. ## Changes made between 3.15.3 and 3.16.0.1 ### Build support for CapFast and dbst removed The build rules associated with the CapFast-related tools `sch2edif` and `e2db` and the database optimization tool `dbst` have been removed, along with the `DB_OPT` build configuration variable. ### compressRecord buffering order The compressRecord has a new field `BALG` which can select between FIFO (append) and LIFO (prepend) ordering for insertion of new elements. FIFO ordering is the default, matching the behviour of previous versions. ### Valgrind Instrumentation Valgrind is a software debugging suite provided by many Linux distributions. The header valgrind/valgrind.h is now included in, and installed by, Base. When included by a C or C++ source file this header defines some macros which expand to provide hints to the Valgrind runtime. These have no effect on normal operation of the software, but when run using the valgrind tool they can help to find memory leaks and buffer overflows. Suitable hints have been added to several free-lists within libCom, including freeListLib, allowing valgrind to provide more accurate information about the source of potential leaks. valgrind.h automatically disables itself when the build target is not supported by the valgrind tool. It can also explicitly be disabled by defining the macro `NVALGRIND`. See `src/libCom/Makefile` for a commented-out example. As a matter of policy valgrind.h will never be included by any header file installed by Base, so its use will remain purely an implementation detail hidden from application software. Support modules which choose to use valgrind.h are advised to do likewise. ### Database Multi-locking The IOC record locking code has been re-written with an expanded API; global locks are no longer required by the IOC database implementation. The new API functions center around `dbScanLockMany()`, which behaves like `dbScanLock()` applied to an arbitrary group of records. `dbLockerAlloc()` is used to prepare a list or record pointers, then `dbScanLockMany()` is called. When it returns, all of the records listed may be accessed (in any order) until `dbScanUnlockMany()` is called. The Application Developer's Guide has been updated to describe the API and implementation is more detail. Previously a global mutex `lockSetModifyLock` was locked and unlocked during `dbScanLock()`, acting as a sequencing point for otherwise unrelated calls. The new dbLock.c implementation does not include any global mutex in `dbScanLock()` or `dbScanLockMany()`. Locking and unlocking of unrelated lock sets is now completely concurrent. ### Generate Version Header A Perl script and Makefile rules have been added to allow modules to generate a C header file with a macro defined with an automatically updated identifier. This is a VCS revision ID (Darcs, Git, Mercurial, Subversion, and Bazaar are supported) or the date/time of the build if no VCS system is in use. The makeBaseApp example template has been updated with a new device support which makes this identifier visible via a lsi (long string input) record. ### epicsTime API return status The epicsTime routines that used to return epicsTimeERROR now return a specific `S_time_` status value, allowing the caller to discover the reason for any failure. The identifier `epicsTimeERROR` is no longer defined, so any references to it in source code will no longer compile. The identifier epicsTimeOK still exists and has the value 0 as before, so most code that uses these APIs can be changed in a way that is backwards-compatible with the previous return status. Time providers that have to return a status value and still need to be built with earlier versions of Base can define the necessary status symbols like this: ``` #include "epicsTime.h" #ifndef M_time /* S_time_... status values were not provided before Base 3.16 */ #define S_time_unsynchronized epicsTimeERROR #define S_time_...whatever... epicsTimeERROR #endif ``` ### Refactoring of epicsReadline The epicsReadline code has been reorganized to allow the commandline history editor to be disabled at runtime. The `EPICS_COMMANDLINE_LIBRARY` build setting still selects the preferred editor, but the new `IOCSH_HISTEDIT_DISABLE` environment variable can be set at runtime to disable history editing and make the IOC or other program use the basic editor instead. This is useful when starting and controlling an IOC from another program through its stdin and stdout streams since history editors often insert invisible escape codes into the stdout stream, making it hard to parse. ### Callback subsystem API Added a new macro `callbackGetPriority(prio, callback)` to the callback.h header and removed the need for dbScan.c to reach into the internals of its `CALLBACK` objects. ## Changes from the 3.15 branch since 3.15.7 > None. ## Changes made between 3.15.6 and 3.15.7 ### GNU Readline detection on Linux Most Linux architectures should now configure themselves automatically to use the GNU Readline library if its main header file can be found in the expected place, and not try to use Readline if the header file isn't present. For older Linux architectures where libncurses or libcurses must also be linked with, the manual configuration of the `COMMANDLINE_LIBRARY` variable in the appropriate `configure/os/CONFIG_SITE.Common.` file will still be necessary. ### Replace `EPICS_TIMEZONE` with `EPICS_TZ` The `EPICS_TIMEZONE` environment parameter provided time-zone information for the IOC's locale in the old ANSI format expected by VxWorks for its `TIMEZONE` environment variable, and can also used by RTEMS to set its `TZ` environment variable. However the `TIMEZONE` value has to be updated every year since it contains the exact dates of the daylight-savings time changes. The Posix TZ format that RTEMS uses contains rules that for calculating those dates, thus its value would only need updating if the rules (or the locale) are changed. This release contains changes that replace the `EPICS_TIMEZONE` environment parameter with one called `EPICS_TZ` and a routine for VxWorks that calculates the `TIMEZONE` environment variable from the current `TZ` value. This routine will be run once at start-up, when the EPICS clock has synchronized to its NTP server. The calculations it contains were worked out and donated to EPICS by Larry Hoff in 2009; it is unforunate that it has taken 10 years for them to be integrated into Base. The default value for the `EPICS_TZ` environment parameter is set in the Base `configure/CONFIG_SITE_ENV` file, which contains example settings for most EPICS sites that use VxWorks, and a link to a page describing the Posix TZ format for any locations that I missed. If a VxWorks IOC runs continuously without being rebooted from December 31st to the start of daylight savings time the following year, its `TIMEZONE` value will be wrong as it was calculated for the previous year. This only affects times that are converted to a string on the IOC however and is easily fixed; just run the command `tz2timezone()` on the VxWorks shell and the calculation will be redone for the current year. IOCs that get rebooted at least once before the start of summer time will not need this to be done. ### Added new decimation channel filter A new server-side filter has been added to the IOC for reducing the number and frequency of monitor updates from a channel by a client-specified factor. The filter's behaviour is quite simplistic, it passes the first monitor event it sees to the client and then drops the next N-1 events before passing another event. For example to sample a 60Hz channel at 1Hz, a 10Hz channel every 6 seconds, or a 1Hz channel once every minute: ``` Hal$ camonitor 'test:channel.{"dec":{"n":60}}' ... ``` More information is included in the filters documentation, which can be found in the `html/filters.html` document that is generated during the build. ### Imported Record Reference Documentation from Wiki The remaining record types that had 3.14 reference documentation in the EPICS Wiki have had that documentation converted and imported into their DBD files. The preferred form for future updates to the record type descriptions is now an emailed patch file, a Pull Request through GitHub, or a Merge Request through Launchpad. Note that in some cases the behavior of a record type in a 7.0.x release may differ from that of the same record type in a 3.15 release, although this would be unusual, so it may be important to indicate the branch that your changes apply to. **NOTE:** *These documentation changes have modified the order of the fields in some record definitions. As a result this release is not compatible with record or device support binaries that were compiled against earlier releases.* ### `make test-results` for Windows The make target `test-results` should now work properly on Windows. Some Perl installations used versions of `prove.bat` that would only display the results of up to 3 tests or didn't return an error status in the event of tests failing. The build system now calls its own perl script to summarize the results instead of passing a list of TAP filenames to `prove`. ### Add option to avoid CALLBACK conflict If a macro `EPICS_NO_CALLBACK` is defined, then callback.h will no longer (re)define CALLBACK. The name `CALLBACK` is used by the WIN32 API, and redefinition in callback.h cause errors if some windows headers are later included. Code which defines `EPICS_NO_CALLBACK`, but still wishes to use callbacks, should use the alternate name `epicsCallback` introduced in 3.15.6, 3.16.2, and 7.0.2. It is also possible, though not encouraged, to use `struct callbackPvt` which has been present since the callback API was introduced. ### Cleaning up with Multiple CA contexts in a Process Bruno Martins reported a problem with the CA client library at shutdown in a process that uses multiple CA client contexts. The first context that triggers the CA client exit handler prevents any others from being able to clean up because it resets the ID of an internal epicsThreadPrivate variable which is shared by all clients. This action has been removed from the client library, which makes cleanup of clients like this possible. ### Perl CA bindings fixed for macOS Mojave Apple removed some Perl header files from macOS Mojave that were available in their SDK, requiring a change to the include paths used when compiling the CA bindings. The new version should build on new and older macOS versions, and these changes may also help other targets that have an incomplete installation of Perl (the build will continue after printing a warning that the Perl CA bindings could not be built). ### Routine `epicsTempName()` removed from libCom This routine was a simple wrapper around the C89 function `tmpnam()` which is now seen as unsafe and causes warning messages to be generated by most modern compilers. The two internal uses of this function have been modified to call `epicsTempFile()` instead. We were unable to find any published code that used this function, so it was removed immediately instead of being deprecated. ### DBD Parsing of Record Types The Perl DBD file parser has been made slightly more liberal; the order in which DBD files must be parsed is now more flexible, so that a record type definition can now be parsed after a device support that referred to that record type. A warning message will be displayed when the device support is seen, but the subsequent loading of the record type will be accepted without triggering an error. See [Launchpad bug 1801145](https://bugs.launchpad.net/epics-base/+bug/1801145). ### menuScan and several record types documented with POD The EPICS Wiki pages describing a number of standard record types has been converted into the Perl POD documentation format and added to the DBD files, so at build-time an HTML version of these documents is generated and installed into the htmls directory. Thanks to Tony Pietryla. ### CA client tools learned `-V` option This displays the version numbers of EPICS Base and the CA protocol. ## Changes made between 3.15.5 and 3.15.6 ### Unsetting environment variables The new command `epicsEnvUnset varname` can be used to unset an environment variable. ### Warning indicators in msi (and macLib) output The libCom macro expansion library has been modified so that when the `SUPPRESS_WARNINGS` flag is set it will no longer include any `,undefined` or `,recursive` indicators in its output when undefined or recursive macros are encountered. These indicators were harmless when the output was fed into an IOC along with a definition for the macro, but when the `msi` tool was used to generate other kinds of files they caused problems. If the `msi -V` flag is used the markers will still be present in the output whenever the appropriate condition is seen. ### Improvements to msi In addition to fixing its response to discovering parsing errors in its substitution input file (reported as Launchpad [bug 1503661](https://bugs.launchpad.net/epics-base/+bug/1503661)) so it now deletes the incomplete output file, the msi program has been cleaned up a little bit internally. ### All array records now post monitors on their array-length fields The waveform record has been posting monitors on its NORD field since Base 3.15.0.1; we finally got around to doing the equivalent in all the other built-in record types, which even required modifying device support in some cases. This fixes [Launchpad bug 1730727](https://bugs.launchpad.net/epics-base/+bug/1730727). ### HOWTO: Converting Wiki Record Reference to POD Some documentation has been added to the `dbdToHtml.pl` script explaining how Perl POD (Plain Old Documentation) markup can be added to `.dbd` files to generate HTML documentation for the record types. To see these instructions, run `perl bin//dbdToHtml.pl -H` or `perldoc bin//dbdToHtml.pl`. ### Fix problem with numeric soft events Changing from numeric to named soft events introduced an incompatibility when a numeric event 1-255 is converted from a DOUBLE, e.g. from a calc record. The `post_event()` API is not marked deprecated any more. Also `scanpel` has been modified to accept a glob pattern for event name filtering and to show events with no connected records as well. ### Add `osiSockOptMcastLoop_t` and osiSockTest Added a new OS-independent typedef for multicast socket options, and a test file to check their correct operation. ### Support for `CONFIG_SITE.local` in Base This feature is mostly meant for use by developers; configuration settings that would normally appear in `base/configure/CONFIG_SITE` can now be put in a locally created `base/configure/CONFIG_SITE.local` file instead of having go modify or replace the original. A new `.gitignore` pattern tells git to ignore all `configure/*.local` files. ### Fix broken `EPICS_IOC_LOG_FILE_LIMIT=0` setting The Application Developers' Guide says this is allowed and disables the limit on the log-file, but it hasn't actually worked for some time (if ever). Note that the iocLogServer will be removed from newer Base release sometime soon as its functionality can be implemented by other dedicated log servers such as logstash or syslog-ng. Fixes [lp:1786858](https://bugs.launchpad.net/bugs/1786858) and part of [lp:1786966](https://bugs.launchpad.net/bugs/1786966). ### Cleanup of startup directory The files in the startup directory have not been maintained in recent years and have grown crufty (technical term). This release includes the following updates to these files: - The Perl `EpicsHostArch.pl` script has been rewritten, and support for a few previously missing host architectures has been added to it. - The `EpicsHostArch.pl` script has also been moved into the standard `src/tools` directory, from where it will be installed into `lib/perl`. In this new location it is no longer executable, so it must be run by the `perl` executable. - The build system has been adjusted to look for `EpicsHostArch.pl` in both places if the `EPICS_HOST_ARCH` environment variable has not been set at build-time. - Sites that used the original Perl script to set `EPICS_HOST_ARCH` as part of their standard environment will need to adjust their scripts when they upgrade to this release. - The `EpicsHostArch` shell script has been replaced with a wrapper routine that calls the Perl `EpicsHostArch.pl` script. Sites that rely on this script to set `EPICS_HOST_ARCH` should consider switching to the Perl script instead. - The `Site.cshrc` and `Site.profile` files have been renamed to `unix.csh` and `unix.sh`, respectively. - The existing `win32.bat` file has been cleaned up and a new `windows.bat` file added for 64-bit targets. The contents of these files should be seen as examples, don't uncomment or install parts for software that you don't explicitly know that you need. ### Recent Apple XCode Build Issues The latest version of XCode will not compile calls to `system()` or `clock_settime()` for iOS targets. There were several places in Base where these were being compiled, although there were probably never called. The code has now been modified to permit iOS builds to complete again. ### Prevent illegal alarm severities A check has been added to `recGblResetAlarms()` that prevents records from getting an alarm severity higher than `INVALID_ALARM`. It is still possible for a field like HSV to get set to a value that is not a legal alarm severity, but the core IOC code should never copy such a value into a record's SEVR or ACKS fields. With this fix the record's alarm severity will be limited to `INVALID_ALARM`. ### Fixes for Launchpad bugs The following launchpad bugs have fixes included: - [lp: 1786320](https://bugs.launchpad.net/epics-base/+bug/1786320), dbCa subscribes twice to ENUM - [lp: 541221](https://bugs.launchpad.net/epics-base/+bug/541221), `assert (pca->pgetNative)` failed in ../dbCa.c - [lp: 1747091](https://bugs.launchpad.net/epics-base/+bug/1747091), epicsTimeGetEvent() / generalTime bug - [lp: 1743076](https://bugs.launchpad.net/epics-base/+bug/1743076), Segfault in `ca_attach_context()` during exits - [lp: 1751380](https://bugs.launchpad.net/epics-base/+bug/1751380), Deadlock in `ca_clear_subscription()` - [lp: 1597809](https://bugs.launchpad.net/epics-base/+bug/1597809), Setting NAME field in DB file may break IOC - [lp: 1770292](https://bugs.launchpad.net/epics-base/+bug/1770292), `get_alarm_double()` inconsistent across record types - [lp: 1771298](https://bugs.launchpad.net/epics-base/+bug/1771298), Conversion of NaN to integer relies on undefined behavior ### Updated VxWorks Timezone settings Removed the settings for 2017; fixed the hour of the change for MET. ### Fixed camonitor server side relative timestamps bug Initialize the first time-stamp from the first monitor, not the client-side current time in this configuration. ### Build changes for MSVC Windows builds using Visual Studio 2015 and later now use the `-FS` compiler option to allow parallel builds to work properly. We now give the `-FC` option to tell the compiler to print absolute paths for source files in diagnostic messages. ### Extend maximum Posix epicsEventWaitWithTimeout() delay The Posix implementation of epicsEventWaitWithTimeout() was limiting the timeout delay to at most 60 minutes (3600.0 seconds). This has been changed to 10 years; significantly longer maximum delays cause problems on systems where `time_t` is still a signed 32-bit integer so cannot represent absolute time-stamps after 2038-01-19. Our assumption is that such 32-bit systems will have been retired before the year 2028, but some additional tests have been added to the epicsTimeTest program to detect and fail if this assumption is violated. ### New test-related make targets This release adds several new make targets intended for use by developers and Continuous Integration systems which simplify the task of running the built-in self-test programs and viewing the results. Since these targets are intended for limited use they can have requirements for the build host which go beyond the standard minimum set needed to build and run Base. #### `test-results` - Summarize test results The new make target `test-results` will run the self-tests if necessary to generate a TAP file for each test, then summarizes the TAP output files in each test directory in turn, displaying the details of any failures. This step uses the program `prove` which comes with Perl, but also needs `cat` to be provided in the default search path so will not work on most Windows systems. #### `junitfiles` - Convert test results to JUnit XML Format The new make target `junitfiles` will run the self-tests if necessary and then convert the TAP output files into the more commonly-supported JUnit XML format. The program that performs this conversion needs the Perl module `XML::Generator` to have been installed. #### `clean-tests` - Delete test result files The new make target `clean-tests` removes any test result files from previous test runs. It cleans both TAP and JUnit XML files. ### Fix DNS related crash on exit The attempt to fix DNS related delays for short lived CLI programs (eg. caget) in [lp:1527636](https://bugs.launchpad.net/epics-base/+bug/1527636) introduced a bug which cased these short lived clients to crash on exit. This bug should now be fixed. ### Server bind issue on Windows When a National Instruments network variables CA server is already running on a Windows system and an IOC or PCAS server is started, the IOC's attempt to bind a TCP socket to the CA server port number fails, but Windows returns a different error status value than the IOC is expecting in that circumstance (because the National Instruments code requests exclusive use of that port, unlike the EPICS code) so the IOC fails to start properly. The relevent EPICS bind() checks have now been updated so the IOC will request that a dynamic port number be allocated for this TCP socket instead when this happens. ### Checking Periodic Scan Rates Code has been added to the IOC startup to better protect it against bad periodic scan rates, including against locales where `.` is not accepted as a decimal separator character. If the scan period in a menuScan choice string cannot be parsed, the associated periodic scan thread will no longer be started by the IOC and a warning message will be displayed at iocInit time. The `scanppl` command will also flag the faulty menuScan value. ## Changes made between 3.15.4 and 3.15.5 ### dbStatic Library Speedup and Cleanup Loading of database files has been optimized to avoid over-proportionally long loading times for large databases. As a part of this, the alphabetical ordering of records instances (within a record type) has been dropped. In the unexpected case that applications were relying on the alphabetic order, setting `dbRecordsAbcSorted = 1` before loading the databases will retain the old behavior. The routine `dbRenameRecord()` has been removed, as it was intended to be used by database configuration tools linked against a host side version of the dbStatic library that is not being built anymore. ### Launchpad Bug-fixes In addition to the more detailed change descriptions below, the following Launchpad bugs have also been fixed in this release: - [lp:1440186](https://bugs.launchpad.net/epics-base/+bug/1440186) Crash due to a too small buffer being provided in `dbContextReadNotifyCache()` - [lp:1479316](https://bugs.launchpad.net/epics-base/+bug/1479316) Some data races found using Helgrind - [lp:1495833](https://bugs.launchpad.net/epics-base/+bug/1495833) biRecord prompt groups are nonsensical - [lp:1606848](https://bugs.launchpad.net/epics-base/+bug/1606848) WSAIoctl `SIO_GET_INTERFACE_LIST` failed in Windows ### Whole-Program Optimization for MS Visual Studio Targets When using the Microsoft compilers a new build system variable is provided that controls whether whole program optimization is used or not. For static builds using Visual Studio 2010 this optimization must be disabled. This is controlled in the files `configure/os/CONFIG_SITE.Common.windows-x64-static` and `configure/os/CONFIG_SITE.Common.win32-x86-static` by setting the variable `OPT_WHOLE_PROGRAM=NO` to override the default value `YES` that would otherwise be used. Note that enabling this optimization slows down the build process. It is not possible to selectively disable this optimization, when building a particular module say; Microsoft's linker will restart itself automatically with the `-LTCG` flag set and display a warning if it is asked to link any object files that were compiled with the `-GL` flag. ### Add dynamic (variable length) array support to PCAS Dynamic array sizing support was added to the IOC server (RSRV) in the Base-3.14.12 release, but has not until now been supported in the Portable Channel Access Server (PCAS). Channel Access server applications using the PCAS may not need to be modified at all; if they already push monitors with different gdd array lengths, those variable sizes will be forwarded to any CA clients who have requested variable length updates. The example CAS server application has been modified to demonstrate this feature. In implementing the above, the gdd method `gdd::put(const gdd *)` now copies the full-sized array from the source gdd if the destination gdd is of type array, has no allocated memory and a boundary size of 0. ### Additional epicsTime conversion The EPICS timestamp library (epicsTime) inside libCom's OSI layer has been extended by routines that convert from `struct tm` to the EPICS internal `epicsTime` type, assuming UTC - i.e. without going through the timezone mechanism. This solves issues with converting from the structured type to the EPICS timestamp at driver level from multiple threads at a high repetition rate, where the timezone mechanism was blocking on file access. ### MinGW Cross-builds from Linux The build configuration files that allow cross-building of the 32-bit win32-x86-mingw cross-target have been adjusted to default to building shared libraries (DLLs) as this is now supported by recent MinGW compilers. The 64-bit windows-x64-mingw cross-target was already being built that way by default. The configuration options to tell the minGW cross-compiler to link programs with static versions of the compiler support libraries have now been moved into the `CONFIG_SITE.linux-x86.` files. ### General Time updates The `iocInit` code now performs a sanity check of the current time returned by the generalTime subsystem and will print a warning if the wall-clock time returned has not been initialized yet. This is just a warning message; when a time provider does synchonize the IOC will subsequently pick up and use the correct time. This check code also primes the registered event system provider if there is one so the `epicsTimeGetEventInt()` routine will work on IOCs that ask for event time within an interrupt service routine. The osiClockTime provider's synchronization thread (which is only used on some embedded targets) will now poll the other time providers at 1Hz until the first time it manages to get a successful timestamp, after which it will poll for updates every 60 seconds as before. The routine `generalTimeGetExceptPriority()` was designed for use by backup (lower priority) time providers like the osiClockTime provider which do not have their own absolute time reference and rely on other providers for an absolute time source. This routine no longer implements the ratchet mechanism that prevented the time it returned from going backwards. If the backup clock's tick-timer runs fast the synchronization of the backup time provider would never allow it to be corrected backwards when the ratchet was in place. The regular `epicsTimeGetCurrent()` API still uses the ratchet mechanism, so this change will not cause the IOC to see time going backwards. ### Microsoft Visual Studio builds The build configuration files for builds using the Microsoft compilers have been updated, although there should be no noticable difference at most sites. One extra compiler warning is now being suppressed for C++ code, `C4344: behavior change: use of explicit template arguments results in ...` which is gratuitous and was appearing frequently in builds of the EPICS V4 modules. Cross-builds of the windows-x64 target from a win32-x86 host have been removed as they don't actually work within the context of a single `make` run. Significant changes to the build configuration files would be necessary for these kinds of cross-builds to work properly, which could be done if someone needs them (email Andrew Johnson before working on this, and see [this stack-overflow answer](http://stackoverflow.com/questions/5807647/how-do-you-compile-32-bit-and-64-bit-applications-at-the-same-time-in-visual-stu) for a starting point). ### Bazaar keywords such as 'Revision-Id' removed In preparation for moving to git in place of the Bazaar revision control system we have removed all the keywords from the Base source code. ### Linux systemd service file for CA Repeater Building this version of Base on a Linux system creates a systemd service file suitable for starting the Channel Access Repeater under systemd. The file will be installed into the target bin directory, from where it can be copied into the appropriate systemd location and modified as necessary. Installation instructions are included as comments in the file. ## Changes made between 3.15.3 and 3.15.4 ### New string input device support "getenv" A new "getenv" device support for both the stringin and lsi (long string input) record types can be used to read the value of an environment variable from the IOC at runtime. See base/db/softIocExit.db for sample usage. ### Build rules and `DELAY_INSTALL_LIBS` A new order-only prerequisite build rule has been added to ensure that library files (and DLL stubs on Windows) get installed before linking any executables, which resolves parallel build problems on high-powered CPUs. There are some (rare) cases though where a Makefile has to build an executable and run it to be able to compile code for a library built by the same Makefile. With this new build rule GNUmake will complain about a circular dependency and the build will probably fail in those cases. To avoid this problem the failing Makefile should set `DELAY_INSTALL_LIBS = YES` before including the `$(TOP)/configure/RULES` file, disabling the new build rule. ### IOC environment variables and build parameters The IOC now sets a number of environment variables at startup that provide the version of EPICS Base it was built against (`EPICS_VERSION_...`) and its build architecture (ARCH). In some cases this allows a single iocBoot/ioc directory to be used to run the same IOC on several different architectures without any changes. There are also 3 new environment parameters (`EPICS_BUILD_...`) available that C/C++ code can use to find out the target architecture, OS class and compiler class it was built with. These may be useful when writing interfaces to other languages. ### New implementation of `promptgroup`/`gui_group` field property The mechanism behind the `promptgroup()` field property inside a record type definition has been changed. Instead of using a fixed set of choices, the static database access library now collects the used gui group names while parsing DBD information. Group names should start with a two-digit number plus space-dash-space to allow proper sorting of groups. The include file `guigroup.h` that defined the fixed set of choices has been deprecated. Instead, use the conversion functions between index number and group string that have been added to dbStaticLib. When a DBD file containing record-type descriptions is expanded, any old-style `GUI_xxx` group names will be replaced by a new-style string for use by the IOC. This permits an older record type to be used with the 3.15.4 release, although eventually record types should be converted by hand with better group names used. ### CA server configuration changes RSRV now honors `EPICS_CAS_INTF_ADDR_LIST` and binds only to the provided list of network interfaces. Name searches (UDP and TCP) on other network interfaces are ignored. For example on a computer with interfaces 10.5.1.1/24, 10.5.2.1/24, and 10.5.3.1/24, setting `EPICS_CAS_INTF_ADDR_LIST='10.5.1.1 10.5.2.1'` will accept traffic on the .1.1 and .2.1, but ignore from .3.1 RSRV now honors `EPICS_CAS_IGNORE_ADDR_LIST` and ignores UDP messages received from addresses in this list. Previously, CA servers (RSRV and PCAS) would build the beacon address list using `EPICS_CA_ADDR_LIST` if `EPICS_CAS_BEACON_ADDR_LIST` was no set. This is no longer done. Sites depending on this should set both environment variables to the same value. ### IPv4 multicast for name search and beacons libca, RSRV, and PCAS may now use IPv4 multicasting for UDP traffic (name search and beacons). This is disabled by default. To enable multicast address(s) must be listed in `EPICS_CA_ADDR_LIST` for clients and `EPICS_CAS_INTF_ADDR_LIST` for servers (IOCs should set both). For example: EPICS_CAS_INTF_ADDR_LIST='224.0.2.9' EPICS_CA_ADDR_LIST=224.0.2.9 Please note that no IPv4 multicast address is officially assigned for Channel Access by IANA. The example 224.0.2.9 is taken from the AD-HOC Block I range. ### Moved `mlockall()` into its own epicsThread routine Since EPICS Base 3.15.0.2 on Posix OSs the initialization of the epicsThread subsystem has called `mlockall()` when the OS supports it and thread priority scheduling is enabled. Doing so has caused problems in third-party applications that call the CA client library, so the functionality has been moved to a separate routine `epicsThreadRealtimeLock()` which will be called by the IOC at iocInit (unless disabled by setting the global variable `dbThreadRealtimeLock` to zero). ### Added dbQuietMacroWarnings control When loading database files, macros get expanded even on comment lines. If a comment contains an undefined macro, the load still continues but an error message gets printed. For this release the error message has been changed to a warning, but even this warning can be made less verbose by setting this new variable to a non-zero value before loading the file, like this: ``` var dbQuietMacroWarnings 1 iocsh dbQuietMacroWarnings=1 VxWorks ``` This was [Launchpad bug 541119](https://bugs.launchpad.net/bugs/541119). ## Changes from the 3.14 branch between 3.15.3 and 3.15.4 ### NTP Time Provider adjusts to OS tick rate changes Dirk Zimoch provided code that allows the NTP Time provider (used on VxWorks and RTEMS only) to adapt to changes in the OS clock tick rate after the provider has been initialized. Note that changing the tick rate after iocInit() is not advisable, and that other software might still misbehave if initialized before an OS tick rate change. This change was back-ported from the 3.15 branch. ### Making IOC `ca_get` operations atomic When a CA client gets data from an IOC record using a compound data type such as `DBR_TIME_DOUBLE` the value field is fetched from the database in a separate call than the other metadata, without keeping the record locked. This allows some other thread such as a periodic scan thread a chance to interrupt the get operation and process the record in between. CA monitors have always been atomic as long as the value data isn't a string or an array, but this race condition in the CA get path has now been fixed so the record will stay locked between the two fetch operations. This fixes [Launchpad bug 1581212](https://bugs.launchpad.net/epics-base/+bug/1581212), thanks to Till Strauman and Dehong Zhang. ### New `CONFIG_SITE` variable for running self-tests The 'make runtests' and 'make tapfiles' build targets normally only run the self-tests for the main `EPICS_HOST_ARCH` architecture. If the host is able to execute self-test programs for other target architectures that are being built by the host, such as when building a `-debug` version of the host architecture for example, the names of those other architectures can be added to the new `CROSS_COMPILER_RUNTEST_ARCHS` variable in either the `configure/CONFIG_SITE` file or in an appropriate `configure/os/CONFIG_SITE..Common` file to have the test programs for those targets be run as well. ### Additional RELEASE file checks An additional check has been added at build-time for the contents of the `configure/RELEASE` file(s), which will mostly only affect users of the Debian EPICS packages published by NSLS-2. Support modules may share an install path, but all such modules must be listed adjacent to each other in any `RELEASE` files that point to them. For example the following will fail the new checks: ``` AUTOSAVE = /usr/lib/epics ASYN = /home/mdavidsaver/asyn EPICS_BASE = /usr/lib/epics ``` giving the compile-time error ``` This application's RELEASE file(s) define EPICS_BASE = /usr/lib/epics after but not adjacent to AUTOSAVE = /usr/lib/epics Module definitions that share paths must be grouped together. Either remove a definition, or move it to a line immediately above or below the other(s). Any non-module definitions belong in configure/CONFIG_SITE. ``` In many cases such as the one above the order of the `AUTOSAVE` and `ASYN` lines can be swapped to let the checks pass, but if the `AUTOSAVE` module depended on `ASYN` and hence had to appear before it in the list this error indicates that `AUTOSAVE` should also be built in its own private area; a shared copy would likely be incompatible with the version of `ASYN` built in the home directory. ### String field buffer overflows Two buffer overflow bugs that can crash the IOC have been fixed, caused by initializing a string field with a value larger than the field size ([Launchpad bug 1563191](https://bugs.launchpad.net/bugs/1563191)). ### Fixed stack corruption bug in epicsThread C++ API The C++ interface to the epicsThread API could corrupt the stack on thread exit in some rare circumstances, usually at program exit. This bug has been fixed ([Launchpad bug 1558206](https://bugs.launchpad.net/bugs/1558206)). ### RTEMS NTP Support Issue On RTEMS the NTP Time Provider could in some circumstances get out of sync with the server because the `osdNTPGet()` code wasn't clearing its input socket before sending out a new request. This ([Launchpad bug 1549908](https://bugs.launchpad.net/bugs/1549908)) has now been fixed. ### CALC engine bitwise operator fixes The bitwise operators in the CALC engine have been modified to work properly with values that have bit 31 (0x80000000) set. This modification involved back-porting some earlier changes from the 3.15 branch, and fixes [Launchpad bug 1514520](https://code.launchpad.net/bugs/1514520). ### Fix `ipAddrToAsciiAsync()`: Don't try to join the daemon thread On process exit, don't try to stop the worker thread that makes DNS lookups asynchronous. Previously this would wait for any lookups still in progress, delaying the exit unnecessarily. This was most obvious with catools (eg. cainfo). [lp:1527636](https://bugs.launchpad.net/bugs/1527636) ### Fix `epicsTime_localtime()` on Windows Simpler versions of the `epicsTime_gmtime()` and `epicsTime_localtime()` routines have been included in the Windows implementations, and a new test program added. The original versions do not report DST status properly. Fixes [Launchpad bug 1528284](https://bugs.launchpad.net/bugs/1528284). base-7.0.3.1/documentation/ReleaseChecklist.html0000664000577000060420000004434413557101274020375 0ustar anjaesctl EPICS Release Procedures & Checklist

EPICS Base Release Procedures & Checklist

This document describes the procedures and provides a checklist of tasks that should be performed when creating production releases of EPICS Base.

The Release Process

Full Process

The version released on the Feature Freeze date is designated the first pre-release, -pre1. The first release candidate -rc1 is the first version that has undergone testing by the developers and has shown no problems that must be fixed before release. New versions should be made at about 2-weekly intervals after the -pre1 release, and designated as either pre-release or release candidate versions by the Release Manager. Release candidates are announced to the whole community via the tech-talk mailing list, pre-releases are announced to to the developers via the core-talk list. After a release candidate has been available for 2 weeks without any new problems being reported or major changes having to be committed, the final release can be made.

Short Process for Patch Releases

The Patch Release date and its scope are agreed upon a few weeks ahead of the release. If no blocking issues are raised, the release is made by the Release Manager on or as soon as possible after that date, following the steps below starting at Release Approval.

Roles

The following roles are used below:

Release Manager ()
Responsible for managing and tagging the release
Platform Developers (optional)
Responsible for individual operating system platforms
Application Developers
Responsible for support modules that depend on EPICS Base.
Website Manager (Andrew Johnson)
Responsible for the EPICS website
Check Who Description
Preparing for a release
  Release Manager Email all developers about the upcoming release and ask for a list of remaining tasks that must be finished.
  All developers Check the bug tracker for any outstanding items and handle appropriately. All bugs that have been fixed should have been marked as Fix Committed.
  Release Manager Set the Feature Freeze date, by which time all Git commits for enhancements and new functionality should have been completed. After this date, commits should only be made to fix problems that show up during testing.
  Release Manager
& all developers
Ensure that documentation will be updated before the release date:
  • Application Developers Guide
  • Release Notes
  • Known Problems
  • Other documents
  Release Manager Review and update this checklist for the upcoming release.
  Website Manager Create a release milestone on Launchpad. If a target release date is known set "Date Targeted" to the expected release date. Note that pre-release and release-candidate versions should not get Launchpad milestones, only the final release.
Creating pre-release and release-candidate versions
Release Manager Edit and commit changes to the EPICS version number file configure/CONFIG_BASE_VERSION.
Release Manager Tag the module in Git, using these tag conventions:
  • R7.0.3.1-pren — pre-release tag
  • R7.0.3.1-rcn — release candidate tag
cd base-7.0
git tag -m 'ANJ: Tagged for 7.0.3.1-rc1' R7.0.3.1-rc1
Note that submodules must not be tagged with the version used for the top-level, they each have their own separate version numbers that are only tagged at the final release.
Release Manager Export the tagged version into a tarfile. The make-tar.sh script generates a gzipped tarfile directly from the tag, excluding the files and directories that are only used for continuous integration:
cd base-7.0
./.tools/make-tar.sh R7.0.3.1-rc1 base-7.0.3.1-rc1.tar.gz base-7.0.3.1-rc1/
Create a GPG signature file of the tarfile as follows:
gpg --armor --sign --detach-sig base-7.0.3.1-rc1.tar.gz
Release Manager Test the tarfile by extracting its contents and building it on at least one supported platform.
Website Manager Copy the tarfile and its signature to the Base download area of the website and add the new files to the website Base download index page.
Website Manager Create or update a website subdirectory to hold the release documentation, and copy in selected files from the base/documentation and base/html directories of the tarfile.
Website Manager Create or modify the webpage for the new release with links to the release documents and tar file. Pre-release and release-candidate versions should use the page and URL for the final release version number.
Testing
  Platform Developers Run the built-in test programs on all available host platforms using
make -s runtests
  Platform Developers Run the CA client side regression tests on all available host platforms.
  Platform Developers Check that all makeBaseApp templates build and run properly, all xxxApp and xxxBoot types and any internal options, e.g. setting STATIC_BUILD=YES or using a different INSTALL_LOCATION in configure/CONFIG_SITE.
  Platform Developers Build the SNL Sequencer against this version of Base, and check that the makeBaseApp example builds and runs correctly with it.
  Application Developers Build external applications against this version of Base on all available platforms and test as appropriate. Application code changes may be necessary where the EPICS Base APIs have been modified.
  Release Manager Check that documentation has been updated:
Release Approval
Release Manager Obtain a positive Ok to release from all platform developers once a release candidate version has gone for 2 weeks without any major new issues being reported.
Creating the final release version
Release Manager

For each external submodule in turn (assuming it has not been tagged yet):

  1. 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
    Commit changes (don't push yet).
  2. Edit the module's release version file configure/CONFIG_module_VERSION and its top-level Doxyfile; set the DEVELOPMENT_FLAG value to 0 and remove -dev from the PROJECT_NUMBER string. Commit changes (don't push).
  3. Tag the module:
    git tag -m 'ANJ: Tag for EPICS 7.0.3.1' <module-version>
  4. Update the git submodule on the Base-7.0 branch to the newly-tagged version, but don't commit yet:
    cd base-7.0/modules
    git add <module>
    git submodule status --cached
  5. Edit the module's release version file configure/CONFIG_module_VERSION and its top-level Doxyfile; increment the MAINTENANCE_VERSION, set the DEVELOPMENT_FLAG value to 1, and update the PROJECT_NUMBER string, appending -dev to the new module version number. Commit changes.
  6. Push commits and the new tag to the submodule's GitHub repository:
    cd base-7.0/modules/<module>
    git push --follow-tags upstream master

Commit all the submodule updates to the 7.0 branch.

Release Manager Edit the main EPICS Base version file and the built-in module version files:
  • configure/CONFIG_BASE_VERSION
  • configure/CONFIG_LIBCOM_VERSION
  • configure/CONFIG_CA_VERSION
  • configure/CONFIG_DATABASE_VERSION

Version numbers should be set according to the level of changes made since the last release. Note that the MAINTENANCE_VERSION or PATCH_LEVEL value should have been incremented after the previous release tag was applied. Set all DEVELOPMENT_FLAG values to 0 and EPICS_DEV_SNAPSHOT to the empty string.

Commit these changes (don't push).

Release Manager Tag the epics-base module in Git:
cd base-7.0
git tag -m 'ANJ: Tagged for 7.0.3.1' R7.0.3.1

Don't push these commits or the new tag to the Launchpad repository yet.

Release Manager Edit the main EPICS Base version file and the built-in module version files:
  • configure/CONFIG_BASE_VERSION
  • configure/CONFIG_LIBCOM_VERSION
  • configure/CONFIG_CA_VERSION
  • configure/CONFIG_DATABASE_VERSION

Version numbers should be set for the next expected patch/maintenance release by incrementing the MAINTENANCE_VERSION or PATCH_LEVEL value in each file. Set all DEVELOPMENT_FLAG values to 1 and EPICS_DEV_SNAPSHOT to "-DEV".

Commit these changes (don't push).

Release Manager Export the tagged version into a tarfile. The make-tar.sh script generates a gzipped tarfile directly from the tag, excluding the files and directories that are only used for continuous integration:
cd base-7.0
./.tools/make-tar.sh R7.0.3.1 base-7.0.3.1.tar.gz base-7.0.3.1/
Create a GPG signature file of the tarfile as follows:
gpg --armor --sign --detach-sig base-7.0.3.1.tar.gz
Release Manager Test the tar file by extracting its contents and building it on at least one supported platform. When this succeeds the commits and new git tag can be pushed to the Launchpad repository:
git push --follow-tags upstream 7.0
Publish to epics.anl.gov
Release Manager Copy the tarfile and its signature to the Base download area of the website.
Website Manager Update the website subdirectory that holds the release documentation, and copy in the files from the base/documentation directory of the tarfile.
Website Manager Update the webpage for the new release with links to the release documents and tar file.
Website Manager Add the new release tar file to the website Base download index page.
Website Manager Link to the release webpage from other relevent areas of the website - update front page and sidebars.
Website Manager Add an entry to the website News page, linking to the new version webpage.
Publish to epics-controls
Website Manager Upload the tar file and its .asc signature file to the epics-controls web-server.
scp base-7.0.3.1.tar.gz base-7.0.3.1.tar.gz.asc epics-controls:download/base
Website Manager Follow instructions on Add a page for a new release to create a new release webpage (not required for a patch release, just edit the existing page). Update the TablePress "Point Releases" table and add the new download, and adjust the XYZ Html Snippet for the series download.
Publish to Launchpad
Website Manager Go to the Launchpad milestone for this release. Click the Create release button and add the release date. Put a URL for the release page in the Release notes box, and click the Create release button. Upload the tar file and its .asc signature file to the new Launchpad release page.
Release Manager Find all Launchpad bug reports with the status Fix Committed which have been fixed in this release and mark them Fix Released.
Make Announcement
Release Manager Announce the release on the tech-talk mailing list.
base-7.0.3.1/modules/CONFIG_SITE.local0000664000577000060420000000102313557101274015664 0ustar anjaesctl#************************************************************************* # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # The name our submodules know us by: PARENT_MODULE = EPICS_BASE # When building submodules, this should always be true: INSTALL_LOCATION := $($(PARENT_MODULE)) # Stop submodules installing their configure/ files into our area CONFIG_INSTALLS = base-7.0.3.1/modules/Makefile0000664000577000060420000000176113557101274014530 0ustar anjaesctl#************************************************************************* # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* TOP = .. include $(TOP)/configure/CONFIG include CONFIG_SITE.local DIRS += libcom DIRS += ca ca_DEPEND_DIRS = libcom DIRS += database database_DEPEND_DIRS = ca # Submodules for bundle build SUBMODULES += pvData pvData_DEPEND_DIRS = libcom SUBMODULES += pvAccess pvAccess_DEPEND_DIRS = pvData database SUBMODULES += normativeTypes normativeTypes_DEPEND_DIRS = pvData SUBMODULES += pvaClient pvaClient_DEPEND_DIRS = pvAccess normativeTypes SUBMODULES += pvDatabase pvDatabase_DEPEND_DIRS = pvAccess SUBMODULES += pva2pva pva2pva_DEPEND_DIRS = pvAccess SUBMODULES += example example_DEPEND_DIRS = pva2pva pvaClient # Allow sites to add extra submodules -include Makefile.local include $(TOP)/configure/RULES_MODULES base-7.0.3.1/modules/ca/Makefile0000664000577000060420000000112213557101274015102 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* TOP = ../.. include $(TOP)/configure/CONFIG DIRS += src include $(TOP)/configure/RULES_TOP base-7.0.3.1/modules/ca/src/Makefile0000664000577000060420000000132513557101274015676 0ustar anjaesctl#************************************************************************* # Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* TOP = ../../.. include $(TOP)/configure/CONFIG # Channel Access Client DIRS += client DIRS += tools tools_DEPEND_DIRS = client DIRS += perl perl_DEPEND_DIRS = client DIRS += template include $(TOP)/configure/RULES_DIRS base-7.0.3.1/modules/ca/src/client/CASG.cpp0000664000577000060420000002131713557101274016740 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeffrey O. Hill */ #include #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "errlog.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "syncGroup.h" #include "oldAccess.h" #include "cac.h" #include "sgAutoPtr.h" CASG::CASG ( epicsGuard < epicsMutex > & guard, ca_client_context & cacIn ) : client ( cacIn ), magic ( CASG_MAGIC ) { client.installCASG ( guard, *this ); } CASG::~CASG () { } void CASG::destructor ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->client.mutexRef() ); if ( this->verify ( guard ) ) { this->reset ( cbGuard, guard ); this->client.uninstallCASG ( guard, *this ); this->magic = 0; } else { this->printFormated ( "cac: attempt to destroy invalid sync group ignored\n" ); } this->~CASG (); } bool CASG::verify ( epicsGuard < epicsMutex > & ) const { return ( this->magic == CASG_MAGIC ); } /* * CASG::block () */ int CASG::block ( epicsGuard < epicsMutex > * pcbGuard, epicsGuard < epicsMutex > & guard, double timeout ) { epicsTime cur_time; epicsTime beg_time; double delay; double remaining; int status; guard.assertIdenticalMutex ( this->client.mutexRef() ); // prevent recursion nightmares by disabling blocking // for IO from within a CA callback. if ( epicsThreadPrivateGet ( caClientCallbackThreadId ) ) { return ECA_EVDISALLOW; } if ( timeout < 0.0 ) { return ECA_TIMEOUT; } cur_time = epicsTime::getMonotonic (); this->client.flush ( guard ); beg_time = cur_time; delay = 0.0; while ( 1 ) { if ( this->ioPendingList.count() == 0u ) { status = ECA_NORMAL; break; } remaining = timeout - delay; if ( remaining <= CAC_SIGNIFICANT_DELAY ) { /* * Make sure that we take care of * recv backlog at least once */ status = ECA_TIMEOUT; break; } if ( pcbGuard ) { epicsGuardRelease < epicsMutex > unguard ( guard ); { epicsGuardRelease < epicsMutex > uncbGuard ( *pcbGuard ); this->sem.wait ( remaining ); } } else { epicsGuardRelease < epicsMutex > unguard ( guard ); this->sem.wait ( remaining ); } /* * force a time update */ cur_time = epicsTime::getMonotonic (); delay = cur_time - beg_time; } return status; } void CASG::reset ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->client.mutexRef() ); this->destroyCompletedIO ( cbGuard, guard ); this->destroyPendingIO ( cbGuard, guard ); } // lock must be applied void CASG::destroyCompletedIO ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->client.mutexRef() ); syncGroupNotify * pNotify; while ( ( pNotify = this->ioCompletedList.get () ) ) { pNotify->destroy ( cbGuard, guard ); } } void CASG::destroyPendingIO ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->client.mutexRef() ); while ( syncGroupNotify * pNotify = this->ioPendingList.first () ) { pNotify->cancel ( cbGuard, guard ); // cancel must release the guard while // canceling put callbacks so we // must double check list membership if ( pNotify->ioPending ( guard ) ) { this->ioPendingList.remove ( *pNotify ); } else { this->ioCompletedList.remove ( *pNotify ); } pNotify->destroy ( cbGuard, guard ); } } void CASG::show ( unsigned level ) const { epicsGuard < epicsMutex > guard ( this->client.mutexRef () ); this->show ( guard, level ); } void CASG::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( this->client.mutexRef() ); ::printf ( "Sync Group: id=%u, magic=%u, opPend=%u\n", this->getId (), this->magic, this->ioPendingList.count () ); if ( level ) { ::printf ( "\tPending" ); tsDLIterConst < syncGroupNotify > notifyPending = this->ioPendingList.firstIter (); while ( notifyPending.valid () ) { notifyPending->show ( guard, level - 1u ); notifyPending++; } ::printf ( "\tCompleted" ); tsDLIterConst < syncGroupNotify > notifyCompleted = this->ioCompletedList.firstIter (); while ( notifyCompleted.valid () ) { notifyCompleted->show ( guard, level - 1u ); notifyCompleted++; } } } bool CASG::ioComplete ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->client.mutexRef() ); this->destroyCompletedIO ( cbGuard, guard ); return this->ioPendingList.count () == 0u; } void CASG::put ( epicsGuard < epicsMutex > & guard, chid pChan, unsigned type, arrayElementCount count, const void * pValue ) { guard.assertIdenticalMutex ( this->client.mutexRef() ); sgAutoPtr < syncGroupWriteNotify > pNotify ( guard, *this ); pNotify = syncGroupWriteNotify::factory ( this->freeListWriteOP, *this, & CASG :: recycleWriteNotifyIO, pChan ); pNotify->begin ( guard, type, count, pValue ); pNotify.release (); } void CASG::get ( epicsGuard < epicsMutex > & guard, chid pChan, unsigned type, arrayElementCount count, void *pValue ) { guard.assertIdenticalMutex ( this->client.mutexRef() ); sgAutoPtr < syncGroupReadNotify > pNotify ( guard, *this ); pNotify = syncGroupReadNotify::factory ( this->freeListReadOP, *this, & CASG :: recycleReadNotifyIO, pChan, pValue ); pNotify->begin ( guard, type, count ); pNotify.release (); } void CASG::completionNotify ( epicsGuard < epicsMutex > & guard, syncGroupNotify & notify ) { guard.assertIdenticalMutex ( this->client.mutexRef() ); this->ioPendingList.remove ( notify ); this->ioCompletedList.add ( notify ); if ( this->ioPendingList.count () == 0u ) { this->sem.signal (); } } void CASG :: recycleReadNotifyIO ( epicsGuard < epicsMutex > & guard, syncGroupReadNotify & io ) { guard.assertIdenticalMutex ( this->client.mutexRef() ); this->freeListReadOP.release ( & io ); } void CASG :: recycleWriteNotifyIO ( epicsGuard < epicsMutex > & guard, syncGroupWriteNotify & io ) { guard.assertIdenticalMutex ( this->client.mutexRef() ); this->freeListWriteOP.release ( & io ); } int CASG :: printFormated ( const char *pformat, ... ) { va_list theArgs; int status; va_start ( theArgs, pformat ); status = this->client.varArgsPrintFormated ( pformat, theArgs ); va_end ( theArgs ); return status; } void CASG::exception ( epicsGuard < epicsMutex > & guard, int status, const char * pContext, const char * pFileName, unsigned lineNo ) { guard.assertIdenticalMutex ( this->client.mutexRef() ); if ( status != ECA_CHANDESTROY ) { this->client.exception ( guard, status, pContext, pFileName, lineNo ); } } void CASG::exception ( epicsGuard < epicsMutex > & guard, int status, const char * pContext, const char * pFileName, unsigned lineNo, oldChannelNotify & chan, unsigned type, arrayElementCount count, unsigned op ) { guard.assertIdenticalMutex ( this->client.mutexRef() ); if ( status != ECA_CHANDESTROY ) { this->client.exception ( guard, status, pContext, pFileName, lineNo, chan, type, count, op ); } } void CASG::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } base-7.0.3.1/modules/ca/src/client/CAref.html0000664000577000060420000051573313557101274017377 0ustar anjaesctl EPICS Channel Access 4.13.1 Reference Manual

EPICS Channel Access 4.13.1 Reference Manual

Jeffrey O. Hill

Los Alamos National Laboratory, SNS Division

Ralph Lange

Helmholtz-Zentrum Berlin (BESSY II)

Copyright © 2009 Helmholtz-Zentrum Berlin für Materialien und Energie GmbH.
Copyright © 2002 The University of Chicago, as Operator of Argonne National Laboratory.
Copyright © 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
Copyright © 2002 Berliner Speicherringgesellschaft für Synchrotronstrahlung GmbH.

EPICS BASE is distributed subject to a Software License Agreement found in the file LICENSE that is included with this distribution.


Table of Contents

Configuration

Building an Application

Command Line Utilities

Command Line Tools

Troubleshooting

Function Call Interface Guidelines

Functionality Index

Function Call Interface Index

Deprecated Function Call Interface Function Index

Return Codes


Configuration

Why Reconfigure Channel Access

Typical reasons to reconfigure EPICS Channel Access:

  • Two independent control systems must share a network without fear of interaction
  • A test system must not interact with an operational system
  • Use of address lists instead of broadcasts for name resolution and server beacons
  • Control system occupies multiple IP subnets
  • Nonstandard client disconnect time outs or server beacon intervals
  • Specify the local time zone
  • Transport of large arrays

EPICS Environment Variables

All Channel Access (CA) configuration occurs through EPICS environment variables. When searching for an EPICS environment variable EPICS first looks in the environment using the ANSI C getenv() call. If no matching variable exists then the default specified in the EPICS build system configuration files is used.

Name Range Default
EPICS_CA_ADDR_LIST {N.N.N.N N.N.N.N:P ...} <none>
EPICS_CA_AUTO_ADDR_LIST {YES, NO} YES
EPICS_CA_NAME_SERVERS {N.N.N.N N.N.N.N:P ...} <none>
EPICS_CA_CONN_TMO r > 0.1 seconds 30.0
EPICS_CA_BEACON_PERIOD r > 0.1 seconds 15.0
EPICS_CA_REPEATER_PORT i > 5000 5065
EPICS_CA_SERVER_PORT i > 5000 5064
EPICS_CA_MAX_ARRAY_BYTES i >= 16384 16384
EPICS_CA_AUTO_ARRAY_BYTES {YES, NO} YES
EPICS_CA_MAX_SEARCH_PERIOD r > 60 seconds 300
EPICS_CA_MCAST_TTL r > 1 1
EPICS_TS_MIN_WEST -720 < i <720 minutes 360

Environment variables are set differently depending on the command line shell that is in use.

C shell setenv EPICS_CA_ADDR_LIST 1.2.3.4
bash export EPICS_CA_ADDR_LIST=1.2.3.4
vxWorks shell putenv ( "EPICS_CA_ADDR_LIST=1.2.3.4" )
DOS command line set EPICS_CA_ADDR_LIST=1.2.3.4
Windows NT / 2000 / XP control panel / system / environment tab

CA and Wide Area Networks

Normally in a local area network (LAN) environment CA discovers the address of the host for an EPICS process variable by broadcasting frames containing a list of channel names (CA search messages) and waiting for responses from the servers that host the channels identified. Likewise CA clients efficiently discover that CA servers have recently joined the LAN or disconnected from the LAN by monitoring periodically broadcasted beacons sent out by the servers. Since hardware broadcasting requires special hardware capabilities, we are required to provide additional configuration information when EPICS is extended to operate over a wide area network (WAN).

IP Network Administration Background Information

Channel Access is implemented using Internet protocols (IP). IP addresses are divided into host and network portions. The boundary between each portion is determined by the IP netmask. Portions of the IP address corresponding to zeros in the netmask specify the hosts address within an IP subnet. Portions of the IP address corresponding to binary ones in the netmask specify the address of a host's IP subnet. Normally the scope of a broadcasted frame will be limited to one IP subnet. Addresses with the host address portion set to all zeros or all ones are special. Modern IP kernel implementations reserve destination addresses with the host portion set to all ones for the purpose of addressing broadcasts to a particular subnet. In theory we can issue a broadcast frame on any broadcast capable LAN within the interconnected Internet by specifying the proper subnet address combined with a host portion set to all ones. In practice these "directed broadcasts" are frequently limited by the default router configuration. The proper directed broadcast address required to reach a particular host can be obtained by logging into that host and typing the command required by your local operating environment. Ignore the loop back interface and use the broadcast address associated with an interface connected to a path through the network to your client. Typically there will be only one Ethernet interface.

UNIX ifconfig -a
vxWorks ifShow
Windows ipconfig

IP ports are positive integers. The IP address, port number, and protocol type uniquely identify the source and destination of a particular frame transmitted between computers. Servers are typically addressed by a well known port number. Clients are assigned a unique ephemeral port number during initialization. IP ports below 1024 are reserved for servers that provide standardized facilities such as mail or file transfer. Port number between 1024 and 5000 are typically reserved for ephemeral port number assignments.

IP port numbers

The two default IP port numbers used by Channel Access may be reconfigured. This might occur when a site decides to set up two or more completely independent control systems that will share the same network. For instance, a site might set up an operational control system and a test control system on the same network. In this situation it is desirable for the test system and the operational system to use identical PV names without fear of collision. A site might also configure the CA port numbers because some other facility is already using the default port numbers. The default Channel Access port numbers have been registered with IANA.

Purpose Default Environment Variable
CA Server 5064 EPICS_CA_SERVER_PORT
CA Beacons (sent to CA repeater daemon) 5065 EPICS_CA_REPEATER_PORT

If a client needs to communicate with two servers that are residing at different port numbers then an extended syntax may be used with the EPICS_CA_ADDR_LIST environment variable. See WAN Environment below.

Firewalls

If you want channel access clients on a machine to be able to see beacons and replies to broadcast PV search requests, you need to permit inbound UDP packets with source port EPICS_CA_SERVER_PORT (default is 5064) or destination port EPICS_CA_REPEATER_PORT (default is 5065). On systems using iptables this can be accomplished by rules like

     -A INPUT -s 192.168.0.0/22 -p udp --sport 5064 -j ACCEPT
     -A INPUT -s 192.168.0.0/22 -p udp --dport 5065 -j ACCEPT

If you want channel access servers (e.g. "soft IOCs") on a machine to be able to be seen by clients, you need to permit inbound TCP or UDP packets with destination port EPICS_CA_SERVER_PORT (default is 5064). On systems using iptables this can be accomplished by rules like

     -A INPUT -s 192.168.0.0/22 -p udp --dport 5064 -j ACCEPT
     -A INPUT -s 192.168.0.0/22 -p tcp --dport 5064 -j ACCEPT

In all cases the "-s 192.168.0.0/22" specifies the range of addresses from which you wish to accept packets.

WAN Environment

When the CA client library connects a channel it must first determine the IP address of the server the channels Process Variable resides on. To accomplish this the client sends name resolution (search) requests to a list of server destination addresses. These server destination addresses can be IP unicast addresses (individual host addresses) or IP broadcast addresses. Each name resolution (search) request contains a list of Process Variable names.If one of the servers reachable by this address list knows the IP address of a CA server that can service one or more of the specified Process Variables, then it sends back a response containing the server's IP address and port number.

During initialization CA builds the list of server destination addresses used when sending CA client name resolution (search) requests. This table is initialized by introspecting the network interfaces attached to the host. For each interface found that is attached to a broadcast capable IP subnet, the broadcast address of that subnet is added to the list. For each point to point interface found, the destination address of that link is added to the list. This automatic server address list initialization can be disabled if the EPICS environment variable EPICS_CA_AUTO_ADDR_LIST exists and its value is either "no" or "NO". The typical default is to enable network interface introspection driven initialization with EPICS_CA_AUTO_ADDR_LIST set to "YES" or "yes".

Following network interface introspection, any IP addresses specified in the EPICS environment variable EPICS_CA_ADDR_LIST are added to the list of destination addresses for CA client name resolution requests. In an EPICS system crossing multiple subnets the EPICS_CA_ADDR_LIST must be set so that CA name resolution (search requests) frames pass from CA clients to the targeted CA servers unless a CA proxy (gateway) is installed. The addresses in EPICS_CA_ADDR_LIST may be dotted IP addresses or host names if the local OS has support for host name to IP address translation. When multiple names are added to EPICS_CA_ADDR_LIST they must be separated by white space. There is no requirement that the addresses specified in the EPICS_CA_ADDR_LIST be broadcast addresses, but this will often be the most convenient choice.

For any IP addresses specified in the EPICS environment variable EPICS_CA_NAME_SERVERS, TCP connections are opened and used for CA client name resolution requests. (Thus, broadcast addresses are not allowed in EPICS_CA_NAME_SERVERS.) When used in combination with an empty EPICS_CA_ADDR_LIST and EPICS_CA_AUTO_ADDR_LIST set to "NO", Channel Access can be run without using UDP for name resolution. Such an TCP-only mode allows for Channel Access to work e.g. through SSH tunnels.

C shell setenv EPICS_CA_ADDR_LIST "1.2.3.255 8.9.10.255"
bash export EPICS_CA_ADDR_LIST="1.2.3.255 8.9.10.255"
vxWorks putenv ( "EPICS_CA_ADDR_LIST=1.2.3.255 8.9.10.255" )

If a client needs to communicate with two servers that are residing at different port numbers then an extended syntax may be used with the EPICS_CA_ADDR_LIST environment variable. Each host name or IP address in the EPICS_CA_ADDR_LIST may be immediately followed by a colon and an IP port number without intervening whitespace. Entries that do not specify a port number will default to EPICS_CA_SERVER_PORT.

C shell setenv EPICS_CA_ADDR_LIST "1.2.3.255 8.9.10.255:10000"

Routing Restrictions on vxWorks Systems

Frequently vxWorks systems boot by default with routes limiting access only to the local subnet. If a EPICS system is operating in a WAN environment it may be necessary to configure routes into the vxWorks system which enable a vxWorks based CA server to respond to requests originating outside its subnet. These routing restrictions can also apply to vxWorks base CA clients communicating with off subnet servers. An EPICS system manager can implement an rudimentary, but robust, form of access control for a particular host by not providing routes in that host that reach outside of a limited set of subnets. See "routeLib" in the vxWorks reference manual.

Disconnect Time Out Interval

If the CA client library does not see a beacon from a server that it is connected to for EPICS_CA_CONN_TMO seconds then an state-of-health message is sent to the server over TCP/IP. If this state-of-health message isn't promptly replied to then the client library will conclude that channels communicating with the server are no longer responsive and inform the CA client side application via function callbacks. The parameter EPICS_CA_CONN_TMO is specified in floating point seconds. The default is typically 30 seconds. For efficient operation it is recommended that EPICS_CA_CONN_TMO be set to no less than twice the value specified for EPICS_CA_BEACON_PERIOD.

Prior to EPICS R3.14.5 an unresponsive server implied an immediate TCP circuit disconnect, immediate resumption of UDP based search requests, and immediate attempts to reconnect. There was concern about excessive levels of additional activity when servers are operated close to the edge of resource limitations. Therefore with version R3.14.5 and greater the CA client library continues to inform client side applications when channels are unresponsive, but does not immediately disconnect the TCP circuit. Instead the CA client library postpones circuit shutdown until receiving indication of circuit disconnect from the IP kernel. This can occur either because a server is restarted or because the IP kernel's internal TCP circuit inactivity keep alive timer has expired after a typically long duration (as is appropriate for IP based systems that need to avoid thrashing during periods of excessive load). The net result is less search and TCP circuit setup and shutdown activity during periods of excessive load.

Dynamic Changes in the CA Client Library Search Interval

The CA client library will continuously attempt to connect any CA channels that an application has created until it is successful. The library periodically queries the server destination address list described above with name resolution requests for any unresolved channels. Since this address list frequently contains broadcast addresses, and because nonexistent process variable names are frequently configured, or servers may be temporarily unavailable, then it is necessary for the CA client library internals to carefully schedule these requests in time to avoid introducing excessive load on the network and the servers.

When the CA client library has many channels to connect, and most of its name resolution requests are responded to, then it sends name resolution requests at an interval that is twice the estimated round trip interval for the set of servers responding, or at the minimum delay quantum for the operating system - whichever is greater. The number of UDP frames per interval is also dynamically adjusted based on the past success rates.

If a name resolution request is not responded to, then the client library doubles the delay between name resolution attempts and reduces the number of requests per interval. The maximum delay between attempts is limited by EPICS_CA_MAX_SEARCH_PERIOD (see Configuring the Maximum Search Period). Note however that prior to R3.14.7, if the client library did not receive any responses over a long interval it stopped sending name resolution attempts altogether until a beacon anomaly was detected (see below).

The CA client library continually estimates the beacon period of all server beacons received. If a particular server's beacon period becomes significantly shorter or longer then the client is said to detect a beacon anomaly. The library boosts the search interval for unresolved channels when a beacon anomaly is seen or when any successful search response is received, but with a longer initial interval between requests than is used when the application creates a channel. Creation of a new channel does not (starting with EPICS R3.14.7) change the interval used when searching for preexisting unresolved channels. The program "casw" prints a message on standard out for each CA client beacon anomaly detect event.

See also When a Client Does not See the Server's Beacon.

Configuring the Maximum Search Period

The rate at which name resolution (search) requests are sent exponentially backs off to a plateau rate. The value of this plateau has an impact on network traffic because it determines the rate that clients search for channel names that are miss-spelled or otherwise don't exist in a server. Furthermore, for clients that are unable to see the beacon from a new server, the plateau rate may also determine the maximum interval that the client will wait until discovering a new server.

Starting with EPICS R3.14.7 this maximum search rate interval plateau in seconds is determined by the EPICS_CA_MAX_SEARCH_PERIOD environment variable.

See also When a Client Does not See the Server's Beacon.

The CA Repeater

When several client processes run on the same host it is not possible for all of them to directly receive a copy of the server beacon messages when the beacon messages are sent to unicast addresses, or when legacy IP kernels are still in use. To avoid confusion over these restrictions a special UDP server, the CA Repeater, is automatically spawned by the CA client library when it is not found to be running. This program listens for server beacons sent to the UDP port specified in the EPICS_CA_REPEATER_PORT parameter and fans any beacons received out to any CA client program running on the same host that have registered themselves with the CA Repeater. If the CA Repeater is not already running on a workstation, then the "caRepeater" program must be in your path before using the CA client library for the first time.

If a host based IOC is run on the same workstation with standalone CA client processes, then it is probably best to start the caRepeater process when the workstation is booted. Otherwise it is possible for the standalone CA client processes to become dependent on a CA repeater started within the confines of the host based IOC. As long as the host based IOC continues to run there is nothing wrong with this situation, but problems could arise if this host based IOC process exits before the standalone client processes which are relying on its CA repeater for services exit.

Since the repeater is intended to be shared by multiple clients then it could be argued that it makes less sense to set up a CA repeater that listens for beacons on only a subset of available network interfaces. In the worst case situation the client library might see beacon anomalies from servers that it is not interested in. Modifications to the CA repeater forcing it to listen only on a subset of network interfaces might be considered for a future release if there appear to be situations that require it.

Configuring the Time Zone

Note: Starting with EPICS R3.14 all of the libraries in the EPICS base distribution rely on facilities built into the operating system to determine the correct time zone. Nevertheless, several programs commonly used with EPICS still use the original "tssubr" library and therefore they still rely on proper configuration of EPICS_TS_MIN_WEST.

While the CA client library does not translate between the local time and the time zone independent internal storage of EPICS time stamps, many EPICS client side applications call core EPICS libraries which provide these services. To set the correct time zone users must compute the number of positive minutes west of GMT (maximum 720 inclusive) or the negative number of minutes east of GMT (minimum -720 inclusive). This integer value is then placed in the variable EPICS_TS_MIN_WEST.

Time Zone EPICS_TS_MIN_WEST
USA Eastern 300
USA Central 360
USA Mountain 420
USA Pacific 480
Alaska 540
Hawaii 600
Japan -540
China -420
Germany -120
United Kingdom 0

Configuring the Maximum Array Size

From version R3.16.1, the default setting of EPICS_CA_AUTO_ARRAY_BYTES=YES will cause the software to ignore EPICS_CA_MAX_ARRAY_BYTES and attempt to allocate network buffer space as needed by the particular client connection using malloc. Setting EPICS_CA_AUTO_ARRAY_BYTES=NO will configure the software to respect the EPICS_CA_MAX_ARRAY_BYTES setting as described below instead.

Starting with version R3.14 the environment variable EPICS_CA_MAX_ARRAY_BYTES determines the size of the largest array that may pass through CA. Prior to this version only arrays smaller than 16k bytes could be transfered. The CA libraries maintains a free list of 16384 byte network buffers that are used for ordinary communication. If EPICS_CA_MAX_ARRAY_BYTES is larger than 16384 then a second free list of larger data buffers is established and used only after a client send its first large array request.

The CA client library uses EPICS_CA_MAX_ARRAY_BYTES to determines the maximum array that it will send or receive. Likewise, the CA server uses EPICS_CA_MAX_ARRAY_BYTES to determine the maximum array that it may send or receive. The client does not influence the server's message size quotas and visa versa. In fact the value of EPICS_CA_MAX_ARRAY_BYTES need not be the same in the client and the server. If the server receives a request which is too large to read or respond to in entirety then it sends an exception message to the client. Likewise, if the CA client library receives a request to send an array larger than EPICS_CA_MAX_ARRAY_BYTES it will return ECA_TOLARGE.

A common mistake is to correctly calculate the maximum datum size in bytes by multiplying the number of elements by the size of a single element, but neglect to add additional bytes for the compound data types (for example DBR_GR_DOUBLE) commonly used by the more sophisticated client side applications.

Configuring a CA Server

Name Range Default
EPICS_CAS_SERVER_PORT i > 5000 EPICS_CA_SERVER_PORT
EPICS_CAS_AUTO_BEACON_ADDR_LIST {YES, NO} EPICS_CA_AUTO_ADDR_LIST
EPICS_CAS_BEACON_ADDR_LIST {N.N.N.N N.N.N.N:P ...} EPICS_CA_ADDR_LIST1
EPICS_CAS_BEACON_PERIOD r > 0.1 seconds EPICS_CA_BEACON_PERIOD
EPICS_CAS_BEACON_PORT i > 5000 EPICS_CA_REPEATER_PORT
EPICS_CAS_INTF_ADDR_LIST {N.N.N.N N.N.N.N:P ...} <none>
EPICS_CAS_IGNORE_ADDR_LIST {N.N.N.N N.N.N.N:P ...} <none>

Server Port

The server configures its port number from the EPICS_CAS_SERVER_PORT environment variable if it is specified. Otherwise the EPICS_CA_SERVER_PORT environment variable determines the server's port number. Two servers can share the same UDP port number on the same machine, but there are restrictions - see a discussion of unicast addresses and two servers sharing the same UDP port on the same host.

Server Beacons

The EPICS_CAS_BEACON_PERIOD parameter determines the server's beacon period and is specified in floating point seconds. The default is typically 15 seconds. See also EPICS_CA_CONN_TMO and Dynamic Changes in the CA Client Library Search Interval.

CA servers build a list of addresses to send beacons to during initialization. If EPICS_CAS_AUTO_BEACON_ADDR_LIST has the value "YES" (the default) this list will be automatically populated with the broadcast addresses of all network interfaces. However, if the user also defines EPICS_CAS_INTF_ADDR_LIST then beacon address list automatic configuration is constrained to the network interfaces specified therein, and therefore only the broadcast addresses of the specified LAN interfaces, will be automatically configured.

If EPICS_CAS_BEACON_ADDR_LIST is defined then its contents will be used to augment any automatic configuration of the beacon address list. Individual entries in EPICS_CAS_BEACON_ADDR_LIST may override the destination port number if ":nnn" follows the host name or IP address there.

The EPICS_CAS_BEACON_PORT parameter specifies the destination port for server beacons. The only exception to this occurs when ports are specified in EPICS_CAS_BEACON_ADDR_LIST or possibly in EPICS_CA_ADDR_LIST. If EPICS_CAS_BEACON_PORT is not specified then beacons are sent to the port specified in EPICS_CA_REPEATER_PORT.

Binding a Server to a Limited Set of Network Interfaces

The parameter EPICS_CAS_INTF_ADDR_LIST allows a ca server to bind itself to, and therefore accept messages received by, a limited set of the local host's network interfaces (each specified by its IP address). On UNIX systems type "netstat -ie" (type "ipconfig" on windows) to see a list of the local host's network interfaces. By default, the CA server is accessible from all network interfaces configured into its host.

Until R3.15.4 the CA server employed by iocCore did not implement the EPICS_CAS_INTF_ADDR_LIST feature.

Prior to R3.15.4 CA servers would build the beacon address list using EPICS_CA_ADDR_LIST if EPICS_CAS_BEACON_ADDR_LIST was no set.

Ignoring Process Variable Name Resolution Requests From Certain Hosts

Name resolution requests originating from any of the IP addresses specified in the EPICS_CAS_IGNORE_ADDR_LIST parameter are not replied to.In R3.14 and previous releases the CA server employed by iocCore does not implement this feature.

Client Configuration that also Applies to Servers

See also Configuring the Maximum Array Size.

See also Routing Restrictions on vxWorks Systems.


Building an Application

Required Header (.h) Files

An application that uses the CA client library functions described in this document will need to include the cadef.h header files as follows.

#include "cadef.h"

This header file is located at "<EPICS base>/include/". It includes many other header files (operating system specific and otherwise), and therefore the application must also specify "<EPICS base>/include/os/<arch>" in its header file search path.

Required Libraries

An application that uses the Channel Access Client Library functions described in this document will need to link with the EPICS CA Client Library and also the EPICS Common Library. The EPICS CA Client Library calls the EPICS Common Library. The following table shows the names of these libraries on UNIX and Windows systems.

UNIX Object UNIX Shareable Windows Object Windows Shareable
EPICS CA Client Library libca.a libca.so ca.lib ca.dll
EPICS Common Library
libCom.a libCom.so Com.lib Com.dll

The above libraries are located in "<EPICS base>/lib/<architecture>".

Compiler and System Specific Build Options

If you do not use the EPICS build environment (layered make files) then it may be helpful to run one of the EPICS make files and watch the compile/link lines. This may be the simplest way to capture the latest system and compiler specific options required by your build environment. Some snapshots of typical build lines are shown below, but this information may be out of date.

Typical Linux Build Options

gcc -D_GNU_SOURCE -DOSITHREAD_USE_DEFAULT_STACK -D_X86_ -DUNIX -Dlinux -O3 -g -Wall -I. -I.. -I../../../../include/compiler/gcc -I../../../../include/os/Linux -I../../../../include -c ../acctst.c

g++ -o acctst -L/home/user/epics/base-3.15/lib/linux-x86 -Wl,-rpath,/home/user/epics/base-3.15/lib/linux-x86 acctstMain.o acctst.o -lca -lCom

Typical Solaris Build Options

/opt/SUNWspro/bin/cc -c -D_POSIX_C_SOURCE=199506L -D_XOPEN_SOURCE=500 -DOSITHREAD_USE_DEFAULT_STACK -DUNIX -DSOLARIS=9 -mt -D__EXTENSIONS__ -Xc -v -xO4 -I. -I.. -I../../../../include/compiler/solStudio -I../../../../include/os/solaris -I../../../../include ../acctst.c

/opt/SUNWspro/bin/CC -o acctst -L/home/user/epics/base-3.15/lib/solaris-sparc/ -mt -z ignore -z combreloc -z lazyload -R/home/user/epics/base-3.15/lib/solaris-sparc acctstMain.o acctst.o -lca -lCom

Typical Windows Build Options

cl -c /nologo /D__STDC__=0 /Ox /GL /W3 /w44355 /MD -I. -I.. -I..\\..\\..\\..\\include\\compiler\\msvc -I..\\..\\..\\..\\include\\os\\WIN32 -I..\\..\\..\\..\\include ..\\acctst.c

link -nologo /LTCG /incremental:no /opt:ref /release /version:3.15 -out:acctst.exe acctstMain.obj acctst.obj d:/user/epics/base-3.15/lib/win32-x86/ca.lib d:/user/epics/base-3.15/lib/win32-x86/Com.lib

Typical vxWorks Build Options

/usr/local/vxWorks-6.9/gnu/4.3.3-vxworks-6.9/x86-linux2/bin/ccppc -DCPU=PPC32 -DvxWorks=vxWorks -O2 -Wall -mstrict-align -mlongcall -fno-builtin -include /usr/local/vxWorks-6.9/vxworks-6.9/target/h/vxWorks.h -I. -I../O.Common -I.. -I../../../../include/compiler/gcc -I../../../../include/os/vxWorks -I../../../../include -I/usr/local/vxWorks-6.9/vxworks-6.9/target/h -I/usr/local/vxWorks-6.9/vxworks-6.9/target/h/wrn/coreip -c ../acctst.c

Other Systems and Compilers

Contributions gratefully accepted.


Command Line Utilities

acctst

acctst <PV name> [progress logging level] [channel duplication count]
                 [test repetition count] [enable preemptive callback]

Description

Channel Access Client Library regression test.

The PV used with the test must be native type DBR_DOUBLE or DBR_FLOAT, and modified only by acctst while the test is running. Therefore, periodically scanned hardware attached analog input records do not work well. Test failure is indicated if the program stops prior to printing "test complete". If unspecified the progress logging level is zero, and no messages are printed while the test is progressing. If unspecified, the channel duplication count is 20000. If unspecified, the test repetition count is once only. If unspecified, preemptive callback is disabled.

catime

catime <PV name> [channel count] [append number to pv name if true]

Description

Channel Access Client Library performance test.

If unspecified, the channel count is 10000. If the "append number to pv name if true" argument is specified and it is greater than zero then the channel names in the test are numbered as follows.

<PV name>000000, <PV name>000001, ... <PV name>nnnnnn

casw

casw [-i <interest level>]

Description

CA server "beacon anomaly" logging.

CA server beacon anomalies 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.

CA clients with unresolved channels reset their search request scheduling timers whenever they see a beacon anomaly.

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.

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.

caEventRate

caEventRate <PV name> [subscription count]

Description

Connect to the specified PV, subscribe for monitor updates the specified number of times (default once), and periodically log the current sampled event rate, average event rate, and the standard deviation of the event rate in Hertz to standard out.

ca_test

ca_test <PV name> [value to be written]

Description

If a value is specified it is written to the PV. Next, the current value of the PV is converted to each of the many external data type that can be specified at the CA client library interface, and each of these is formated and then output to the console.


Command Line Tools

caget

caget [options] <PV name> ...

Description

Get and print value for PV(s).

The values for one or multiple PVs are read and printed to stdout. The DBR_... format in which the data is read, the output format, and a number of details of how integer and float values are represented can be controlled using command line options.

When getting multiple PVs, their order on the command line is retained in the output.

Option Description
-h Print usage information
CA options:
-w <sec> Wait time, specifies longer CA timeout, default is 1.0 second
-c Asynchronous get (use ca_get_callback instead of ca_get)
-p <prio> CA priority (0-99, default 0=lowest)
Format and data type options:
Default output format is "name value"
-t Terse mode - print only value, without name
-a Wide mode "name timestamp value stat sevr" (read PVs as DBR_TIME_xxx)
-d <type> Request specific dbr type; use string (DBR_ prefix may be omitted)
or number of one of the following types:
DBR_STRING 0 DBR_STS_FLOAT 9 DBR_TIME_LONG 19 DBR_CTRL_SHORT 29
DBR_INT 1 DBR_STS_ENUM 10 DBR_TIME_DOUBLE 20 DBR_CTRL_INT 29
DBR_SHORT 1 DBR_STS_CHAR 11 DBR_GR_STRING 21 DBR_CTRL_FLOAT 30
DBR_FLOAT 2 DBR_STS_LONG 12 DBR_GR_SHORT 22 DBR_CTRL_ENUM 31
DBR_ENUM 3 DBR_STS_DOUBLE 13 DBR_GR_INT 22 DBR_CTRL_CHAR 32
DBR_CHAR 4 DBR_TIME_STRING 14 DBR_GR_FLOAT 23 DBR_CTRL_LONG 33
DBR_LONG 5 DBR_TIME_INT 15 DBR_GR_ENUM 24 DBR_CTRL_DOUBLE 34
DBR_DOUBLE 6 DBR_TIME_SHORT 15 DBR_GR_CHAR 25 DBR_STSACK_STRING 37
DBR_STS_STRING 7 DBR_TIME_FLOAT 16 DBR_GR_LONG 26 DBR_CLASS_NAME 38
DBR_STS_SHORT 8 DBR_TIME_ENUM 17 DBR_GR_DOUBLE 27
DBR_STS_INT 8 DBR_TIME_CHAR 18 DBR_CTRL_STRING 28
Enum format:
-n Print DBF_ENUM value as number (default is enum string)
Arrays:
Value format: Print number of requested values, then list of values
Default: Print all values
-# <count> Print first <count> elements of an array
-S Print array of char as a string (long string)
Floating point type format:
Default: Use %g format
-e <nr> Use %e format, with a precision of <nr> digits
-f <nr> Use %f format, with a precision of <nr> digits
-g <nr> Use %g format, with a precision of <nr> digits
-s Get value as string (honors server-side precision)
-lx Round to long integer and print as hex number
-lo Round to long integer and print as octal number
-lb Round to long integer and print as binary number
Integer number format:
Default: Print as decimal number
-0x Print as hex number
-0o Print as octal number
-0b Print as binary number
Alternate output field separator:
-F <ofs> Use <ofs> as an alternate output field separator

camonitor

camonitor [options] <PV name> ...

Description

Subscribe to and print value updates for PV(s).

Option Description
-h Print usage information
CA options:
-w <sec> Wait time, specifies longer CA timeout, default is 1.0 second
-m <msk> Specify CA event mask to use. <msk> is any combination of
'v' (value), 'a' (alarm), 'l' (log/archive), 'p' (property).
Default event mask is 'va'
-p <prio> CA priority (0-99, default 0=lowest)
Timestamps:
Default: Print absolute timestamps (as reported by CA server)
-t <key> Specify timestamp source(s) and type, with <key> containing
's' = CA server (remote) timestamps
'c' = CA client (local) timestamps (shown in '()'s)
'n' = no timestamps
'r' = relative timestamps (time elapsed since start of program)
'i' = incremental timestamps (time elapsed since last update)
'I' = incremental timestamps (time since last update, by channel)
'r', 'i' or 'I' require 's' or 'c' to select the time source
Enum Format:
-n Print DBF_ENUM values as number (default is enum string)
Arrays:
Array values: Print number of elements, then list of values
Default: Default: Request and print all elements (dynamic arrays supported)
-# <num> Request and print up to <num> elements
-S Print array of char as a string (long string)
Floating point format:
Default: Use %g format
-e <num> Use %e format, with a precision of <num> digits
-f <num> Use %f format, with a precision of <num> digits
-g <num> Use %g format, with a precision of <num> digits
-s Get value as string (honors server-side precision)
-lx Round to long integer and print as hex number
-lo Round to long integer and print as octal number
-lb Round to long integer and print as binary number
Integer number format:
Default: Print as decimal number
-0x Print as hex number
-0o Print as octal number
-0b Print as binary number

caput

caput [options] <PV name> <value> ...
caput -a [options] <PV name> <no of elements> <value> ...

Description

Put value to a PV.

The specified value is written to the PV (as a string). The PV's value is read before and after the write operation and printed as "Old" and "New" values on stdout.

There are two variants to the arguments for this command. For the scalar variant without the -a flag, all the value arguments provided after the PV name are concatenated with a single space character between them, and the resulting string (up to 40 characters long unless the -S flag is given) is written to the specified PV.

The array variant with the -a flag writes an array of string values to the specified PV. The numeric argument giving the number of array elements is actually ignored, the array length to be written is actually controlled by the number of values provided on the command line.

Option Description
-h Print usage information
CA options:
-w <sec> Wait time, specifies longer CA timeout, default is 1.0 second
-c Asynchronous put (use ca_put_callback and wait for completion)
-p <prio> CA priority (0-99, default 0=lowest)
Format options:
-t Terse mode - print only successfully written value, without name
-l Long mode "name timestamp value stat sevr" (read PVs as DBR_TIME_xxx)
Enum Format:
Default: Auto - try value as ENUM string, then as index number
-n Force interpretation of values as numbers
-s Force interpretation of values as strings
Arrays:
Default: Put scalar
Value format: all value arguments concatenated with spaces
-S Put string as an array of chars (long string)
-a Put array
Value format: number of values, then list of values

cainfo

cainfo [options] <PV name> ...

Description

Get and print channel and connection information for PV(s).

All available Channel Access related information about PV(s) is printed to stdout.

The -s option allows to specify an interest level for calling Channel Access' internal report function ca_client_status(), that prints lots of internal informations on stdout, including environment settings, used CA ports etc.

Option Description
-h Print usage information
CA options:
-w <sec> Wait time, specifies longer CA timeout, default is 1.0 second
-s <level> Call ca_client_status with the specified interest level
-p <prio> CA priority (0-99, default 0=lowest)

excas

excas [options]

This is an example CA server that is sometimes used for testing purposes. An example server can be created with the makeBaseApp Perl script, as described in the application Developer's Guide.

Option Description
-d <uuuu> set level uuuu for debug messages, where uuuu is an positive integer number
-p <aaaa> prefix all of the PV names below with aaaa changing, for example, the name of "bill" to "xyz:bill"
-t <n.n> set execution time where n.n is a positive real number
-c <uuuu> set the numbered alias count
-s <nnn> the default, nnn is one, enables periodic scanning of the PV replacing the PV with its value added with a small random change, when nnn is zero it turns off this type of periodic scanning
-ss <nnn> the default, nnn is one, enables synchronous scanning, and if nnn is zero it turns on asynchronous scanning
-ad <n.n> set the delay before asynchronous operations complete (defaults to 0.1 seconds)
-an <nnn> set the maximum number of simultaneous asynchronous operations (defaults to 1000)

The example server has a compile time fixed set of example variables.

Process Variable Name Number of Elements IO Type Data Type High Limit Low Limit Scan Period
jane 1 Synchronous float point, 64 bits 10.0 0.0 0.1 Seconds, random noise changes
fred 1 Synchronous float point, 64 bits 10.0 -10.0 2.0 Seconds, random noise changes
janet 1 Asynchronous float point, 64 bits 10.0 0.0 0.1 Seconds, random noise changes
freddy 1 Asynchronous float point, 64 bits 10.0 -10.0 2.0 Seconds, random noise changes
alan 100 Synchronous float point, 64 bits 10.0 -10.0 2.0 Seconds, random noise changes
albert 1000 Synchronous float point, 64 bits 10.0 -10.0 20.0 Seconds, random noise changes
boot 1 Synchronous enumerated, 16 bits 10.0 -10.0 changed only by client
booty 1 Asynchronous enumerated, 16 bits 10.0 -10.0 1.0 Seconds, random noise changes
bill 1 Synchronous float point, 64 bits 10.0 -10.0 changed only by client
billy 1 Asynchronous float point, 64 bits 10.0 -10.0 changed only by client
bloaty 100000 Synchronous float point, 64 bits 10.0 -10.0 changed only by client

Bugs

Not all of the options listed above have been tested recently.


Troubleshooting

When Clients Do Not Connect to Their Server

Client and Server Broadcast Addresses Don't Match

Verify that the broadcast addresses are identical on the server's host and on the client's host. This can be checked on UNIX with "netstat -i" or "ifconfig -a"; on vxWorks with ifShow; and on windows with ipconfig. It is normal for the broadcast addresses to not be identical if the client and server are not directly attached to the same IP subnet, and in this situation the EPICS_CA_ADDR_LIST must be set. Otherwise, if the client and server are intended to be on the same IP subnet, then the problem may be that the IP netmask is incorrectly set in the network interface configuration. On most operating systems, when the host's IP address is configured, the host's IP subnet mask is also configured.

Client Isn't Configured to Use the Server's Port

Verify that the client and server are using the same UDP port. Check the server's port by running "netstat -a | grep nnn" where nnn is the port number configured in the client. If you do not set EPICS_CA_SERVER_PORT or EPICS_CAS_SERVER_PORT then the default port will be 5064.

Unicast Addresses in the EPICS_CA_ADDR_LIST Does not Reliably Contact Servers Sharing the Same UDP Port on the Same Host

Two servers can run on the same host with the same server port number, but there are restrictions. If the host has a modern IP kernel it is possible to have two or more servers share the same UDP port. It is not possible for these servers to run on the same host using the same TCP port. If the CA server library detects that a server is attempting to start on the same port as an existing CA server then both servers will use the same UDP port, and the 2nd server will be allocated an ephemeral TCP port. Clients can be configured to use the same port number for both servers. They will locate the 2nd server via the shared UDP port, and transparently connect to the 2nd server's ephemeral TCP port. Be aware however that If there are two server's running on the same host sharing the same UDP port then they will both receive UDP search requests sent as broadcasts, but unfortunately (due to a weakness of most IP kernel implementations) only one of the servers will typically receive UDP search requests sent to unicast addresses (i.e. a single specific host's ip address).

Client Does not See Server's Beacons

Two conclusions deserve special emphasis. First, if a client does not see the server's beacons, then it will use additional network and server resources sending periodic state-of-health messages. Second, if a client does not see a newly introduced server's beacon, then it will take up to EPICS_CA_MAX_SEARCH_PERIOD to find that newly introduced server. Also, starting with EPICS R3.14.7 the client library does not suspend searching for a channel after 100 unsuccessful attempts until a beacon anomaly is seen. Therefore, if the client library is from before version R3.14.7 of EPICS and it timed out attempting to find a server whose beacon can't be seen by the client library then the client application might need to be restarted in order to connect to this new beacon-out-of-range server. The typical situation where a client would not see the server's beacon might be when the client isn't on the same IP subnet as the server, and the client's EPICS_CA_ADDR_LIST was modified to include a destination address for the server, but the server's beacon address list was not modified so that its beacons are received by the client.

A Server's IP Address Was Changed

When communication over a virtual circuit times out, then each channel attached to the circuit enters a disconnected state and the disconnect callback handler specified for the channel is called. However, the circuit is not disconnected until TCP/IP's internal, typically long duration, keep alive timer expires. The disconnected channels remain attached to the beleaguered circuit and no attempt is made to search for, or to reestablish, a new circuit. If, at some time in the future, the circuit becomes responsive again, then the attached channels enter a connected state again and reconnect callback handlers are called. Any monitor subscriptions that received an update message while the channel was disconnected are also refreshed. If at any time the library receives an indication from the operating system that a beleaguered circuit has shutdown or was disconnected then the library will immediately reattempt to find servers for each channel and connect circuits to them.

A well known negative side effect of the above behavior is that CA clients will wait the full (typically long) duration of TCP/IP's internal keep alive timer prior to reconnecting under the following scenario (all of the following occur):

  • An server's (IOC's) operating system crashes (or is abruptly turned off) or a vxWorks system is stopped by any means
  • This operating system does not immediately reboot using the same IP address
  • A duplicate of the server (IOC) is started appearing at a different IP address

It is unlikely that any rational organization will advocate the above scenario in a production system. Nevertheless, there are opportunities for users to become confused during control system development, but it is felt that the robustness improvements justify isolated confusion during the system integration and checkout activities where the above scenarios are most likely to occur.

Contrast the above behavior with the CA client library behavior of releases prior to R3.14.5 where the beleaguered circuit was immediately closed when communication over it timed out. Any attached channels were immediately searched for, and after successful search responses arrived then attempts were made to build a new circuit. This behavior could result in undesirable resource consumption resulting from periodic circuit setup and teardown overhead (thrashing) during periods of CPU / network / IP kernel buffer congestion.

Put Requests Just Prior to Process Termination Appear to be Ignored

Short lived CA client applications that issue a CA put request and then immediately exit the process (return from main or call exit) may find that there request isn't executed. To guarantee that the request is sent call ca_flush_io() followed by ca_context_destroy() prior to terminating the process.

ENOBUFS Messages

Many Berkley UNIX derived Internet Protocol (IP) kernels use a memory management scheme with a fixed sized low level memory allocation quantum called an "mbuf". Messages about "ENOBUFS" are an indication that your IP kernel is running low on mbuf buffers. An IP kernel mbuf starvation situation may lead to temporary IP communications stalls or reduced throughput. This issue has to date been primarily associated with vxWorks systems where mbuf starvation on earlier vxWorks versions is rumored to lead to permanent IP communications stalls which are resolved only by a system reboot. IP kernels that use mbufs frequently allow the initial and maximum number of mbufs to be configured. Consult your OS's documentation for configuration procedures which vary between OS and even between different versions of the same OS.

Contributing Circumstances

  • The total number of connected clients is high. Each active socket requires dedicated mbufs for protocol control blocks, and for any data that might be pending in the operating system for transmission to Channel Access or to the network at a given instant. If you increase the vxWorks limit on the maximum number of file descriptors then it may also be necessary to increase the size of the mbuf pool.
  • The server has multiple connections where the server's sustained event (monitor subscription update) production rate is higher than the client's or the network's sustained event consumption rate. This ties up a per socket quota of mbufs for data that are pending transmission to the client via the network. In particular, if there are multiple clients that subscribe for monitor events but do not call ca_pend_event() or ca_poll() to process their CA input queue, then a significant mbuf consuming backlog can occur in the server.
  • The server does not get a chance to run (because some other higher priority thread is running) and the CA clients are sending a high volume of data over TCP or UDP. This ties up a quota of mbufs for each socket in the server that isn't being reduced by the server's socket read system calls.
  • The server has multiple stale connections. Stale connections occur when a client is abruptly turned off or disconnected from the network, and an internal "keepalive" timer has not yet expired for the virtual circuit in the operating system, and therefore mbufs may be dedicated to unused virtual circuits. This situation is made worse if there are active monitor subscriptions associated with stale connections which will rapidly increase the number of dedicated mbufs to the quota available for each circuit.
  • When sites switch to the vxWorks 5.4 IP kernel they frequently run into network pool exhaustion problems. This may be because the original vxWorks IP kernel expanded the network pool as needed at runtime while the new kernel's pool is statically configured at compile time, and does not expand as needed at runtime. Also, at certain sites problems related to vxWorks network driver pool exhaustion have also been reported (this can also result in ENOBUF diagnostic messages).

Related Diagnostics

  • The EPICS command "casr [interest level]" displays information about the CA server and how many clients are connected.
  • The vxWorks command "inetstatShow" indicates how many bytes are pending in mbufs and indirectly (based on the number of circuits listed) how many mbuf based protocol control blocks have been consumed. The vxWorks commands (availability depending on vxWorks version) mbufShow, netStackSysPoolShow, and netStackDataPoolShow indicate how much space remains in the network stack pool.
  • The RTEMS command "netstat [interest level]" displays network information including mbuf consumption statistics.

Server Subscription Update Queuing

If the subscription update producer in the server produces subscription updates faster than the subscription update consumer in the client consumes them, then events have to be discarded if the buffering in the server isn't allowed to grow to an infinite size. This is a law of nature – based on queuing theory of course.

What is done depends on the version of the CA server. All server versions place quotas on the maximum number of subscription updates allowed on the subscription update queue at any given time. If this limit is reached, an intervening update is discarded in favor of a more recent update. Depending on the version of the server, rapidly updating subscriptions are or are not allowed to cannibalize the quotas of slow updating subscriptions in limited ways. Nevertheless, there is always room on the queue for at least one update for each subscription. This guarantees that the most recent update is always sent.

Adding further complication, the CA client library also implements a primitive type of flow control. If the client library sees that it is reading a large number of messages one after another w/o intervening delay it knows that it is not consuming events as fast as they are produced. In that situation it sends a message telling the server to temporarily stop sending subscription update messages. When the client catches up it sends another message asking the server to resume with subscription updates. This prevents slow clients from getting time warped, but also guarantees that intervening events are discarded until the slow client catches up.

There is currently no message on the IOC's console when a particular client is slow on the uptake. A message of this type used to exist many years ago, but it was a source of confusion (and what we will call message noise) so it was removed.

There is unfortunately no field in the protocol allowing the server to indicate that an intervening subscription update was discarded. We should probably add that capability in a future version. Such a feature would, for example, be beneficial when tuning an archiver installation.


Function Call Interface General Guidelines

Flushing and Blocking

Significant performance gains can be realized when the CA client library doesn't wait for a response to return from the server after each request. All requests which require interaction with a CA server are accumulated (buffered) and not forwarded to the IOC until one of ca_flush_io(), ca_pend_io(), ca_pend_event(), or ca_sg_block() are called allowing several operations to be efficiently sent over the network together. Any process variable values written into your program's variables by ca_get() should not be referenced by your program until ECA_NORMAL has been received from ca_pend_io().

Status Codes

If successful, the routines described here return the status code ECA_NORMAL. Unsuccessful status codes returned from the client library are listed with each routine in this manual. Operations that appear to be valid to the client can still fail in the server. Writing the string "off" to a floating point field is an example of this type of error. If the server for a channel is located in a different address space than the client then the ca_xxx() operations that communicate with the server return status indicating the validity of the request and whether it was successfully enqueued to the server, but communication of completion status is deferred until a user callback is called, or lacking that an exception handler is called. An error number and the error's severity are embedded in CA status (error) constants. Applications shouldn't test the success of a CA function call by checking to see if the returned value is zero as is the UNIX convention. Below are several methods to test CA function returns. See ca_signal() and SEVCHK() for more information on this topic.

status = ca_XXXX();
SEVCHK( status, "ca_XXXX() returned failure status");

if ( status & CA_M_SUCCESS ) {
        printf ( "The requested ca_XXXX() operation didn't complete successfully");
}

if ( status != ECA_NORMAL ) {
        printf("The requested ca_XXXX() operation didn't complete successfully because \"%s\"\n",
                ca_message ( status ) );
}

Channel Access Data Types

CA channels form a virtual circuit between a process variable (PV) and a client side application program. It is possible to connect a wide variety of data sources into EPICS using the CA server library. When a CA channel communicates with an EPICS Input Output Controller (IOC) then a field is a specialization of a PV, and an EPICS record is a plug compatible function block that contains fields, and the meta data below frequently are mapped onto specific fields within the EPICS records by the EPICS record support (see the EPICS Application Developer Guide).

Arguments of type chtype specifying the data type you wish to transfer. They expect one of the set of DBR_XXXX data type codes defined in db_access.h. There are data types for all of the C primitive types, and there are also compound (C structure) types that include various process variable properties such as units, limits, time stamp, or alarm status. The primitive C types follow a naming convention where the C typedef dbr_xxxx_t corresponds to the DBR_XXXX data type code. The compound (C structure) types follow a naming convention where the C structure tag dbr_xxxx corresponds to the DBR_XXXX data type code. The following tables provides more details on the structure of the CA data type space. Since data addresses are passed to the CA client library as typeless "void *" pointers then care should be taken to ensure that you have passed the correct C data type corresponding to the DBR_XXXX type that you have specified. Architecture independent types are provided in db_access.h to assist programmers in writing portable code. For example "dbr_short_t" should be used to send or receive type DBR_SHORT. Be aware that type name DBR_INT has been deprecated in favor of the less confusing type name DBR_SHORT. In practice, both the DBR_INT type code and the DBR_SHORT type code refer to a 16 bit integer type, and are functionally equivalent.

Channel Access Primitive Data Types
CA Type Code Primitive C Data Type Data Size
DBR_CHAR dbr_char_t 8 bit character
DBR_SHORT dbr_short_t 16 bit integer
DBR_ENUM dbr_enum_t 16 bit unsigned integer
DBR_LONG dbr_long_t 32 bit signed integer
DBR_FLOAT dbr_float_t 32 bit IEEE floating point
DBR_DOUBLE dbr_double_t 64 bit IEEE floating point
DBR_STRING dbr_string_t 40 character string
Structure of the Channel Access Data Type Space
CA Type Code Read / Write Primitive C Data Type Process Variable Properties
DBR_<PRIMITIVE TYPE> RW dbr_<primitive type>_t value
DBR_STS_<PRIMITIVE TYPE> R struct dbr_sts_<primitive type> value, alarm status, and alarm severity
DBR_TIME_<PRIMITIVE TYPE> R struct dbr_time_<primitive type> value, alarm status, alarm severity, and time stamp
DBR_GR_<PRIMITIVE TYPE> R struct dbr_gr_<primitive type> value, alarm status, alarm severity, units, display precision, and graphic limits
DBR_CTRL_<PRIMITIVE TYPE> R struct dbr_ctrl_<primitive type> value, alarm status, alarm severity, units, display precision, graphic limits, and control limits
DBR_PUT_ACKT W dbr_put_ackt_t Used for global alarm acknowledgement. Do transient alarms have to be acknowledged? (0,1) means (no, yes).
DBR_PUT_ACKS W dbr_put_acks_t Used for global alarm acknowledgement. The highest alarm severity to acknowledge. If the current alarm severity is less then or equal to this value the alarm is acknowledged.
DBR_STSACK_STRING R struct dbr_stsack_string value, alarm status, alarm severity, ackt, acks
DBR_CLASS_NAME R dbr_class_name_t name of enclosing interface (name of the record if channel is attached to EPICS run time database)

Channel value arrays can also be included within the structured CA data types. If more than one element is requested, then the individual elements can be accessed in an application program by indexing a pointer to the value field in the DBR_XXX structure. For example, the following code computes the sum of the elements in a array process variable and prints its time stamp. The dbr_size_n() function can be used to determine the correct number of bytes to reserve when there are more than one value field elements in a structured CA data type.

#include <stdio.h>
#include <stdlib.h>

#include "cadef.h"

int main ( int argc, char ** argv )
{
    struct dbr_time_double * pTD;
    const dbr_double_t * pValue;
    unsigned nBytes;
    unsigned elementCount;
    char timeString[32];
    unsigned i;
    chid chan;
    double sum;
    int status;

    if ( argc != 2 ) {
        fprintf ( stderr, "usage: %s <channel name>", argv[0] );
        return -1;
    }

    status = ca_create_channel ( argv[1], 0, 0, 0, & chan );
    SEVCHK ( status, "ca_create_channel()" );
    status = ca_pend_io ( 15.0 );
    if ( status != ECA_NORMAL ) {
        fprintf ( stderr, "\"%s\" not found.\n", argv[1] );
        return -1;
    }

    elementCount = ca_element_count ( chan );
    nBytes = dbr_size_n ( DBR_TIME_DOUBLE, elementCount );
    pTD = ( struct dbr_time_double * ) malloc ( nBytes );
    if ( ! pTD ) {
        fprintf ( stderr, "insufficient memory to complete request\n" );
        return -1;
    }

    status = ca_array_get ( DBR_TIME_DOUBLE, elementCount, chan, pTD );
    SEVCHK ( status, "ca_array_get()" );
    status = ca_pend_io ( 15.0 );
    if ( status != ECA_NORMAL ) {
        fprintf ( stderr, "\"%s\" didn't return a value.\n", argv[1] );
        return -1;
    }

    pValue = & pTD->value;
    sum = 0.0;
    for ( i = 0; i < elementCount; i++ ) {
        sum += pValue[i];
    }

    epicsTimeToStrftime ( timeString, sizeof ( timeString ),
        "%a %b %d %Y %H:%M:%S.%f", & pTD->stamp );

    printf ( "The sum of elements in %s at %s was %f\n",
        argv[1], timeString, sum );

    ca_clear_channel ( chan );
    ca_task_exit ();
    free ( pTD );

    return 0;
}

User Supplied Callback Functions

Certain CA client initiated requests asynchronously execute an application supplied callback in the client process when a response arrives. The functions ca_put_callback(), ca_get_callback(), and ca_create_subscription() all request notification of asynchronous completion via this mechanism. The event_handler_args structure is passed by value to the application supplied callback. In this structure the dbr field is a void pointer to any data that might be returned. The status field will be set to one of the CA error codes in caerr.h and will indicate the status of the operation performed in the IOC. If the status field isn't set to ECA_NORMAL or data isn't normally returned from the operation (i.e. put callback) then you should expect that the dbr field will be set to a null pointer (zero). The fields usr, chid, and type are set to the values specified when the request was made by the application. The dbr pointer, and any data that it points to, are valid only when executing within the user's callback function.

typedef struct event_handler_args {
    void            *usr;   /* user argument supplied with request */
    chanId          chid;   /* channel id */
    long            type;   /* the type of the item returned */
    long            count;  /* the element count of the item returned */
    const void      *dbr;   /* a pointer to the item returned */
    int             status; /* ECA_XXX status of the requested op from the server */
} evargs;

void myCallback ( struct event_handler_args args )
{
    if ( args.status != ECA_NORMAL ) {
    }
    if ( args.type == DBR_TIME_DOUBLE ) {
         const struct dbr_time_double * pTD =
              ( const struct dbr_time_double * ) args.dbr;
    }
}

Channel Access Exceptions

When the server detects a failure, and there is no client callback function attached to the request, an exception handler is executed in the client. The default exception handler prints a message on the console and exits if the exception condition is severe. Certain internal exceptions within the CA client library, and failures detected by the SEVCHK macro may also cause the exception handler to be invoked. To modify this behavior see ca_add_exception_event().

Server and Client Share the Same Address Space on The Same Host

If the Process Variable's server and it's client are colocated within the same memory address space and the same host then the ca_xxx() operations bypass the server and directly interact with the server tool component (commonly the IOC's function block database). In this situation the ca_xxx() routines frequently return the completion status of the requested operation directly to the caller with no opportunity for asynchronous notification of failure via an exception handler. Likewise, callbacks may be directly invoked by the CA library functions that request them.

Arrays

For routines that require an argument specifying the number of array elements, no more than the process variable's maximum native element count may be requested. The process variable's maximum native element count is available from ca_element_count() when the channel is connected. If fewer elements than the process variable's native element count are requested, the requested values will be fetched beginning at element zero. By default CA limits the number of elements in an array to be no more than approximately 16k divided by the size of one element in the array. Starting with EPICS R3.14 the maximum array size may be configured in the client and in the server.

Connection Management

Application programs should assume that CA servers may be restarted, and that network connectivity is transient. When you create a CA channel its initial connection state will most commonly be disconnected. If the Process Variable's server is available the library will immediately initiate the necessary actions to make a connection with it. Otherwise, the client library will monitor the state of servers on the network and connect or reconnect with the process variable's server as it becomes available. After the channel connects the application program can freely perform IO operations through the channel, but should expect that the channel might disconnect at any time due to network connectivity disruptions or server restarts.

Three methods can be used to determine if a channel is connected: the application program might call ca_state() to obtain the current connection state, block in ca_pend_io() until the channel connects, or install a connection callback handler when it calls ca_create_channel(). The ca_pend_io() approach is best suited to simple command line programs with short runtime duration, and the connection callback method is best suited to toolkit components with long runtime duration. Use of ca_state() is appropriate only in programs that prefer to poll for connection state changes instead of opting for asynchronous notification. The ca_pend_io() function blocks only for channels created specifying a null connection handler callback function. The user's connection state change function will be run immediately from within ca_create_channel() if the CA client and CA server are both hosted within the same address space (within the same process).

Thread Safety and Preemptive Callback to User Code

Starting with EPICS R3.14 the CA client libraries are fully thread safe on all OS (in past releases the library was thread safe only on vxWorks). When the client library is initialized the programmer may specify if preemptive callback is to be enabled. Preemptive callback is disabled by default. If preemptive callback is enabled, then the user's callback functions might be called by CA's auxiliary threads when the main initiating channel access thread is not inside of a function in the channel access client library. Otherwise, the user's callback functions will be called only when the main initiating channel access thread is executing inside of the CA client library. When the CA client library invokes a user's callback function, it will always wait for the current callback to complete prior to executing another callback function. Programmers enabling preemptive callback should be familiar with using mutex locks to create a reliable multi-threaded program.

To set up a traditional single threaded client, you will need code like this (see ca_context_create() and CA Client Contexts and Application Specific Auxiliary Threads) .

SEVCHK ( ca_context_create(ca_disable_preemptive_callback ), "application pdq calling ca_context_create" );

To set up a preemptive callback enabled CA client context you will need code like this (see ca_context_create() and CA Client Contexts and Application Specific Auxiliary Threads).

SEVCHK ( ca_context_create(ca_enable_preemptive_callback ), "application pdq calling ca_context_create" );

CA Client Contexts and Application Specific Auxiliary Threads

It is often necessary for several CA client side tools running in the same address space (process) to be independent of each other. For example, the database CA links and the sequencer are designed to not use the same CA client library threads, network circuits, and data structures. Each thread that calls ca_context_create() for the first time either directly or implicitly when calling any CA library function for the first time, creates a CA client library context. A CA client library context contains all of the threads, network circuits, and data structures required to connect and communicate with the channels that a CA client application has created. The priority of auxiliary threads spawned by the CA client library are at fixed offsets from the priority of the thread that called ca_context_create(). An application specific auxiliary thread can join a CA context by calling ca_attach_context() using the CA context identifier that was returned from ca_current_context() when it is called by the thread that created the context which needs to be joined. A context which is to be joined must be preemptive - it must be created using ca_context_create(ca_enable_preemptive_callback). It is not possible to attach a thread to a non-preemptive CA context created explicitly or implicitly with ca_create_context(ca_disable_preemptive_callback). Once a thread has joined with a CA context it need only make ordinary ca_xxxx() library calls to use the context.

A CA client library context can be shut down and cleaned up, after destroying any channels or application specific threads that are attached to it, by calling ca_context_destroy(). The context may be created and destroyed by different threads as long as they are both part of the same context.

Polling the CA Client Library From Single Threaded Applications

If preemptive callback is not enabled, then for proper operation CA must periodically be polled to take care of background activity. This requires that your application must either wait in one of ca_pend_event(), ca_pend_io(), or ca_sg_block() or alternatively it should call ca_poll() at least every 100 milliseconds. In single threaded applications a file descriptor manager like Xt or the interface described in fdManager.h can be used to monitor both mouse clicks and also CA's file descriptors so that ca_poll() can be called immediately when CA server messages arrives over the network.

Avoid Emulating Bad Practices that May Still be Common

With the embryonic releases of EPICS it was a common practice to examine a channel's connection state, its native type, and its native element count by directly accessing fields in a structure using a pointer stored in type chid. Likewise, a user private pointer in the per channel structure was also commonly set by directly accessing fields in the channel structure. A number of difficulties arise from this practice, which has long since been deprecated. For example, prior to release 3.13 it was recognized that transient changes in certain private fields in the per channel structure would make it difficult to reliably test the channels connection state using these private fields directly. Therefore, in release 3.13 the names of certain fields were changed to discourage this practice. Starting with release 3.14 codes written this way will not compile. Codes intending to maintain the highest degree of portability over a wide range of EPICS versions should be especially careful. For example you should replace all instances off channel_id->count with ca_element_count(channel_id). This approach should be reliable on all versions of EPICS in use today. The construct ca_puser(chid) = xxxx is particularly problematic. The best mechanisms for setting the per channel private pointer will be to pass the user private pointer in when creating the channel. This approach is implemented on all versions. Otherwise, you can also use ca_set_puser(CHID,PUSER), but this function is available only after the first official (post beta) release of EPICS 3.13.

Calling CA Functions from the vxWorks Shell Thread

Calling CA functions from the vxWorks shell thread is a somewhat questionable practice for the following reasons.

  • The vxWorks shell thread runs at the very highest priority in the system and therefore socket calls are made at a priority that is above the priority of tNetTask. This has caused problems with the WRS IP kernel in the past. That symptom was observed some time ago, but we don't know if WRS has fixed the problem.
  • The vxWorks shell thread runs at the very highest priority in the system and therefore certain CA auxiliary threads will not get the priorities that are requested for them. This might cause problems only when in a CPU saturation situations.
  • If the code does not call ca_context_destroy() (named ca_task_exit() in past releases) then resources are left dangling.
  • In EPICS R3.13 the CA client library installed vxWorks task exit handlers behaved strangely if CA functions were called from the vxWorks shell, ca_task_exit() wasn't called, and the vxWorks shell restarted. In EPICS R3.14 vxWorks task exit handlers are not installed and therefore cleanup is solely the responsibility of the user. With EPICS R3.14 the user must call ca_context_destroy() or ca_task_exit() to clean up on vxWorks. This is the same behavior as on all other OS.

Calling CA Functions from POSIX signal handlers

As you might expect, it isn't safe to call the CA client library from a POSIX signal handler. Likewise, it isn't safe to call the CA client library from interrupt context.


Function Call Reference

ca_context_create()

#include <cadef.h>
enum ca_preemptive_callback_select
    { ca_disable_preemptive_callback, ca_enable_preemptive_callback };
int ca_context_create ( enum ca_preemptive_callback_select SELECT );

Description

This function, or ca_attach_context(), should be called once from each thread prior to making any of the other Channel Access calls. If one of the above is not called before making other CA calls then a non-preemptive context is created by default, and future attempts to create a preemptive context for the current threads will fail.

If ca_disable_preemptive_callback is specified then additional threads are not allowed to join the CA context using ca_context_attach() because allowing other threads to join implies that CA callbacks will be called preemptively from more than one thread.

Arguments

SELECT
This argument specifies if preemptive invocation of callback functions is allowed. If so your callback functions might be called when the thread that calls this routine is not executing in the CA client library. There are two implications to consider.

First, if preemptive callback mode is enabled the developer must provide mutual exclusion protection for his data structures. In this mode it's possible for two threads to touch the application's data structures at once: this might be the initializing thread (the thread that called ca_context_create) and also a private thread created by the CA client library for the purpose of receiving network messages and calling callbacks. It might be prudent for developers who are unfamiliar with mutual exclusion locking in a multi-threaded environment to specify ca_disable_preemptive_callback.

Second, if preemptive callback mode is enabled the application is no longer burdened with the necessity of periodically polling the CA client library in order that it might take care of its background activities. If ca_enable_preemptive_callback is specified then CA client background activities, such as connection management, will proceed even if the thread that calls this routine is not executing in the CA client library. Furthermore, in preemptive callback mode callbacks might be called with less latency because the library is not required to wait until the initializing thread (the thread that called ca_context_create) is executing within the CA client library.

Returns

ECA_NORMAL - Normal successful completion

ECA_ALLOCMEM - Failed, unable to allocate space in pool

ECA_NOTTHREADED - Current thread is already a member of a non-preemptive callback CA context (possibly created implicitly)

See Also

ca_context_destroy()

ca_context_destroy()

#include <cadef.h>
void ca_context_destroy();

Description

Shut down the calling thread's channel access client context and free any resources allocated. Detach the calling thread from any CA client context.

Any user-created threads that have attached themselves to the CA context must stop using it prior to its being destroyed. A program running in an IOC context must delete all of its channels prior to calling ca_context_destroy() to avoid a crash.

A CA client application that calls epicsExit() must install an EPICS exit handler that calls ca_context_destroy() only after first calling ca_create_context(). This will guarantee that the EPICS exit handlers get called in the correct order.

On many OS that execute programs in a process based environment the resources used by the client library such as sockets and allocated memory are automatically released by the system when the process exits and ca_context_destroy() hasn't been called, but on light weight systems such as vxWorks or RTEMS no cleanup occurs unless the application calls ca_context_destroy().

Note: This operation blocks until any user callbacks for any channel created in the current context have run to completion. If callbacks take a lock (mutex) then it is the user's responsibility to ensure that this lock is not held when ca_clear_context() is called, otherwise a deadlock may ensue. (See also ca_clear_channel() and ca_clear_subscription().)

Returns

ECA_NORMAL - Normal successful completion

See Also

ca_context_create()

ca_create_channel()

#include <cadef.h>
typedef void ( caCh ) (struct connection_handler_args);
int ca_create_channel (const char *PVNAME,
        caCh *USERFUNC, void *PUSER,
        capri PRIORITY, chid *PCHID );

Description

This function creates a CA channel. The CA client library will attempt to establish and maintain a virtual circuit between the caller's application and a named process variable in a CA server. Each call to ca_create_channel() allocates resources in the CA client library and potentially also a CA server. The function ca_clear_channel() is used to release these resources. If successful, the routine writes a channel identifier into the user's variable of type "chid". This identifier can be used with any channel access call that operates on a channel.

The circuit may be initially connected or disconnected depending on the state of the network and the location of the channel. A channel will only enter a connected state after the server's address is determined, and only if channel access successfully establishes a virtual circuit through the network to the server. Channel access routines that send a request to a server will return ECA_DISCONNCHID if the channel is currently disconnected.

There are two ways to obtain asynchronous notification when a channel enters a connected state.

  • The first and simplest method requires that you call ca_pend_io(), and wait for successful completion, prior to using a channel that was created specifying a null connection callback function pointer.
  • The second method requires that you register a connection handler by supplying a valid connection callback function pointer. This connection handler is called whenever the connection state of the channel changes. If you have installed a connection handler then ca_pend_io() will not block waiting for the channel to enter a connected state.

The function ca_state(CHID) can be used to test the connection state of a channel. Valid connections may be isolated from invalid ones with this function if ca_pend_io() times out.

Due to the inherently transient nature of network connections the order of connection callbacks relative to the order that ca_create_channel() calls are made by the application can't be guaranteed, and application programs may need to be prepared for a connected channel to enter a disconnected state at any time.

Example

See caExample.c in the example application created by makeBaseApp.pl.

Arguments

PVNAME
A nil terminated process variable name string. EPICS process control function block database variable names are of the form "<record name>.<field name>". If the field name and the period separator are omitted then the "VAL" field is implicit. For example "RFHV01" and "RFHV01.VAL" reference the same EPICS process control function block database variable.
USERFUNC
Optional pointer to the user's callback function to be run when the connection state changes. Casual users of channel access may decide to set this field to null or 0 if they do not need to have a callback function run in response to each connection state change event.

The following structure is passed by value to the user's connection callback function. The op field will be set by the CA client library to CA_OP_CONN_UP when the channel connects, and to CA_OP_CONN_DOWN when the channel disconnects. See ca_puser() if the PUSER argument is required in your callback handler.

struct  ca_connection_handler_args {
    chanId  chid;  /* channel id */
    long    op;    /* one of CA_OP_CONN_UP or CA_OP_CONN_DOWN */
};
PUSER
The value of this void pointer argument is retained in storage associated with the specified channel. See the MACROS manual page for reading and writing this field. Casual users of channel access may wish to set this field to null or 0.
PRIORITY
The priority level for dispatch within the server or network with 0 specifying the lowest dispatch priority and 99 the highest. This parameter currently does not impact dispatch priorities within the client, but this might change in the future. The abstract priority range specified is mapped into an operating system specific range of priorities within the server. This parameter is ignored if the server is running on a network or operating system that does not have native support for prioritized delivery or execution respectively. Specifying many different priorities within the same program can increase resource consumption in the client and the server because an independent virtual circuit, and associated data structures, is created for each priority that is used on a particular server.
PCHID
The user supplied channel identifier storage is overwritten with a channel identifier if this routine is successful.

Returns

ECA_NORMAL - Normal successful completion

ECA_BADTYPE - Invalid DBR_XXXX type

ECA_STRTOBIG - Unusually large string

ECA_ALLOCMEM - Unable to allocate memory

ca_clear_channel()

#include <cadef.h>
int ca_clear_channel (chid CHID);

Description

Shutdown and reclaim resources associated with a channel created by ca_create_channel().

All remote operation requests such as the above are accumulated (buffered) and not forwarded to the IOC until one of ca_flush_io(), ca_pend_io(), ca_pend_event(), or ca_sg_block() are called. This allows several requests to be efficiently sent over the network in one message.

Clearing a channel does not cause its disconnect handler to be called, but clearing a channel does shutdown and reclaim any channel state change event subscriptions (monitors) registered with the channel.

Note: This operation blocks until any user callbacks for this channel have run to completion. If callbacks take a lock (mutex) then it is the user's responsibility to ensure that this lock is not held when ca_clear_channel() is called, otherwise a deadlock may ensue. (See also ca_clear_subscription().)

Arguments

CHID
Identifies the channel to delete.

Returns

ECA_NORMAL - Normal successful completion

ECA_BADCHID - Corrupted CHID

ca_put()

#include <cadef.h>
int ca_put ( chtype TYPE,
        chid CHID, void *PVALUE );
int ca_array_put ( chtype TYPE, unsigned long COUNT,
        chid CHID, const void *PVALUE);
typedef void ( caEventCallBackFunc ) (struct event_handler_args);
int ca_put_callback ( chtype TYPE,
        chid CHID, const void *PVALUE,
        caEventCallBackFunc PFUNC, void *USERARG );
int ca_array_put_callback ( chtype TYPE, unsigned long COUNT,
        chid CHID, const void *PVALUE,
        caEventCallBackFunc PFUNC, void *USERARG );

Description

Write a scalar or array value to a process variable.

When ca_put() or ca_array_put() are invoked the client will receive no response unless the request can not be fulfilled in the server. If unsuccessful an exception handler is run on the client side.

When ca_put_callback() or ca_array_put_callback() are invoked the user supplied asynchronous callback is called only after the initiated write operation, and all actions resulting from the initiating write operation, complete.

If unsuccessful the callback function is invoked indicating failure status.

If the channel disconnects before a put callback request can be completed, then the client's callback function is called with failure status, but this does not guarantee that the server did not receive and process the request before the disconnect. If a connection is lost and then resumed outstanding ca put requests are not automatically reissued following reconnect.

All of these functions return ECA_DISCONN if the channel is currently disconnected.

All put requests are accumulated (buffered) and not forwarded to the IOC until one of ca_flush_io(), ca_pend_io(), ca_pend_event(), or ca_sg_block() are called. This allows several requests to be efficiently combined into one message.

Description (IOC Database Specific)

A CA put request causes the record to process if the record's SCAN field is set to passive, and the field being written has its process passive attribute set to true. If such a record is already processing when a put request is initiated the specified field is written immediately, and the record is scheduled to process again as soon as it finishes processing. Earlier instances of multiple put requests initiated while the record is being processing may be discarded, but the last put request initiated is always written and processed.

A CA put callback request causes the record to process if the record's SCAN field is set to passive, and the field being written has its process passive attribute set to true. For such a record, the user's put callback function is not called until after the record, and any records that the record links to, finish processing. If such a record is already processing when a put callback request is initiated the put callback request is postponed until the record, and any records it links to, finish processing.

If the record's SCAN field is not set to passive, or the field being written has its process passive attribute set to false then the CA put or CA put callback request cause the specified field to be immediately written, but they do not cause the record to be processed.

Arguments

TYPE
The external type of the supplied value to be written. Conversion will occur if this does not match the native type. Specify one from the set of DBR_XXXX in db_access.h
COUNT
Element count to be written to the specified channel. This must match the array pointed to by PVALUE.
CHID
Channel identifier
PVALUE
Pointer to a value or array of values provided by the application to be written to the channel.
PFUNC
Pointer to a user supplied callback function to be run when the requested operation completes
USERARG
pointer sized variable retained and then passed back to user supplied function above

Returns

ECA_NORMAL - Normal successful completion

ECA_BADCHID - Corrupted CHID

ECA_BADTYPE - Invalid DBR_XXXX type

ECA_BADCOUNT - Requested count larger than native element count

ECA_STRTOBIG - Unusually large string supplied

ECA_NOWTACCESS - Write access denied

ECA_ALLOCMEM - Unable to allocate memory

ECA_DISCONN - Channel is disconnected

See Also

ca_flush_io()

ca_pend_event()

ca_sg_array_put()

ca_get()

#include <cadef.h>
int ca_get ( chtype TYPE,
        chid CHID, void *PVALUE );
int ca_array_get ( chtype TYPE, unsigned long COUNT,
        chid CHID, void *PVALUE );
typedef void ( caEventCallBackFunc ) (struct event_handler_args);
int ca_get_callback ( chtype TYPE,
        chid CHID,
        caEventCallBackFunc USERFUNC, void *USERARG);
int ca_array_get_callback ( chtype TYPE, unsigned long COUNT,
        chid CHID,
        caEventCallBackFunc USERFUNC, void *USERARG);

Description

Read a scalar or array value from a process variable.

When ca_get() or ca_array_get() are invoked the returned channel value can't be assumed to be stable in the application supplied buffer until after ECA_NORMAL is returned from ca_pend_io(). If a connection is lost outstanding ca get requests are not automatically reissued following reconnect.

When ca_get_callback() or ca_array_get_callback() are invoked a value is read from the channel and then the user's callback is invoked with a pointer to the retrieved value. Note that ca_pend_io() will not block for the delivery of values requested by ca_get_callback(). If the channel disconnects before a ca_get_callback() request can be completed, then the client's callback function is called with failure status.

All of these functions return ECA_DISCONN if the channel is currently disconnected.

All get requests are accumulated (buffered) and not forwarded to the IOC until one of ca_flush_io(), ca_pend_io(), ca_pend_event(), or ca_sg_block() are called. This allows several requests to be efficiently sent over the network in one message.

Description (IOC Database Specific)

A CA get or CA get callback request causes the record's field to be read immediately independent of whether the record is currently being processed or not. There is currently no mechanism in place to cause a record to be processed when a CA get request is initiated.

Example

See caExample.c in the example application created by makeBaseApp.pl.

Arguments

TYPE
The external type of the user variable to return the value into. Conversion will occur if this does not match the native type. Specify one from the set of DBR_XXXX in db_access.h
COUNT
Element count to be read from the specified channel. Must match the array pointed to by PVALUE. For ca_array_get_callback() a count of zero means use the current element count from the server.
CHID
Channel identifier
PVALUE
Pointer to an application supplied buffer where the current value of the channel is to be written.
USERFUNC
Pointer to a user supplied callback function to be run when the requested operation completes.
USERARG
Pointer sized variable retained and then passed back to user supplied callback function above.

Returns

ECA_NORMAL - Normal successful completion

ECA_BADTYPE - Invalid DBR_XXXX type

ECA_BADCHID - Corrupted CHID

ECA_BADCOUNT - Requested count larger than native element count

ECA_GETFAIL - A local database get failed

ECA_NORDACCESS - Read access denied

ECA_ALLOCMEM - Unable to allocate memory

ECA_DISCONN - Channel is disconnected

See Also

ca_pend_io()

ca_pend_event()

ca_sg_array_get()

ca_create_subscription()

#include <cadef.h>
typedef void ( caEventCallBackFunc ) (struct event_handler_args);
int ca_create_subscription ( chtype TYPE, unsigned long COUNT,
        chid CHID, unsigned long MASK,
        caEventCallBackFunc USERFUNC, void *USERARG,
        evid *PEVID );

Description

Register a state change subscription and specify a callback function to be invoked whenever the process variable undergoes significant state changes. A significant change can be a change in the process variable's value, alarm status, or alarm severity. In the process control function block database the deadband field determines the magnitude of a significant change for the process variable's value. Each call to this function consumes resources in the client library and potentially a CA server until one of ca_clear_channel() or ca_clear_subscription() is called.

Subscriptions may be installed or canceled against both connected and disconnected channels. The specified USERFUNC is called once immediately after the subscription is installed with the process variable's current state if the process variable is connected. Otherwise, the specified USERFUNC is called immediately after establishing a connection (or reconnection) with the process variable. The specified USERFUNC is called immediately with the process variable's current state from within ca_create_subscription() if the client and the process variable share the same address space.

If a subscription is installed on a channel in a disconnected state then the requested count will be set to the native maximum element count of the channel if the requested count is larger.

All subscription requests such as the above are accumulated (buffered) and not forwarded to the IOC until one of ca_flush_io(), ca_pend_io(), ca_pend_event(), or ca_sg_block() are called. This allows several requests to be efficiently sent over the network in one message.

If at any time after subscribing, read access to the specified process variable is lost, then the callback will be invoked immediately indicating that read access was lost via the status argument. When read access is restored normal event processing will resume starting always with at least one update indicating the current state of the channel.

A better name for this function might have been ca_subscribe().

Example

See caMonitor.c in the example application created by makeBaseApp.pl.

Arguments

TYPE
The type of value presented to the callback function. Conversion will occur if it does not match native type. Specify one from the set of DBR_XXXX in db_access.h
COUNT
The element count to be read from the specified channel. A count of zero means use the current element count from the server, effectively resulting in a variable size array subscription.
CHID
channel identifier
USERFUNC
Pointer to a user supplied callback function to be invoked with each subscription update.
USERARG
pointer sized variable retained and passed back to user callback function
RESERVED
Reserved for future use. Specify 0.0 to remain upwardly compatible.
PEVID
This is a pointer to user supplied event id which is overwritten if successful. This event id can later be used to clear a specific event. This option may be omitted by passing a null pointer.
MASK
A mask with bits set for each of the event trigger types requested. The event trigger mask must be a bitwise or of one or more of the following constants.
  • DBE_VALUE - Trigger events when the channel value exceeds the monitor dead band
  • DBE_ARCHIVE (or DBE_LOG) - Trigger events when the channel value exceeds the archival dead band
  • DBE_ALARM - Trigger events when the channel alarm state changes
  • DBE_PROPERTY - Trigger events when a channel property changes.

For functions above that do not include a trigger specification, events will be triggered when there are significant changes in the channel's value or when there are changes in the channel's alarm state. This is the same as "DBE_VALUE | DBE_ALARM."

Returns

ECA_NORMAL - Normal successful completion

ECA_BADCHID - Corrupted CHID

ECA_BADTYPE - Invalid DBR_XXXX type

ECA_ALLOCMEM - Unable to allocate memory

ECA_ADDFAIL - A local database event add failed

See Also

ca_pend_event()

ca_flush_io()

ca_clear_subscription()

#include <cadef.h>
int ca_clear_subscription ( evid EVID );

Description

Cancel a subscription.

All cancel-subscription requests such as the above are accumulated (buffered) and not forwarded to the server until one of ca_flush_io(), ca_pend_io(), ca_pend_event(), or ca_sg_block() are called. This allows several requests to be efficiently sent together in one message.

Note: This operation blocks until any user callbacks for this channel have run to completion. If callbacks take a lock (mutex) then it is the user's responsibility to ensure that this lock is not held when ca_clear_subscription() is called, otherwise a deadlock may ensue. (See also ca_clear_channel().)

Arguments

EVID
event id returned by ca_create_subscription()

Returns

ECA_NORMAL - Normal successful completion

ECA_BADCHID - Corrupted CHID

See Also

ca_create_subscription()

ca_pend_io()

#include <cadef.h>
int ca_pend_io ( double TIMEOUT );

Description

This function flushes the send buffer and then blocks until outstanding ca_get() requests complete, and until channels created specifying null connection handler function pointers connect for the first time.

  • If ECA_NORMAL is returned then it can be safely assumed that all outstanding ca_get() requests have completed successfully and channels created specifying null connection handler function pointers have connected for the first time.
  • If ECA_TIMEOUT is returned then it must be assumed for all previous ca_get() requests and properly qualified first time channel connects have failed.

If ECA_TIMEOUT is returned then get requests may be reissued followed by a subsequent call to ca_pend_io(). Specifically, the function will block only for outstanding ca_get() requests issued, and also any channels created specifying a null connection handler function pointer, after the last call to ca_pend_io() or ca client context creation whichever is later. Note that ca_create_channel() requests generally should not be reissued for the same process variable unless ca_clear_channel() is called first.

If no ca_get() or connection state change events are outstanding then ca_pend_io() will flush the send buffer and return immediately without processing any outstanding channel access background activities.

The delay specified to ca_pend_io() should take into account worst case network delays such as Ethernet collision exponential back off until retransmission delays which can be quite long on overloaded networks.

Unlike ca_pend_event(), this routine will not process CA's background activities if none of the selected IO requests are pending.

Arguments

TIMEOUT
Specifies the time out interval. A TIMEOUT interval of zero specifies forever.

Returns

ECA_NORMAL - Normal successful completion

ECA_TIMEOUT - Selected IO requests didn't complete before specified timeout

ECA_EVDISALLOW - Function inappropriate for use within an event handler

See Also

ca_get()

ca_create_channel()

ca_test_io()

ca_test_io()

#include <cadef.h>
int ca_test_io();

Description

This function tests to see if all ca_get() requests are complete and channels created specifying a null connection callback function pointer are connected. It will report the status of outstanding ca_get() requests issued, and channels created specifying a null connection callback function pointer, after the last call to ca_pend_io() or CA context initialization whichever is later.

Returns

ECA_IODONE - All IO operations completed

ECA_IOINPROGRESS - IO operations still in progress

See Also

ca_pend_io()

ca_pend_event()

#include <cadef.h>
int ca_pend_event ( double TIMEOUT );
int ca_poll ();

Description

When ca_pend_event() is invoked the send buffer is flushed and CA background activity is processed for TIMEOUT seconds.

When ca_poll() is invoked the send buffer is flushed and any outstanding CA background activity is processed.

The ca_pend_event() function will not return before the specified timeout expires and all unfinished channel access labor has been processed, and unlike ca_pend_io() returning from the function does not indicate anything about the status of pending IO requests.

Both ca_pend_event() and ca_poll() return ECA_TIMEOUT when successful. This behavior probably isn't intuitive, but it is preserved to insure backwards compatibility.

See also Thread Safety and Preemptive Callback to User Code.

Arguments

TIMEOUT
The duration to block in this routine in seconds. A timeout of zero seconds blocks forever.

Returns

ECA_TIMEOUT - The operation timed out

ECA_EVDISALLOW - Function inappropriate for use within a callback handler

ca_flush_io()

#include <cadef.h>
int ca_flush_io();

Description

Flush outstanding IO requests to the server. This routine might be useful to users who need to flush requests prior to performing client side labor in parallel with labor performed in the server.

Outstanding requests are also sent whenever the buffer which holds them becomes full.

Returns

ECA_NORMAL - Normal successful completion

ca_signal()

#include <cadef.h>
int ca_signal ( long CA_STATUS, const char * CONTEXT_STRING );
void SEVCHK( CA_STATUS, CONTEXT_STRING );

Description

Provide the error message character string associated with the supplied channel access error code and the supplied error context to diagnostics. If the error code indicates an unsuccessful operation a stack dump is printed, if this capability is available on the local operating system, and execution is terminated.

SEVCHK is a macro envelope around ca_signal() which only calls ca_signal() if the supplied error code indicates an unsuccessful operation. SEVCHK is the recommended error handler for simple applications which do not wish to write code testing the status returned from each channel access call.

Examples

status = ca_context_create (...);
SEVCHK ( status, "Unable to create a CA client context" );

If the application only wishes to print the message associated with an error code or test the severity of an error there are also functions provided for this purpose.

Arguments

CA_STATUS
The status (error code) returned from a channel access function.
CONTEXT_STRING
A null terminated character string to supply as error context to diagnostics.

Returns

ECA_NORMAL - Normal successful completion

ca_add_exception_event()

#include <cadef.h>
typedef void (*pCallback) ( struct exception_handler_args HANDLERARGS );
int ca_add_exception_event ( pCallback  USERFUNC, void *USERARG );

Description

Replace the currently installed CA context global exception handler callback.

When an error occurs in the server asynchronous to the clients thread then information about this type of error is passed from the server to the client in an exception message. When the client receives this exception message an exception handler callback is called.The default exception handler prints a diagnostic message on the client's standard out and terminates execution if the error condition is severe.

Note that certain fields in "struct exception_handler_args" are not applicable in the context of some error messages. For instance, a failed get will supply the address in the client task where the returned value was requested to be written. For other failed operations the value of the addr field should not be used.

Arguments

USERFUNC
Pointer to a user callback function to be executed when exceptions occur. Passing a null value causes the default exception handler to be reinstalled. The following structure is passed by value to the user's callback function. Currently, the op field can be one of CA_OP_GET, CA_OP_PUT, CA_OP_CREATE_CHANNEL, CA_OP_ADD_EVENT, CA_OP_CLEAR_EVENT, or CA_OP_OTHER.
struct exception_handler_args {
    void            *usr;   /* user argument supplied when installed */
    chanId          chid;   /* channel id (may be null) */
    long            type;   /* type requested */
    long            count;  /* count requested */
    void            *addr;  /* user's address to write results of CA_OP_GET */
    long            stat;   /* channel access ECA_XXXX status code */
    long            op;     /* CA_OP_GET, CA_OP_PUT, ..., CA_OP_OTHER */
    const char      *ctx;   /* a character string containing context info */
    sonst char      *pFile; /* source file name (may be NULL) */
    unsigned        lineNo; /* source file line number (may be zero) */
};
USERARG
pointer sized variable retained and passed back to user function above

Example

void ca_exception_handler (
    struct exception_handler_args args)
{
    char buf[512];
    char *pName;

    if ( args.chid ) {
        pName = ca_name ( args.chid );
    }
    else {
        pName = "?";
    }
    sprintf ( buf,
            "%s - with request chan=%s op=%d data type=%s count=%d",
            args.ctx, pName, args.op, dbr_type_to_text ( args.type ), args.count );
    ca_signal ( args.stat, buf );
}
ca_add_exception_event ( ca_exception_handler , 0 );

Returns

ECA_NORMAL - Normal successful completion

ca_add_fd_registration()

#include <cadef.h>
int ca_add_fd_registration ( void ( USERFUNC * ) ( void *USERARG, int FD, int OPENED ), void * USERARG )

Description

For use with the services provided by a file descriptor manager (IO multiplexor) such as "fdmgr.c". A file descriptor manager is often needed when two file descriptor IO intensive libraries such as the EPICS channel access client library and the X window system client library must coexist in the same UNIX process. This function allows an application code to be notified whenever the CA client library places a new file descriptor into service and whenever the CA client library removes a file descriptor from service. Specifying USERFUNC=NULL disables file descriptor registration (this is the default).

Arguments

USERFUNC

Pointer to a user supplied C function returning null with the above arguments.

USERARG

User supplied pointer sized variable passed to the above function.

FD

A file descriptor.

OPENED

Boolean argument is true if the file descriptor was opened and false if the file descriptor was closed.

Example

int s;
static struct myStruct aStruct;

void fdReg ( struct myStruct *pStruct, int fd, int opened )
{
    if ( opened ) printf ( "fd %d was opened\n", fd );
    else printf ( "fd %d was closed\n", fd );
}
s = ca_add_fd_registration ( fdReg, & aStruct );
SEVCHK ( s, NULL );

Comments

When using this function it is advisable to call it only once prior to calling any other CA function, or once just after creating the CA context (if you create the context explicitly). Use of this interface can improve latency slightly in applications that use non preemptive callback mode at the expense of some additional runtime overhead when compared to the alternative which is just polling ca_pend_event() periodically. It would probably not be appropriate to use this function with preemptive callback mode. Starting with R3.14 this function is implemented in a special backward compatibility mode. if ca_add_fd_registration() is called, a single pseudo UDP fd is created which CA pokes whenever something significant happens. Xt and others can watch this fd so that backwards compatibility is preserved, and so that they will not need to use preemptive callback mode but they will nevertheless get the lowest latency response to the arrival of CA messages.

Returns

"ECA_NORMAL - Normal successful completion

ca_replace_printf_handler ()

#include <cadef.h>
typedef int caPrintfFunc ( const char *pFormat, va_list args );
int ca_replace_printf_handler ( caPrintfFunc *PFUNC );

Description

Replace the default handler for formatted diagnostic message output. The default handler uses fprintf to send messages to 'stderr'.

Arguments

PFUNC
A pointer to a user supplied callback handler to be invoked when CA prints diagnostic messages. Installing a null pointer will cause the default callback handler to be reinstalled.

Examples

int my_printf ( char *pformat, va_list args ) {
        int status;
        status = vfprintf( stderr, pformat, args);
        return status;
}
status = ca_replace_printf_handler ( my_printf );
SEVCHK ( status, "failed to install my printf handler" );

Returns

ECA_NORMAL - Normal successful completion

ca_replace_access_rights_event()

#include <cadef.h>
typedef void ( caEventCallBackFunc )(struct access_rights_handler_args);
int ca_replace_access_rights_event ( chid CHAN,
        caEventCallBackFunc PFUNC );

Description

Install or replace the access rights state change callback handler for the specified channel.

The callback handler is called in the following situations.

  • whenever CA connects the channel immediately before the channel's connection handler is called
  • whenever CA disconnects the channel immediately after the channel's disconnect callback is called
  • once immediately after installation if the channel is connected.
  • whenever the access rights state of a connected channel changes

When a channel is created no access rights handler is installed.

Arguments

CHAN
The channel identifier.
PFUNC
Pointer to a user supplied callback function. A null pointer uninstalls the current handler. The following arguments are passed by value to the supplied callback handler.
typedef struct ca_access_rights {
    unsigned    read_access:1;
    unsigned    write_access:1;
} caar;

/* arguments passed to user access rights handlers */
struct  access_rights_handler_args {
    chanId  chid;   /* channel id */
    caar    ar;     /* new access rights state */
};

Returns

ECA_NORMAL - Normal successful completion

ca_field_type()

#include <cadef.h>
chtype ca_field_type ( CHID );

Description

Return the native type in the server of the process variable.

Arguments

CHID
channel identifier

Returns

TYPE
The data type code will be a member of the set of DBF_XXXX in db_access.h. The constant TYPENOTCONN is returned if the channel is disconnected.

ca_element_count()

#include <cadef.h>
unsigned ca_element_count ( CHID );

Description

Return the maximum array element count in the server for the specified IO channel.

Arguments

CHID
channel identifier

Returns

COUNT
The maximum array element count in the server. An element count of zero is returned if the channel is disconnected.

ca_name()

#include <cadef.h>
char * ca_name ( CHID );

Description

Return the name provided when the supplied channel id was created.

Arguments

CHID
channel identifier

Returns

PNAME
The channel name. The string returned is valid as long as the channel specified exists.

ca_set_puser()

#include <cadef.h>
void ca_set_puser ( chid CHID, void *PUSER );

Description

Set a user private void pointer variable retained with each channel for use at the users discretion.

Arguments

CHID
channel identifier
PUSER
user private void pointer

ca_puser()

#include <cadef.h>
void * ca_puser ( CHID );

Description

Return a user private void pointer variable retained with each channel for use at the users discretion.

Arguments

CHID
channel identifier

Returns

PUSER
user private pointer

ca_state()

#include <cadef.h>
enum channel_state {
    cs_never_conn, /* valid chid, server not found or unavailable */
    cs_prev_conn,  /* valid chid, previously connected to server */
    cs_conn,       /* valid chid, connected to server */
    cs_closed };   /* channel deleted by user */
enum channel_state ca_state ( CHID );

Description

Returns an enumerated type indicating the current state of the specified IO channel.

Arguments

CHID
channel identifier

Returns

STATE
the connection state

ca_message()

#include <cadef.h>
const char * ca_message ( STATUS );

Description

return a message character string corresponding to a user specified CA status code.

Arguments

STATUS
a CA status code

Returns

STRING
the corresponding error message string

ca_host_name()

#include <cadef.h>
char * ca_host_name ( CHID );

Description

Return a character string which contains the name of the host to which a channel is currently connected.

Arguments

CHID
the channel identifier

Returns

STRING
The process variable server's host name. If the channel is disconnected the string "<disconnected>" is returned.

ca_read_access()

#include <cadef.h>
int ca_read_access ( CHID );

Description

Returns boolean true if the client currently has read access to the specified channel and boolean false otherwise.

Arguments

CHID
the channel identifier

Returns

STRING
boolean true if the client currently has read access to the specified channel and boolean false otherwise

ca_write_access()

#include <cadef.h>
int ca_write_access ( CHID );

Description

Returns boolean true if the client currently has write access to the specified channel and boolean false otherwise.

Arguments

CHID
the channel identifier

Returns

STRING
boolean true if the client currently has write access to the specified channel and boolean false otherwise

dbr_size[]

#include <db_access.h>
extern unsigned dbr_size[/* TYPE */];

Description

An array that returns the size in bytes for a DBR_XXXX type.

Arguments

TYPE
The data type code. A member of the set of DBF_XXXX in db_access.h.

Returns

SIZE
the size in bytes of the specified type

dbr_size_n()

#include <db_access.h>
unsigned dbr_size_n ( TYPE, COUNT );

Description

Returns the size in bytes for a DBR_XXXX type with COUNT elements. If the DBR type is a structure then the value field is the last field in the structure. If COUNT is greater than one then COUNT-1 elements are appended to the end of the structure so that they can be addressed as an array through a pointer to the value field.

Arguments

TYPE
The data type
COUNT
The element count

Returns

SIZE
the size in bytes of the specified type with the specified number of elements

dbr_value_size[]

#include <db_access.h>
extern unsigned dbr_value_size[/* TYPE */];

Description

The array dbr_value_size[TYPE] returns the size in bytes for the value stored in a DBR_XXXX type. If the type is a structure the size of the value field is returned otherwise the size of the type is returned.

Arguments

TYPE
The data type code. A member of the set of DBF_XXXX in db_access.h.

Returns

SIZE
the size in bytes of the value field if the type is a structure and otherwise the size in bytes of the type

dbr_type_to_text()

#include <db_access.h>
const char * dbr_type_text ( chtype TYPE );

Description

Returns a constant null terminated string corresponding to the specified dbr type.

Arguments

TYPE
The data type code. A member of the set of DBR_XXXX in db_access.h.

Returns

STRING
The const string corresponding to the DBR_XXX type.

ca_test_event()

#include <cadef.h>

Description

void ca_test_event ( struct event_handler_args );

A built-in subscription update callback handler for debugging purposes that prints diagnostics to standard out.

Examples

void ca_test_event ();
status = ca_create_subscription ( type, chid, ca_test_event, NULL, NULL );
SEVCHK ( status, .... );

See Also

ca_create_subscription()

ca_sg_create()

#include <cadef.h>
int ca_sg_create ( CA_SYNC_GID *PGID );

Description

Create a synchronous group and return an identifier for it.

A synchronous group can be used to guarantee that a set of channel access requests have completed. Once a synchronous group has been created then channel access get and put requests may be issued within it using ca_sg_array_get() and ca_sg_array_put() respectively. The routines ca_sg_block() and ca_sg_test() can be used to block for and test for completion respectively. The routine ca_sg_reset() is used to discard knowledge of old requests which have timed out and in all likelihood will never be satisfied.

Any number of asynchronous groups can have application requested operations outstanding within them at any given time.

Arguments

PGID
Pointer to a user supplied CA_SYNC_GID.

Examples

CA_SYNC_GID gid;
status = ca_sg_create ( &gid );
SEVCHK ( status, Sync group create failed );

Returns

ECA_NORMAL - Normal successful completion

ECA_ALLOCMEM - Failed, unable to allocate memory

See Also

ca_sg_delete()

ca_sg_block()

ca_sg_test()

ca_sg_reset()

ca_sg_array_put()

ca_sg_array_get()

ca_sg_delete()

#include <cadef.h>
int ca_sg_delete ( CA_SYNC_GID GID );

Description

Deletes a synchronous group.

Arguments

GID
Identifier of the synchronous group to be deleted.

Examples

CA_SYNC_GID gid;
status = ca_sg_delete ( gid );
SEVCHK ( status, Sync group delete failed );

Returns

ECA_NORMAL - Normal successful completion

ECA_BADSYNCGRP - Invalid synchronous group

See Also

ca_sg_create()

ca_sg_block()

#include <cadef.h>
int ca_sg_block ( CA_SYNC_GID GID, double TIMEOUT );

Description

Flushes the send buffer and then waits until outstanding requests complete or the specified time out expires. At this time outstanding requests include calls to ca_sg_array_get() and calls to ca_sg_array_put(). If ECA_TIMEOUT is returned then failure must be assumed for all outstanding queries. Operations can be reissued followed by another ca_sg_block(). This routine will only block on outstanding queries issued after the last call to ca_sg_block(), ca_sg_reset(), or ca_sg_create() whichever occurs later in time. If no queries are outstanding then ca_sg_block() will return immediately without processing any pending channel access activities.

Values written into your program's variables by a channel access synchronous group request should not be referenced by your program until ECA_NORMAL has been received from ca_sg_block(). This routine will process pending channel access background activity while it is waiting.

Arguments

GID
Identifier of the synchronous group.
TIMEOUT
The duration to block in this routine in seconds. A timeout of zero seconds blocks forever.

Examples

CA_SYNC_GID gid;
status = ca_sg_block(gid, 0.0);
SEVCHK(status, Sync group block failed);

Returns

ECA_NORMAL - Normal successful completion

ECA_TIMEOUT - The operation timed out

ECA_EVDISALLOW - Function inappropriate for use within an event handler

ECA_BADSYNCGRP - Invalid synchronous group

See Also

ca_sg_test()

ca_sg_reset()

ca_sg_test()

#include <cadef.h>
int ca_sg_test ( CA_SYNC_GID GID )

Description

Test to see if all requests made within a synchronous group have completed.

Arguments

GID
Identifier of the synchronous group.

Description

Test to see if all requests made within a synchronous group have completed.

Examples

CA_SYNC_GID gid;
status = ca_sg_test ( gid );

Returns

ECA_IODONE - IO operations completed

ECA_IOINPROGRESS - Some IO operations still in progress

ca_sg_reset()

#include <cadef.h>
int ca_sg_reset ( CA_SYNC_GID GID )

Description

Reset the number of outstanding requests within the specified synchronous group to zero so that ca_sg_test() will return ECA_IODONE and ca_sg_block() will not block unless additional subsequent requests are made.

Arguments

GID
Identifier of the synchronous group.

Examples

CA_SYNC_GID gid;
status = ca_sg_reset(gid);

Returns

ECA_NORMAL - Normal successful completion

ECA_BADSYNCGRP - Invalid synchronous group

ca_sg_array_put()

#include <cadef.h>
int ca_sg_put ( CA_SYNC_GID GID, chtype TYPE,
        chid CHID, void *PVALUE );
int ca_sg_array_put ( CA_SYNC_GID GID, chtype TYPE,
        unsigned long COUNT, chid CHID, void *PVALUE );

Write a value, or array of values, to a channel and increment the outstanding request count of a synchronous group. The ca_sg_put() and ca_sg_array_put() functionality is implemented using ca_array_put_callback().

All remote operation requests such as the above are accumulated (buffered) and not forwarded to the server until one of ca_flush_io(), ca_pend_io(), ca_pend_event(), or ca_sg_block() are called. This allows several requests to be efficiently sent in one message.

If a connection is lost and then resumed outstanding puts are not reissued.

Arguments

GID
synchronous group identifier
TYPE
The type of supplied value. Conversion will occur if it does not match the native type. Specify one from the set of DBR_XXXX in db_access.h.
COUNT
element count to be written to the specified channel - must match the array pointed to by PVALUE
CHID
channel identifier
PVALUE
A pointer to an application supplied buffer containing the value or array of values returned

Returns

ECA_NORMAL - Normal successful completion

ECA_BADSYNCGRP - Invalid synchronous group

ECA_BADCHID - Corrupted CHID

ECA_BADTYPE - Invalid DBR_XXXX type

ECA_BADCOUNT - Requested count larger than native element count

ECA_STRTOBIG - Unusually large string supplied

ECA_PUTFAIL - A local database put failed

See Also

ca_flush_io()

ca_sg_array_get()

#include <cadef.h>
int ca_sg_get ( CA_SYNC_GID GID, chtype TYPE,
        chid CHID, void *PVALUE );
int ca_sg_array_get ( CA_SYNC_GID GID,
        chtype TYPE, unsigned long COUNT,
        chid CHID, void *PVALUE );

Description

Read a value from a channel and increment the outstanding request count of a synchronous group. The ca_sg_get() and ca_sg_array_get() functionality is implemented using ca_array_get_callback().

The values written into your program's variables by ca_sg_get() or ca_sg_array_get() should not be referenced by your program until ECA_NORMAL has been received from ca_sg_block(), or until ca_sg_test() returns ECA_IODONE.

All remote operation requests such as the above are accumulated (buffered) and not forwarded to the server until one of ca_flush_io(), ca_pend_io(), ca_pend_event(), or ca_sg_block() are called. This allows several requests to be efficiently sent in one message.

If a connection is lost and then resumed outstanding gets are not reissued.

Arguments

GID
Identifier of the synchronous group.
TYPE
External type of returned value. Conversion will occur if this does not match native type. Specify one from the set of DBR_XXXX in db_access.h
COUNT
Element count to be read from the specified channel. It must match the array pointed to by PVALUE.
CHID
channel identifier
PVALUE
Pointer to application supplied buffer that is to contain the value or array of values to be returned

Returns

ECA_NORMAL - Normal successful completion

ECA_BADSYNCGRP - Invalid synchronous group

ECA_BADCHID - Corrupted CHID

ECA_BADCOUNT - Requested count larger than native element count

ECA_BADTYPE - Invalid DBR_XXXX type

ECA_GETFAIL - A local database get failed

See Also

ca_pend_io()

ca_flush_io()

ca_get_callback()

ca_client_status()

int ca_client_status ( unsigned level );
int ca_context_status ( struct ca_client_context *CONTEXT,
       unsigned LEVEL );

Description

Prints information about the client context including, at higher interest levels, status for each channel. Lacking a CA context pointer, ca_client_status() prints information about the calling threads CA context.

Arguments

CONTEXT
A pointer to the CA context to examine.
LEVEL
The interest level. Increasing level produces increasing detail.

ca_current_context()

struct ca_client_context * ca_current_context ();

Description

Returns a pointer to the current thread's CA context. If none then null is returned.

See Also

ca_attach_context()

ca_detach_context()

ca_context_create()

ca_context_destroy()

ca_attach_context()

int ca_attach_context (struct ca_client_context *CONTEXT);

Description

The calling thread becomes a member of the specified CA context. If ca_disable_preemptive_callback is specified when ca_context_create() is called (or if ca_task_initialize() is called) then additional threads are not allowed to join the CA context because allowing other threads to join implies that CA callbacks will be called preemptively from more than one thread.

Arguments

CONTEXT
A pointer to the CA context to join with.

Returns

ECA_NORMAL - Normal successful completion

ECA_NOTTHREADED - Context is not preemptive so cannot be joined

ECA_ISATTACHED - Thread already attached to a CA context

See Also

ca_current_context()

ca_detach_context()

ca_context_create()

ca_context_destroy()

ca_detach_context()

void ca_detach_context();

Description

Detach from any CA context currently attached to the calling thread. This does not cleanup or shutdown any currently attached CA context (for that use ca_context_destroy()).

See Also

ca_current_context()

ca_attach_context()

ca_context_create()

ca_context_destroy()

ca_dump_dbr()

void ca_dump_dbr (chtype TYPE, unsigned COUNT, const void * PDBR);

Description

Dumps the specified dbr data type to standard out.

Arguments

TYPE
The data type (from the DBR_XXX set described in db_access.h).
COUNT
The array element count
PDBR
A pointer to data of the specified count and number.

Return Codes

ECA_NORMAL
Normal successful completion
ECA_ALLOCMEM
Unable to allocate additional dynamic memory
ECA_TOLARGE
The requested data transfer is greater than available memory or EPICS_CA_MAX_ARRAY_BYTES
ECA_BADTYPE
The data type specified is invalid
ECA_BADSTR
Invalid string
ECA_BADCHID
Invalid channel identifier
ECA_BADCOUNT
Invalid element count requested
ECA_PUTFAIL
Channel write request failed
ECA_GETFAIL
Channel read request failed
ECA_ADDFAIL
unable to install subscription request
ECA_TIMEOUT
User specified timeout on IO operation expired
ECA_EVDISALLOW
function called was inappropriate for use within a callback function
ECA_IODONE
IO operations have completed
ECA_IOINPROGRESS
IO operations are in progress
ECA_BADSYNCGRP
Invalid synchronous group identifier
ECA_NORDACCESS
Read access denied
ECA_NOWTACCESS
Write access denied
ECA_DISCONN
Virtual circuit disconnect"
ECA_DBLCHNL
Identical process variable name on multiple servers
ECA_EVDISALLOW
Request inappropriate within subscription (monitor) update callback
ECA_BADMONID
Bad event subscription (monitor) identifier
ECA_BADMASK
Invalid event selection mask
ECA_PUTCBINPROG
Put callback timed out
ECA_PUTCBINPROG
Put callback timed out
ECA_ANACHRONISM
Requested feature is no longer supported
ECA_NOSEARCHADDR
Empty PV search address list
ECA_NOCONVERT
No reasonable data conversion between client and server types
ECA_BADFUNCPTR
Invalid function pointer
ECA_ISATTACHED
Thread is already attached to a client context
ECA_UNAVAILINSERV
Not supported by attached service
ECA_CHANDESTROY
User destroyed channel
ECA_BADPRIORITY
Invalid channel priority
ECA_NOTTHREADED
Preemptive callback not enabled - additional threads may not join context
ECA_16KARRAYCLIENT
Client's protocol revision does not support transfers exceeding 16k bytes

base-7.0.3.1/modules/ca/src/client/Makefile0000664000577000060420000000727013557101274017161 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* CURDIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) TOP = ../../../.. include $(TOP)/configure/CONFIG HTMLS += CAref.html # # includes to install from this subproject # INC += cadef.h INC += caerr.h INC += caeventmask.h INC += caProto.h INC += db_access.h INC += addrList.h INC += cacIO.h INC += caDiagnostics.h INC += net_convert.h INC += caVersion.h INC += caVersionNum.h LIBSRCS += cac.cpp LIBSRCS += cacChannel.cpp LIBSRCS += cacChannelNotify.cpp LIBSRCS += cacContextNotify.cpp LIBSRCS += cacReadNotify.cpp LIBSRCS += cacWriteNotify.cpp LIBSRCS += cacStateNotify.cpp LIBSRCS += access.cpp LIBSRCS += iocinf.cpp LIBSRCS += convert.cpp LIBSRCS += test_event.cpp LIBSRCS += repeater.cpp LIBSRCS += searchTimer.cpp LIBSRCS += disconnectGovernorTimer.cpp LIBSRCS += repeaterSubscribeTimer.cpp LIBSRCS += baseNMIU.cpp LIBSRCS += nciu.cpp LIBSRCS += netiiu.cpp LIBSRCS += udpiiu.cpp LIBSRCS += tcpiiu.cpp LIBSRCS += noopiiu.cpp LIBSRCS += netReadNotifyIO.cpp LIBSRCS += netWriteNotifyIO.cpp LIBSRCS += netSubscription.cpp LIBSRCS += tcpSendWatchdog.cpp LIBSRCS += tcpRecvWatchdog.cpp LIBSRCS += bhe.cpp LIBSRCS += ca_client_context.cpp LIBSRCS += oldChannelNotify.cpp LIBSRCS += oldSubscription.cpp LIBSRCS += getCallback.cpp LIBSRCS += getCopy.cpp LIBSRCS += putCallback.cpp LIBSRCS += syncgrp.cpp LIBSRCS += CASG.cpp LIBSRCS += syncGroupNotify.cpp LIBSRCS += syncGroupReadNotify.cpp LIBSRCS += syncGroupWriteNotify.cpp LIBSRCS += localHostName.cpp LIBSRCS += comQueRecv.cpp LIBSRCS += comQueSend.cpp LIBSRCS += comBuf.cpp LIBSRCS += hostNameCache.cpp LIBSRCS += msgForMultiplyDefinedPV.cpp LIBRARY=ca ca_RCS = ca.rc ca_LIBS = Com ca_SYS_LIBS_WIN32 = ws2_32 advapi32 user32 # libs needed for PROD and TESTPRODUCT PROD_LIBS = ca Com # needed when its an object library build PROD_SYS_LIBS_WIN32 = ws2_32 advapi32 user32 PROD_DEFAULT += caRepeater catime acctst caConnTest casw caEventRate PROD_vxWorks = -nil- PROD_RTEMS = -nil- PROD_iOS = -nil- OBJS_vxWorks = catime acctst caConnTest casw caEventRate acctstRegister caRepeater_SRCS = caRepeater.cpp catime_SRCS = catimeMain.c catime.c acctst_SRCS = acctstMain.c acctst.c caEventRate_SRCS = caEventRateMain.cpp caEventRate.cpp casw_SRCS = casw.cpp caConnTest_SRCS = caConnTestMain.cpp caConnTest.cpp casw_SYS_LIBS_solaris = socket SCRIPTS_HOST = S99caRepeater SCRIPTS_Linux = caRepeater.service EXPAND += S99caRepeater@ EXPAND += caRepeater.service@ EXPAND_VARS = INSTALL_BIN=$(abspath $(INSTALL_BIN)) 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 EXPANDVARS += EPICS_CA_MAJOR_VERSION EXPANDVARS += EPICS_CA_MINOR_VERSION EXPANDVARS += EPICS_CA_MAINTENANCE_VERSION EXPANDVARS += EPICS_CA_DEVELOPMENT_FLAG EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))") # shared library ABI version. SHRLIB_VERSION = $(EPICS_CA_MAJOR_VERSION).$(EPICS_CA_MINOR_VERSION).$(EPICS_CA_MAINTENANCE_VERSION) include $(TOP)/configure/RULES # Can't use EXPAND as generated headers must appear # in O.Common, but EXPAND emits rules for O.$(T_A) ../O.Common/caVersionNum.h: ../caVersionNum.h@ $(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@ base-7.0.3.1/modules/ca/src/client/S99caRepeater@0000664000577000060420000000135213557101274020117 0ustar anjaesctl#!/bin/sh # # System-V init script for the EPICS CA Repeater. # INSTALL_BIN=@INSTALL_BIN@ # To change the default values for the EPICS environment parameters, # uncomment and modify the relevant lines below. These are the only # EPICS environment variables that the CA Repeater makes use of. # EPICS_CA_REPEATER_PORT="5065" export EPICS_CA_REPEATER_PORT if [ $1 = "start" ]; then if [ -x $INSTALL_BIN/caRepeater ]; then echo "Starting EPICS CA Repeater " $INSTALL_BIN/caRepeater & fi else if [ $1 = "stop" ]; then pid=`ps -e | sed -ne '/caRepeat/s/^ *\([1-9][0-9]*\).*$/\1/p'` if [ "${pid}" != "" ]; then echo "Stopping EPICS CA Repeater " kill ${pid} fi fi fi base-7.0.3.1/modules/ca/src/client/SearchDest.h0000664000577000060420000000251713557101274017716 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #ifndef SearchDest_h #define SearchDest_h #include #include #include #include "caProto.h" class channelNode; class epicsMutex; template < class T > class epicsGuard; struct SearchDest : public tsDLNode < SearchDest > { virtual ~SearchDest () {}; struct Callback { virtual ~Callback () {}; virtual void notify ( const caHdr & msg, const void * pPayload, const osiSockAddr & addr, const epicsTime & ) = 0; virtual void show ( epicsGuard < epicsMutex > &, unsigned level ) const = 0; }; virtual void searchRequest ( epicsGuard < epicsMutex > &, const char * pbuf, size_t len ) = 0; virtual void show ( epicsGuard < epicsMutex > &, unsigned level ) const = 0; }; #endif // SearchDest_h base-7.0.3.1/modules/ca/src/client/access.cpp0000664000577000060420000007275113557101274017474 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeffrey O. Hill * */ #include #include #include "dbDefs.h" #include "epicsExit.h" #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" /* * allocate error message string array * here so I can use sizeof */ #define CA_ERROR_GLBLSOURCE /* * allocate header version strings here */ #define CAC_VERSION_GLOBAL #define epicsExportSharedSymbols #include "iocinf.h" #include "oldAccess.h" #include "cac.h" epicsThreadPrivateId caClientContextId; const char * ca_message_text [] = { "Normal successful completion", "Maximum simultaneous IOC connections exceeded", "Unknown internet host", "Unknown internet service", "Unable to allocate a new socket", "Unable to connect to internet host or service", "Unable to allocate additional dynamic memory", "Unknown IO channel", "Record field specified inappropriate for channel specified", "The requested data transfer is greater than available memory or EPICS_CA_MAX_ARRAY_BYTES", "User specified timeout on IO operation expired", "Sorry, that feature is planned but not supported at this time", "The supplied string is unusually large", "The request was ignored because the specified channel is disconnected", "The data type specifed is invalid", "Remote Channel not found", "Unable to locate all user specified channels", "Channel Access Internal Failure", "The requested local DB operation failed", "Channel read request failed", "Channel write request failed", "Channel subscription request failed", "Invalid element count requested", "Invalid string", "Virtual circuit disconnect", "Identical process variable names on multiple servers", "Request inappropriate within subscription (monitor) update callback", "Database value get for that channel failed during channel search", "Unable to initialize without the vxWorks VX_FP_TASK task option set", "Event queue overflow has prevented first pass event after event add", "Bad event subscription (monitor) identifier", "Remote channel has new network address", "New or resumed network connection", "Specified task isnt a member of a CA context", "Attempt to use defunct CA feature failed", "The supplied string is empty", "Unable to spawn the CA repeater thread- auto reconnect will fail", "No channel id match for search reply- search reply ignored", "Reseting dead connection- will try to reconnect", "Server (IOC) has fallen behind or is not responding- still waiting", "No internet interface with broadcast available", "Invalid event selection mask", "IO operations have completed", "IO operations are in progress", "Invalid synchronous group identifier", "Put callback timed out", "Read access denied", "Write access denied", "Requested feature is no longer supported", "Empty PV search address list", "No reasonable data conversion between client and server types", "Invalid channel identifier", "Invalid function pointer", "Thread is already attached to a client context", "Not supported by attached service", "User destroyed channel", "Invalid channel priority", "Preemptive callback not enabled - additional threads may not join context", "Client's protocol revision does not support transfers exceeding 16k bytes", "Virtual circuit connection sequence aborted", "Virtual circuit unresponsive" }; static epicsThreadOnceId caClientContextIdOnce = EPICS_THREAD_ONCE_INIT; // runs once only for each process extern "C" void ca_init_client_context ( void * ) { caClientContextId = epicsThreadPrivateCreate (); } /* * fetchClientContext (); */ int fetchClientContext ( ca_client_context **ppcac ) { epicsThreadOnce ( &caClientContextIdOnce, ca_init_client_context, 0 ); if ( caClientContextId == 0 ) { return ECA_ALLOCMEM; } int status; *ppcac = ( ca_client_context * ) epicsThreadPrivateGet ( caClientContextId ); if ( *ppcac ) { status = ECA_NORMAL; } else { status = ca_task_initialize (); if ( status == ECA_NORMAL ) { *ppcac = (ca_client_context *) epicsThreadPrivateGet ( caClientContextId ); if ( ! *ppcac ) { status = ECA_INTERNAL; } } } return status; } /* * ca_task_initialize () */ // extern "C" int epicsShareAPI ca_task_initialize ( void ) { return ca_context_create ( ca_disable_preemptive_callback ); } // extern "C" int epicsShareAPI ca_context_create ( ca_preemptive_callback_select premptiveCallbackSelect ) { ca_client_context *pcac; try { epicsThreadOnce ( & caClientContextIdOnce, ca_init_client_context, 0); if ( caClientContextId == 0 ) { return ECA_ALLOCMEM; } pcac = ( ca_client_context * ) epicsThreadPrivateGet ( caClientContextId ); if ( pcac ) { if ( premptiveCallbackSelect == ca_enable_preemptive_callback && ! pcac->preemptiveCallbakIsEnabled() ) { return ECA_NOTTHREADED; } return ECA_NORMAL; } pcac = new ca_client_context ( premptiveCallbackSelect == ca_enable_preemptive_callback ); if ( ! pcac ) { return ECA_ALLOCMEM; } epicsThreadPrivateSet ( caClientContextId, (void *) pcac ); } catch ( ... ) { return ECA_ALLOCMEM; } return ECA_NORMAL; } // // ca_modify_host_name () // // defunct // // extern "C" int epicsShareAPI ca_modify_host_name ( const char * ) { return ECA_NORMAL; } // // ca_modify_user_name() // // defunct // // extern "C" int epicsShareAPI ca_modify_user_name ( const char * ) { return ECA_NORMAL; } // // ca_context_destroy () // // extern "C" void epicsShareAPI ca_context_destroy () { ca_client_context *pcac; if ( caClientContextId != NULL ) { pcac = (ca_client_context *) epicsThreadPrivateGet ( caClientContextId ); if ( pcac ) { delete pcac; epicsThreadPrivateSet ( caClientContextId, 0 ); } } } /* * ca_task_exit() * * releases all resources alloc to a channel access client */ // extern "C" int epicsShareAPI ca_task_exit () { ca_context_destroy (); return ECA_NORMAL; } /* * * CA_BUILD_AND_CONNECT * * backwards compatible entry point to ca_search_and_connect() */ // extern "C" int epicsShareAPI ca_build_and_connect ( const char *name_str, chtype get_type, arrayElementCount get_count, chid * chan, void *pvalue, caCh *conn_func, void *puser ) { if ( get_type != TYPENOTCONN && pvalue != 0 && get_count != 0 ) { return ECA_ANACHRONISM; } return ca_search_and_connect ( name_str, chan, conn_func, puser ); } /* * ca_search_and_connect() */ // extern "C" int epicsShareAPI ca_search_and_connect ( const char * name_str, chid * chanptr, caCh * conn_func, void * puser ) { return ca_create_channel ( name_str, conn_func, puser, CA_PRIORITY_DEFAULT, chanptr ); } // extern "C" int epicsShareAPI ca_create_channel ( const char * name_str, caCh * conn_func, void * puser, capri priority, chid * chanptr ) { ca_client_context * pcac; int caStatus = fetchClientContext ( & pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } { CAFDHANDLER * pFunc = 0; void * pArg = 0; { epicsGuard < epicsMutex > guard ( pcac->mutex ); if ( pcac->fdRegFuncNeedsToBeCalled ) { pFunc = pcac->fdRegFunc; pArg = pcac->fdRegArg; pcac->fdRegFuncNeedsToBeCalled = false; } } if ( pFunc ) { ( *pFunc ) ( pArg, pcac->sock, true ); } } try { epicsGuard < epicsMutex > guard ( pcac->mutex ); oldChannelNotify * pChanNotify = new ( pcac->oldChannelNotifyFreeList ) oldChannelNotify ( guard, *pcac, name_str, conn_func, puser, priority ); // make sure that their chan pointer is set prior to // calling connection call backs *chanptr = pChanNotify; pChanNotify->initiateConnect ( guard ); // no need to worry about a connect preempting here because // the connect sequence will not start untill initiateConnect() // is called } catch ( cacChannel::badString & ) { return ECA_BADSTR; } catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } catch ( cacChannel::badPriority & ) { return ECA_BADPRIORITY; } catch ( cacChannel::unsupportedByService & ) { return ECA_UNAVAILINSERV; } catch ( std :: exception & except ) { pcac->printFormated ( "ca_create_channel: " "unexpected exception was \"%s\"", except.what () ); return ECA_INTERNAL; } catch ( ... ) { return ECA_INTERNAL; } return ECA_NORMAL; } /* * ca_clear_channel () * * a known defect here is that there will be a * crash if they destroy the channel after destroying * its context */ // extern "C" int epicsShareAPI ca_clear_channel ( chid pChan ) { ca_client_context & cac = pChan->getClientCtx (); { epicsGuard < epicsMutex > guard ( cac.mutex ); try { pChan->eliminateExcessiveSendBacklog ( guard ); } catch ( cacChannel::notConnected & ) { // intentionally ignored } } if ( cac.pCallbackGuard.get() && cac.createdByThread == epicsThreadGetIdSelf () ) { epicsGuard < epicsMutex > guard ( cac.mutex ); pChan->destructor ( *cac.pCallbackGuard.get(), guard ); cac.oldChannelNotifyFreeList.release ( pChan ); } else { // // we will definately stall out here if all of the // following are true // // o user creates non-preemtive mode client library context // o user doesnt periodically call a ca function // o user calls this function from an auxiillary thread // CallbackGuard cbGuard ( cac.cbMutex ); epicsGuard < epicsMutex > guard ( cac.mutex ); pChan->destructor ( *cac.pCallbackGuard.get(), guard ); cac.oldChannelNotifyFreeList.release ( pChan ); } return ECA_NORMAL; } /* * Specify an event subroutine to be run for asynch exceptions */ // extern "C" int epicsShareAPI ca_add_exception_event ( caExceptionHandler *pfunc, void *arg ) { ca_client_context *pcac; int caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } pcac->changeExceptionEvent ( pfunc, arg ); return ECA_NORMAL; } /* * ca_add_masked_array_event */ int epicsShareAPI ca_add_masked_array_event ( chtype type, arrayElementCount count, chid pChan, caEventCallBackFunc *pCallBack, void *pCallBackArg, ca_real, ca_real, ca_real, evid *monixptr, long mask ) { return ca_create_subscription ( type, count, pChan, mask, pCallBack, pCallBackArg, monixptr ); } /* * ca_clear_event () */ int epicsShareAPI ca_clear_event ( evid pMon ) { return ca_clear_subscription ( pMon ); } // extern "C" chid epicsShareAPI ca_evid_to_chid ( evid pMon ) { return & pMon->channel (); } // extern "C" int epicsShareAPI ca_pend ( ca_real timeout, int early ) { if ( early ) { return ca_pend_io ( timeout ); } else { return ca_pend_event ( timeout ); } } /* * ca_pend_event () */ // extern "C" int epicsShareAPI ca_pend_event ( ca_real timeout ) { ca_client_context *pcac; int status = fetchClientContext ( &pcac ); if ( status != ECA_NORMAL ) { return status; } try { // preserve past odd ball behavior of waiting forever when // the delay is zero if ( timeout == 0.0 ) { while ( true ) { pcac->pendEvent ( 60.0 ); } } return pcac->pendEvent ( timeout ); } catch ( ... ) { return ECA_INTERNAL; } } /* * ca_pend_io () */ // extern "C" int epicsShareAPI ca_pend_io ( ca_real timeout ) { ca_client_context *pcac; int status = fetchClientContext ( &pcac ); if ( status != ECA_NORMAL ) { return status; } try { // preserve past odd ball behavior of waiting forever when // the delay is zero if ( timeout == 0.0 ) { return pcac->pendIO ( DBL_MAX ); } return pcac->pendIO ( timeout ); } catch ( ... ) { return ECA_INTERNAL; } } /* * ca_flush_io () */ int epicsShareAPI ca_flush_io () { ca_client_context * pcac; int caStatus = fetchClientContext (&pcac); if ( caStatus != ECA_NORMAL ) { return caStatus; } epicsGuard < epicsMutex > guard ( pcac->mutex ); pcac->flush ( guard ); return ECA_NORMAL; } /* * CA_TEST_IO () */ int epicsShareAPI ca_test_io () { ca_client_context *pcac; int caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } if ( pcac->ioComplete () ) { return ECA_IODONE; } else{ return ECA_IOINPROGRESS; } } /* * CA_SIGNAL() */ // extern "C" void epicsShareAPI ca_signal ( long ca_status, const char *message ) { ca_signal_with_file_and_lineno ( ca_status, message, NULL, 0 ); } /* * ca_message (long ca_status) * * - if it is an unknown error code then it possible * that the error string generated below * will be overwritten before (or while) the caller * of this routine is calling this routine * (if they call this routine again). */ // extern "C" const char * epicsShareAPI ca_message ( long ca_status ) { unsigned msgNo = CA_EXTRACT_MSG_NO ( ca_status ); if ( msgNo < NELEMENTS (ca_message_text) ) { return ca_message_text[msgNo]; } else { return "new CA message number known only by server - see caerr.h"; } } /* * ca_signal_with_file_and_lineno() */ // extern "C" void epicsShareAPI ca_signal_with_file_and_lineno ( long ca_status, const char *message, const char *pfilenm, int lineno ) { ca_signal_formated ( ca_status, pfilenm, lineno, message ); } /* * ca_signal_formated() */ // extern "C" void epicsShareAPI ca_signal_formated ( long ca_status, const char *pfilenm, int lineno, const char *pFormat, ... ) { ca_client_context *pcac; if ( caClientContextId ) { pcac = ( ca_client_context * ) epicsThreadPrivateGet ( caClientContextId ); } else { pcac = 0; } va_list theArgs; va_start ( theArgs, pFormat ); if ( pcac ) { pcac->vSignal ( ca_status, pfilenm, lineno, pFormat, theArgs ); } else { fprintf ( stderr, "CA exception in thread w/o CA ctx: status=%s file=%s line=%d: \n", ca_message ( ca_status ), pfilenm, lineno ); if ( pFormat ) { vfprintf ( stderr, pFormat, theArgs ); } } va_end ( theArgs ); } /* * CA_ADD_FD_REGISTRATION * * call their function with their argument whenever * a new fd is added or removed * (for a manager of the select system call under UNIX) * */ // extern "C" int epicsShareAPI ca_add_fd_registration ( CAFDHANDLER * func, void * arg ) { ca_client_context *pcac; int caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } pcac->registerForFileDescriptorCallBack ( func, arg ); return ECA_NORMAL; } /* * ca_version() * function that returns the CA version string */ // extern "C" const char * epicsShareAPI ca_version () { return CA_VERSION_STRING ( CA_MINOR_PROTOCOL_REVISION ); } /* * ca_replace_printf_handler () */ // extern "C" int epicsShareAPI ca_replace_printf_handler ( caPrintfFunc *ca_printf_func ) { ca_client_context *pcac; int caStatus = fetchClientContext (&pcac); if ( caStatus != ECA_NORMAL ) { return caStatus; } pcac->replaceErrLogHandler ( ca_printf_func ); return ECA_NORMAL; } /* * ca_get_ioc_connection_count() * * returns the number of IOC's that CA is connected to * (for testing purposes only) */ // extern "C" unsigned epicsShareAPI ca_get_ioc_connection_count () { ca_client_context * pcac; int caStatus = fetchClientContext ( & pcac ); if ( caStatus != ECA_NORMAL ) { return 0u; } return pcac->circuitCount (); } unsigned epicsShareAPI ca_beacon_anomaly_count () { ca_client_context * pcac; int caStatus = fetchClientContext ( & pcac ); if ( caStatus != ECA_NORMAL ) { return 0u; } return pcac->beaconAnomaliesSinceProgramStart (); } // extern "C" int epicsShareAPI ca_channel_status ( epicsThreadId /* tid */ ) { ::printf ("The R3.14 EPICS OS abstraction API does not allow peeking at thread private storage of another thread.\n"); ::printf ("Please call \"ca_client_status ( unsigned level )\" from the subsystem specific diagnostic code.\n"); return ECA_ANACHRONISM; } // extern "C" int epicsShareAPI ca_client_status ( unsigned level ) { ca_client_context *pcac; int caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } pcac->show ( level ); return ECA_NORMAL; } int epicsShareAPI ca_context_status ( ca_client_context * pcac, unsigned level ) { pcac->show ( level ); return ECA_NORMAL; } /* * ca_current_context () * * used when an auxillary thread needs to join a CA client context started * by another thread */ // extern "C" struct ca_client_context * epicsShareAPI ca_current_context () { struct ca_client_context *pCtx; if ( caClientContextId ) { pCtx = ( struct ca_client_context * ) epicsThreadPrivateGet ( caClientContextId ); } else { pCtx = 0; } return pCtx; } /* * ca_attach_context () * * used when an auxillary thread needs to join a CA client context started * by another thread */ // extern "C" int epicsShareAPI ca_attach_context ( struct ca_client_context * pCtx ) { ca_client_context *pcac = (ca_client_context *) epicsThreadPrivateGet ( caClientContextId ); if ( pcac && pCtx != 0 ) { return ECA_ISATTACHED; } if ( ! pCtx->preemptiveCallbakIsEnabled() ) { return ECA_NOTTHREADED; } epicsThreadPrivateSet ( caClientContextId, pCtx ); return ECA_NORMAL; } void epicsShareAPI ca_detach_context () { if ( caClientContextId ) { epicsThreadPrivateSet ( caClientContextId, 0 ); } } int epicsShareAPI ca_preemtive_callback_is_enabled () { ca_client_context *pcac = (ca_client_context *) epicsThreadPrivateGet ( caClientContextId ); if ( ! pcac ) { return 0; } return pcac->preemptiveCallbakIsEnabled (); } // extern "C" void epicsShareAPI ca_self_test () { ca_client_context *pcac = (ca_client_context *) epicsThreadPrivateGet ( caClientContextId ); if ( ! pcac ) { return; } pcac->selfTest (); } // extern "C" epicsShareDef const int epicsTypeToDBR_XXXX [lastEpicsType+1] = { DBR_SHORT, /* forces conversion fronm uint8 to int16 */ DBR_CHAR, DBR_SHORT, DBR_LONG, /* forces conversion fronm uint16 to int32 */ DBR_ENUM, DBR_LONG, DBR_LONG, /* very large unsigned number will not map */ DBR_FLOAT, DBR_DOUBLE, DBR_STRING, DBR_STRING }; // extern "C" epicsShareDef const epicsType DBR_XXXXToEpicsType [LAST_BUFFER_TYPE+1] = { epicsOldStringT, epicsInt16T, epicsFloat32T, epicsEnum16T, epicsUInt8T, epicsInt32T, epicsFloat64T, epicsOldStringT, epicsInt16T, epicsFloat32T, epicsEnum16T, epicsUInt8T, epicsInt32T, epicsFloat64T, epicsOldStringT, epicsInt16T, epicsFloat32T, epicsEnum16T, epicsUInt8T, epicsInt32T, epicsFloat64T, epicsOldStringT, epicsInt16T, epicsFloat32T, epicsEnum16T, epicsUInt8T, epicsInt32T, epicsFloat64T, epicsOldStringT, epicsInt16T, epicsFloat32T, epicsEnum16T, epicsUInt8T, epicsInt32T, epicsFloat64T, epicsUInt16T, epicsUInt16T, epicsOldStringT, epicsOldStringT }; // extern "C" epicsShareDef const unsigned short dbr_size[LAST_BUFFER_TYPE+1] = { sizeof(dbr_string_t), /* string max size */ sizeof(dbr_short_t), /* short */ sizeof(dbr_float_t), /* IEEE Float */ sizeof(dbr_enum_t), /* item number */ sizeof(dbr_char_t), /* character */ sizeof(dbr_long_t), /* long */ sizeof(dbr_double_t), /* double */ sizeof(struct dbr_sts_string), /* string field with status */ sizeof(struct dbr_sts_short), /* short field with status */ sizeof(struct dbr_sts_float), /* float field with status */ sizeof(struct dbr_sts_enum), /* item number with status */ sizeof(struct dbr_sts_char), /* char field with status */ sizeof(struct dbr_sts_long), /* long field with status */ sizeof(struct dbr_sts_double), /* double field with time */ sizeof(struct dbr_time_string), /* string field with time */ sizeof(struct dbr_time_short), /* short field with time */ sizeof(struct dbr_time_float), /* float field with time */ sizeof(struct dbr_time_enum), /* item number with time */ sizeof(struct dbr_time_char), /* char field with time */ sizeof(struct dbr_time_long), /* long field with time */ sizeof(struct dbr_time_double), /* double field with time */ sizeof(struct dbr_sts_string), /* graphic string info */ sizeof(struct dbr_gr_short), /* graphic short info */ sizeof(struct dbr_gr_float), /* graphic float info */ sizeof(struct dbr_gr_enum), /* graphic item info */ sizeof(struct dbr_gr_char), /* graphic char info */ sizeof(struct dbr_gr_long), /* graphic long info */ sizeof(struct dbr_gr_double), /* graphic double info */ sizeof(struct dbr_sts_string), /* control string info */ sizeof(struct dbr_ctrl_short), /* control short info */ sizeof(struct dbr_ctrl_float), /* control float info */ sizeof(struct dbr_ctrl_enum), /* control item info */ sizeof(struct dbr_ctrl_char), /* control char info */ sizeof(struct dbr_ctrl_long), /* control long info */ sizeof(struct dbr_ctrl_double), /* control double info */ sizeof(dbr_put_ackt_t), /* put ackt */ sizeof(dbr_put_acks_t), /* put acks */ sizeof(struct dbr_stsack_string),/* string field with status/ack*/ sizeof(dbr_string_t), /* string max size */ }; // extern "C" epicsShareDef const unsigned short dbr_value_size[LAST_BUFFER_TYPE+1] = { sizeof(dbr_string_t), /* string max size */ sizeof(dbr_short_t), /* short */ sizeof(dbr_float_t), /* IEEE Float */ sizeof(dbr_enum_t), /* item number */ sizeof(dbr_char_t), /* character */ sizeof(dbr_long_t), /* long */ sizeof(dbr_double_t), /* double */ sizeof(dbr_string_t), /* string max size */ sizeof(dbr_short_t), /* short */ sizeof(dbr_float_t), /* IEEE Float */ sizeof(dbr_enum_t), /* item number */ sizeof(dbr_char_t), /* character */ sizeof(dbr_long_t), /* long */ sizeof(dbr_double_t), /* double */ sizeof(dbr_string_t), /* string max size */ sizeof(dbr_short_t), /* short */ sizeof(dbr_float_t), /* IEEE Float */ sizeof(dbr_enum_t), /* item number */ sizeof(dbr_char_t), /* character */ sizeof(dbr_long_t), /* long */ sizeof(dbr_double_t), /* double */ sizeof(dbr_string_t), /* string max size */ sizeof(dbr_short_t), /* short */ sizeof(dbr_float_t), /* IEEE Float */ sizeof(dbr_enum_t), /* item number */ sizeof(dbr_char_t), /* character */ sizeof(dbr_long_t), /* long */ sizeof(dbr_double_t), /* double */ sizeof(dbr_string_t), /* string max size */ sizeof(dbr_short_t), /* short */ sizeof(dbr_float_t), /* IEEE Float */ sizeof(dbr_enum_t), /* item number */ sizeof(dbr_char_t), /* character */ sizeof(dbr_long_t), /* long */ sizeof(dbr_double_t), /* double */ sizeof(dbr_ushort_t), /* put_ackt */ sizeof(dbr_ushort_t), /* put_acks */ sizeof(dbr_string_t), /* string max size */ sizeof(dbr_string_t), /* string max size */ }; //extern "C" epicsShareDef const enum dbr_value_class dbr_value_class[LAST_BUFFER_TYPE+1] = { dbr_class_string, /* string max size */ dbr_class_int, /* short */ dbr_class_float, /* IEEE Float */ dbr_class_int, /* item number */ dbr_class_int, /* character */ dbr_class_int, /* long */ dbr_class_float, /* double */ dbr_class_string, /* string max size */ dbr_class_int, /* short */ dbr_class_float, /* IEEE Float */ dbr_class_int, /* item number */ dbr_class_int, /* character */ dbr_class_int, /* long */ dbr_class_float, /* double */ dbr_class_string, /* string max size */ dbr_class_int, /* short */ dbr_class_float, /* IEEE Float */ dbr_class_int, /* item number */ dbr_class_int, /* character */ dbr_class_int, /* long */ dbr_class_float, /* double */ dbr_class_string, /* string max size */ dbr_class_int, /* short */ dbr_class_float, /* IEEE Float */ dbr_class_int, /* item number */ dbr_class_int, /* character */ dbr_class_int, /* long */ dbr_class_float, /* double */ dbr_class_string, /* string max size */ dbr_class_int, /* short */ dbr_class_float, /* IEEE Float */ dbr_class_int, /* item number */ dbr_class_int, /* character */ dbr_class_int, /* long */ dbr_class_float, /* double */ dbr_class_int, dbr_class_int, dbr_class_string, dbr_class_string, /* string max size */ }; // extern "C" epicsShareDef const unsigned short dbr_value_offset[LAST_BUFFER_TYPE+1] = { 0, /* string */ 0, /* short */ 0, /* IEEE Float */ 0, /* item number */ 0, /* character */ 0, /* long */ 0, /* IEEE double */ (unsigned short) offsetof(dbr_sts_string,value[0]),/* string field with status */ (unsigned short) offsetof(dbr_sts_short,value), /* short field with status */ (unsigned short) offsetof(dbr_sts_float,value), /* float field with status */ (unsigned short) offsetof(dbr_sts_enum,value), /* item number with status */ (unsigned short) offsetof(dbr_sts_char,value), /* char field with status */ (unsigned short) offsetof(dbr_sts_long,value), /* long field with status */ (unsigned short) offsetof(dbr_sts_double,value), /* double field with time */ (unsigned short) offsetof(dbr_time_string,value[0] ),/* string field with time */ (unsigned short) offsetof(dbr_time_short,value), /* short field with time */ (unsigned short) offsetof(dbr_time_float,value), /* float field with time */ (unsigned short) offsetof(dbr_time_enum,value), /* item number with time */ (unsigned short) offsetof(dbr_time_char,value), /* char field with time */ (unsigned short) offsetof(dbr_time_long,value), /* long field with time */ (unsigned short) offsetof(dbr_time_double,value), /* double field with time */ (unsigned short) offsetof(dbr_sts_string,value[0]),/* graphic string info */ (unsigned short) offsetof(dbr_gr_short,value), /* graphic short info */ (unsigned short) offsetof(dbr_gr_float,value), /* graphic float info */ (unsigned short) offsetof(dbr_gr_enum,value), /* graphic item info */ (unsigned short) offsetof(dbr_gr_char,value), /* graphic char info */ (unsigned short) offsetof(dbr_gr_long,value), /* graphic long info */ (unsigned short) offsetof(dbr_gr_double,value), /* graphic double info */ (unsigned short) offsetof(dbr_sts_string,value[0]),/* control string info */ (unsigned short) offsetof(dbr_ctrl_short,value), /* control short info */ (unsigned short) offsetof(dbr_ctrl_float,value), /* control float info */ (unsigned short) offsetof(dbr_ctrl_enum,value), /* control item info */ (unsigned short) offsetof(dbr_ctrl_char,value), /* control char info */ (unsigned short) offsetof(dbr_ctrl_long,value), /* control long info */ (unsigned short) offsetof(dbr_ctrl_double,value), /* control double info */ 0, /* put ackt */ 0, /* put acks */ (unsigned short) offsetof(dbr_stsack_string,value[0]),/* string field with status */ 0, /* string */ }; // extern "C" epicsShareDef const char *dbf_text[LAST_TYPE+3] = { "TYPENOTCONN", "DBF_STRING", "DBF_SHORT", "DBF_FLOAT", "DBF_ENUM", "DBF_CHAR", "DBF_LONG", "DBF_DOUBLE", "DBF_NO_ACCESS" }; // extern "C" epicsShareDef const char *dbf_text_invalid = "DBF_invalid"; // extern "C" epicsShareDef const short dbf_text_dim = (sizeof dbf_text)/(sizeof (char *)); // extern "C" epicsShareDef const char *dbr_text[LAST_BUFFER_TYPE+1] = { "DBR_STRING", "DBR_SHORT", "DBR_FLOAT", "DBR_ENUM", "DBR_CHAR", "DBR_LONG", "DBR_DOUBLE", "DBR_STS_STRING", "DBR_STS_SHORT", "DBR_STS_FLOAT", "DBR_STS_ENUM", "DBR_STS_CHAR", "DBR_STS_LONG", "DBR_STS_DOUBLE", "DBR_TIME_STRING", "DBR_TIME_SHORT", "DBR_TIME_FLOAT", "DBR_TIME_ENUM", "DBR_TIME_CHAR", "DBR_TIME_LONG", "DBR_TIME_DOUBLE", "DBR_GR_STRING", "DBR_GR_SHORT", "DBR_GR_FLOAT", "DBR_GR_ENUM", "DBR_GR_CHAR", "DBR_GR_LONG", "DBR_GR_DOUBLE", "DBR_CTRL_STRING", "DBR_CTRL_SHORT", "DBR_CTRL_FLOAT", "DBR_CTRL_ENUM", "DBR_CTRL_CHAR", "DBR_CTRL_LONG", "DBR_CTRL_DOUBLE", "DBR_PUT_ACKT", "DBR_PUT_ACKS", "DBR_STSACK_STRING", "DBR_CLASS_NAME" }; // extern "C" epicsShareDef const char *dbr_text_invalid = "DBR_invalid"; // extern "C" epicsShareDef const short dbr_text_dim = (sizeof dbr_text) / (sizeof (char *)) + 1; base-7.0.3.1/modules/ca/src/client/acctst.c0000664000577000060420000032623413557101274017152 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * CA regression test * Authors: * Jeff Hill * Murali Shankar - initial versions of verifyMultithreadSubscr * Michael Abbott - initial versions of multiSubscrDestroyNoLateCallbackTest * */ /* * ANSI */ #include #include #include #include #include /* * EPICS */ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "epicsAssert.h" #include "epicsMutex.h" #include "epicsEvent.h" #include "epicsTime.h" #include "dbDefs.h" #include "envDefs.h" #include "caDiagnostics.h" #include "cadef.h" #include "fdmgr.h" #include "epicsExit.h" typedef struct appChan { char name[64]; chid channel; evid subscription; unsigned char connected; unsigned char accessRightsHandlerInstalled; unsigned subscriptionUpdateCount; unsigned accessUpdateCount; unsigned connectionUpdateCount; unsigned getCallbackCount; } appChan; unsigned subscriptionUpdateCount; unsigned accessUpdateCount; unsigned connectionUpdateCount; unsigned getCallbackCount; static epicsTimeStamp showProgressBeginTime; static const double timeoutToPendIO = 1e20; #define verify(exp) ((exp) ? (void)0 : \ epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) void showProgressBegin ( const char *pTestName, unsigned interestLevel ) { if ( interestLevel > 0 ) { if ( interestLevel > 1 ) { printf ( "%s ", pTestName ); epicsTimeGetCurrent ( & showProgressBeginTime ); } printf ( "{" ); } fflush ( stdout ); } void showProgressEnd ( unsigned interestLevel ) { if ( interestLevel > 0 ) { printf ( "}" ); if ( interestLevel > 1 ) { epicsTimeStamp showProgressEndTime; double delay; epicsTimeGetCurrent ( & showProgressEndTime ); delay = epicsTimeDiffInSeconds ( &showProgressEndTime, &showProgressBeginTime ); printf ( " %f sec\n", delay ); } else { fflush ( stdout ); } } } void showProgress ( unsigned interestLevel ) { if ( interestLevel > 0 ) { printf ( "." ); fflush ( stdout ); } } void nUpdatesTester ( struct event_handler_args args ) { if ( args.status == ECA_NORMAL ) { unsigned *pCtr = (unsigned *) args.usr; ( *pCtr ) ++; } else { printf ( "subscription update failed for \"%s\" because \"%s\"", ca_name ( args.chid ), ca_message ( args.status ) ); } } void monitorSubscriptionFirstUpdateTest ( const char *pName, chid chan, unsigned interestLevel ) { int status; struct dbr_ctrl_double currentVal; double delta; unsigned eventCount = 0u; unsigned waitCount = 0u; evid id; chid chan2; showProgressBegin ( "monitorSubscriptionFirstUpdateTest", interestLevel ); /* * verify that the first event arrives (with evid) * and channel connected */ status = ca_add_event ( DBR_FLOAT, chan, nUpdatesTester, &eventCount, &id ); SEVCHK ( status, 0 ); ca_flush_io (); epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ while ( eventCount < 1 && waitCount++ < 100 ) { printf ( "e" ); fflush ( stdout ); epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } verify ( eventCount > 0 ); /* clear any knowledge of old gets */ ca_pend_io ( 1e-5 ); /* verify that a ca_put() produces an update, but */ /* this may fail if there is an unusual deadband */ status = ca_get ( DBR_CTRL_DOUBLE, chan, ¤tVal ); SEVCHK ( status, NULL ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, NULL ); eventCount = 0u; waitCount = 0u; delta = ( currentVal.upper_ctrl_limit - currentVal.lower_ctrl_limit ) / 4.0; if ( delta <= 0.0 ) { delta = 100.0; } if ( currentVal.value + delta < currentVal.upper_ctrl_limit ) { currentVal.value += delta; } else { currentVal.value -= delta; } status = ca_put ( DBR_DOUBLE, chan, ¤tVal.value ); SEVCHK ( status, NULL ); ca_flush_io (); epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ while ( eventCount < 1 && waitCount++ < 100 ) { printf ( "p" ); fflush ( stdout ); epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } verify ( eventCount > 0 ); status = ca_clear_event ( id ); SEVCHK (status, 0); /* * verify that the first event arrives (w/o evid) * and when channel initially disconnected */ eventCount = 0u; waitCount = 0u; status = ca_search ( pName, &chan2 ); SEVCHK ( status, 0 ); status = ca_add_event ( DBR_FLOAT, chan2, nUpdatesTester, &eventCount, 0 ); SEVCHK ( status, 0 ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK (status, 0); epicsThreadSleep ( 0.1 ); ca_poll (); while ( eventCount < 1 && waitCount++ < 100 ) { printf ( "w" ); fflush ( stdout ); epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } verify ( eventCount > 0 ); /* verify that a ca_put() produces an update, but */ /* this may fail if there is an unusual deadband */ status = ca_get ( DBR_CTRL_DOUBLE, chan2, ¤tVal ); SEVCHK ( status, NULL ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, NULL ); eventCount = 0u; waitCount = 0u; delta = ( currentVal.upper_ctrl_limit - currentVal.lower_ctrl_limit ) / 4.0; if ( delta <= 0.0 ) { delta = 100.0; } if ( currentVal.value + delta < currentVal.upper_ctrl_limit ) { currentVal.value += delta; } else { currentVal.value -= delta; } status = ca_put ( DBR_DOUBLE, chan2, ¤tVal.value ); SEVCHK ( status, NULL ); ca_flush_io (); epicsThreadSleep ( 0.1 ); ca_poll (); while ( eventCount < 1 && waitCount++ < 100 ) { printf ( "t" ); fflush ( stdout ); epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } verify ( eventCount > 0 ); /* clean up */ status = ca_clear_channel ( chan2 ); SEVCHK ( status, 0 ); showProgressEnd ( interestLevel ); } void ioTesterGet ( struct event_handler_args args ) { if ( args.status == ECA_NORMAL ) { unsigned *pCtr = (unsigned *) args.usr; ( *pCtr ) ++; } else { printf("get call back failed for \"%s\" because \"%s\"", ca_name ( args.chid ), ca_message ( args.status ) ); } } void ioTesterEvent ( struct event_handler_args args ) { if ( args.status == ECA_NORMAL ) { int status; status = ca_get_callback ( DBR_STS_STRING, args.chid, ioTesterGet, args.usr ); SEVCHK ( status, 0 ); } else { printf ( "subscription update failed for \"%s\" because \"%s\"", ca_name ( args.chid ), ca_message ( args.status ) ); } } void verifyMonitorSubscriptionFlushIO ( chid chan, unsigned interestLevel ) { int status; unsigned eventCount = 0u; unsigned waitCount = 0u; evid id; showProgressBegin ( "verifyMonitorSubscriptionFlushIO", interestLevel ); /* * verify that the first event arrives */ status = ca_add_event ( DBR_FLOAT, chan, nUpdatesTester, &eventCount, &id ); SEVCHK (status, 0); ca_flush_io (); epicsThreadSleep ( 0.1 ); ca_poll (); while ( eventCount < 1 && waitCount++ < 100 ) { printf ( "-" ); fflush ( stdout ); epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } verify ( eventCount > 0 ); status = ca_clear_event ( id ); SEVCHK (status, 0); showProgressEnd ( interestLevel ); } void accessRightsStateChange ( struct access_rights_handler_args args ) { appChan *pChan = (appChan *) ca_puser ( args.chid ); verify ( pChan->channel == args.chid ); verify ( args.ar.read_access == ca_read_access ( args.chid ) ); verify ( args.ar.write_access == ca_write_access ( args.chid ) ); accessUpdateCount++; pChan->accessUpdateCount++; } void getCallbackStateChange ( struct event_handler_args args ) { appChan *pChan = (appChan *) args.usr; verify ( pChan->channel == args.chid ); verify ( pChan->connected ); if ( args.status != ECA_NORMAL ) { printf ( "getCallbackStateChange abnormal status was \"%s\"\n", ca_message ( args.status ) ); verify ( args.status == ECA_NORMAL ); } getCallbackCount++; pChan->getCallbackCount++; } void connectionStateChange ( struct connection_handler_args args ) { int status; appChan *pChan = (appChan *) ca_puser ( args.chid ); verify ( pChan->channel == args.chid ); if ( args.op == CA_OP_CONN_UP ) { if ( pChan->accessRightsHandlerInstalled ) { verify ( pChan->accessUpdateCount > 0u ); } verify ( ! pChan->connected ); pChan->connected = 1; status = ca_get_callback ( DBR_STS_STRING, args.chid, getCallbackStateChange, pChan ); SEVCHK (status, 0); } else if ( args.op == CA_OP_CONN_DOWN ) { verify ( pChan->connected ); pChan->connected = 0u; verify ( ! ca_read_access ( args.chid ) ); verify ( ! ca_write_access ( args.chid ) ); } else { verify ( 0 ); } pChan->connectionUpdateCount++; connectionUpdateCount++; } void subscriptionStateChange ( struct event_handler_args args ) { struct dbr_sts_string * pdbrgs = ( struct dbr_sts_string * ) args.dbr; appChan *pChan = (appChan *) args.usr; verify ( args.status == ECA_NORMAL ); verify ( pChan->channel == args.chid ); verify ( pChan->connected ); verify ( args.type == DBR_STS_STRING ); verify ( strlen ( pdbrgs->value ) <= MAX_STRING_SIZE ); pChan->subscriptionUpdateCount++; subscriptionUpdateCount++; } void noopSubscriptionStateChange ( struct event_handler_args args ) { if ( args.status != ECA_NORMAL ) { printf ( "noopSubscriptionStateChange: subscription update failed for \"%s\" because \"%s\"", ca_name ( args.chid ), ca_message ( args.status ) ); } } /* * verifyConnectionHandlerConnect () * * 1) verify that connection handler runs during connect * * 2) verify that access rights handler runs during connect * * 3) verify that get call back runs from connection handler * (and that they are not required to flush in the connection handler) * * 4) verify that first event callback arrives after connect * * 5) verify subscription can be cleared before channel is cleared * * 6) verify subscription can be cleared by clearing the channel * * 7) verify that a nill access rights handler can be installed */ void verifyConnectionHandlerConnect ( appChan *pChans, unsigned chanCount, unsigned repetitionCount, unsigned interestLevel ) { int status; unsigned i, j; showProgressBegin ( "verifyConnectionHandlerConnect", interestLevel ); for ( i = 0; i < repetitionCount; i++ ) { subscriptionUpdateCount = 0u; accessUpdateCount = 0u; connectionUpdateCount = 0u; getCallbackCount = 0u; for ( j = 0u; j < chanCount; j++ ) { pChans[j].subscriptionUpdateCount = 0u; pChans[j].accessUpdateCount = 0u; pChans[j].accessRightsHandlerInstalled = 0; pChans[j].connectionUpdateCount = 0u; pChans[j].getCallbackCount = 0u; pChans[j].connected = 0u; status = ca_search_and_connect ( pChans[j].name, &pChans[j].channel, connectionStateChange, &pChans[j] ); SEVCHK ( status, NULL ); status = ca_replace_access_rights_event ( pChans[j].channel, accessRightsStateChange ); SEVCHK ( status, NULL ); pChans[j].accessRightsHandlerInstalled = 1; status = ca_add_event ( DBR_STS_STRING, pChans[j].channel, subscriptionStateChange, &pChans[j], &pChans[j].subscription ); SEVCHK ( status, NULL ); verify ( ca_test_io () == ECA_IODONE ); } ca_flush_io (); showProgress ( interestLevel ); while ( connectionUpdateCount < chanCount || getCallbackCount < chanCount ) { epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } for ( j = 0u; j < chanCount; j++ ) { verify ( pChans[j].getCallbackCount == 1u); verify ( pChans[j].connectionUpdateCount > 0 ); if ( pChans[j].connectionUpdateCount > 1u ) { printf ("Unusual connection activity count = %u on channel %s?\n", pChans[j].connectionUpdateCount, pChans[j].name ); } verify ( pChans[j].accessUpdateCount > 0 ); if ( pChans[j].accessUpdateCount > 1u ) { printf ("Unusual access rights activity count = %u on channel %s?\n", pChans[j].connectionUpdateCount, pChans[j].name ); } } ca_self_test (); showProgress ( interestLevel ); for ( j = 0u; j < chanCount; j += 2 ) { status = ca_clear_event ( pChans[j].subscription ); SEVCHK ( status, NULL ); } ca_self_test (); showProgress ( interestLevel ); for ( j = 0u; j < chanCount; j++ ) { status = ca_replace_access_rights_event ( pChans[j].channel, 0 ); SEVCHK ( status, NULL ); } for ( j = 0u; j < chanCount; j++ ) { status = ca_clear_channel ( pChans[j].channel ); SEVCHK ( status, NULL ); } ca_self_test (); showProgress ( interestLevel ); } showProgressEnd ( interestLevel ); } /* * verifyBlockingConnect () * * 1) verify that we dont print a disconnect message when * we delete the last channel * * 2) verify that we delete the connection to the IOC * when the last channel is deleted. * * 3) verify channel connection state variables * * 4) verify ca_test_io () and ca_pend_io () work with * channels w/o connection handlers * * 5) verify that the pending IO count is properly * maintained when we are add/removing a connection * handler * * 6) verify that the pending IO count goes to zero * if the channel is deleted before it connects. */ void verifyBlockingConnect ( appChan *pChans, unsigned chanCount, unsigned repetitionCount, unsigned interestLevel ) { int status; unsigned i, j; unsigned connections; unsigned backgroundConnCount = ca_get_ioc_connection_count (); showProgressBegin ( "verifyBlockingConnect", interestLevel ); i = 0; while ( backgroundConnCount > 1u ) { backgroundConnCount = ca_get_ioc_connection_count (); verify ( i++ < 10 ); printf ( "Z" ); fflush ( stdout ); epicsThreadSleep ( 1.0 ); } for ( i = 0; i < repetitionCount; i++ ) { for ( j = 0u; j < chanCount; j++ ) { pChans[j].subscriptionUpdateCount = 0u; pChans[j].accessUpdateCount = 0u; pChans[j].accessRightsHandlerInstalled = 0; pChans[j].connectionUpdateCount = 0u; pChans[j].getCallbackCount = 0u; pChans[j].connected = 0u; status = ca_search_and_connect ( pChans[j].name, &pChans[j].channel, NULL, &pChans[j] ); SEVCHK ( status, NULL ); if ( ca_state ( pChans[j].channel ) == cs_conn ) { verify ( VALID_DB_REQ ( ca_field_type ( pChans[j].channel ) ) ); } else { verify ( INVALID_DB_REQ ( ca_field_type ( pChans[j].channel ) ) ); verify ( ca_test_io () == ECA_IOINPROGRESS ); } status = ca_replace_access_rights_event ( pChans[j].channel, accessRightsStateChange ); SEVCHK ( status, NULL ); pChans[j].accessRightsHandlerInstalled = 1; } showProgress ( interestLevel ); for ( j = 0u; j < chanCount; j += 2 ) { status = ca_change_connection_event ( pChans[j].channel, connectionStateChange ); SEVCHK ( status, NULL ); } for ( j = 0u; j < chanCount; j += 2 ) { status = ca_change_connection_event ( pChans[j].channel, NULL ); SEVCHK ( status, NULL ); } for ( j = 0u; j < chanCount; j += 2 ) { status = ca_change_connection_event ( pChans[j].channel, connectionStateChange ); SEVCHK ( status, NULL ); } for ( j = 0u; j < chanCount; j += 2 ) { status = ca_change_connection_event ( pChans[j].channel, NULL ); SEVCHK ( status, NULL ); } status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, NULL ); ca_self_test (); showProgress ( interestLevel ); verify ( ca_test_io () == ECA_IODONE ); connections = ca_get_ioc_connection_count (); verify ( connections == backgroundConnCount ); for ( j = 0u; j < chanCount; j++ ) { verify ( VALID_DB_REQ ( ca_field_type ( pChans[j].channel ) ) ); verify ( ca_state ( pChans[j].channel ) == cs_conn ); SEVCHK ( ca_clear_channel ( pChans[j].channel ), NULL ); } ca_self_test (); ca_flush_io (); showProgress ( interestLevel ); /* * verify that connections to IOC's that are * not in use are dropped */ if ( ca_get_ioc_connection_count () != backgroundConnCount ) { epicsThreadSleep ( 0.1 ); ca_poll (); j=0; while ( ca_get_ioc_connection_count () != backgroundConnCount ) { epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ verify ( ++j < 100 ); } } showProgress ( interestLevel ); } for ( j = 0u; j < chanCount; j++ ) { status = ca_search ( pChans[j].name, &pChans[j].channel ); SEVCHK ( status, NULL ); } for ( j = 0u; j < chanCount; j++ ) { status = ca_clear_channel ( pChans[j].channel ); SEVCHK ( status, NULL ); } verify ( ca_test_io () == ECA_IODONE ); /* * verify ca_pend_io() does not see old search requests * (that did not specify a connection handler) */ status = ca_search_and_connect ( pChans[0].name, &pChans[0].channel, NULL, NULL); SEVCHK ( status, NULL ); if ( ca_state ( pChans[0].channel ) == cs_never_conn ) { /* force an early timeout */ status = ca_pend_io ( 1e-16 ); if ( status == ECA_TIMEOUT ) { /* * we end up here if the channel isnt on the same host */ epicsThreadSleep ( 0.1 ); ca_poll (); if ( ca_state( pChans[0].channel ) != cs_conn ) { while ( ca_state ( pChans[0].channel ) != cs_conn ) { epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } } status = ca_search_and_connect ( pChans[1].name, &pChans[1].channel, NULL, NULL ); SEVCHK ( status, NULL ); status = ca_pend_io ( 1e-16 ); if ( status != ECA_TIMEOUT ) { verify ( ca_state ( pChans[1].channel ) == cs_conn ); } status = ca_clear_channel ( pChans[1].channel ); SEVCHK ( status, NULL ); } else { verify ( ca_state( pChans[0].channel ) == cs_conn ); } } status = ca_clear_channel( pChans[0].channel ); SEVCHK ( status, NULL ); ca_self_test (); showProgressEnd ( interestLevel ); } /* * 1) verify that use of NULL evid does not cause problems * 2) verify clear before connect */ void verifyClear ( appChan *pChans, unsigned interestLevel ) { int status; showProgressBegin ( "verifyClear", interestLevel ); /* * verify channel clear before connect */ status = ca_search ( pChans[0].name, &pChans[0].channel ); SEVCHK ( status, NULL ); status = ca_clear_channel ( pChans[0].channel ); SEVCHK ( status, NULL ); /* * verify subscription clear before connect * and verify that NULL evid does not cause failure */ status = ca_search ( pChans[0].name, &pChans[0].channel ); SEVCHK ( status, NULL ); SEVCHK ( status, NULL ); status = ca_add_event ( DBR_GR_DOUBLE, pChans[0].channel, noopSubscriptionStateChange, NULL, NULL ); SEVCHK ( status, NULL ); status = ca_clear_channel ( pChans[0].channel ); SEVCHK ( status, NULL ); showProgressEnd ( interestLevel ); } /* * grEnumTest */ void grEnumTest ( chid chan, unsigned interestLevel ) { struct dbr_gr_enum ge; unsigned count; int status; unsigned i; showProgressBegin ( "grEnumTest", interestLevel ); ge.no_str = -1; status = ca_get (DBR_GR_ENUM, chan, &ge); SEVCHK (status, "DBR_GR_ENUM ca_get()"); status = ca_pend_io (timeoutToPendIO); verify (status == ECA_NORMAL); verify ( ge.no_str >= 0 && ge.no_str < NELEMENTS(ge.strs) ); if ( ge.no_str > 0 ) { printf ("Enum state str = {"); count = (unsigned) ge.no_str; for (i=0; i epsilon ) { printf ( "float test failed val written %f\n", fval ); printf ( "float test failed val read %f\n", fretval ); verify (0); } fval += increment; } } /* * doubleTest () */ void doubleTest ( chid chan, dbr_double_t beginValue, dbr_double_t increment, dbr_double_t epsilon, unsigned iterations) { unsigned i; dbr_double_t fval; dbr_double_t fretval; int status; fval = beginValue; for ( i = 0; i < iterations; i++ ) { fretval = DBL_MAX; status = ca_put ( DBR_DOUBLE, chan, &fval ); SEVCHK ( status, NULL ); status = ca_get ( DBR_DOUBLE, chan, &fretval ); SEVCHK ( status, NULL ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, NULL ); if ( fabs ( fval - fretval ) > epsilon ) { printf ( "double test failed val written %f\n", fval ); printf ( "double test failed val read %f\n", fretval ); verify ( 0 ); } fval += increment; } } /* * Verify that we can write and then read back * the same analog value */ void verifyAnalogIO ( chid chan, int dataType, double min, double max, int minExp, int maxExp, double epsilon, unsigned interestLevel ) { int i; double incr; double epsil; double base; unsigned iter; if ( ! ca_write_access ( chan ) ) { printf ("skipped analog test - no write access\n"); return; } if ( dbr_value_class[ca_field_type ( chan )] != dbr_class_float ) { printf ("skipped analog test - not an analog type\n"); return; } showProgressBegin ( "verifyAnalogIO", interestLevel ); epsil = epsilon * 4.0; base = min; for ( i = minExp; i <= maxExp; i += maxExp / 10 ) { incr = ldexp ( 0.5, i ); if ( fabs (incr) > max /10.0 ) { iter = ( unsigned ) ( max / fabs (incr) ); } else { iter = 10u; } if ( dataType == DBR_FLOAT ) { floatTest ( chan, (dbr_float_t) base, (dbr_float_t) incr, (dbr_float_t) epsil, iter ); } else if (dataType == DBR_DOUBLE ) { doubleTest ( chan, (dbr_double_t) base, (dbr_double_t) incr, (dbr_double_t) epsil, iter ); } else { verify ( 0 ); } } base = max; for ( i = minExp; i <= maxExp; i += maxExp / 10 ) { incr = - ldexp ( 0.5, i ); if ( fabs (incr) > max / 10.0 ) { iter = (unsigned) ( max / fabs (incr) ); } else { iter = 10u; } if ( dataType == DBR_FLOAT ) { floatTest ( chan, (dbr_float_t) base, (dbr_float_t) incr, (dbr_float_t) epsil, iter ); } else if (dataType == DBR_DOUBLE ) { doubleTest ( chan, (dbr_double_t) base, (dbr_double_t) incr, (dbr_double_t) epsil, iter ); } else { verify ( 0 ); } } base = - max; for ( i = minExp; i <= maxExp; i += maxExp / 10 ) { incr = ldexp ( 0.5, i ); if ( fabs (incr) > max / 10.0 ) { iter = (unsigned) ( max / fabs ( incr ) ); } else { iter = 10l; } if ( dataType == DBR_FLOAT ) { floatTest ( chan, (dbr_float_t) base, (dbr_float_t) incr, (dbr_float_t) epsil, iter ); } else if (dataType == DBR_DOUBLE ) { doubleTest ( chan, (dbr_double_t) base, (dbr_double_t) incr, (dbr_double_t) epsil, iter ); } else { verify ( 0 ); } } showProgressEnd ( interestLevel ); } /* * Verify that we can write and then read back * the same DBR_LONG value */ void verifyLongIO ( chid chan, unsigned interestLevel ) { int status; dbr_long_t iter, rdbk, incr; struct dbr_ctrl_long cl; if ( ca_write_access ( chan ) ) { return; } status = ca_get ( DBR_CTRL_LONG, chan, &cl ); SEVCHK ( status, "control long fetch failed\n" ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "control long pend failed\n" ); incr = ( cl.upper_ctrl_limit - cl.lower_ctrl_limit ); if ( incr >= 1 ) { showProgressBegin ( "verifyLongIO", interestLevel ); incr /= 1000; if ( incr == 0 ) { incr = 1; } for ( iter = cl.lower_ctrl_limit; iter <= cl.upper_ctrl_limit; iter+=incr ) { ca_put ( DBR_LONG, chan, &iter ); ca_get ( DBR_LONG, chan, &rdbk ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "get pend failed\n" ); verify ( iter == rdbk ); } showProgressEnd ( interestLevel ); } else { printf ( "strange limits configured for channel \"%s\"\n", ca_name (chan) ); } } /* * Verify that we can write and then read back * the same DBR_SHORT value */ void verifyShortIO ( chid chan, unsigned interestLevel ) { int status; dbr_short_t iter, rdbk, incr; struct dbr_ctrl_short cl; if ( ca_write_access ( chan ) ) { return; } status = ca_get ( DBR_CTRL_SHORT, chan, &cl ); SEVCHK ( status, "control short fetch failed\n" ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "control short pend failed\n" ); incr = (dbr_short_t) ( cl.upper_ctrl_limit - cl.lower_ctrl_limit ); if ( incr >= 1 ) { showProgressBegin ( "verifyShortIO", interestLevel ); incr /= 1000; if ( incr == 0 ) { incr = 1; } for ( iter = (dbr_short_t) cl.lower_ctrl_limit; iter <= (dbr_short_t) cl.upper_ctrl_limit; iter = (dbr_short_t) (iter + incr) ) { ca_put ( DBR_SHORT, chan, &iter ); ca_get ( DBR_SHORT, chan, &rdbk ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "get pend failed\n" ); verify ( iter == rdbk ); } showProgressEnd ( interestLevel ); } else { printf ( "Strange limits configured for channel \"%s\"\n", ca_name (chan) ); } } void verifyHighThroughputRead ( chid chan, unsigned interestLevel ) { int status; unsigned i; /* * verify we dont jam up on many uninterrupted * solicitations */ if ( ca_read_access (chan) ) { dbr_float_t temp; showProgressBegin ( "verifyHighThroughputRead", interestLevel ); for ( i=0; i<10000; i++ ) { status = ca_get ( DBR_FLOAT, chan, &temp ); SEVCHK ( status ,NULL ); } status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, NULL ); showProgressEnd ( interestLevel ); } else { printf ( "Skipped highthroughput read test - no read access\n" ); } } void verifyHighThroughputWrite ( chid chan, unsigned interestLevel ) { int status; unsigned i; if (ca_write_access ( chan ) ) { showProgressBegin ( "verifyHighThroughputWrite", interestLevel ); for ( i=0; i<10000; i++ ) { dbr_double_t fval = 3.3; status = ca_put ( DBR_DOUBLE, chan, &fval ); SEVCHK ( status, NULL ); } SEVCHK ( ca_pend_io (timeoutToPendIO), NULL ); showProgressEnd ( interestLevel ); } else{ printf("Skipped multiple put test - no write access\n"); } } /* * verify we dont jam up on many uninterrupted * get callback requests */ void verifyHighThroughputReadCallback ( chid chan, unsigned interestLevel ) { unsigned i; int status; if ( ca_read_access ( chan ) ) { unsigned count = 0u; showProgressBegin ( "verifyHighThroughputReadCallback", interestLevel ); for ( i=0; i<10000; i++ ) { status = ca_array_get_callback ( DBR_FLOAT, 1, chan, nUpdatesTester, &count ); SEVCHK ( status, NULL ); } SEVCHK ( ca_flush_io (), NULL ); while ( count < 10000u ) { epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } showProgressEnd ( interestLevel ); } else { printf ( "Skipped multiple get cb test - no read access\n" ); } } /* * verify we dont jam up on many uninterrupted * put callback request */ void verifyHighThroughputWriteCallback ( chid chan, unsigned interestLevel ) { unsigned i; int status; if ( ca_write_access (chan) && ca_v42_ok (chan) ) { unsigned count = 0u; dbr_double_t dval; showProgressBegin ( "verifyHighThroughputWriteCallback", interestLevel ); for ( i=0; i<10000; i++ ) { dval = i + 1; status = ca_array_put_callback ( DBR_DOUBLE, 1, chan, &dval, nUpdatesTester, &count ); SEVCHK ( status, NULL ); } SEVCHK ( ca_flush_io (), NULL ); dval = 0.0; status = ca_get ( DBR_DOUBLE, chan, &dval ); SEVCHK ( status, "verifyHighThroughputWriteCallback, verification get" ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "verifyHighThroughputWriteCallback, verification get pend" ); verify ( dval == i ); showProgressEnd ( interestLevel ); } else { printf ( "Skipped multiple put cb test - no write access\n" ); } } void verifyBadString ( chid chan, unsigned interestLevel ) { int status; /* * verify that we detect that a large string has been written */ if ( ca_write_access (chan) ) { dbr_string_t stimStr; dbr_string_t respStr; showProgressBegin ( "verifyBadString", interestLevel ); memset (stimStr, 'a', sizeof (stimStr) ); status = ca_array_put ( DBR_STRING, 1u, chan, stimStr ); verify ( status != ECA_NORMAL ); sprintf ( stimStr, "%u", 8u ); status = ca_array_put ( DBR_STRING, 1u, chan, stimStr ); verify ( status == ECA_NORMAL ); status = ca_array_get ( DBR_STRING, 1u, chan, respStr ); verify ( status == ECA_NORMAL ); status = ca_pend_io ( timeoutToPendIO ); verify ( status == ECA_NORMAL ); if ( strcmp ( stimStr, respStr ) ) { printf ( "Test fails if stim \"%s\" isnt roughly equiv to resp \"%s\"\n", stimStr, respStr); } showProgressEnd ( interestLevel ); } else { printf ( "Skipped bad string test - no write access\n" ); } } /* * multiple_sg_requests() */ void multiple_sg_requests ( chid chix, CA_SYNC_GID gid ) { int status; unsigned i; static dbr_float_t fvalput = 3.3F; static dbr_float_t fvalget; for ( i=0; i < 1000; i++ ) { if ( ca_write_access (chix) ){ status = ca_sg_array_put ( gid, DBR_FLOAT, 1, chix, &fvalput); SEVCHK ( status, NULL ); } if ( ca_read_access (chix) ) { status = ca_sg_array_get ( gid, DBR_FLOAT, 1, chix, &fvalget); SEVCHK ( status, NULL ); } } } /* * test_sync_groups() */ void test_sync_groups ( chid chan, unsigned interestLevel ) { int status; CA_SYNC_GID gid1=0; CA_SYNC_GID gid2=0; if ( ! ca_v42_ok ( chan ) ) { printf ( "skipping sync group test - server is on wrong version\n" ); } showProgressBegin ( "test_sync_groups", interestLevel ); status = ca_sg_create ( &gid1 ); SEVCHK ( status, NULL ); multiple_sg_requests ( chan, gid1 ); status = ca_sg_reset ( gid1 ); SEVCHK ( status, NULL ); status = ca_sg_create ( &gid2 ); SEVCHK ( status, NULL ); multiple_sg_requests ( chan, gid2 ); multiple_sg_requests ( chan, gid1 ); status = ca_sg_test ( gid2 ); SEVCHK ( status, "SYNC GRP2" ); status = ca_sg_test ( gid1 ); SEVCHK ( status, "SYNC GRP1" ); status = ca_sg_block ( gid1, 500.0 ); SEVCHK ( status, "SYNC GRP1" ); status = ca_sg_block ( gid2, 500.0 ); SEVCHK ( status, "SYNC GRP2" ); status = ca_sg_delete ( gid2 ); SEVCHK (status, NULL); status = ca_sg_create ( &gid2 ); SEVCHK (status, NULL); multiple_sg_requests ( chan, gid1 ); multiple_sg_requests ( chan, gid2 ); status = ca_sg_block ( gid1, 500.0 ); SEVCHK ( status, "SYNC GRP1" ); status = ca_sg_block ( gid2, 500.0 ); SEVCHK ( status, "SYNC GRP2" ); status = ca_sg_delete ( gid1 ); SEVCHK ( status, NULL ); status = ca_sg_delete ( gid2 ); SEVCHK ( status, NULL ); showProgressEnd ( interestLevel ); } #define multiSubscrDestroyNoLateCallbackEventCount 500 struct MultiSubscrDestroyNoLateCallbackEventData { evid m_id; size_t m_nCallback; int m_callbackIsOk; struct MultiSubscrDestroyNoLateCallbackTestData * m_pTestData; }; struct MultiSubscrDestroyNoLateCallbackTestData { const char * m_pChanName; chid m_chan; epicsMutexId m_mutex; epicsEventId m_testDoneEvent; unsigned m_interestLevel; struct MultiSubscrDestroyNoLateCallbackEventData m_eventData [multiSubscrDestroyNoLateCallbackEventCount]; }; static void noLateCallbackDetect ( struct event_handler_args args ) { int callbackIsOk; struct MultiSubscrDestroyNoLateCallbackEventData * const pEventData = args.usr; epicsMutexLockStatus lockStatus = epicsMutexLock ( pEventData->m_pTestData->m_mutex ); callbackIsOk = pEventData->m_callbackIsOk; pEventData->m_nCallback++; epicsMutexUnlock ( pEventData->m_pTestData->m_mutex ); verify ( lockStatus == epicsMutexLockOK ); verify ( callbackIsOk ); } static void multiSubscrDestroyNoLateCallbackThread ( void * pParm ) { struct MultiSubscrDestroyNoLateCallbackTestData * const pTestData = ( struct MultiSubscrDestroyNoLateCallbackTestData * ) pParm; unsigned i, j; int status; status = ca_context_create ( ca_enable_preemptive_callback ); verify ( status == ECA_NORMAL ); status = ca_create_channel ( pTestData->m_pChanName, 0, 0, CA_PRIORITY_DEFAULT, &pTestData->m_chan ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "multiSubscrDestroyLateNoCallbackTest: channel connect failed" ); verify ( status == ECA_NORMAL ); /* * create a set of subscriptions */ for ( i=0; i < 10000; i++ ) { unsigned int priorityOfTestThread; for ( j=0; j < multiSubscrDestroyNoLateCallbackEventCount; j++ ) { epicsMutexLockStatus lockStatus = epicsMutexLock ( pTestData->m_mutex ); verify ( lockStatus == epicsMutexLockOK ); pTestData->m_eventData[j].m_nCallback = 0; pTestData->m_eventData[j].m_callbackIsOk = TRUE; pTestData->m_eventData[j].m_pTestData = pTestData; epicsMutexUnlock ( pTestData->m_mutex ); SEVCHK ( ca_add_event ( DBR_GR_FLOAT, pTestData->m_chan, noLateCallbackDetect, &pTestData->m_eventData[j], &pTestData->m_eventData[j].m_id ) , NULL ); } SEVCHK ( ca_flush_io(), NULL ); /* * raise the priority of the current thread hoping to improve our * likelyhood of detecting a bug */ priorityOfTestThread = epicsThreadGetPrioritySelf (); epicsThreadSetPriority ( epicsThreadGetIdSelf(), epicsThreadPriorityHigh ); /* * wait for the first subscription update to arrive */ { epicsMutexLockStatus lockStatus = epicsMutexLock ( pTestData->m_mutex ); verify ( lockStatus == epicsMutexLockOK ); while ( pTestData->m_eventData[0].m_nCallback == 0 ) { epicsMutexUnlock ( pTestData->m_mutex ); epicsThreadSleep ( 50e-6 ); lockStatus = epicsMutexLock ( pTestData->m_mutex ); verify ( lockStatus == epicsMutexLockOK ); } epicsMutexUnlock ( pTestData->m_mutex ); } /* * try to destroy all of the subscriptions at precisely the same time that * their first callbacks are running */ for ( j=0; j < multiSubscrDestroyNoLateCallbackEventCount; j++ ) { epicsMutexLockStatus lockStatus; SEVCHK ( ca_clear_event ( pTestData->m_eventData[j].m_id ) , NULL ); lockStatus = epicsMutexLock ( pTestData->m_mutex ); verify ( lockStatus == epicsMutexLockOK ); pTestData->m_eventData[j].m_callbackIsOk = FALSE; epicsMutexUnlock ( pTestData->m_mutex ); } /* * return to the original priority */ epicsThreadSetPriority ( epicsThreadGetIdSelf(), priorityOfTestThread ); if ( i % 1000 == 0 ) { showProgress ( pTestData->m_interestLevel ); } } SEVCHK ( ca_clear_channel ( pTestData->m_chan ), NULL ); ca_context_destroy (); epicsEventMustTrigger ( pTestData->m_testDoneEvent ); } /* * verify that, in a preemtive callback mode client, a subscription callback never * comes after the subscription is destroyed */ static void multiSubscrDestroyNoLateCallbackTest ( const char *pName, unsigned interestLevel ) { struct MultiSubscrDestroyNoLateCallbackTestData * pTestData; showProgressBegin ( "multiSubscrDestroyNoLateCallbackTest", interestLevel ); pTestData = calloc ( 1u, sizeof ( struct MultiSubscrDestroyNoLateCallbackTestData ) ); verify ( pTestData ); pTestData->m_mutex = epicsMutexMustCreate (); pTestData->m_testDoneEvent = epicsEventMustCreate ( epicsEventEmpty ); pTestData->m_pChanName = pName; pTestData->m_interestLevel = interestLevel; epicsThreadMustCreate ( "multiSubscrDestroyNoLateCallbackTest", epicsThreadPriorityLow, epicsThreadGetStackSize ( epicsThreadStackMedium ), multiSubscrDestroyNoLateCallbackThread, pTestData ); /* * wait for test to complete */ epicsEventMustWait ( pTestData->m_testDoneEvent ); /* * cleanup */ epicsMutexDestroy ( pTestData->m_mutex ); epicsEventDestroy ( pTestData->m_testDoneEvent ); free ( pTestData ); showProgressEnd ( interestLevel ); } /* * multiSubscriptionDeleteTest * * 1) verify we can add many monitors at once * 2) verify that under heavy load the last monitor * returned is the last modification sent * 3) attempt to delete monitors while many monitors * are running */ void multiSubscriptionDeleteTest ( chid chan, unsigned interestLevel ) { unsigned count = 0u; evid mid[1000]; dbr_float_t temp, getResp; unsigned i; showProgressBegin ( "multiSubscriptionDeleteTest", interestLevel ); for ( i=0; i < NELEMENTS (mid); i++ ) { SEVCHK ( ca_add_event ( DBR_GR_FLOAT, chan, noopSubscriptionStateChange, &count, &mid[i]) , NULL ); } /* * force all of the monitors subscription requests to * complete * * NOTE: this hopefully demonstrates that when the * server is very busy with monitors the client * is still able to punch through with a request. */ SEVCHK ( ca_get ( DBR_FLOAT,chan,&getResp ), NULL ); SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL ); showProgress ( interestLevel ); /* * attempt to generate heavy event traffic before initiating * the monitor delete */ if ( ca_write_access (chan) ) { for ( i=0; i < NELEMENTS (mid); i++ ) { temp = (float) i; SEVCHK ( ca_put (DBR_FLOAT, chan, &temp), NULL); } } showProgress ( interestLevel ); /* * without pausing begin deleting the event subscriptions * while the queue is full * * continue attempting to generate heavy event traffic * while deleting subscriptions with the hope that we will * deleting an event at the instant that its callback is * occurring */ for ( i=0; i < NELEMENTS (mid); i++ ) { if ( ca_write_access (chan) ) { temp = (float) i; SEVCHK ( ca_put (DBR_FLOAT, chan, &temp), NULL); } SEVCHK ( ca_clear_event ( mid[i]), NULL ); } showProgress ( interestLevel ); /* * force all of the clear event requests to complete */ SEVCHK ( ca_get (DBR_FLOAT,chan,&temp), NULL ); SEVCHK ( ca_pend_io (timeoutToPendIO), NULL ); showProgressEnd ( interestLevel ); } /* * singleSubscriptionDeleteTest * * verify that we dont fail when we repeatedly create * and delete only one subscription with a high level of * traffic on it */ void singleSubscriptionDeleteTest ( chid chan, unsigned interestLevel ) { unsigned count = 0u; evid sid; dbr_float_t temp, getResp; unsigned i; showProgressBegin ( "singleSubscriptionDeleteTest", interestLevel ); for ( i=0; i < 1000; i++ ){ count = 0u; SEVCHK ( ca_add_event ( DBR_GR_FLOAT, chan, noopSubscriptionStateChange, &count, &sid) , NULL ); if ( i % 100 == 0 ) { showProgress ( interestLevel ); } /* * force the subscription request to complete */ SEVCHK ( ca_get ( DBR_FLOAT, chan, &getResp ), NULL ); SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL ); /* * attempt to generate heavy event traffic before initiating * the monitor delete * * try to interrupt the recv thread while it is processing * incoming subscription updates */ if ( ca_write_access (chan) ) { unsigned j = 0; while ( j < i ) { temp = (float) j++; SEVCHK ( ca_put (DBR_FLOAT, chan, &temp), "singleSubscriptionDeleteTest - one of multiple puts" ); } ca_flush_io (); } SEVCHK ( ca_clear_event ( sid ), NULL ); } showProgressEnd ( interestLevel ); } /* * channelClearWithEventTrafficTest * * verify that we can delete a channel that has subcriptions * attached with heavy update traffic */ void channelClearWithEventTrafficTest ( const char *pName, unsigned interestLevel ) { unsigned count = 0u; evid sid; dbr_float_t temp, getResp; unsigned i; showProgressBegin ( "channelClearWithEventTrafficTest", interestLevel ); for ( i=0; i < 1000; i++ ) { chid chan; int status = ca_create_channel ( pName, 0, 0, CA_PRIORITY_DEFAULT, &chan ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "channelClearWithEventTrafficTest: channel connect failed" ); verify ( status == ECA_NORMAL ); count = 0u; SEVCHK ( ca_add_event ( DBR_GR_FLOAT, chan, noopSubscriptionStateChange, &count, &sid ) , NULL ); /* * force the subscription request to complete */ SEVCHK ( ca_get ( DBR_FLOAT, chan, &getResp ), NULL ); SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL ); /* * attempt to generate heavy event traffic before initiating * the channel delete * * try to interrupt the recv thread while it is processing * incoming subscription updates */ if ( ca_write_access (chan) ) { unsigned j = 0; while ( j < i ) { temp = (float) j++; SEVCHK ( ca_put (DBR_FLOAT, chan, &temp), NULL); } ca_flush_io (); epicsThreadSleep ( 0.001 ); } SEVCHK ( ca_clear_channel ( chan ), NULL ); } showProgressEnd ( interestLevel ); } evid globalEventID; void selfDeleteEvent ( struct event_handler_args args ) { int status; status = ca_clear_event ( globalEventID ); verify ( status == ECA_NORMAL ); } void eventClearTest ( chid chan ) { int status; evid monix1, monix2, monix3; status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, NULL, &monix1 ); SEVCHK ( status, NULL ); status = ca_clear_event ( monix1 ); SEVCHK ( status, NULL ); status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, NULL, &monix1 ); SEVCHK ( status, NULL ); status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, NULL, &monix2); SEVCHK (status, NULL); status = ca_clear_event ( monix2 ); SEVCHK ( status, NULL); status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, NULL, &monix2); SEVCHK ( status, NULL ); status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, NULL, &monix3); SEVCHK ( status, NULL ); status = ca_clear_event ( monix2 ); SEVCHK ( status, NULL); status = ca_clear_event ( monix1 ); SEVCHK ( status, NULL); status = ca_clear_event ( monix3 ); SEVCHK ( status, NULL); status = ca_add_event ( DBR_FLOAT, chan, selfDeleteEvent, 0, &globalEventID ); SEVCHK ( status, NULL ); } unsigned acctstExceptionCount = 0u; void acctstExceptionNotify ( struct exception_handler_args args ) { acctstExceptionCount++; } static unsigned arrayEventExceptionNotifyComplete = 0; void arrayEventExceptionNotify ( struct event_handler_args args ) { if ( args.status == ECA_NORMAL ) { printf ( "arrayEventExceptionNotify: expected " "exception but didnt receive one against \"%s\" \n", ca_name ( args.chid ) ); } else { arrayEventExceptionNotifyComplete = 1; } } void exceptionTest ( chid chan, unsigned interestLevel ) { int status; showProgressBegin ( "exceptionTest", interestLevel ); /* * force a get exception to occur */ { dbr_put_ackt_t *pRS; acctstExceptionCount = 0u; status = ca_add_exception_event ( acctstExceptionNotify, 0 ); SEVCHK ( status, "exception notify install failed" ); pRS = malloc ( ca_element_count (chan) * sizeof (*pRS) ); verify ( pRS ); status = ca_array_get ( DBR_PUT_ACKT, ca_element_count (chan), chan, pRS ); SEVCHK ( status, "array read request failed" ); ca_pend_io ( 1e-5 ); epicsThreadSleep ( 0.1 ); ca_poll (); while ( acctstExceptionCount < 1u ) { printf ( "G" ); fflush ( stdout ); epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } status = ca_add_exception_event ( 0, 0 ); SEVCHK ( status, "exception notify install failed" ); free ( pRS ); } /* * force a get call back exception to occur */ { arrayEventExceptionNotifyComplete = 0u; status = ca_array_get_callback ( DBR_PUT_ACKT, ca_element_count (chan), chan, arrayEventExceptionNotify, 0 ); if ( status != ECA_NORMAL ) { verify ( status == ECA_BADTYPE || status == ECA_GETFAIL ); arrayEventExceptionNotifyComplete = 1; } ca_flush_io (); epicsThreadSleep ( 0.1 ); ca_poll (); while ( ! arrayEventExceptionNotifyComplete ) { printf ( "GCB" ); fflush ( stdout ); epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } } /* * force a subscription exception to occur */ { evid id; arrayEventExceptionNotifyComplete = 0u; status = ca_add_array_event ( DBR_PUT_ACKT, ca_element_count ( chan ), chan, arrayEventExceptionNotify, 0, 0.0, 0.0, 0.0, &id ); if ( status != ECA_NORMAL ) { verify ( status == ECA_BADTYPE || status == ECA_GETFAIL ); arrayEventExceptionNotifyComplete = 1; } ca_flush_io (); epicsThreadSleep ( 0.1 ); ca_poll (); while ( ! arrayEventExceptionNotifyComplete ) { printf ( "S" ); fflush ( stdout ); epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } status = ca_clear_event ( id ); SEVCHK ( status, "subscription clear failed" ); } /* * force a put exception to occur */ { dbr_string_t * pWS; unsigned i; acctstExceptionCount = 0u; status = ca_add_exception_event ( acctstExceptionNotify, 0 ); SEVCHK ( status, "exception notify install failed" ); pWS = malloc ( ca_element_count (chan) * MAX_STRING_SIZE ); verify ( pWS ); for ( i = 0; i < ca_element_count (chan); i++ ) { strcpy ( pWS[i], "@#$%" ); } status = ca_array_put ( DBR_STRING, ca_element_count (chan), chan, pWS ); if ( status != ECA_NORMAL ) { verify ( status == ECA_BADTYPE || status == ECA_PUTFAIL ); acctstExceptionCount++; /* local PV case */ } ca_flush_io (); epicsThreadSleep ( 0.1 ); ca_poll (); while ( acctstExceptionCount < 1u ) { printf ( "P" ); fflush ( stdout ); epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } status = ca_add_exception_event ( 0, 0 ); SEVCHK ( status, "exception notify install failed" ); free ( pWS ); } /* * force a put callback exception to occur */ { dbr_string_t *pWS; unsigned i; pWS = malloc ( ca_element_count (chan) * MAX_STRING_SIZE ); verify ( pWS ); for ( i = 0; i < ca_element_count (chan); i++ ) { strcpy ( pWS[i], "@#$%" ); } arrayEventExceptionNotifyComplete = 0u; status = ca_array_put_callback ( DBR_STRING, ca_element_count (chan), chan, pWS, arrayEventExceptionNotify, 0); if ( status != ECA_NORMAL ) { arrayEventExceptionNotifyComplete = 1; } ca_flush_io (); epicsThreadSleep ( 0.1 ); ca_poll (); while ( ! arrayEventExceptionNotifyComplete ) { printf ( "PCB" ); fflush ( stdout ); epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } free ( pWS ); } showProgressEnd ( interestLevel ); } /* * array test * * verify that we can at least write and read back the same array * if multiple elements are present */ static unsigned arrayReadNotifyComplete = 0; static unsigned arrayWriteNotifyComplete = 0; void arrayReadNotify ( struct event_handler_args args ) { dbr_double_t *pWF = ( dbr_double_t * ) ( args.usr ); dbr_double_t *pRF = ( dbr_double_t * ) ( args.dbr ); int i; for ( i = 0; i < args.count; i++ ) { verify ( pWF[i] == pRF[i] ); } arrayReadNotifyComplete = 1; } void arrayWriteNotify ( struct event_handler_args args ) { if ( args.status == ECA_NORMAL ) { arrayWriteNotifyComplete = 1; } else { printf ( "arrayWriteNotify: update failed for \"%s\" because \"%s\"", ca_name ( args.chid ), ca_message ( args.status ) ); } } void arrayTest ( chid chan, unsigned maxArrayBytes, unsigned interestLevel ) { dbr_double_t *pRF, *pWF; unsigned i; int status; evid id; if ( ! ca_write_access ( chan ) ) { printf ( "skipping array test - no write access\n" ); return; } showProgressBegin ( "arrayTest", interestLevel ); pRF = (dbr_double_t *) calloc ( ca_element_count (chan), sizeof (*pRF) ); verify ( pRF != NULL ); pWF = (dbr_double_t *) calloc ( ca_element_count (chan), sizeof (*pWF) ); verify ( pWF != NULL ); /* * write some random numbers into the array */ for ( i = 0; i < ca_element_count (chan); i++ ) { pWF[i] = rand (); pRF[i] = - pWF[i]; } status = ca_array_put ( DBR_DOUBLE, ca_element_count ( chan ), chan, pWF ); SEVCHK ( status, "array write request failed" ); /* * read back the array */ status = ca_array_get ( DBR_DOUBLE, ca_element_count (chan), chan, pRF ); SEVCHK ( status, "array read request failed" ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "array read failed" ); /* * verify read response matches values written */ for ( i = 0; i < ca_element_count ( chan ); i++ ) { if ( pWF[i] != pRF[i] ) { printf ( "i=%u, pWF[i]=%f, pRF[i]=%f\n", i, pWF[i], pRF[i]); } verify ( pWF[i] == pRF[i] ); } /* * read back the array as strings */ { char *pRS; unsigned size = ca_element_count (chan) * MAX_STRING_SIZE; if ( size <= maxArrayBytes ) { pRS = malloc ( ca_element_count (chan) * MAX_STRING_SIZE ); verify ( pRS ); status = ca_array_get ( DBR_STRING, ca_element_count (chan), chan, pRS ); SEVCHK ( status, "array read request failed" ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "array read failed" ); free ( pRS ); } else { printf ( "skipping the fetch array in string data type test - does not fit\n" ); } } /* * write some random numbers into the array */ for ( i = 0; i < ca_element_count (chan); i++ ) { pWF[i] = rand (); pRF[i] = - pWF[i]; } status = ca_array_put_callback ( DBR_DOUBLE, ca_element_count (chan), chan, pWF, arrayWriteNotify, 0 ); SEVCHK ( status, "array write notify request failed" ); status = ca_array_get_callback ( DBR_DOUBLE, ca_element_count (chan), chan, arrayReadNotify, pWF ); SEVCHK ( status, "array read notify request failed" ); ca_flush_io (); while ( ! arrayWriteNotifyComplete || ! arrayReadNotifyComplete ) { epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } /* * write some random numbers into the array */ for ( i = 0; i < ca_element_count (chan); i++ ) { pWF[i] = rand (); pRF[i] = - pWF[i]; } arrayReadNotifyComplete = 0; status = ca_array_put ( DBR_DOUBLE, ca_element_count ( chan ), chan, pWF ); SEVCHK ( status, "array write notify request failed" ); status = ca_add_array_event ( DBR_DOUBLE, ca_element_count ( chan ), chan, arrayReadNotify, pWF, 0.0, 0.0, 0.0, &id ); SEVCHK ( status, "array subscription request failed" ); ca_flush_io (); while ( ! arrayReadNotifyComplete ) { epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } status = ca_clear_event ( id ); SEVCHK ( status, "clear event request failed" ); /* * a get request should fail or fill with zeros * when the array size is too large */ { acctstExceptionCount = 0u; status = ca_add_exception_event ( acctstExceptionNotify, 0 ); SEVCHK ( status, "exception notify install failed" ); status = ca_array_get ( DBR_DOUBLE, ca_element_count (chan)+1, chan, pRF ); if ( status == ECA_NORMAL ) { ca_poll (); while ( acctstExceptionCount < 1u ) { printf ( "N" ); fflush ( stdout ); epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ } } else { verify ( status == ECA_BADCOUNT ); } status = ca_add_exception_event ( 0, 0 ); SEVCHK ( status, "exception notify install failed" ); } free ( pRF ); free ( pWF ); showProgressEnd ( interestLevel ); } /* * verify that unequal send/recv buffer sizes work * (a bug related to this test was detected in early R3.14) * * this test must be run when no other channels are connected */ void unequalServerBufferSizeTest ( const char * pName, unsigned interestLevel ) { dbr_double_t *pRF, *pWF; unsigned connections; chid newChan; int status; showProgressBegin ( "unequalServerBufferSizeTest", interestLevel ); /* this test must be run when no channels are connected */ connections = ca_get_ioc_connection_count (); verify ( connections == 0u ); status = ca_create_channel ( pName, 0, 0, 0, & newChan ); verify ( status == ECA_NORMAL ); status = ca_pend_io ( timeoutToPendIO ); verify ( status == ECA_NORMAL ); showProgress ( interestLevel ); if ( ! ca_write_access ( newChan ) ) { printf ( "skipping unequal buffer size test - no write access\n" ); status = ca_clear_channel ( newChan ); verify ( status == ECA_NORMAL ); return; } pRF = (dbr_double_t *) calloc ( ca_element_count (newChan), sizeof (*pRF) ); verify ( pRF != NULL ); pWF = (dbr_double_t *) calloc ( ca_element_count (newChan), sizeof (*pWF) ); verify ( pWF != NULL ); status = ca_array_get ( DBR_DOUBLE, ca_element_count ( newChan ), newChan, pRF ); status = ca_pend_io ( timeoutToPendIO ); verify ( status == ECA_NORMAL ); status = ca_clear_channel ( newChan ); verify ( status == ECA_NORMAL ); showProgress ( interestLevel ); status = ca_create_channel ( pName, 0, 0, 0, &newChan ); verify ( status == ECA_NORMAL ); status = ca_pend_io ( timeoutToPendIO ); verify ( status == ECA_NORMAL ); showProgress ( interestLevel ); status = ca_array_put ( DBR_DOUBLE, ca_element_count ( newChan ), newChan, pWF ); verify ( status == ECA_NORMAL ); status = ca_array_get ( DBR_DOUBLE, 1, newChan, pRF ); verify ( status == ECA_NORMAL ); status = ca_pend_io ( timeoutToPendIO ); verify ( status == ECA_NORMAL ); status = ca_clear_channel ( newChan ); verify ( status == ECA_NORMAL ); free ( pRF ); free ( pWF ); showProgressEnd ( interestLevel ); } /* * pend_event_delay_test() */ void pend_event_delay_test ( dbr_double_t request ) { int status; epicsTimeStamp end_time; epicsTimeStamp start_time; dbr_double_t delay; dbr_double_t accuracy; epicsTimeGetCurrent ( &start_time ); status = ca_pend_event ( request ); if (status != ECA_TIMEOUT) { SEVCHK(status, NULL); } epicsTimeGetCurrent(&end_time); delay = epicsTimeDiffInSeconds ( &end_time, &start_time ); accuracy = 100.0*(delay-request)/request; printf ( "CA pend event delay = %f sec results in error = %f %%\n", request, accuracy ); verify ( fabs(accuracy) < 10.0 ); } void caTaskExitTest ( unsigned interestLevel ) { int status; showProgressBegin ( "caTaskExitTest", interestLevel ); status = ca_task_exit (); SEVCHK ( status, NULL ); showProgressEnd ( interestLevel ); } void verifyDataTypeMacros (void) { int type; type = dbf_type_to_DBR ( DBF_SHORT ); verify ( type == DBR_SHORT ); type = dbf_type_to_DBR_STS ( DBF_SHORT ); verify ( type == DBR_STS_SHORT ); type = dbf_type_to_DBR_GR ( DBF_SHORT ); verify ( type == DBR_GR_SHORT ); type = dbf_type_to_DBR_CTRL ( DBF_SHORT ); verify ( type == DBR_CTRL_SHORT ); type = dbf_type_to_DBR_TIME ( DBF_SHORT ); verify ( type == DBR_TIME_SHORT ); verify ( strcmp ( dbr_type_to_text( DBR_SHORT ), "DBR_SHORT" ) == 0 ); verify ( strcmp ( dbf_type_to_text( DBF_SHORT ), "DBF_SHORT" ) == 0 ); verify ( dbr_type_is_SHORT ( DBR_SHORT ) ); verify ( dbr_type_is_valid ( DBR_SHORT ) ); verify ( dbf_type_is_valid ( DBF_SHORT ) ); { int dataType = -1; dbf_text_to_type ( "DBF_SHORT", dataType ); verify ( dataType == DBF_SHORT ); dbr_text_to_type ( "DBR_CLASS_NAME", dataType ); verify ( dataType == DBR_CLASS_NAME ); } } typedef struct { evid id; dbr_float_t lastValue; unsigned count; } eventTest; /* * updateTestEvent () */ void updateTestEvent ( struct event_handler_args args ) { eventTest *pET = (eventTest *) args.usr; struct dbr_gr_float *pGF = (struct dbr_gr_float *) args.dbr; pET->lastValue = pGF->value; pET->count++; } dbr_float_t monitorUpdateTestPattern ( unsigned iter ) { return ( (float) iter ) * 10.12345f + 10.7f; } void callbackClearsChannel ( struct event_handler_args args ) { int status; status = ca_clear_channel ( args.chid ); SEVCHK ( status, "clearChannelInXxxxCallbackTest clear channel" ); } void clearChannelInGetCallbackTest ( const char *pName, unsigned level ) { unsigned i; chid chan; int status; showProgressBegin ( "clearChannelInGetCallbackTest", level ); for ( i = 0; ca_get_ioc_connection_count () > 0 ; i++ ) { ca_pend_event ( 0.1 ); verify ( i < 100 ); } status = ca_create_channel ( pName, 0, 0, 0, & chan ); SEVCHK ( status, "clearChannelInGetCallbackTest create channel" ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "clearChannelInGetCallbackTest connect channel" ); status = ca_get_callback ( DBR_DOUBLE, chan, callbackClearsChannel, 0 ); SEVCHK ( status, "clearChannelInGetCallbackTest get callback" ); for ( i = 0; ca_get_ioc_connection_count () > 0 ; i++ ) { ca_pend_event ( 0.1 ); verify ( i < 100 ); } showProgressEnd ( level ); } void clearChannelInPutCallbackTest ( const char *pName, unsigned level ) { unsigned i; const dbr_double_t value = 1.1; chid chan; int status; showProgressBegin ( "clearChannelInPutCallbackTest", level ); for ( i = 0; ca_get_ioc_connection_count () > 0 ; i++ ) { ca_pend_event ( 0.1 ); verify ( i < 100 ); } status = ca_create_channel ( pName, 0, 0, 0, & chan ); SEVCHK ( status, "clearChannelInPutCallbackTest create channel" ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "clearChannelInPutCallbackTest connect channel" ); status = ca_put_callback ( DBR_DOUBLE, chan, & value, callbackClearsChannel, 0 ); SEVCHK ( status, "clearChannelInPutCallbackTest get callback" ); for ( i = 0; ca_get_ioc_connection_count () > 0 ; i++ ) { ca_pend_event ( 0.1 ); verify ( i < 100 ); } showProgressEnd ( level ); } void clearChannelInSubscrCallbackTest ( const char *pName, unsigned level ) { unsigned i; chid chan; int status; showProgressBegin ( "clearChannelInSubscrCallbackTest", level ); for ( i = 0; ca_get_ioc_connection_count () > 0 ; i++ ) { ca_pend_event ( 0.1 ); verify ( i < 100 ); } status = ca_create_channel ( pName, 0, 0, 0, & chan ); SEVCHK ( status, "clearChannelInSubscrCallbackTest create channel" ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "clearChannelInSubscrCallbackTest connect channel" ); status = ca_create_subscription ( DBR_DOUBLE, 1, chan, DBE_VALUE, callbackClearsChannel, 0, 0 ); SEVCHK ( status, "clearChannelInSubscrCallbackTest subscribe" ); for ( i = 0; ca_get_ioc_connection_count () > 0 ; i++ ) { ca_pend_event ( 0.1 ); verify ( i < 100 ); } showProgressEnd ( level ); } void monitorAddConnectionCallback ( struct connection_handler_args args ) { if ( args.op == CA_OP_CONN_UP ) { unsigned * pEventCount = ( unsigned * ) ca_puser ( args.chid ); int status; verify ( *pEventCount == 0u ); (*pEventCount)++; status = ca_create_subscription ( DBR_DOUBLE, 1, args.chid, DBE_VALUE, nUpdatesTester, ca_puser ( args.chid ), 0 ); SEVCHK ( status, "monitorAddConnectionCallback create subscription" ); } else { fprintf ( stderr, "disconnect during monitorAddConnectionCallbackTest?\n" ); } } /* * monitorAddConnectionCallbackTest * 1) subscription add from within connection callback needs to work * 2) check for problems where subscription is installed twice if * its installed within the connection callback handler */ void monitorAddConnectionCallbackTest ( const char *pName, unsigned interestLevel ) { unsigned i; chid chan; int status; unsigned eventCount = 0u; unsigned getCallbackCount = 0u; showProgressBegin ( "monitorAddConnectionCallbackTest", interestLevel ); for ( i = 0; ca_get_ioc_connection_count () > 0 ; i++ ) { ca_pend_event ( 0.1 ); verify ( i < 100 ); } status = ca_create_channel ( pName, monitorAddConnectionCallback, &eventCount, 0, & chan ); SEVCHK ( status, "monitorAddConnectionCallbackTest create channel" ); while ( eventCount < 2 ) { ca_pend_event ( 0.1 ); } verify ( eventCount >= 2u ); status = ca_get_callback ( DBR_DOUBLE, chan, nUpdatesTester, &getCallbackCount ); SEVCHK ( status, "monitorAddConnectionCallback get callback" ); while ( getCallbackCount == 0 ) { ca_pend_event ( 0.1 ); } verify ( eventCount >= 2u ); verify ( getCallbackCount == 1u ); status = ca_clear_channel ( chan ); SEVCHK ( status, "monitorAddConnectionCallbackTest clear channel" ); status = ca_flush_io (); SEVCHK ( status, "monitorAddConnectionCallbackTest flush" ); showProgressEnd ( interestLevel ); } /* * monitorUpdateTest * * 1) verify we can add many monitors at once * 2) verify that under heavy load the last monitor * returned is the last modification sent */ void monitorUpdateTest ( chid chan, unsigned interestLevel ) { eventTest test[100]; dbr_float_t temp, getResp; unsigned i, j; unsigned flowCtrlCount = 0u; unsigned prevPassCount; unsigned tries; if ( ! ca_write_access ( chan ) ) { printf ("skipped monitorUpdateTest test - no write access\n"); return; } if ( dbr_value_class[ca_field_type ( chan )] != dbr_class_float ) { printf ("skipped monitorUpdateTest test - not an analog type\n"); return; } showProgressBegin ( "monitorUpdateTest", interestLevel ); /* * set channel to known value */ temp = 1; SEVCHK ( ca_put ( DBR_FLOAT, chan, &temp ), NULL ); for ( i = 0; i < NELEMENTS(test); i++ ) { test[i].count = 0; test[i].lastValue = -1.0f; SEVCHK(ca_add_event(DBR_GR_FLOAT, chan, updateTestEvent, &test[i], &test[i].id),NULL); } /* * force all of the monitors subscription requests to * complete * * NOTE: this hopefully demonstrates that when the * server is very busy with monitors the client * is still able to punch through with a request. */ SEVCHK ( ca_get ( DBR_FLOAT, chan, &getResp) ,NULL ); SEVCHK ( ca_pend_io ( timeoutToPendIO ) ,NULL ); showProgress ( interestLevel ); /* * pass the test only if we get the first monitor update */ tries = 0; while ( 1 ) { unsigned nFailed = 0u; epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ for ( i = 0; i < NELEMENTS ( test ); i++ ) { if ( test[i].count > 0 ) { if ( test[i].lastValue != temp ) { nFailed++; } } else { nFailed++; } } if ( nFailed == 0u ) { break; } printf ( "-" ); fflush ( stdout ); verify ( tries++ < 50 ); } showProgress ( interestLevel ); /* * attempt to uncover problems where the last event isnt sent * and hopefully get into a flow control situation */ prevPassCount = 0u; for ( i = 0; i < 10; i++ ) { for ( j = 0; j < NELEMENTS(test); j++ ) { SEVCHK ( ca_clear_event ( test[j].id ), NULL ); test[j].count = 0; test[j].lastValue = -1.0f; SEVCHK ( ca_add_event ( DBR_GR_FLOAT, chan, updateTestEvent, &test[j], &test[j].id ) , NULL ); } for ( j = 0; j <= i; j++ ) { temp = monitorUpdateTestPattern ( j ); SEVCHK ( ca_put ( DBR_FLOAT, chan, &temp ), NULL ); } /* * wait for the above to complete */ getResp = -1; SEVCHK ( ca_get ( DBR_FLOAT, chan, &getResp ), NULL ); SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL ); if ( getResp != temp ) { printf ( "getResp=%f, temp=%f\n", getResp, temp ); verify ( getResp == temp ); } /* * wait for all of the monitors to have correct values */ tries = 0; while (1) { unsigned passCount = 0; unsigned tmpFlowCtrlCount = 0u; epicsThreadSleep ( 0.1 ); ca_poll (); /* emulate typical GUI */ for ( j = 0; j < NELEMENTS ( test ); j++ ) { /* * we shouldnt see old monitors because * we resubscribed */ verify ( test[j].count <= i + 2 ); if ( test[j].lastValue == temp ) { if ( test[j].count < i + 1 ) { tmpFlowCtrlCount++; } passCount++; } } if ( passCount == NELEMENTS ( test ) ) { flowCtrlCount += tmpFlowCtrlCount; break; } if ( passCount == prevPassCount ) { verify ( tries++ < 500 ); if ( tries % 50 == 0 ) { for ( j = 0; j <= i; j++ ) { dbr_float_t pat = monitorUpdateTestPattern ( j ); if ( pat == test[0].lastValue ) { break; } } if ( j <= i) { printf ( "-(%d)", i-j ); } else { printf ( "." ); } fflush ( stdout ); } } prevPassCount = passCount; } } showProgress ( interestLevel ); /* * delete the event subscriptions */ for ( i = 0; i < NELEMENTS ( test ); i++ ) { SEVCHK ( ca_clear_event ( test[i].id ), NULL ); } /* * force all of the clear event requests to * complete */ SEVCHK ( ca_get ( DBR_FLOAT, chan, &temp ), NULL ); SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL ); /* printf ( "flow control bypassed %u events\n", flowCtrlCount ); */ showProgressEnd ( interestLevel ); } void verifyReasonableBeaconPeriod ( chid chan, unsigned interestLevel ) { if ( ca_get_ioc_connection_count () > 0 ) { double beaconPeriod; double watchDogDelay; unsigned i; showProgressBegin ( "verifyReasonableBeaconPeriod", interestLevel ); printf ( "Beacon anomalies detected since program start %u\n", ca_beacon_anomaly_count () ); beaconPeriod = ca_beacon_period ( chan ); printf ( "Estimated beacon period for channel %s = %g sec.\n", ca_name ( chan ), beaconPeriod ); watchDogDelay = ca_receive_watchdog_delay ( chan ); verify ( watchDogDelay >= 0.0 ); printf ( "busy: receive watchdog for \"%s\" expires in %g sec.\n", ca_name ( chan ), watchDogDelay ); /* * let one default connection timeout go by w/o receive activity * so we can see if beacons reset the watchdog */ for ( i = 0u; i < 15u; i++ ) { ca_pend_event ( 2.0 ); showProgress ( interestLevel ); } if ( interestLevel > 0 ) { printf ( "\n" ); } watchDogDelay = ca_receive_watchdog_delay ( chan ); verify ( watchDogDelay >= 0.0 ); printf ( "inactive: receive watchdog for \"%s\" expires in %g sec.\n", ca_name ( chan ), watchDogDelay ); showProgressEnd ( interestLevel ); } } void verifyOldPend ( unsigned interestLevel ) { int status; showProgressBegin ( "verifyOldPend", interestLevel ); /* * verify that the old ca_pend() is in the symbol table */ status = ca_pend ( 100000.0, 1 ); verify ( status == ECA_NORMAL ); status = ca_pend ( 1e-12, 0 ); verify ( status == ECA_TIMEOUT ); showProgressEnd ( interestLevel ); } void verifyTimeStamps ( chid chan, unsigned interestLevel ) { struct dbr_time_double first; struct dbr_time_double last; epicsTimeStamp localTime; char buf[128]; size_t length; double diff; int status; showProgressBegin ( "verifyTimeStamps", interestLevel ); status = epicsTimeGetCurrent ( & localTime ); verify ( status >= 0 ); status = ca_get ( DBR_TIME_DOUBLE, chan, & first ); SEVCHK ( status, "fetch of dbr time double failed\n" ); status = ca_pend_io ( timeoutToPendIO ); verify ( status == ECA_NORMAL ); status = ca_get ( DBR_TIME_DOUBLE, chan, & last ); SEVCHK ( status, "fetch of dbr time double failed\n" ); status = ca_pend_io ( timeoutToPendIO ); verify ( status == ECA_NORMAL ); length = epicsTimeToStrftime ( buf, sizeof ( buf ), "%a %b %d %Y %H:%M:%S.%f", & first.stamp ); verify ( length ); printf ("Processing time of channel \"%s\" was \"%s\"\n", ca_name ( chan ), buf ); diff = epicsTimeDiffInSeconds ( & last.stamp, & first.stamp ); printf ("Time difference between two successive reads was %g sec\n", diff ); diff = epicsTimeDiffInSeconds ( & first.stamp, & localTime ); printf ("Time difference between client and server %g sec\n", diff ); showProgressEnd ( interestLevel ); } /* * attempts to verify from the client side that * channel priorities work correctly */ void verifyChannelPriorities ( const char *pName, unsigned interestLevel ) { static const unsigned nPrio = 30; unsigned i; showProgressBegin ( "verifyChannelPriorities", interestLevel ); for ( i = 0u; i < nPrio; i++ ) { int status; double value; chid chan0, chan1; unsigned priority0, priority1; priority0 = ( i * ( CA_PRIORITY_MAX - CA_PRIORITY_MIN ) ) / nPrio; priority0 += CA_PRIORITY_MIN; if ( priority0 > CA_PRIORITY_MAX ) { priority0 = CA_PRIORITY_MAX; } priority1 = ( (i+1) * ( CA_PRIORITY_MAX - CA_PRIORITY_MIN ) ) / nPrio; priority1 += CA_PRIORITY_MIN; if ( priority1 > CA_PRIORITY_MAX ) { priority1 = CA_PRIORITY_MAX; } status = ca_create_channel ( pName, 0, 0, priority0, &chan0 ); SEVCHK ( status, "prioritized channel create failed" ); status = ca_create_channel ( pName, 0, 0, priority1, &chan1 ); SEVCHK ( status, "prioritized channel create failed" ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "prioritized channel connect failed" ); verify ( status == ECA_NORMAL ); value = i; status = ca_put ( DBR_DOUBLE, chan0, &value ); SEVCHK ( status, "prioritized channel put failed" ); status = ca_put ( DBR_DOUBLE, chan1, &value ); SEVCHK ( status, "prioritized channel put failed" ); status = ca_flush_io (); SEVCHK ( status, "prioritized channel flush failed" ); status = ca_get ( DBR_DOUBLE, chan0, &value ); SEVCHK ( status, "prioritized channel get failed" ); status = ca_get ( DBR_DOUBLE, chan1, &value ); SEVCHK ( status, "prioritized channel get failed" ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "prioritized channel pend io failed" ); status = ca_clear_channel ( chan0 ); SEVCHK ( status, "prioritized channel clear failed" ); status = ca_clear_channel ( chan1 ); SEVCHK ( status, "prioritized channel clear failed" ); } showProgressEnd ( interestLevel ); } void verifyTearDownWhenChannelConnected ( const char * pName, enum ca_preemptive_callback_select select, unsigned interestLevel ) { static const unsigned chanCount = 20; static const unsigned loopCount = 100; chid * const pChans = (chid * const) calloc ( chanCount, sizeof ( *pChans ) ); double * const pValues = (double * const) calloc ( chanCount, sizeof ( *pValues ) ); unsigned i, j; verify ( pChans && pValues ); showProgressBegin ( "verifyTearDownWhenChannelConnected", interestLevel ); for ( i = 0u; i < loopCount; i++ ) { int status; ca_context_create ( select ); for ( j = 0; j < chanCount; j++ ) { status = ca_create_channel ( pName, 0, 0, 0, & pChans[j] ); SEVCHK ( status, "immediate tear down channel create failed" ); } status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "immediate tear down channel connect failed" ); verify ( status == ECA_NORMAL ); for ( j = 0; j < chanCount; j++ ) { status = ca_get ( DBR_DOUBLE, pChans[j], &pValues[j] ); SEVCHK ( status, "immediate tear down channel get failed" ); } status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "immediate tear down get pend io failed" ); ca_context_destroy (); } ca_context_create ( select ); free ( pChans ); free ( pValues ); showProgressEnd ( interestLevel ); } void verifyImmediateTearDown ( const char * pName, enum ca_preemptive_callback_select select, unsigned interestLevel ) { int i; showProgressBegin ( "verifyImmediateTearDown", interestLevel ); for ( i = 0u; i < 100; i++ ) { chid chan; int status; dbr_long_t value = i % 8; ca_context_create ( select ); ca_task_initialize (); status = ca_create_channel ( pName, 0, 0, 0, & chan ); SEVCHK ( status, "immediate tear down channel create failed" ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "immediate tear down channel connect failed" ); verify ( status == ECA_NORMAL ); /* * verify that puts pending when we call ca_task_exit() * get flushed out */ if ( i > 0 ) { dbr_long_t currentValue = value; status = ca_get ( DBR_LONG, chan, & currentValue ); SEVCHK ( status, "immediate tear down channel get failed" ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "immediate tear down channel get failed" ); if ( currentValue != ( (i - 1) % 8 ) ) { printf ( "currentValue = %i, i = %i\n", currentValue, i ); verify ( currentValue == ( (i - 1) % 8 ) ); } } status = ca_put ( DBR_LONG, chan, & value ); SEVCHK ( status, "immediate tear down channel put failed" ); status = ca_clear_channel ( chan ); SEVCHK ( status, "immediate tear down channel clear failed" ); ca_context_destroy (); /* epicsThreadSleep ( 1e-15 ); */ if ( i % 10 == 0 ) { showProgress ( interestLevel ); } } ca_context_create ( select ); showProgressEnd ( interestLevel ); } /* * keeping these tests together detects a bug */ void eventClearAndMultipleMonitorTest ( chid chan, unsigned interestLevel ) { eventClearTest ( chan ); monitorUpdateTest ( chan, interestLevel ); } void fdcb ( void * parg ) { ca_poll (); } void fdRegCB ( void * parg, int fd, int opened ) { int status; fdctx * mgrCtx = ( fdctx * ) parg; if ( opened ) { status = fdmgr_add_callback ( mgrCtx, fd, fdi_read, fdcb, 0 ); verify ( status >= 0 ); } else { status = fdmgr_clear_callback ( mgrCtx, fd, fdi_read ); verify ( status >= 0 ); } } typedef struct { char m_chanName[100u]; struct ca_client_context * m_pCtx; chid m_chan; epicsMutexId m_mutex; epicsEventId m_testCompleteEvent; epicsEventId m_threadExitEvent; size_t m_nUpdatesReceived; size_t m_nUpdatesRequired; int m_testInitiated; int m_testComplete; unsigned m_interestLevel; } MultiThreadSubscrTest; static void testMultithreadSubscrSubscrCallback ( struct event_handler_args eha ) { const epicsEventId firstUpdateEvent = ( epicsEventId ) eha.usr; epicsEventSignal ( firstUpdateEvent ); } static void testMultithreadSubscrCreateSubscr ( void * pParm ) { static unsigned nElem = 0; int testComplete = FALSE; evid id; epicsEventId firstUpdateEvent; epicsEventWaitStatus eventWaitStatus; MultiThreadSubscrTest * const pMultiThreadSubscrTest = ( MultiThreadSubscrTest * ) pParm; /* this is required for the ca_flush below to work correctly */ int status = ca_attach_context ( pMultiThreadSubscrTest->m_pCtx ); verify ( status == ECA_NORMAL ); firstUpdateEvent = epicsEventMustCreate ( epicsEventEmpty ); verify ( firstUpdateEvent ); status = ca_create_subscription ( DBR_TIME_LONG, nElem, pMultiThreadSubscrTest->m_chan, DBE_VALUE, testMultithreadSubscrSubscrCallback, firstUpdateEvent, & id ); verify ( status == ECA_NORMAL ); status = ca_flush_io (); verify ( status == ECA_NORMAL ); /* wait for first update */ eventWaitStatus = epicsEventWaitWithTimeout ( firstUpdateEvent, 60.0 * 10 ); verify ( eventWaitStatus == epicsEventWaitOK ); epicsEventDestroy ( firstUpdateEvent ); status = ca_clear_subscription ( id ); verify ( status == ECA_NORMAL ); epicsMutexMustLock ( pMultiThreadSubscrTest->m_mutex ); pMultiThreadSubscrTest->m_nUpdatesReceived++; testComplete = ( pMultiThreadSubscrTest->m_nUpdatesReceived == pMultiThreadSubscrTest->m_nUpdatesRequired ); pMultiThreadSubscrTest->m_testComplete = testComplete; epicsMutexUnlock ( pMultiThreadSubscrTest->m_mutex ); if ( testComplete ) { epicsEventSignal ( pMultiThreadSubscrTest->m_testCompleteEvent ); } } void testMultithreadSubscrConnHandler ( struct connection_handler_args args ) { MultiThreadSubscrTest * const pMultiThreadSubscrTest = ( MultiThreadSubscrTest * ) ca_puser ( args.chid ); epicsMutexMustLock ( pMultiThreadSubscrTest->m_mutex ); if ( !pMultiThreadSubscrTest->m_testInitiated && args.op == CA_OP_CONN_UP ) { int i; pMultiThreadSubscrTest->m_testInitiated = TRUE; for ( i = 0; i < pMultiThreadSubscrTest->m_nUpdatesRequired; i++ ) { char threadname[64]; epicsThreadId threadId; sprintf(threadname, "testSubscr%06u", i); threadId = epicsThreadCreate ( threadname, epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackSmall), testMultithreadSubscrCreateSubscr, pMultiThreadSubscrTest ); verify ( threadId ); } } epicsMutexUnlock ( pMultiThreadSubscrTest->m_mutex ); } void testMultithreadSubscr ( void * pParm ) { MultiThreadSubscrTest * const pMultiThreadSubscrTest = ( MultiThreadSubscrTest * ) pParm; int status; unsigned i; status = ca_context_create ( ca_enable_preemptive_callback ); verify ( status == ECA_NORMAL ); pMultiThreadSubscrTest->m_pCtx = ca_current_context (); verify ( pMultiThreadSubscrTest->m_pCtx ); status = ca_create_channel ( pMultiThreadSubscrTest->m_chanName, testMultithreadSubscrConnHandler, pMultiThreadSubscrTest, CA_PRIORITY_MIN, & pMultiThreadSubscrTest->m_chan ); verify ( status == ECA_NORMAL ); showProgressBegin ( "verifyMultithreadSubscr", pMultiThreadSubscrTest->m_interestLevel ); i = 0; while ( TRUE ) { int success = FALSE; epicsEventWaitStatus eventWaitStatus; epicsMutexMustLock ( pMultiThreadSubscrTest->m_mutex ); success = pMultiThreadSubscrTest->m_testComplete; epicsMutexUnlock ( pMultiThreadSubscrTest->m_mutex ); if ( success ) { break; } eventWaitStatus = epicsEventWaitWithTimeout ( pMultiThreadSubscrTest->m_testCompleteEvent, 0.1 ); verify ( eventWaitStatus == epicsEventWaitOK || eventWaitStatus == epicsEventWaitTimeout ); if ( i++ % 100 == 0u ) showProgress ( pMultiThreadSubscrTest->m_interestLevel ); verify ( i < 1000 ); } showProgressEnd ( pMultiThreadSubscrTest->m_interestLevel ); status = ca_clear_channel ( pMultiThreadSubscrTest->m_chan ); verify ( status == ECA_NORMAL ); ca_context_destroy (); epicsEventSignal ( pMultiThreadSubscrTest->m_threadExitEvent ); } /* * test installation of subscriptions similar to usage paterns * employed by modern versions of the sequencer */ void verifyMultithreadSubscr ( const char * pName, unsigned interestLevel ) { static unsigned nSubscr = 3000; epicsThreadId threadId; MultiThreadSubscrTest * const pMultiThreadSubscrTest = (MultiThreadSubscrTest*) calloc ( 1, sizeof ( MultiThreadSubscrTest ) ); verify ( pMultiThreadSubscrTest); pMultiThreadSubscrTest->m_mutex = epicsMutexMustCreate (); verify ( pMultiThreadSubscrTest->m_mutex ); pMultiThreadSubscrTest->m_testCompleteEvent = epicsEventMustCreate ( epicsEventEmpty ); verify ( pMultiThreadSubscrTest->m_testCompleteEvent ); pMultiThreadSubscrTest->m_threadExitEvent = epicsEventMustCreate ( epicsEventEmpty ); verify ( pMultiThreadSubscrTest->m_threadExitEvent ); strncpy ( pMultiThreadSubscrTest->m_chanName, pName, sizeof ( pMultiThreadSubscrTest->m_chanName ) ); pMultiThreadSubscrTest->m_chanName [ sizeof ( pMultiThreadSubscrTest->m_chanName ) - 1u ] = '\0'; pMultiThreadSubscrTest->m_nUpdatesRequired = nSubscr; pMultiThreadSubscrTest->m_interestLevel = interestLevel; threadId = epicsThreadCreate ( "testMultithreadSubscr", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackSmall), testMultithreadSubscr, pMultiThreadSubscrTest ); verify ( threadId ); { epicsEventWaitStatus eventWaitStatus; eventWaitStatus = epicsEventWaitWithTimeout ( pMultiThreadSubscrTest->m_threadExitEvent, 1000.0 ); verify ( eventWaitStatus == epicsEventWaitOK ); } epicsEventDestroy ( pMultiThreadSubscrTest->m_testCompleteEvent ); epicsEventDestroy ( pMultiThreadSubscrTest->m_threadExitEvent ); epicsMutexDestroy ( pMultiThreadSubscrTest->m_mutex ); free ( pMultiThreadSubscrTest ); } void fdManagerVerify ( const char * pName, unsigned interestLevel ) { int status; fdctx * mgrCtx; struct timeval tmo; chid newChan; evid subscription; unsigned eventCount = 0u; epicsTimeStamp begin, end; mgrCtx = fdmgr_init (); verify ( mgrCtx ); showProgressBegin ( "fdManagerVerify", interestLevel ); status = ca_add_fd_registration ( fdRegCB, mgrCtx ); verify ( status == ECA_NORMAL ); status = ca_create_channel ( pName, 0, 0, 0, & newChan ); verify ( status == ECA_NORMAL ); while ( ca_state ( newChan ) != cs_conn ) { tmo.tv_sec = 6000; tmo.tv_usec = 0; status = fdmgr_pend_event ( mgrCtx, & tmo ); verify ( status >= 0 ); } showProgress ( interestLevel ); status = ca_add_event ( DBR_FLOAT, newChan, nUpdatesTester, & eventCount, & subscription ); verify ( status == ECA_NORMAL ); status = ca_flush_io (); verify ( status == ECA_NORMAL ); while ( eventCount < 1 ) { tmo.tv_sec = 6000; tmo.tv_usec = 0; status = fdmgr_pend_event ( mgrCtx, & tmo ); verify ( status >= 0 ); } showProgress ( interestLevel ); status = ca_clear_event ( subscription ); verify ( status == ECA_NORMAL ); status = ca_flush_io (); verify ( status == ECA_NORMAL ); /* look for infinite loop in fd manager schedualing */ epicsTimeGetCurrent ( & begin ); eventCount = 0u; while ( 1 ) { double delay; tmo.tv_sec = 1; tmo.tv_usec = 0; status = fdmgr_pend_event ( mgrCtx, & tmo ); verify ( status >= 0 ); epicsTimeGetCurrent ( & end ); delay = epicsTimeDiffInSeconds ( & end, & begin ); if ( delay >= 1.0 ) { break; } verify ( eventCount++ < 100 ); } showProgress ( interestLevel ); status = ca_clear_channel ( newChan ); verify ( status == ECA_NORMAL ); status = ca_add_fd_registration ( 0, 0 ); verify ( status == ECA_NORMAL ); status = fdmgr_delete ( mgrCtx ); verify ( status >= 0 ); showProgressEnd ( interestLevel ); } void verifyConnectWithDisconnectedChannels ( const char *pName, unsigned interestLevel ) { int status; chid bogusChan[300]; chid validChan; unsigned i; showProgressBegin ( "verifyConnectWithDisconnectedChannels", interestLevel ); for ( i= 0u; i < NELEMENTS ( bogusChan ); i++ ) { char buf[256]; sprintf ( buf, "aChannelThatShouldNeverNeverNeverExist%u", i ); status = ca_create_channel ( buf, 0, 0, 0, & bogusChan[i] ); verify ( status == ECA_NORMAL ); } status = ca_pend_io ( 0.001 ); verify ( status == ECA_TIMEOUT ); /* wait a long time for the search interval to increase */ for ( i= 0u; i < 10; i++ ) { epicsThreadSleep ( 1.0 ); showProgress ( interestLevel ); } status = ca_create_channel ( pName, 0, 0, 0, & validChan ); verify ( status == ECA_NORMAL ); /* * we should be able to connect to a valid * channel within a reasonable delay even * though there is one permanently * diasconnected channel */ status = ca_pend_io ( timeoutToPendIO ); verify ( status == ECA_NORMAL ); status = ca_clear_channel ( validChan ); verify ( status == ECA_NORMAL ); for ( i= 0u; i < NELEMENTS ( bogusChan ); i++ ) { status = ca_clear_channel ( bogusChan[i] ); verify ( status == ECA_NORMAL ); } showProgressEnd ( interestLevel ); } void verifyClearChannelOnDisconnectCallback ( struct connection_handler_args args ) { int * pDisconnectFlag = ca_puser ( args.chid ); if ( args.op == CA_OP_CONN_DOWN ) { ca_clear_channel ( args.chid ); *pDisconnectFlag = 1; } } void noopExceptionCallback ( struct exception_handler_args args ) { } void verifyDisconnect ( const char * pName, unsigned interestLevel ) { int disconnectFlag = 0; unsigned count = 0; chid chan0; chid chan1; int status; status = ca_create_channel ( pName, verifyClearChannelOnDisconnectCallback, & disconnectFlag, 0, & chan0 ); SEVCHK ( status, NULL ); fprintf ( stdout, "Waiting for test channel to connect." ); fflush ( stdout ); do { ca_pend_event ( 0.1 ); if ( count++%50 == 0 ) { fprintf ( stdout, "." ); fflush ( stdout ); } } while ( ca_state ( chan0 ) != cs_conn ); fprintf ( stdout, "confirmed.\n" ); /* * if its a local channel and will never disconnect * then skip the portions of this test that cant be * completed. */ if ( ca_get_ioc_connection_count () == 0 ) { status = ca_clear_channel ( chan0 ); SEVCHK ( status, NULL ); return; } status = ca_add_exception_event ( noopExceptionCallback, 0 ); SEVCHK ( status, NULL ); fprintf ( stdout, "Please force test channel to disconnect." ); fflush ( stdout ); do { ca_pend_event ( 0.1 );; if ( count++%50 == 0 ) { fprintf ( stdout, "." ); fflush ( stdout ); } } while ( ! disconnectFlag ); fprintf ( stdout, "confirmed.\n" ); /* channel cleared by disconnect handler */ status = ca_create_channel ( pName, 0, 0, 0, & chan1 ); SEVCHK ( status, NULL ); fprintf ( stdout, "Waiting for test channel to connect." ); fflush ( stdout ); while ( ca_state ( chan1 ) != cs_conn ) { ca_pend_event ( 5.0 ); fprintf ( stdout, "." ); fflush ( stdout ); } status = ca_clear_channel ( chan1 ); SEVCHK ( status, NULL ); fprintf ( stdout, "confirmed.\n" ); status = ca_add_exception_event ( 0, 0 ); SEVCHK ( status, NULL ); } void verifyName ( const char * pName, unsigned interestLevel ) { chid chan; int status = ca_create_channel ( pName, 0, 0, 0, & chan ); SEVCHK ( status, NULL ); if ( strcmp ( pName, ca_name ( chan ) ) != 0 ) { printf ( "Canonical name for channel was \"%s\"\n", ca_name ( chan ) ); } status = ca_clear_channel ( chan ); SEVCHK ( status, NULL ); } void verifyContextRundownFlush ( const char * pName, unsigned interestLevel ) { unsigned i; showProgressBegin ( "verifyContextRundownFlush", interestLevel ); for ( i=0u; i < 1000; i++ ) { const dbr_double_t stim = i; { chid chan; int status; status = ca_context_create ( ca_disable_preemptive_callback ); SEVCHK ( status, "context create failed" ); status = ca_create_channel ( pName, 0, 0, 0, & chan ); /* * currently in-memory channels cant be used with this test * !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!! */ if ( status != ECA_UNAVAILINSERV ) { SEVCHK ( status, NULL ); status = ca_pend_io( timeoutToPendIO ); SEVCHK ( status, "channel connect failed" ); status = ca_put ( DBR_DOUBLE, chan, & stim ); SEVCHK ( status, "channel put failed" ); status = ca_clear_channel ( chan ); SEVCHK ( status, NULL ); } ca_context_destroy (); } { chid chan; int status; dbr_double_t resp; status = ca_context_create ( ca_disable_preemptive_callback ); SEVCHK ( status, "context create failed" ); status = ca_create_channel ( pName, 0, 0, 0, & chan ); SEVCHK ( status, NULL ); /* * currently in-memory channels cant be used with this test * !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!! */ if ( status != ECA_UNAVAILINSERV ) { status = ca_pend_io( timeoutToPendIO ); SEVCHK ( status, "channel connect failed" ); status = ca_get ( DBR_DOUBLE, chan, & resp ); SEVCHK ( status, "channel get failed" ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, "get, pend io failed" ); verify ( stim == resp ); status = ca_clear_channel ( chan ); SEVCHK ( status, NULL ); } ca_context_destroy (); } if ( i % 100 == 0 ) { showProgress ( interestLevel ); } } showProgressEnd ( interestLevel ); } void verifyContextRundownChanStillExist ( const char * pName, unsigned interestLevel ) { chid chan[10000]; int status; unsigned i; showProgressBegin ( "verifyContextRundownChanStillExist", interestLevel ); status = ca_context_create ( ca_disable_preemptive_callback ); SEVCHK ( status, "context create failed" ); for ( i = 0; i < NELEMENTS ( chan ); i++ ) { status = ca_create_channel ( pName, 0, 0, 0, & chan[i] ); /* * currently in-memory channels cant be used with this test * !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!! */ if ( status == ECA_UNAVAILINSERV ) { break; } SEVCHK ( status, NULL ); } status = ca_pend_io( timeoutToPendIO ); SEVCHK ( status, "channel connect failed" ); ca_context_destroy (); showProgressEnd ( interestLevel ); } int acctst ( const char * pName, unsigned interestLevel, unsigned channelCount, unsigned repetitionCount, enum ca_preemptive_callback_select select ) { chid chan; int status; unsigned i; appChan *pChans; unsigned connections; unsigned maxArrayBytes = 10000000; printf ( "CA Client V%s, channel name \"%s\", timeout %g\n", ca_version (), pName, timeoutToPendIO ); if ( select == ca_enable_preemptive_callback ) { printf ( "Preemptive call back is enabled.\n" ); } { char tmpString[32]; sprintf ( tmpString, "%u", maxArrayBytes ); epicsEnvSet ( "EPICS_CA_MAX_ARRAY_BYTES", tmpString ); } /* * this test creates, and then destroys, a private CA context */ multiSubscrDestroyNoLateCallbackTest ( pName, interestLevel ); status = ca_context_create ( select ); SEVCHK ( status, NULL ); verifyDisconnect ( pName, interestLevel ); verifyImmediateTearDown ( pName, select, interestLevel ); verifyTearDownWhenChannelConnected ( pName, select, interestLevel ); verifyDataTypeMacros (); connections = ca_get_ioc_connection_count (); verify ( connections == 0u ); unequalServerBufferSizeTest ( pName, interestLevel ); clearChannelInGetCallbackTest ( pName, interestLevel ); clearChannelInPutCallbackTest ( pName, interestLevel ); clearChannelInSubscrCallbackTest ( pName, interestLevel ); monitorAddConnectionCallbackTest ( pName, interestLevel ); showProgressBegin ( "connecting to test channel", interestLevel ); status = ca_search ( pName, & chan ); SEVCHK ( status, NULL ); status = ca_pend_io ( timeoutToPendIO ); SEVCHK ( status, NULL ); showProgressEnd ( interestLevel ); printf ( "native type was %s, native count was %lu\n", dbf_type_to_text ( ca_field_type ( chan ) ), ca_element_count ( chan ) ); connections = ca_get_ioc_connection_count (); verify ( connections == 1u || connections == 0u ); if ( connections == 0u ) { printf ( "testing with a local channel\n" ); } verifyName ( pName, interestLevel ); verifyConnectWithDisconnectedChannels ( pName, interestLevel ); grEnumTest ( chan, interestLevel ); test_sync_groups ( chan, interestLevel ); verifyChannelPriorities ( pName, interestLevel ); verifyTimeStamps ( chan, interestLevel ); verifyOldPend ( interestLevel ); exceptionTest ( chan, interestLevel ); arrayTest ( chan, maxArrayBytes, interestLevel ); verifyMonitorSubscriptionFlushIO ( chan, interestLevel ); monitorSubscriptionFirstUpdateTest ( pName, chan, interestLevel ); ctrlDoubleTest ( chan, interestLevel ); verifyBlockInPendIO ( chan, interestLevel ); verifyAnalogIO ( chan, DBR_FLOAT, FLT_MIN, FLT_MAX, FLT_MIN_EXP, FLT_MAX_EXP, FLT_EPSILON, interestLevel ); verifyAnalogIO ( chan, DBR_DOUBLE, DBL_MIN, DBL_MAX, DBL_MIN_EXP, DBL_MAX_EXP, DBL_EPSILON, interestLevel ); verifyLongIO ( chan, interestLevel ); verifyShortIO ( chan, interestLevel ); multiSubscriptionDeleteTest ( chan, interestLevel ); singleSubscriptionDeleteTest ( chan, interestLevel ); channelClearWithEventTrafficTest ( pName, interestLevel ); eventClearAndMultipleMonitorTest ( chan, interestLevel ); verifyHighThroughputRead ( chan, interestLevel ); verifyHighThroughputWrite ( chan, interestLevel ); verifyHighThroughputReadCallback ( chan, interestLevel ); verifyHighThroughputWriteCallback ( chan, interestLevel ); verifyBadString ( chan, interestLevel ); verifyMultithreadSubscr ( pName, interestLevel ); if ( select != ca_enable_preemptive_callback ) { fdManagerVerify ( pName, interestLevel ); } /* * CA pend event delay accuracy test * (CA asssumes that search requests can be sent * at least every 25 mS on all supported os) */ printf ( "\n" ); pend_event_delay_test ( 1.0 ); pend_event_delay_test ( 0.1 ); pend_event_delay_test ( 0.25 ); /* ca_channel_status ( 0 ); */ ca_client_status ( 0 ); /* ca_client_status ( 6u ); info about each channel */ pChans = calloc ( channelCount, sizeof ( *pChans ) ); verify ( pChans ); for ( i = 0; i < channelCount; i++ ) { strncpy ( pChans[ i ].name, pName, sizeof ( pChans[ i ].name ) ); pChans[ i ].name[ sizeof ( pChans[i].name ) - 1 ] = '\0'; } verifyConnectionHandlerConnect ( pChans, channelCount, repetitionCount, interestLevel ); verifyBlockingConnect ( pChans, channelCount, repetitionCount, interestLevel ); verifyClear ( pChans, interestLevel ); verifyReasonableBeaconPeriod ( chan, interestLevel ); /* * Verify that we can do IO with the new types for ALH */ #if 0 if ( ca_read_access (chan) && ca_write_access (chan) ) { { dbr_put_ackt_t acktIn = 1u; dbr_put_acks_t acksIn = 1u; struct dbr_stsack_string stsackOut; SEVCHK ( ca_put ( DBR_PUT_ACKT, chan, &acktIn ), NULL ); SEVCHK ( ca_put ( DBR_PUT_ACKS, chan, &acksIn ), NULL ); SEVCHK ( ca_get ( DBR_STSACK_STRING, chan, &stsackOut ), NULL ); SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL ); } #endif /* test that ca_task_exit () works when there is still one channel remaining */ /* status = ca_clear_channel ( chan ); */ /* SEVCHK ( status, NULL ); */ caTaskExitTest ( interestLevel ); verifyContextRundownFlush ( pName, interestLevel ); verifyContextRundownChanStillExist ( pName, interestLevel ); free ( pChans ); printf ( "\nTest Complete\n" ); epicsExit ( EXIT_SUCCESS ); return 0; } base-7.0.3.1/modules/ca/src/client/acctstMain.c0000664000577000060420000000333113557101274017745 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include #include #include "cadef.h" #include "caDiagnostics.h" int main ( int argc, char **argv ) { unsigned progressLoggingLevel; unsigned channelCount; unsigned repetitionCount; enum ca_preemptive_callback_select preempt; int aBoolean; if ( argc < 2 || argc > 6 ) { printf ("usage: %s [progress logging level] [channel count] " "[repetition count] [enable preemptive callback]\n", argv[0] ); return 1; } if ( argc >= 3 ) { progressLoggingLevel = atoi ( argv[2] ); } else { progressLoggingLevel = 0; } if ( argc >= 4 ) { channelCount = atoi ( argv[3] ); } else { channelCount = 20000; } if ( argc >= 5 ) { repetitionCount = atoi ( argv[4] ); } else { repetitionCount = 1; } if ( argc >= 6 ) { aBoolean = atoi ( argv[5] ); } else { aBoolean = 0; } if ( aBoolean ) { preempt = ca_enable_preemptive_callback; } else { preempt = ca_disable_preemptive_callback; } acctst ( argv[1], progressLoggingLevel, channelCount, repetitionCount, preempt ); return 0; } base-7.0.3.1/modules/ca/src/client/acctstRegister.cpp0000664000577000060420000000421513557101274021207 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * CA client library diagnostics IOC shell registration * Authors: * Jeff Hill */ #include #include "caDiagnostics.h" /* Information needed by iocsh */ static const iocshArg acctstArg0 = { "channel name", iocshArgString }; static const iocshArg acctstArg1 = { "interest level", iocshArgInt }; static const iocshArg acctstArg2 = { "channel count", iocshArgInt }; static const iocshArg acctstArg3 = { "repetition count", iocshArgInt }; static const iocshArg acctstArg4 = { "preemptive callback select", iocshArgInt }; static const iocshArg *acctstArgs[] = { &acctstArg0, &acctstArg1, &acctstArg2, &acctstArg3, &acctstArg4 }; static const iocshFuncDef acctstFuncDef = {"acctst", 5, acctstArgs}; /* Wrapper called by iocsh, selects the argument types that print needs */ static void acctstCallFunc(const iocshArgBuf *args) { if ( args[1].ival < 0 ) { printf ( "negative interest level not allowed\n" ); return; } if ( args[2].ival < 0 ) { printf ( "negative channel count not allowed\n" ); return; } if ( args[3].ival < 0 ) { printf ( "negative repetition count not allowed\n" ); return; } acctst ( args[0].sval, /* channel name */ ( unsigned ) args[1].ival, /* interest level */ ( unsigned ) args[2].ival, /* channel count */ ( unsigned ) args[3].ival, /* repetition count */ ( ca_preemptive_callback_select ) args[4].ival ); /* preemptive callback select */ } struct AutoInit { AutoInit (); }; AutoInit :: AutoInit () { iocshRegister ( &acctstFuncDef, acctstCallFunc ); } AutoInit autoInit; base-7.0.3.1/modules/ca/src/client/addrList.h0000664000577000060420000000237213557101274017436 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #ifndef addrListh #define addrListh #include "shareLib.h" #include "envDefs.h" #include "osiSock.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void epicsShareAPI configureChannelAccessAddressList ( struct ELLLIST *pList, SOCKET sock, unsigned short port ); epicsShareFunc int epicsShareAPI addAddrToChannelAccessAddressList ( struct ELLLIST *pList, const ENV_PARAM *pEnv, unsigned short port, int ignoreNonDefaultPort ); epicsShareFunc void epicsShareAPI printChannelAccessAddressList ( const struct ELLLIST *pList ); epicsShareFunc void epicsShareAPI removeDuplicateAddresses ( struct ELLLIST *pDestList, ELLLIST *pSrcList, int silent); #ifdef __cplusplus } #endif #endif /* ifndef addrListh */ base-7.0.3.1/modules/ca/src/client/autoPtrFreeList.h0000664000577000060420000000575113557101274020770 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef autoPtrFreeListh #define autoPtrFreeListh #ifdef epicsExportSharedSymbols # define autoPtrFreeListh_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "tsFreeList.h" #include "compilerDependencies.h" #ifdef autoPtrFreeListh_epicsExportSharedSymbols # define epicsExportSharedSymbols #endif template < class T, unsigned N = 0x400, class MUTEX = epicsMutex > class autoPtrFreeList { public: autoPtrFreeList ( tsFreeList < T, N, MUTEX > &, T * ); ~autoPtrFreeList (); T & operator * () const; T * operator -> () const; T * get () const; T * release (); private: T * p; tsFreeList < T, N, MUTEX > & freeList; // not implemented autoPtrFreeList & operator = ( const autoPtrFreeList & ); autoPtrFreeList ( const autoPtrFreeList < T, N, MUTEX > & ); }; template < class T, unsigned N, class MUTEX > inline autoPtrFreeList < T, N, MUTEX >::autoPtrFreeList ( tsFreeList < T, N, MUTEX > & freeListIn, T * pIn ) : p ( pIn ), freeList ( freeListIn ) {} template < class T, unsigned N, class MUTEX > inline autoPtrFreeList < T, N, MUTEX >::~autoPtrFreeList () { if ( this->p ) { this->p->~T(); // its probably a good idea to require that the class has placement delete // by calling it during cleanup if the compiler supports it # if defined ( CXX_PLACEMENT_DELETE ) T::operator delete ( this->p, this->freeList ); # else this->freeList.release ( this->p ); # endif } } template < class T, unsigned N, class MUTEX > inline T & autoPtrFreeList < T, N, MUTEX >::operator * () const { return * this->p; } template < class T, unsigned N, class MUTEX > inline T * autoPtrFreeList < T, N, MUTEX >::operator -> () const { return this->p; } template < class T, unsigned N, class MUTEX > inline T * autoPtrFreeList < T, N, MUTEX >::get () const { return this->p; } template < class T, unsigned N, class MUTEX > inline T * autoPtrFreeList < T, N, MUTEX >::release () { T *pTmp = this->p; this->p = 0; return pTmp; } #endif // #ifdef autoPtrFreeListh base-7.0.3.1/modules/ca/src/client/autoPtrRecycle.h0000664000577000060420000000457513557101274020644 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef autoPtrRecycleh #define autoPtrRecycleh template < class T > class autoPtrRecycle { public: autoPtrRecycle ( epicsGuard < epicsMutex > &, chronIntIdResTable < baseNMIU > &, cacRecycle &, T * ); ~autoPtrRecycle (); T & operator * () const; T * operator -> () const; T * get () const; T * release (); private: T * p; cacRecycle & r; chronIntIdResTable < baseNMIU > & ioTable; epicsGuard < epicsMutex > & guard; // not implemented autoPtrRecycle ( const autoPtrRecycle & ); autoPtrRecycle & operator = ( const autoPtrRecycle & ); }; template < class T > inline autoPtrRecycle::autoPtrRecycle ( epicsGuard < epicsMutex > & guardIn, chronIntIdResTable < baseNMIU > & tbl, cacRecycle & rIn, T * pIn ) : p ( pIn ), r ( rIn ), ioTable ( tbl ), guard ( guardIn ) {} template < class T > inline autoPtrRecycle::~autoPtrRecycle () { if ( this->p ) { baseNMIU *pb = this->p; this->ioTable.remove ( *pb ); pb->destroy ( this->guard, this->r ); } } template < class T > inline T & autoPtrRecycle::operator * () const { return * this->p; } template < class T > inline T * autoPtrRecycle::operator -> () const { return this->p; } template < class T > inline T * autoPtrRecycle::get () const { return this->p; } template < class T > inline T * autoPtrRecycle::release () { T *pTmp = this->p; this->p = 0; return pTmp; } #endif // #ifdef autoPtrRecycleh base-7.0.3.1/modules/ca/src/client/baseNMIU.cpp0000664000577000060420000000175513557101274017632 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, the Regents of the University of California. * * Author: Jeff Hill */ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "iocinf.h" #include "nciu.h" #include "netIO.h" baseNMIU::~baseNMIU () { } void baseNMIU::forceSubscriptionUpdate ( epicsGuard < epicsMutex > &, nciu & ) { } base-7.0.3.1/modules/ca/src/client/bhe.cpp0000664000577000060420000002543613557101274016767 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #include #include #include #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "errlog.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "virtualCircuit.h" #include "bhe.h" /* * set average to -1.0 so that when the next beacon * occurs we can distinguish between: * o new server * o existing server's beacon we are seeing * for the first time shortly after program * start up * * if creating this in response to a search reply * and not in response to a beacon then * we set the beacon time stamp to * zero (so we can correctly compute the period * between the 1st and 2nd beacons) */ bhe::bhe ( epicsMutex & mutexIn, const epicsTime & initialTimeStamp, unsigned initialBeaconNumber, const inetAddrID & addr ) : inetAddrID ( addr ), timeStamp ( initialTimeStamp ), averagePeriod ( - DBL_MAX ), mutex ( mutexIn ), pIIU ( 0 ), lastBeaconNumber ( initialBeaconNumber ) { # ifdef DEBUG { char name[64]; addr.name ( name, sizeof ( name ) ); ::printf ( "created beacon entry for %s\n", name ); } # endif } bhe::~bhe () { } void bhe::beaconAnomalyNotify ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( this->pIIU ) { this->pIIU->beaconAnomalyNotify ( guard ); } } #ifdef DEBUG void bhe::logBeacon ( const char * pDiagnostic, const double & currentPeriod, const epicsTime & currentTime ) { if ( this->pIIU ) { char name[64]; this->name ( name, sizeof ( name ) ); char date[64]; currentTime.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); ::printf ( "%s cp=%g ap=%g %s %s\n", pDiagnostic, currentPeriod, this->averagePeriod, name, date ); } } #else inline void bhe::logBeacon ( const char * /* pDiagnostic */, const double & /* currentPeriod */, const epicsTime & /* currentTime */ ) { } #endif #ifdef DEBUG void bhe::logBeaconDiscard ( unsigned beaconAdvance, const epicsTime & currentTime ) { if ( this->pIIU ) { char name[64]; this->name ( name, sizeof ( name ) ); char date[64]; currentTime.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); ::printf ( "bb %u %s %s\n", beaconAdvance, name, date ); } } #else void bhe::logBeaconDiscard ( unsigned /* beaconAdvance */, const epicsTime & /* currentTime */ ) { } #endif /* * update beacon period * * updates beacon period, and looks for beacon anomalies */ bool bhe::updatePeriod ( epicsGuard < epicsMutex > & guard, const epicsTime & programBeginTime, const epicsTime & currentTime, ca_uint32_t beaconNumber, unsigned protocolRevision ) { guard.assertIdenticalMutex ( this->mutex ); // // this block is enetered if the beacon was created as a side effect of // creating a connection and so we dont yet know the first beacon time // and sequence number // if ( this->timeStamp == epicsTime () ) { if ( CA_V410 ( protocolRevision ) ) { this->lastBeaconNumber = beaconNumber; } this->beaconAnomalyNotify ( guard ); /* * this is the 1st beacon seen - the beacon time stamp * was not initialized during BHE create because * a TCP/IP connection created the beacon. * (nothing to do but set the beacon time stamp and return) */ this->timeStamp = currentTime; logBeacon ( "fb", - DBL_MAX, currentTime ); return false; } // 1) detect beacon duplications due to redundant routes // 2) detect lost beacons due to input queue overrun or damage if ( CA_V410 ( protocolRevision ) ) { unsigned beaconSeqAdvance; if ( beaconNumber >= this->lastBeaconNumber ) { beaconSeqAdvance = beaconNumber - this->lastBeaconNumber; } else { beaconSeqAdvance = ( ca_uint32_max - this->lastBeaconNumber ) + beaconNumber; } this->lastBeaconNumber = beaconNumber; // throw out sequence numbers just prior to, or the same as, the last one received // (this situation is probably caused by a temporary duplicate route ) if ( beaconSeqAdvance == 0 || beaconSeqAdvance > ca_uint32_max - 256 ) { logBeaconDiscard ( beaconSeqAdvance, currentTime ); return false; } // throw out sequence numbers that jump forward by only a few numbers // (this situation is probably caused by a duplicate route // or a beacon due to input queue overun) if ( beaconSeqAdvance > 1 && beaconSeqAdvance < 4 ) { logBeaconDiscard ( beaconSeqAdvance, currentTime ); return false; } } // compute the beacon period (if we have seen at least two beacons) bool netChange = false; double currentPeriod = currentTime - this->timeStamp; if ( this->averagePeriod < 0.0 ) { double totalRunningTime; this->beaconAnomalyNotify ( guard ); /* * this is the 2nd beacon seen. We cant tell about * the change in period at this point so we just * initialize the average period and return. */ this->averagePeriod = currentPeriod; logBeacon ( "fp", currentPeriod, currentTime ); /* * ignore beacons seen for the first time shortly after * init, but do not ignore beacons arriving with a short * period because the IOC was rebooted soon after the * client starts up. */ totalRunningTime = this->timeStamp - programBeginTime; if ( currentPeriod <= totalRunningTime ) { netChange = true; } } else { /* * Is this an IOC seen because of a restored * network segment? * * It may be possible to get false triggers here * if the client is busy, but this does not cause * problems because the echo response will tell us * that the server is available */ if ( currentPeriod >= this->averagePeriod * 1.25 ) { /* * trigger on any missing beacon * if connected to this server */ this->beaconAnomalyNotify ( guard ); if ( currentPeriod >= this->averagePeriod * 3.25 ) { /* * trigger on any 3 contiguous missing beacons * if not connected to this server */ netChange = true; } logBeacon ( "bah", currentPeriod, currentTime ); } /* * Is this an IOC seen because of an IOC reboot * (beacon come at a higher rate just after the * IOC reboots). Lower tolarance here because we * dont have to worry about lost beacons. * * It may be possible to get false triggers here * if the client is busy, but this does not cause * problems because the echo response will tell us * that the server is available */ else if ( currentPeriod <= this->averagePeriod * 0.80 ) { this->beaconAnomalyNotify ( guard ); netChange = true; logBeacon ( "bal", currentPeriod, currentTime ); } else if ( this->pIIU ) { // update state of health for active virtual circuits // if the beacon looks ok this->pIIU->beaconArrivalNotify ( guard ); logBeacon ( "vb", currentPeriod, currentTime ); } // update a running average period this->averagePeriod = currentPeriod * 0.125 + this->averagePeriod * 0.875; } this->timeStamp = currentTime; return netChange; } void bhe::show ( unsigned level ) const { epicsGuard < epicsMutex > guard ( this->mutex ); this->show ( guard, level ); } void bhe::show ( epicsGuard < epicsMutex > &, unsigned level ) const { char host [64]; this->name ( host, sizeof ( host ) ); if ( this->averagePeriod == -DBL_MAX ) { ::printf ( "CA beacon hash entry for %s \n", host ); } else { ::printf ( "CA beacon hash entry for %s with period estimate %f\n", host, this->averagePeriod ); } if ( level > 0u ) { char date[64]; this->timeStamp.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S"); ::printf ( "\tbeacon number %u, on %s\n", this->lastBeaconNumber, date ); } } double bhe::period ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); return this->averagePeriod; } epicsTime bhe::updateTime ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); return this->timeStamp; } void bhe::registerIIU ( epicsGuard < epicsMutex > & guard, tcpiiu & iiu ) { guard.assertIdenticalMutex ( this->mutex ); this->pIIU = & iiu; } void bhe::unregisterIIU ( epicsGuard < epicsMutex > & guard, tcpiiu & iiu ) { guard.assertIdenticalMutex ( this->mutex ); if ( this->pIIU == & iiu ) { this->pIIU = 0; this->timeStamp = epicsTime(); this->averagePeriod = - DBL_MAX; logBeacon ( "ui", this->averagePeriod, epicsTime::getCurrent () ); } } void bhe::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } void * bheFreeStore::allocate ( size_t size ) { return freeList.allocate ( size ); } void bheFreeStore::release ( void * pCadaver ) { freeList.release ( pCadaver ); } bheMemoryManager::~bheMemoryManager () {} base-7.0.3.1/modules/ca/src/client/bhe.h0000664000577000060420000000737613557101274016437 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #ifndef bheh #define bheh #ifdef epicsExportSharedSymbols # define bhehEpicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "tsDLList.h" #include "tsFreeList.h" #include "epicsTime.h" #include "compilerDependencies.h" #ifdef bhehEpicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif #include "inetAddrID.h" #include "caProto.h" class tcpiiu; class bheMemoryManager; // using a pure abstract wrapper class around the free list avoids // Tornado 2.0.1 GNU compiler bugs class epicsShareClass bheMemoryManager { public: virtual ~bheMemoryManager (); virtual void * allocate ( size_t ) = 0; virtual void release ( void * ) = 0; }; class bhe : public tsSLNode < bhe >, public inetAddrID { public: epicsShareFunc bhe ( epicsMutex &, const epicsTime & initialTimeStamp, unsigned initialBeaconNumber, const inetAddrID & addr ); epicsShareFunc ~bhe (); epicsShareFunc bool updatePeriod ( epicsGuard < epicsMutex > &, const epicsTime & programBeginTime, const epicsTime & currentTime, ca_uint32_t beaconNumber, unsigned protocolRevision ); epicsShareFunc double period ( epicsGuard < epicsMutex > & ) const; epicsShareFunc epicsTime updateTime ( epicsGuard < epicsMutex > & ) const; epicsShareFunc void show ( unsigned level ) const; epicsShareFunc void show ( epicsGuard < epicsMutex > &, unsigned /* level */ ) const; epicsShareFunc void registerIIU ( epicsGuard < epicsMutex > &, tcpiiu & ); epicsShareFunc void unregisterIIU ( epicsGuard < epicsMutex > &, tcpiiu & ); epicsShareFunc void * operator new ( size_t size, bheMemoryManager & ); #ifdef CXX_PLACEMENT_DELETE epicsShareFunc void operator delete ( void *, bheMemoryManager & ); #endif private: epicsTime timeStamp; double averagePeriod; epicsMutex & mutex; tcpiiu * pIIU; ca_uint32_t lastBeaconNumber; void beaconAnomalyNotify ( epicsGuard < epicsMutex > & ); void logBeacon ( const char * pDiagnostic, const double & currentPeriod, const epicsTime & currentTime ); void logBeaconDiscard ( unsigned beaconAdvance, const epicsTime & currentTime ); bhe ( const bhe & ); bhe & operator = ( const bhe & ); epicsShareFunc void operator delete ( void * ); }; // using a wrapper class around the free list avoids // Tornado 2.0.1 GNU compiler bugs class bheFreeStore : public bheMemoryManager { public: bheFreeStore () {} void * allocate ( size_t ); void release ( void * ); private: tsFreeList < bhe, 0x100 > freeList; bheFreeStore ( const bheFreeStore & ); bheFreeStore & operator = ( const bheFreeStore & ); }; inline void * bhe::operator new ( size_t size, bheMemoryManager & mgr ) { return mgr.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void bhe::operator delete ( void * pCadaver, bheMemoryManager & mgr ) { mgr.release ( pCadaver ); } #endif #endif // ifdef bheh base-7.0.3.1/modules/ca/src/client/ca.rc0000664000577000060420000000220613557101274016424 0ustar anjaesctl#include #include "epicsVersion.h" VS_VERSION_INFO VERSIONINFO FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_UNKNOWN FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "Comments","Channel Access Library for EPICS\0" VALUE "CompanyName", "The EPICS collaboration\0" VALUE "FileDescription", "Channel Access Library\0" VALUE "FileVersion", EPICS_VERSION_STRING "\0" VALUE "InternalName", "ca\0" VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" VALUE "OriginalFilename", "ca.dll\0" VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" VALUE "ProductVersion", EPICS_VERSION_STRING "\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END base-7.0.3.1/modules/ca/src/client/caConnTest.cpp0000664000577000060420000000601313557101274020260 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "cadef.h" #include "epicsTime.h" static unsigned channelCount = 0u; static unsigned connCount = 0u; static bool subsequentConnect = false; epicsTime begin; extern "C" void caConnTestConnHandler ( struct connection_handler_args args ) { if ( args.op == CA_OP_CONN_UP ) { if ( connCount == 0u ) { if ( subsequentConnect ) { printf ("the first channel connected\n"); begin = epicsTime::getCurrent (); } } connCount++; // printf ( "." ); // fflush ( stdout ); if ( connCount == channelCount ) { epicsTime current = epicsTime::getCurrent (); double delay = current - begin; printf ( "all channels connected after %f sec ( %f sec per channel)\n", delay, delay / channelCount ); } } else if ( args.op == CA_OP_CONN_DOWN ) { if ( connCount == channelCount ) { printf ( "channels are disconnected\n" ); subsequentConnect = true; } connCount--; if ( connCount == 0u ) { printf ( "all channels are disconnected\n" ); } } else { assert ( 0 ); } } void caConnTest ( const char *pNameIn, unsigned channelCountIn, double delayIn ) { unsigned iteration = 0u; int status; unsigned i; chid *pChans; channelCount = channelCountIn; pChans = new chid [channelCount]; while ( 1 ) { connCount = 0u; subsequentConnect = false; begin = epicsTime::getCurrent (); printf ( "initializing CA client library\n" ); status = ca_task_initialize(); SEVCHK ( status, "CA init failed" ); printf ( "creating channels\n" ); for ( i = 0u; i < channelCount; i++ ) { status = ca_search_and_connect ( pNameIn, &pChans[i], caConnTestConnHandler, 0 ); SEVCHK ( status, "CA search problems" ); } printf ( "all channels were created\n" ); ca_pend_event ( delayIn ); if ( iteration & 1 ) { for ( i = 0u; i < channelCount; i++ ) { status = ca_clear_channel ( pChans[i] ); SEVCHK ( status, "ca_clear_channel() problems" ); } printf ( "all channels were destroyed\n" ); } printf ( "shutting down CA client library\n" ); status = ca_task_exit (); SEVCHK ( status, "task exit problems" ); iteration++; } //delete [] pChans; } base-7.0.3.1/modules/ca/src/client/caConnTestMain.cpp0000664000577000060420000000260213557101274021065 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include #include #include "caDiagnostics.h" int main ( int argc, char **argv ) { double delay = 60.0 * 5.0; unsigned count = 2000; if ( argc < 2 || argc > 4 ) { printf ( "usage: %s < channel name > [ < count > ] [ < delay sec > ]\n", argv[0] ); return -1; } if ( argc >= 3 ) { int nConverted = sscanf ( argv[2], "%u", &count ); if ( nConverted != 1 ) { printf ( "conversion failed, changing channel count arg \"%s\" to %u\n", argv[1], count ); } } if ( argc >= 4 ) { int nConverted = epicsScanDouble( argv[3], &delay ); if ( nConverted != 1 ) { printf ( "conversion failed, changing delay arg \"%s\" to %f\n", argv[2], delay ); } } caConnTest ( argv[1], count, delay ); return 0; } base-7.0.3.1/modules/ca/src/client/caDiagnostics.h0000664000577000060420000000221213557101274020434 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #ifndef caDiagnosticsh #define caDiagnosticsh #include "cadef.h" #ifdef __cplusplus extern "C" { #endif enum appendNumberFlag {appendNumber, dontAppendNumber}; int catime ( const char *channelName, unsigned channelCount, enum appendNumberFlag appNF ); int acctst ( const char *pname, unsigned logggingInterestLevel, unsigned channelCount, unsigned repetitionCount, enum ca_preemptive_callback_select select ); #define CATIME_OK 0 #define CATIME_ERROR -1 #ifdef __cplusplus } #endif void caConnTest ( const char *pNameIn, unsigned channelCountIn, double delayIn ); #endif /* caDiagnosticsh */ base-7.0.3.1/modules/ca/src/client/caEventRate.cpp0000664000577000060420000000762313557101274020430 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include #include #include #include "cadef.h" #include "dbDefs.h" #include "epicsTime.h" #include "errlog.h" /* * event_handler() */ extern "C" void eventCallBack ( struct event_handler_args args ) { unsigned *pCount = static_cast < unsigned * > ( args.usr ); (*pCount)++; } /* * caEventRate () */ void caEventRate ( const char *pName, unsigned count ) { static const double initialSamplePeriod = 1.0; static const double maxSamplePeriod = 60.0 * 5.0; unsigned eventCount = 0u; chid * pChidTable = new chid [ count ]; { printf ( "Connecting to CA Channel \"%s\" %u times.", pName, count ); fflush ( stdout ); epicsTime begin = epicsTime::getCurrent (); for ( unsigned i = 0u; i < count; i++ ) { int status = ca_search ( pName, & pChidTable[i] ); SEVCHK ( status, NULL ); } int status = ca_pend_io ( 10000.0 ); if ( status != ECA_NORMAL ) { fprintf ( stderr, " not found.\n" ); return; } epicsTime end = epicsTime::getCurrent (); printf ( " done(%f sec).\n", end - begin ); } { printf ( "Subscribing %u times.", count ); fflush ( stdout ); epicsTime begin = epicsTime::getCurrent (); for ( unsigned i = 0u; i < count; i++ ) { int addEventStatus = ca_add_event ( DBR_FLOAT, pChidTable[i], eventCallBack, &eventCount, NULL); SEVCHK ( addEventStatus, __FILE__ ); } int status = ca_flush_io (); SEVCHK ( status, __FILE__ ); epicsTime end = epicsTime::getCurrent (); printf ( " done(%f sec).\n", end - begin ); } { printf ( "Waiting for initial value events." ); fflush ( stdout ); // let the first one go by epicsTime begin = epicsTime::getCurrent (); while ( eventCount < count ) { int status = ca_pend_event ( 0.01 ); if ( status != ECA_TIMEOUT ) { SEVCHK ( status, NULL ); } } epicsTime end = epicsTime::getCurrent (); printf ( " done(%f sec).\n", end - begin ); } double samplePeriod = initialSamplePeriod; double X = 0.0; double XX = 0.0; unsigned N = 0u; while ( true ) { unsigned nEvents, lastEventCount, curEventCount; epicsTime beginPend = epicsTime::getCurrent (); lastEventCount = eventCount; int status = ca_pend_event ( samplePeriod ); curEventCount = eventCount; epicsTime endPend = epicsTime::getCurrent (); if ( status != ECA_TIMEOUT ) { SEVCHK ( status, NULL ); } if ( curEventCount >= lastEventCount ) { nEvents = curEventCount - lastEventCount; } else { nEvents = ( UINT_MAX - lastEventCount ) + curEventCount + 1u; } N++; double period = endPend - beginPend; double Hz = nEvents / period; X += Hz; XX += Hz * Hz; double mean = X / N; double stdDev = sqrt ( XX / N - mean * mean ); printf ( "CA Event Rate (Hz): current %g mean %g std dev %g\n", Hz, mean, stdDev ); if ( samplePeriod < maxSamplePeriod ) { samplePeriod += samplePeriod; } } } base-7.0.3.1/modules/ca/src/client/caEventRateMain.cpp0000664000577000060420000000217213557101274021227 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include #include void caEventRate ( const char *pName, unsigned count ); int main ( int argc, char **argv ) { if ( argc < 2 || argc > 3 ) { fprintf ( stderr, "usage: %s < PV name > [subscription count]\n", argv[0] ); return 0; } unsigned count; if ( argc == 3 ) { int status = sscanf ( argv[2], " %u ", & count ); if ( status != 1 ) { fprintf ( stderr, "expected unsigned integer 2nd argument\n" ); return 0; } } else { count = 1; } caEventRate ( argv[1], count ); return 0; } base-7.0.3.1/modules/ca/src/client/caProto.h0000664000577000060420000001624513557101274017303 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef __CAPROTO__ #define __CAPROTO__ #define capStrOf(A) #A #define capStrOfX(A) capStrOf ( A ) /* * CA protocol revision * TCP/UDP port number (bumped each major protocol change) */ #define CA_MAJOR_PROTOCOL_REVISION 4 #define CA_VERSION_STRING( MINOR_REVISION ) \ ( capStrOfX ( CA_MAJOR_PROTOCOL_REVISION ) "." capStrOfX ( MINOR_REVISION ) ) #define CA_UKN_MINOR_VERSION 0u /* unknown minor version */ #define CA_MINIMUM_SUPPORTED_VERSION 4u # define CA_VSUPPORTED(MINOR) ((MINOR)>=CA_MINIMUM_SUPPORTED_VERSION) # define CA_V41(MINOR) ((MINOR)>=1u) # define CA_V42(MINOR) ((MINOR)>=2u) # define CA_V43(MINOR) ((MINOR)>=3u) # define CA_V44(MINOR) ((MINOR)>=4u) # define CA_V45(MINOR) ((MINOR)>=5u) # define CA_V46(MINOR) ((MINOR)>=6u) # define CA_V47(MINOR) ((MINOR)>=7u) # define CA_V48(MINOR) ((MINOR)>=8u) # define CA_V49(MINOR) ((MINOR)>=9u) /* large arrays, dispatch priorities */ # define CA_V410(MINOR) ((MINOR)>=10u) /* beacon counter */ # define CA_V411(MINOR) ((MINOR)>=11u) /* sequence numbers in UDP version command */ # define CA_V412(MINOR) ((MINOR)>=12u) /* TCP-based search requests */ # define CA_V413(MINOR) ((MINOR)>=13u) /* Allow zero length in requests. */ /* * These port numbers are only used if the CA repeater and * CA server port numbers cant be obtained from the EPICS * environment variables "EPICS_CA_REPEATER_PORT" and * "EPICS_CA_SERVER_PORT" */ #define CA_PORT_BASE IPPORT_USERRESERVED + 56U #define CA_SERVER_PORT (CA_PORT_BASE+CA_MAJOR_PROTOCOL_REVISION*2u) #define CA_REPEATER_PORT (CA_PORT_BASE+CA_MAJOR_PROTOCOL_REVISION*2u+1u) /* * 1500 (max of ethernet and 802.{2,3} MTU) - 20(IP) - 8(UDP) * (the MTU of Ethernet is currently independent of its speed varient) */ #define ETHERNET_MAX_UDP ( 1500u - 20u - 8u ) #define MAX_UDP_RECV ( 0xffff + 16u ) /* allow large frames to be received in the future */ #define MAX_UDP_SEND 1024u /* original MAX_UDP */ #define MAX_TCP ( 1024 * 16u ) /* so waveforms fit */ #define MAX_MSG_SIZE ( MAX_TCP ) /* the larger of tcp and udp max */ #define CA_PROTO_PRIORITY_MIN 0u #define CA_PROTO_PRIORITY_MAX 99u /* * architecture independent types * * (so far this works on all archs we have ported to) */ typedef unsigned char ca_uint8_t; typedef unsigned short ca_uint16_t; typedef unsigned int ca_uint32_t; typedef float ca_float32_t; typedef ca_uint32_t caResId; #define ca_uint32_max 0xffffffff /* values for m_cmmd */ #define CA_PROTO_VERSION 0u /* set minor version and priority (used to be NOOP cmd) */ #define CA_PROTO_EVENT_ADD 1u /* add an event */ #define CA_PROTO_EVENT_CANCEL 2u /* cancel an event */ #define CA_PROTO_READ 3u /* read and return a channel value*/ #define CA_PROTO_WRITE 4u /* write a channel value */ #define CA_PROTO_SNAPSHOT 5u /* snapshot of the system */ #define CA_PROTO_SEARCH 6u /* IOC channel search */ #define CA_PROTO_BUILD 7u /* build - obsolete */ #define CA_PROTO_EVENTS_OFF 8u /* flow control */ #define CA_PROTO_EVENTS_ON 9u /* flow control */ #define CA_PROTO_READ_SYNC 10u /* purge old reads */ #define CA_PROTO_ERROR 11u /* an operation failed */ #define CA_PROTO_CLEAR_CHANNEL 12u /* free chan resources */ #define CA_PROTO_RSRV_IS_UP 13u /* CA server has joined the net */ #define CA_PROTO_NOT_FOUND 14u /* channel not found */ #define CA_PROTO_READ_NOTIFY 15u /* add a one shot event */ #define CA_PROTO_READ_BUILD 16u /* read and build - obsolete */ #define REPEATER_CONFIRM 17u /* registration confirmation */ #define CA_PROTO_CREATE_CHAN 18u /* client creates channel in server */ #define CA_PROTO_WRITE_NOTIFY 19u /* notify after write chan value */ #define CA_PROTO_CLIENT_NAME 20u /* CA V4.1 identify client */ #define CA_PROTO_HOST_NAME 21u /* CA V4.1 identify client */ #define CA_PROTO_ACCESS_RIGHTS 22u /* CA V4.2 asynch access rights chg */ #define CA_PROTO_ECHO 23u /* CA V4.3 connection verify */ #define REPEATER_REGISTER 24u /* register for repeater fan out */ #define CA_PROTO_SIGNAL 25u /* knock the server out of select */ #define CA_PROTO_CREATE_CH_FAIL 26u /* unable to create chan resource in server */ #define CA_PROTO_SERVER_DISCONN 27u /* server deletes PV (or channel) */ #define CA_PROTO_LAST_CMMD CA_PROTO_SERVER_DISCONN /* * for use with search and not_found (if search fails and * its not a broadcast tell the client to look elesewhere) */ #define DOREPLY 10u #define DONTREPLY 5u /* * for use with the m_dataType field in UDP messages emitted by servers */ #define sequenceNoIsValid 1 /* size of object in bytes rounded up to nearest oct word */ #define OCT_ROUND(A) (((A)+7)/8) #define OCT_SIZEOF(A) (OCT_ROUND(sizeof(A))) /* size of object in bytes rounded up to nearest long word */ #define QUAD_ROUND(A) ((A)+3)/4) #define QUAD_SIZEOF(A) (QUAD_ROUND(sizeof(A))) /* size of object in bytes rounded up to nearest short word */ #define BI_ROUND(A) (((A)+1)/2) #define BI_SIZEOF(A) (BI_ROUND(sizeof(A))) /* * For communicating access rights to the clients * * (placed in m_available hdr field of CA_PROTO_ACCESS_RIGHTS cmmd */ #define CA_PROTO_ACCESS_RIGHT_READ (1u<<0u) #define CA_PROTO_ACCESS_RIGHT_WRITE (1u<<1u) /* * All structures passed in the protocol must have individual * fields aligned on natural boundaries. * * NOTE: all structures declared in this file must have a * byte count which is evenly divisible by 8 matching * the largest atomic data type in db_access.h. */ #define CA_MESSAGE_ALIGN(A) (OCT_ROUND(A)<<3u) /* * the common part of each message sent/recv by the * CA server. */ typedef struct ca_hdr { ca_uint16_t m_cmmd; /* operation to be performed */ ca_uint16_t m_postsize; /* size of payload */ ca_uint16_t m_dataType; /* operation data type */ ca_uint16_t m_count; /* operation data count */ ca_uint32_t m_cid; /* channel identifier */ ca_uint32_t m_available; /* protocol stub dependent */ } caHdr; /* * for monitor (event) message extension */ struct mon_info { ca_float32_t m_lval; /* low delta */ ca_float32_t m_hval; /* high delta */ ca_float32_t m_toval; /* period btween samples */ ca_uint16_t m_mask; /* event select mask */ ca_uint16_t m_pad; /* extend to 32 bits */ }; /* * PV names greater than this length assumed to be invalid */ #define unreasonablePVNameSize 500u #endif /* __CAPROTO__ */ base-7.0.3.1/modules/ca/src/client/caRepeater.cpp0000664000577000060420000000234713557101274020300 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * CA UDP repeater standalone executable * * Author: Jeff Hill * Date: 3-27-90 * * PURPOSE: * Broadcasts fan out over the LAN, but old IP kernels do not allow * two processes on the same machine to get the same broadcast * (and modern IP kernels do not allow two processes on the same machine * to receive the same unicast). * * This code fans out UDP messages sent to the CA repeater port * to all CA client processes that have subscribed. * * NOTES: * * see repeater.c * */ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "epicsAssert.h" #include "osiUnistd.h" #include "udpiiu.h" int main() { chdir ( "/" ); ca_repeater (); return ( 0 ); } base-7.0.3.1/modules/ca/src/client/caRepeater.service@0000664000577000060420000000102513557101274021246 0ustar anjaesctl# # Linux systemd service file for the EPICS CA Repeater # # To install this file, as root: # cp caRepeater.service /etc/systemd/system # chmod 664 /etc/systemd/system/caRepeater.service # systemctl daemon-reload # systemctl enable caRepeater # systemctl start caRepeater # # To check the status: # systemctl status caRepeater [Unit] Description=EPICS CA Repeater Requires=network.target After=network.target [Service] ExecStart=@INSTALL_BIN@/caRepeater Restart=always User=daemon [Install] WantedBy=multi-user.target base-7.0.3.1/modules/ca/src/client/caServerID.h0000664000577000060420000000461513557101274017661 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #ifndef caServerIDh #define caServerIDh #include "osiSock.h" #include "resourceLib.h" #include "caProto.h" class caServerID { public: caServerID ( const struct sockaddr_in & addrIn, unsigned priority ); bool operator == ( const caServerID & ) const; resTableIndex hash () const; osiSockAddr address () const; unsigned priority () const; private: struct sockaddr_in addr; ca_uint8_t pri; }; inline caServerID::caServerID ( const struct sockaddr_in & addrIn, unsigned priorityIn ) : addr ( addrIn ), pri ( static_cast ( priorityIn ) ) { assert ( priorityIn <= 0xff ); } inline bool caServerID::operator == ( const caServerID & rhs ) const { if ( this->addr.sin_addr.s_addr == rhs.addr.sin_addr.s_addr && this->addr.sin_port == rhs.addr.sin_port && this->pri == rhs.pri ) { return true; } return false; } inline resTableIndex caServerID::hash () const { // start with a very small server table to speed // up the flush traverse for the frequent case - // a small numbers of servers const unsigned caServerMinIndexBitWidth = 2u; const unsigned caServerMaxIndexBitWidth = 32u; unsigned index; index = this->addr.sin_addr.s_addr; index ^= this->addr.sin_port; index ^= this->addr.sin_port >> 8u; index ^= this->pri; return integerHash ( caServerMinIndexBitWidth, caServerMaxIndexBitWidth, index ); } inline osiSockAddr caServerID::address () const { osiSockAddr tmp; tmp.ia = this->addr; return tmp; } inline unsigned caServerID::priority () const { return this->pri; } #endif // ifdef caServerID base-7.0.3.1/modules/ca/src/client/caVersion.h0000664000577000060420000000160013557101274017612 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef CAVERSION_H #define CAVERSION_H #include #include #ifndef VERSION_INT # define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P)) #endif /* include generated headers with: * EPICS_CA_MAJOR_VERSION * EPICS_CA_MINOR_VERSION * EPICS_CA_MAINTENANCE_VERSION * EPICS_CA_DEVELOPMENT_FLAG */ #include "caVersionNum.h" #define CA_VERSION_INT VERSION_INT(EPICS_CA_MAJOR_VERSION, EPICS_CA_MINOR_VERSION, EPICS_CA_MAINTENANCE_VERSION, 0) #endif // CAVERSION_H base-7.0.3.1/modules/ca/src/client/caVersionNum.h@0000664000577000060420000000047313557101274020401 0ustar anjaesctl#ifndef CAVERSION_H # error include caVersion.h, not this header #endif #define EPICS_CA_MAJOR_VERSION @EPICS_CA_MAJOR_VERSION@ #define EPICS_CA_MINOR_VERSION @EPICS_CA_MINOR_VERSION@ #define EPICS_CA_MAINTENANCE_VERSION @EPICS_CA_MAINTENANCE_VERSION@ #define EPICS_CA_DEVELOPMENT_FLAG @EPICS_CA_DEVELOPMENT_FLAG@ base-7.0.3.1/modules/ca/src/client/ca_client_context.cpp0000664000577000060420000006136513557101274021717 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifdef _MSC_VER # pragma warning(disable:4355) #endif #include #include // vxWorks 6.0 requires this include #include #include "epicsExit.h" #include "errlog.h" #include "locationException.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "oldAccess.h" #include "cac.h" epicsShareDef epicsThreadPrivateId caClientCallbackThreadId; static epicsThreadOnceId cacOnce = EPICS_THREAD_ONCE_INIT; const unsigned ca_client_context :: flushBlockThreshold = 0x58000; // runs once only for each process extern "C" void cacOnceFunc ( void * ) { caClientCallbackThreadId = epicsThreadPrivateCreate (); assert ( caClientCallbackThreadId ); ca_client_context::pDefaultServiceInstallMutex = newEpicsMutex; } extern epicsThreadPrivateId caClientContextId; cacService * ca_client_context::pDefaultService = 0; epicsMutex * ca_client_context::pDefaultServiceInstallMutex; ca_client_context::ca_client_context ( bool enablePreemptiveCallback ) : mutex(__FILE__, __LINE__), cbMutex(__FILE__, __LINE__), createdByThread ( epicsThreadGetIdSelf () ), ca_exception_func ( 0 ), ca_exception_arg ( 0 ), pVPrintfFunc ( errlogVprintf ), fdRegFunc ( 0 ), fdRegArg ( 0 ), pndRecvCnt ( 0u ), ioSeqNo ( 0u ), callbackThreadsPending ( 0u ), localPort ( 0 ), fdRegFuncNeedsToBeCalled ( false ), noWakeupSincePend ( true ) { static const unsigned short PORT_ANY = 0u; if ( ! osiSockAttach () ) { throwWithLocation ( noSocket () ); } epicsThreadOnce ( & cacOnce, cacOnceFunc, 0 ); { epicsGuard < epicsMutex > guard ( *ca_client_context::pDefaultServiceInstallMutex ); if ( ca_client_context::pDefaultService ) { this->pServiceContext.reset ( & ca_client_context::pDefaultService->contextCreate ( this->mutex, this->cbMutex, *this ) ); } else { this->pServiceContext.reset ( new cac ( this->mutex, this->cbMutex, *this ) ); } } this->sock = epicsSocketCreate ( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if ( this->sock == INVALID_SOCKET ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); this->printFormated ( "ca_client_context: unable to create " "datagram socket because = \"%s\"\n", sockErrBuf ); throwWithLocation ( noSocket () ); } { osiSockIoctl_t yes = true; int status = socket_ioctl ( this->sock, FIONBIO, & yes); if ( status < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); epicsSocketDestroy ( this->sock ); this->printFormated ( "%s: non blocking IO set fail because \"%s\"\n", __FILE__, sockErrBuf ); throwWithLocation ( noSocket () ); } } // force a bind to an unconstrained address so we can obtain // the local port number below { osiSockAddr addr; memset ( (char *)&addr, 0 , sizeof ( addr ) ); addr.ia.sin_family = AF_INET; addr.ia.sin_addr.s_addr = htonl ( INADDR_ANY ); addr.ia.sin_port = htons ( PORT_ANY ); int status = bind (this->sock, &addr.sa, sizeof (addr) ); if ( status < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); epicsSocketDestroy (this->sock); this->printFormated ( "CAC: unable to bind to an unconstrained " "address because = \"%s\"\n", sockErrBuf ); throwWithLocation ( noSocket () ); } } { osiSockAddr tmpAddr; osiSocklen_t saddr_length = sizeof ( tmpAddr ); int status = getsockname ( this->sock, & tmpAddr.sa, & saddr_length ); if ( status < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); epicsSocketDestroy ( this->sock ); this->printFormated ( "CAC: getsockname () error was \"%s\"\n", sockErrBuf ); throwWithLocation ( noSocket () ); } if ( tmpAddr.sa.sa_family != AF_INET) { epicsSocketDestroy ( this->sock ); this->printFormated ( "CAC: UDP socket was not inet addr family\n" ); throwWithLocation ( noSocket () ); } this->localPort = htons ( tmpAddr.ia.sin_port ); } std::auto_ptr < CallbackGuard > pCBGuard; if ( ! enablePreemptiveCallback ) { pCBGuard.reset ( new CallbackGuard ( this->cbMutex ) ); } // multiple steps ensure exception safety this->pCallbackGuard = pCBGuard; } ca_client_context::~ca_client_context () { if ( this->fdRegFunc ) { ( *this->fdRegFunc ) ( this->fdRegArg, this->sock, false ); } epicsSocketDestroy ( this->sock ); osiSockRelease (); // force a logical shutdown order // so that the cac class does not hang its // receive threads during their shutdown sequence // and so that classes using this classes mutex // are destroyed before the mutex is destroyed if ( this->pCallbackGuard.get() ) { epicsGuardRelease < epicsMutex > unguard ( *this->pCallbackGuard ); this->pServiceContext.reset ( 0 ); } else { this->pServiceContext.reset ( 0 ); } } void ca_client_context::destroyGetCopy ( epicsGuard < epicsMutex > & guard, getCopy & gc ) { guard.assertIdenticalMutex ( this->mutex ); gc.~getCopy (); this->getCopyFreeList.release ( & gc ); } void ca_client_context::destroyGetCallback ( epicsGuard < epicsMutex > & guard, getCallback & gcb ) { guard.assertIdenticalMutex ( this->mutex ); gcb.~getCallback (); this->getCallbackFreeList.release ( & gcb ); } void ca_client_context::destroyPutCallback ( epicsGuard < epicsMutex > & guard, putCallback & pcb ) { guard.assertIdenticalMutex ( this->mutex ); pcb.~putCallback (); this->putCallbackFreeList.release ( & pcb ); } void ca_client_context::destroySubscription ( epicsGuard < epicsMutex > & guard, oldSubscription & os ) { guard.assertIdenticalMutex ( this->mutex ); os.~oldSubscription (); this->subscriptionFreeList.release ( & os ); } void ca_client_context::changeExceptionEvent ( caExceptionHandler * pfunc, void * arg ) { epicsGuard < epicsMutex > guard ( this->mutex ); this->ca_exception_func = pfunc; this->ca_exception_arg = arg; // should block here until releated callback in progress completes } void ca_client_context::replaceErrLogHandler ( caPrintfFunc * ca_printf_func ) { epicsGuard < epicsMutex > guard ( this->mutex ); if ( ca_printf_func ) { this->pVPrintfFunc = ca_printf_func; } else { this->pVPrintfFunc = epicsVprintf; } // should block here until releated callback in progress completes } void ca_client_context::registerForFileDescriptorCallBack ( CAFDHANDLER *pFunc, void *pArg ) { epicsGuard < epicsMutex > guard ( this->mutex ); this->fdRegFunc = pFunc; this->fdRegArg = pArg; this->fdRegFuncNeedsToBeCalled = true; if ( pFunc ) { // the receive thread might already be blocking // w/o having sent the wakeup message this->_sendWakeupMsg (); } // should block here until releated callback in progress completes } int ca_client_context :: printFormated ( const char *pformat, ... ) const { va_list theArgs; int status; va_start ( theArgs, pformat ); status = this->ca_client_context :: varArgsPrintFormated ( pformat, theArgs ); va_end ( theArgs ); return status; } int ca_client_context :: varArgsPrintFormated ( const char *pformat, va_list args ) const { caPrintfFunc * pFunc; { epicsGuard < epicsMutex > guard ( this->mutex ); pFunc = this->pVPrintfFunc; } if ( pFunc ) { return ( *pFunc ) ( pformat, args ); } else { return :: vfprintf ( stderr, pformat, args ); } } void ca_client_context::exception ( epicsGuard < epicsMutex > & guard, int stat, const char * pCtx, const char * pFile, unsigned lineNo ) { struct exception_handler_args args; caExceptionHandler * pFunc = this->ca_exception_func; void * pArg = this->ca_exception_arg; { epicsGuardRelease < epicsMutex > unguard ( guard ); // NOOP if they disable exceptions if ( pFunc ) { args.chid = NULL; args.type = TYPENOTCONN; args.count = 0; args.addr = NULL; args.stat = stat; args.op = CA_OP_OTHER; args.ctx = pCtx; args.pFile = pFile; args.lineNo = lineNo; args.usr = pArg; ( *pFunc ) ( args ); } else { this->signal ( stat, pFile, lineNo, pCtx ); } } } void ca_client_context::exception ( epicsGuard < epicsMutex > & guard, int status, const char * pContext, const char * pFileName, unsigned lineNo, oldChannelNotify & chan, unsigned type, arrayElementCount count, unsigned op ) { struct exception_handler_args args; caExceptionHandler * pFunc = this->ca_exception_func; void * pArg = this->ca_exception_arg; { epicsGuardRelease < epicsMutex > unguard ( guard ); // NOOP if they disable exceptions if ( pFunc ) { args.chid = &chan; args.type = type; args.count = count; args.addr = NULL; args.stat = status; args.op = op; args.ctx = pContext; args.pFile = pFileName; args.lineNo = lineNo; args.usr = pArg; ( *pFunc ) ( args ); } else { this->signal ( status, pFileName, lineNo, "op=%u, channel=%s, type=%s, count=%lu, ctx=\"%s\"", op, ca_name ( &chan ), dbr_type_to_text ( static_cast ( type ) ), count, pContext ); } } } void ca_client_context::signal ( int ca_status, const char * pfilenm, int lineno, const char * pFormat, ... ) { va_list theArgs; va_start ( theArgs, pFormat ); this->vSignal ( ca_status, pfilenm, lineno, pFormat, theArgs); va_end ( theArgs ); } void ca_client_context :: vSignal ( int ca_status, const char *pfilenm, int lineno, const char *pFormat, va_list args ) { static const char *severity[] = { "Warning", "Success", "Error", "Info", "Fatal", "Fatal", "Fatal", "Fatal" }; this->printFormated ( "CA.Client.Exception...............................................\n" ); this->printFormated ( " %s: \"%s\"\n", severity[ CA_EXTRACT_SEVERITY ( ca_status ) ], ca_message ( ca_status ) ); if ( pFormat ) { this->printFormated ( " Context: \"" ); this->varArgsPrintFormated ( pFormat, args ); this->printFormated ( "\"\n" ); } if ( pfilenm ) { this->printFormated ( " Source File: %s line %d\n", pfilenm, lineno ); } epicsTime current = epicsTime::getCurrent (); char date[64]; current.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); this->printFormated ( " Current Time: %s\n", date ); /* * Terminate execution if unsuccessful */ if( ! ( ca_status & CA_M_SUCCESS ) && CA_EXTRACT_SEVERITY ( ca_status ) != CA_K_WARNING ){ errlogFlush (); abort (); } this->printFormated ( "..................................................................\n" ); } void ca_client_context::show ( unsigned level ) const { epicsGuard < epicsMutex > guard ( this->mutex ); ::printf ( "ca_client_context at %p pndRecvCnt=%u ioSeqNo=%u\n", static_cast ( this ), this->pndRecvCnt, this->ioSeqNo ); if ( level > 0u ) { this->pServiceContext->show ( guard, level - 1u ); ::printf ( "\tpreemptive callback is %s\n", this->pCallbackGuard.get() ? "disabled" : "enabled" ); ::printf ( "\tthere are %u unsatisfied IO operations blocking ca_pend_io()\n", this->pndRecvCnt ); ::printf ( "\tthe current io sequence number is %u\n", this->ioSeqNo ); ::printf ( "IO done event:\n"); this->ioDone.show ( level - 1u ); ::printf ( "Synchronous group identifier hash table:\n" ); this->sgTable.show ( level - 1u ); } } void ca_client_context::attachToClientCtx () { assert ( ! epicsThreadPrivateGet ( caClientContextId ) ); epicsThreadPrivateSet ( caClientContextId, this ); } void ca_client_context::incrementOutstandingIO ( epicsGuard < epicsMutex > & guard, unsigned ioSeqNoIn ) { guard.assertIdenticalMutex ( this->mutex ); if ( this->ioSeqNo == ioSeqNoIn ) { assert ( this->pndRecvCnt < UINT_MAX ); this->pndRecvCnt++; } } void ca_client_context::decrementOutstandingIO ( epicsGuard < epicsMutex > & guard, unsigned ioSeqNoIn ) { guard.assertIdenticalMutex ( this->mutex ); if ( this->ioSeqNo == ioSeqNoIn ) { assert ( this->pndRecvCnt > 0u ); this->pndRecvCnt--; if ( this->pndRecvCnt == 0u ) { this->ioDone.signal (); } } } // !!!! This routine is only visible in the old interface - or in a new ST interface. // !!!! In the old interface we restrict thread attach so that calls from threads // !!!! other than the initializing thread are not allowed if preemptive callback // !!!! is disabled. This prevents the preemptive callback lock from being released // !!!! by other threads than the one that locked it. // int ca_client_context::pendIO ( const double & timeout ) { // prevent recursion nightmares by disabling calls to // pendIO () from within a CA callback. if ( epicsThreadPrivateGet ( caClientCallbackThreadId ) ) { return ECA_EVDISALLOW; } int status = ECA_NORMAL; epicsTime beg_time = epicsTime::getMonotonic (); double remaining = timeout; epicsGuard < epicsMutex > guard ( this->mutex ); this->flush ( guard ); while ( this->pndRecvCnt > 0 ) { if ( remaining < CAC_SIGNIFICANT_DELAY ) { status = ECA_TIMEOUT; break; } { epicsGuardRelease < epicsMutex > unguard ( guard ); this->blockForEventAndEnableCallbacks ( this->ioDone, remaining ); } double delay = epicsTime::getMonotonic () - beg_time; if ( delay < timeout ) { remaining = timeout - delay; } else { remaining = 0.0; } } this->ioSeqNo++; this->pndRecvCnt = 0u; return status; } // !!!! This routine is only visible in the old interface - or in a new ST interface. // !!!! In the old interface we restrict thread attach so that calls from threads // !!!! other than the initializing thread are not allowed if preemptive callback // !!!! is disabled. This prevents the preemptive callback lock from being released // !!!! by other threads than the one that locked it. // int ca_client_context::pendEvent ( const double & timeout ) { // prevent recursion nightmares by disabling calls to // pendIO () from within a CA callback. if ( epicsThreadPrivateGet ( caClientCallbackThreadId ) ) { return ECA_EVDISALLOW; } epicsTime current = epicsTime::getMonotonic (); { epicsGuard < epicsMutex > guard ( this->mutex ); this->flush ( guard ); } // process at least once if preemptive callback is disabled if ( this->pCallbackGuard.get() ) { epicsGuardRelease < epicsMutex > cbUnguard ( *this->pCallbackGuard ); epicsGuard < epicsMutex > guard ( this->mutex ); // // This is needed because in non-preemptive callback mode // legacy applications that use file descriptor managers // will register for ca receive thread activity and keep // calling ca_pend_event until all of the socket data has // been read. We must guarantee that other threads get a // chance to run if there is data in any of the sockets. // if ( this->fdRegFunc ) { epicsGuardRelease < epicsMutex > unguard ( guard ); // remove short udp message sent to wake // up a file descriptor manager osiSockAddr tmpAddr; osiSocklen_t addrSize = sizeof ( tmpAddr.sa ); char buf = 0; int status = 0; do { status = recvfrom ( this->sock, & buf, sizeof ( buf ), 0, & tmpAddr.sa, & addrSize ); } while ( status > 0 ); } while ( this->callbackThreadsPending > 0 ) { epicsGuardRelease < epicsMutex > unguard ( guard ); this->callbackThreadActivityComplete.wait ( 30.0 ); } this->noWakeupSincePend = true; } double elapsed = epicsTime::getMonotonic() - current; double delay; if ( timeout > elapsed ) { delay = timeout - elapsed; } else { delay = 0.0; } if ( delay >= CAC_SIGNIFICANT_DELAY ) { if ( this->pCallbackGuard.get() ) { epicsGuardRelease < epicsMutex > unguard ( *this->pCallbackGuard ); epicsThreadSleep ( delay ); } else { epicsThreadSleep ( delay ); } } return ECA_TIMEOUT; } void ca_client_context::blockForEventAndEnableCallbacks ( epicsEvent & event, const double & timeout ) { if ( this->pCallbackGuard.get() ) { epicsGuardRelease < epicsMutex > unguard ( *this->pCallbackGuard ); event.wait ( timeout ); } else { event.wait ( timeout ); } } void ca_client_context::callbackProcessingInitiateNotify () { // if preemptive callback is enabled then this is a noop if ( this->pCallbackGuard.get() ) { bool sendNeeded = false; { epicsGuard < epicsMutex > guard ( this->mutex ); this->callbackThreadsPending++; if ( this->fdRegFunc && this->noWakeupSincePend ) { this->noWakeupSincePend = false; sendNeeded = true; } } if ( sendNeeded ) { _sendWakeupMsg (); } } } void ca_client_context :: _sendWakeupMsg () { // send short udp message to wake up a file descriptor manager // when a message arrives osiSockAddr tmpAddr; tmpAddr.ia.sin_family = AF_INET; tmpAddr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK ); tmpAddr.ia.sin_port = htons ( this->localPort ); char buf = 0; sendto ( this->sock, & buf, sizeof ( buf ), 0, & tmpAddr.sa, sizeof ( tmpAddr.sa ) ); } void ca_client_context::callbackProcessingCompleteNotify () { // if preemptive callback is enabled then this is a noop if ( this->pCallbackGuard.get() ) { bool signalNeeded = false; { epicsGuard < epicsMutex > guard ( this->mutex ); if ( this->callbackThreadsPending <= 1 ) { if ( this->callbackThreadsPending == 1 ) { this->callbackThreadsPending = 0; signalNeeded = true; } } else { this->callbackThreadsPending--; } } if ( signalNeeded ) { this->callbackThreadActivityComplete.signal (); } } } cacChannel & ca_client_context::createChannel ( epicsGuard < epicsMutex > & guard, const char * pChannelName, cacChannelNotify & chan, cacChannel::priLev pri ) { guard.assertIdenticalMutex ( this->mutex ); return this->pServiceContext->createChannel ( guard, pChannelName, chan, pri ); } void ca_client_context::flush ( epicsGuard < epicsMutex > & guard ) { this->pServiceContext->flush ( guard ); } unsigned ca_client_context::circuitCount () const { epicsGuard < epicsMutex > guard ( this->mutex ); return this->pServiceContext->circuitCount ( guard ); } unsigned ca_client_context::beaconAnomaliesSinceProgramStart () const { epicsGuard < epicsMutex > guard ( this->mutex ); return this->pServiceContext->beaconAnomaliesSinceProgramStart ( guard ); } void ca_client_context::installCASG ( epicsGuard < epicsMutex > & guard, CASG & sg ) { guard.assertIdenticalMutex ( this->mutex ); this->sgTable.idAssignAdd ( sg ); } void ca_client_context::uninstallCASG ( epicsGuard < epicsMutex > & guard, CASG & sg ) { guard.assertIdenticalMutex ( this->mutex ); this->sgTable.remove ( sg ); } CASG * ca_client_context::lookupCASG ( epicsGuard < epicsMutex > & guard, unsigned idIn ) { guard.assertIdenticalMutex ( this->mutex ); CASG * psg = this->sgTable.lookup ( idIn ); if ( psg ) { if ( ! psg->verify ( guard ) ) { psg = 0; } } return psg; } void ca_client_context::selfTest () const { epicsGuard < epicsMutex > guard ( this->mutex ); this->sgTable.verify (); this->pServiceContext->selfTest ( guard ); } epicsMutex & ca_client_context::mutexRef () const { return this->mutex; } cacContext & ca_client_context::createNetworkContext ( epicsMutex & mutexIn, epicsMutex & cbMutexIn ) { return * new cac ( mutexIn, cbMutexIn, *this ); } void ca_client_context::installDefaultService ( cacService & service ) { epicsThreadOnce ( & cacOnce, cacOnceFunc, 0 ); epicsGuard < epicsMutex > guard ( *ca_client_context::pDefaultServiceInstallMutex ); if ( ca_client_context::pDefaultService ) { throw std::logic_error ( "CA in-memory service already installed and can't be replaced"); } ca_client_context::pDefaultService = & service; } void epicsShareAPI caInstallDefaultService ( cacService & service ) { ca_client_context::installDefaultService ( service ); } epicsShareFunc int epicsShareAPI ca_clear_subscription ( evid pMon ) { oldChannelNotify & chan = pMon->channel (); ca_client_context & cac = chan.getClientCtx (); // !!!! the order in which we take the mutex here prevents deadlocks { epicsGuard < epicsMutex > guard ( cac.mutex ); try { // if this stalls out on a live circuit then an exception // can be forthcoming which we must ignore as the clear // request must always be successful chan.eliminateExcessiveSendBacklog ( guard ); } catch ( cacChannel::notConnected & ) { // intentionally ignored } } if ( cac.pCallbackGuard.get() && cac.createdByThread == epicsThreadGetIdSelf () ) { epicsGuard < epicsMutex > guard ( cac.mutex ); pMon->cancel ( *cac.pCallbackGuard.get(), guard ); } else { // // we will definately stall out here if all of the // following are true // // o user creates non-preemtive mode client library context // o user doesnt periodically call a ca function // o user calls this function from an auxiillary thread // CallbackGuard cbGuard ( cac.cbMutex ); epicsGuard < epicsMutex > guard ( cac.mutex ); pMon->cancel ( cbGuard, guard ); } return ECA_NORMAL; } void ca_client_context :: eliminateExcessiveSendBacklog ( epicsGuard < epicsMutex > & guard, cacChannel & chan ) { if ( chan.requestMessageBytesPending ( guard ) > ca_client_context :: flushBlockThreshold ) { if ( this->pCallbackGuard.get() && this->createdByThread == epicsThreadGetIdSelf () ) { // we need to be very careful about lock hierarchy // inversion in this situation epicsGuardRelease < epicsMutex > unguard ( guard ); { epicsGuardRelease < epicsMutex > cbunguard ( * this->pCallbackGuard.get() ); { epicsGuard < epicsMutex > nestedGuard ( this->mutex ); chan.flush ( nestedGuard ); } } } else { chan.flush ( guard ); } } } base-7.0.3.1/modules/ca/src/client/cac.cpp0000664000577000060420000012402313557101274016747 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill * */ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include #include #include // vxWorks 6.0 requires this include #include "dbDefs.h" #include "epicsGuard.h" #include "epicsVersion.h" #include "osiProcess.h" #include "epicsSignal.h" #include "envDefs.h" #include "locationException.h" #include "errlog.h" #include "epicsExport.h" #define epicsExportSharedSymbols #include "addrList.h" #include "iocinf.h" #include "cac.h" #include "inetAddrID.h" #include "caServerID.h" #include "virtualCircuit.h" #include "syncGroup.h" #include "nciu.h" #include "autoPtrRecycle.h" #include "msgForMultiplyDefinedPV.h" #include "udpiiu.h" #include "bhe.h" #include "net_convert.h" #include "autoPtrFreeList.h" #include "noopiiu.h" static const char pVersionCAC[] = "@(#) " EPICS_VERSION_STRING ", CA Client Library " __DATE__; // TCP response dispatch table const cac::pProtoStubTCP cac::tcpJumpTableCAC [] = { &cac::versionAction, &cac::eventRespAction, &cac::badTCPRespAction, &cac::readRespAction, &cac::badTCPRespAction, &cac::badTCPRespAction, &cac::searchRespAction, &cac::badTCPRespAction, &cac::badTCPRespAction, &cac::badTCPRespAction, // legacy CA_PROTO_READ_SYNC used as an echo with legacy server &cac::echoRespAction, &cac::exceptionRespAction, &cac::clearChannelRespAction, &cac::badTCPRespAction, &cac::badTCPRespAction, &cac::readNotifyRespAction, &cac::badTCPRespAction, &cac::badTCPRespAction, &cac::createChannelRespAction, &cac::writeNotifyRespAction, &cac::badTCPRespAction, &cac::badTCPRespAction, &cac::accessRightsRespAction, &cac::echoRespAction, &cac::badTCPRespAction, &cac::badTCPRespAction, &cac::verifyAndDisconnectChan, &cac::verifyAndDisconnectChan }; // TCP exception dispatch table const cac::pExcepProtoStubTCP cac::tcpExcepJumpTableCAC [] = { &cac::defaultExcep, // CA_PROTO_VERSION &cac::eventAddExcep, // CA_PROTO_EVENT_ADD &cac::defaultExcep, // CA_PROTO_EVENT_CANCEL &cac::readExcep, // CA_PROTO_READ &cac::writeExcep, // CA_PROTO_WRITE &cac::defaultExcep, // CA_PROTO_SNAPSHOT &cac::defaultExcep, // CA_PROTO_SEARCH &cac::defaultExcep, // CA_PROTO_BUILD &cac::defaultExcep, // CA_PROTO_EVENTS_OFF &cac::defaultExcep, // CA_PROTO_EVENTS_ON &cac::defaultExcep, // CA_PROTO_READ_SYNC &cac::defaultExcep, // CA_PROTO_ERROR &cac::defaultExcep, // CA_PROTO_CLEAR_CHANNEL &cac::defaultExcep, // CA_PROTO_RSRV_IS_UP &cac::defaultExcep, // CA_PROTO_NOT_FOUND &cac::readNotifyExcep, // CA_PROTO_READ_NOTIFY &cac::defaultExcep, // CA_PROTO_READ_BUILD &cac::defaultExcep, // REPEATER_CONFIRM &cac::defaultExcep, // CA_PROTO_CREATE_CHAN &cac::writeNotifyExcep, // CA_PROTO_WRITE_NOTIFY &cac::defaultExcep, // CA_PROTO_CLIENT_NAME &cac::defaultExcep, // CA_PROTO_HOST_NAME &cac::defaultExcep, // CA_PROTO_ACCESS_RIGHTS &cac::defaultExcep, // CA_PROTO_ECHO &cac::defaultExcep, // REPEATER_REGISTER &cac::defaultExcep, // CA_PROTO_SIGNAL &cac::defaultExcep, // CA_PROTO_CREATE_CH_FAIL &cac::defaultExcep // CA_PROTO_SERVER_DISCONN }; // // cac::cac () // cac::cac ( epicsMutex & mutualExclusionIn, epicsMutex & callbackControlIn, cacContextNotify & notifyIn ) : _refLocalHostName ( localHostNameCache.getReference () ), programBeginTime ( epicsTime::getMonotonic() ), connTMO ( CA_CONN_VERIFY_PERIOD ), mutex ( mutualExclusionIn ), cbMutex ( callbackControlIn ), ipToAEngine ( ipAddrToAsciiEngine::allocate () ), timerQueue ( epicsTimerQueueActive::allocate ( false, lowestPriorityLevelAbove(epicsThreadGetPrioritySelf()) ) ), pUserName ( 0 ), pudpiiu ( 0 ), tcpSmallRecvBufFreeList ( 0 ), tcpLargeRecvBufFreeList ( 0 ), notify ( notifyIn ), initializingThreadsId ( epicsThreadGetIdSelf() ), initializingThreadsPriority ( epicsThreadGetPrioritySelf() ), maxRecvBytesTCP ( MAX_TCP ), maxContigFrames ( contiguousMsgCountWhichTriggersFlowControl ), beaconAnomalyCount ( 0u ), iiuExistenceCount ( 0u ), cacShutdownInProgress ( false ) { if ( ! osiSockAttach () ) { throwWithLocation ( udpiiu :: noSocket () ); } try { long status; /* * Certain os, such as HPUX, do not unblock a socket system call * when another thread asynchronously calls both shutdown() and * close(). To solve this problem we need to employ OS specific * mechanisms. */ epicsSignalInstallSigAlarmIgnore (); epicsSignalInstallSigPipeIgnore (); { char tmp[256]; size_t len; osiGetUserNameReturn gunRet; gunRet = osiGetUserName ( tmp, sizeof (tmp) ); if ( gunRet != osiGetUserNameSuccess ) { tmp[0] = '\0'; } len = strlen ( tmp ) + 1; this->pUserName = new char [ len ]; strncpy ( this->pUserName, tmp, len ); } this->_serverPort = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT, static_cast (CA_SERVER_PORT) ); status = envGetDoubleConfigParam ( &EPICS_CA_CONN_TMO, &this->connTMO ); if ( status ) { this->connTMO = CA_CONN_VERIFY_PERIOD; epicsGuard < epicsMutex > cbGuard ( this->cbMutex ); errlogPrintf ( "EPICS \"%s\" double fetch failed\n", EPICS_CA_CONN_TMO.name ); errlogPrintf ( "Defaulting \"%s\" = %f\n", EPICS_CA_CONN_TMO.name, this->connTMO ); } long maxBytesAsALong; status = envGetLongConfigParam ( &EPICS_CA_MAX_ARRAY_BYTES, &maxBytesAsALong ); if ( status || maxBytesAsALong < 0 ) { errlogPrintf ( "cac: EPICS_CA_MAX_ARRAY_BYTES was not a positive integer\n" ); } else { /* allow room for the protocol header so that they get the array size they requested */ static const unsigned headerSize = sizeof ( caHdr ) + 2 * sizeof ( ca_uint32_t ); ca_uint32_t maxBytes = ( unsigned ) maxBytesAsALong; if ( maxBytes < 0xffffffff - headerSize ) { maxBytes += headerSize; } else { maxBytes = 0xffffffff; } if ( maxBytes < MAX_TCP ) { errlogPrintf ( "cac: EPICS_CA_MAX_ARRAY_BYTES was rounded up to %u\n", MAX_TCP ); } else { this->maxRecvBytesTCP = maxBytes; } } freeListInitPvt ( &this->tcpSmallRecvBufFreeList, MAX_TCP, 1 ); if ( ! this->tcpSmallRecvBufFreeList ) { throw std::bad_alloc (); } int autoMaxBytes; if(envGetBoolConfigParam(&EPICS_CA_AUTO_ARRAY_BYTES, &autoMaxBytes)) autoMaxBytes = 1; if(!autoMaxBytes) { freeListInitPvt ( &this->tcpLargeRecvBufFreeList, this->maxRecvBytesTCP, 1 ); if ( ! this->tcpLargeRecvBufFreeList ) { throw std::bad_alloc (); } } unsigned bufsPerArray = this->maxRecvBytesTCP / comBuf::capacityBytes (); if ( bufsPerArray > 1u ) { maxContigFrames = bufsPerArray * contiguousMsgCountWhichTriggersFlowControl; } } catch ( ... ) { osiSockRelease (); delete [] this->pUserName; freeListCleanup ( this->tcpSmallRecvBufFreeList ); if ( this->tcpLargeRecvBufFreeList ) { freeListCleanup ( this->tcpLargeRecvBufFreeList ); } this->timerQueue.release (); throw; } /* * load user configured tcp name server address list, * create virtual circuits, and add them to server table */ ELLLIST dest, tmpList; ellInit ( & dest ); ellInit ( & tmpList ); addAddrToChannelAccessAddressList ( &tmpList, &EPICS_CA_NAME_SERVERS, this->_serverPort, false ); removeDuplicateAddresses ( &dest, &tmpList, 0 ); epicsGuard < epicsMutex > guard ( this->mutex ); while ( osiSockAddrNode * pNode = reinterpret_cast < osiSockAddrNode * > ( ellGet ( & dest ) ) ) { tcpiiu * piiu = NULL; SearchDestTCP * pdst = new SearchDestTCP ( *this, pNode->addr ); this->registerSearchDest ( guard, * pdst ); /* Initially assume that servers listed in EPICS_CA_NAME_SERVERS support at least minor * version 11. This causes tcpiiu to send the user and host name authentication * messages. When the actual Version message is received from the server it will * be overwrite this assumption. */ bool newIIU = findOrCreateVirtCircuit ( guard, pNode->addr, cacChannel::priorityDefault, piiu, 11, pdst ); free ( pNode ); if ( newIIU ) { piiu->start ( guard ); } } } cac::~cac () { // this blocks until the UDP thread exits so that // it will not sneak in any new clients // // lock intentionally not held here so that we dont deadlock // waiting for the UDP thread to exit while it is waiting to // get the lock. { epicsGuard < epicsMutex > cbGuard ( this->cbMutex ); epicsGuard < epicsMutex > guard ( this->mutex ); if ( this->pudpiiu ) { this->pudpiiu->shutdown ( cbGuard, guard ); // make sure no new tcp circuits are created this->cacShutdownInProgress = true; // // shutdown all tcp circuits // tsDLIter < tcpiiu > iter = this->circuitList.firstIter (); while ( iter.valid() ) { // this causes a clean shutdown to occur iter->unlinkAllChannels ( cbGuard, guard ); iter++; } } } // // wait for all tcp threads to exit // // this will block for oustanding sends to go out so dont // hold a lock while waiting // { epicsGuard < epicsMutex > guard ( this->mutex ); while ( this->iiuExistenceCount > 0 ) { epicsGuardRelease < epicsMutex > unguard ( guard ); this->iiuUninstall.wait (); } } if ( this->pudpiiu ) { delete this->pudpiiu; } freeListCleanup ( this->tcpSmallRecvBufFreeList ); if ( this->tcpLargeRecvBufFreeList ) { freeListCleanup ( this->tcpLargeRecvBufFreeList ); } delete [] this->pUserName; tsSLList < bhe > tmpBeaconList; this->beaconTable.removeAll ( tmpBeaconList ); while ( bhe * pBHE = tmpBeaconList.get() ) { pBHE->~bhe (); this->bheFreeList.release ( pBHE ); } this->timerQueue.release (); this->ipToAEngine.release (); // clean-up the list of un-notified msg objects while ( msgForMultiplyDefinedPV * msg = this->msgMultiPVList.get() ) { msg->~msgForMultiplyDefinedPV (); this->mdpvFreeList.release ( msg ); } errlogFlush (); osiSockRelease (); // its ok for channels and subscriptions to still // exist at this point. The user created them and // its his responsibility to clean them up. } unsigned cac::lowestPriorityLevelAbove ( unsigned priority ) { unsigned abovePriority; epicsThreadBooleanStatus tbs; tbs = epicsThreadLowestPriorityLevelAbove ( priority, & abovePriority ); if ( tbs != epicsThreadBooleanStatusSuccess ) { abovePriority = priority; } return abovePriority; } unsigned cac::highestPriorityLevelBelow ( unsigned priority ) { unsigned belowPriority; epicsThreadBooleanStatus tbs; tbs = epicsThreadHighestPriorityLevelBelow ( priority, & belowPriority ); if ( tbs != epicsThreadBooleanStatusSuccess ) { belowPriority = priority; } return belowPriority; } // // set the push pending flag on all virtual circuits // void cac::flush ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); tsDLIter < tcpiiu > iter = this->circuitList.firstIter (); while ( iter.valid () ) { iter->flushRequest ( guard ); iter++; } } unsigned cac::circuitCount ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); return this->circuitList.count (); } void cac::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( this->mutex ); ::printf ( "Channel Access Client Context at %p for user %s\n", static_cast ( this ), this->pUserName ); // this also supresses the "defined, but not used" // warning message ::printf ( "\trevision \"%s\"\n", pVersionCAC ); if ( level > 0u ) { this->serverTable.show ( level - 1u ); ::printf ( "\tconnection time out watchdog period %f\n", this->connTMO ); } if ( level > 1u ) { if ( this->pudpiiu ) { this->pudpiiu->show ( level - 2u ); } } if ( level > 2u ) { ::printf ( "Program begin time:\n"); this->programBeginTime.show ( level - 3u ); ::printf ( "Channel identifier hash table:\n" ); this->chanTable.show ( level - 3u ); ::printf ( "IO identifier hash table:\n" ); this->ioTable.show ( level - 3u ); ::printf ( "Beacon source identifier hash table:\n" ); this->beaconTable.show ( level - 3u ); ::printf ( "Timer queue:\n" ); this->timerQueue.show ( level - 3u ); ::printf ( "IP address to name conversion engine:\n" ); this->ipToAEngine.show ( level - 3u ); } if ( level > 3u ) { ::printf ( "Default mutex:\n"); this->mutex.show ( level - 4u ); ::printf ( "mutex:\n" ); this->mutex.show ( level - 4u ); } } /* * cac::beaconNotify */ void cac::beaconNotify ( const inetAddrID & addr, const epicsTime & currentTime, ca_uint32_t beaconNumber, unsigned protocolRevision ) { epicsGuard < epicsMutex > guard ( this->mutex ); if ( ! this->pudpiiu ) { return; } /* * look for it in the hash table */ bhe *pBHE = this->beaconTable.lookup ( addr ); if ( pBHE ) { /* * return if the beacon period has not changed significantly */ if ( ! pBHE->updatePeriod ( guard, this->programBeginTime, currentTime, beaconNumber, protocolRevision ) ) { return; } } else { /* * This is the first beacon seen from this server. * Wait until 2nd beacon is seen before deciding * if it is a new server (or just the first * time that we have seen a server's beacon * shortly after the program started up) */ pBHE = new ( this->bheFreeList ) bhe ( this->mutex, currentTime, beaconNumber, addr ); if ( pBHE ) { if ( this->beaconTable.add ( *pBHE ) < 0 ) { pBHE->~bhe (); this->bheFreeList.release ( pBHE ); } } return; } this->beaconAnomalyCount++; this->pudpiiu->beaconAnomalyNotify ( guard ); # ifdef DEBUG { char buf[128]; addr.name ( buf, sizeof ( buf ) ); ::printf ( "New server available: %s\n", buf ); } # endif } cacChannel & cac::createChannel ( epicsGuard < epicsMutex > & guard, const char * pName, cacChannelNotify & chan, cacChannel::priLev pri ) { guard.assertIdenticalMutex ( this->mutex ); if ( pri > cacChannel::priorityMax ) { throw cacChannel::badPriority (); } if ( pName == 0 || pName[0] == '\0' ) { throw cacChannel::badString (); } if ( ! this->pudpiiu ) { this->pudpiiu = new udpiiu ( guard, this->timerQueue, this->cbMutex, this->mutex, this->notify, *this, this->_serverPort, this->searchDestList ); } nciu * pNetChan = new ( this->channelFreeList ) nciu ( *this, noopIIU, chan, pName, pri ); this->chanTable.idAssignAdd ( *pNetChan ); return *pNetChan; } bool cac::findOrCreateVirtCircuit ( epicsGuard < epicsMutex > & guard, const osiSockAddr & addr, unsigned priority, tcpiiu *& piiu, unsigned minorVersionNumber, SearchDestTCP * pSearchDest ) { guard.assertIdenticalMutex ( this->mutex ); bool newIIU = false; if ( piiu ) { if ( ! piiu->alive ( guard ) ) { return newIIU; } } else { try { autoPtrFreeList < tcpiiu, 32, epicsMutexNOOP > pnewiiu ( this->freeListVirtualCircuit, new ( this->freeListVirtualCircuit ) tcpiiu ( *this, this->mutex, this->cbMutex, this->notify, this->connTMO, this->timerQueue, addr, this->comBufMemMgr, minorVersionNumber, this->ipToAEngine, priority, pSearchDest ) ); bhe * pBHE = this->beaconTable.lookup ( addr.ia ); if ( ! pBHE ) { pBHE = new ( this->bheFreeList ) bhe ( this->mutex, epicsTime (), 0u, addr.ia ); if ( this->beaconTable.add ( *pBHE ) < 0 ) { return newIIU; } } this->serverTable.add ( *pnewiiu ); this->circuitList.add ( *pnewiiu ); this->iiuExistenceCount++; pBHE->registerIIU ( guard, *pnewiiu ); piiu = pnewiiu.release (); newIIU = true; } catch ( std :: exception & except ) { errlogPrintf ( "CAC: exception during virtual circuit creation \"%s\"\n", except.what () ); return newIIU; } catch ( ... ) { errlogPrintf ( "CAC: Nonstandard exception during virtual circuit creation\n" ); return newIIU; } } return newIIU; } void cac::transferChanToVirtCircuit ( unsigned cid, unsigned sid, ca_uint16_t typeCode, arrayElementCount count, unsigned minorVersionNumber, const osiSockAddr & addr, const epicsTime & currentTime ) { if ( addr.sa.sa_family != AF_INET ) { return; } epicsGuard < epicsMutex > guard ( this->mutex ); /* * Do not open new circuits while the cac is shutting down */ if ( this->cacShutdownInProgress ) { return; } /* * ignore search replies for deleted channels */ nciu * pChan = this->chanTable.lookup ( cid ); if ( ! pChan ) { return; } /* * Ignore duplicate search replies */ osiSockAddr chanAddr = pChan->getPIIU(guard)->getNetworkAddress (guard); if ( chanAddr.sa.sa_family != AF_UNSPEC ) { if ( ! sockAddrAreIdentical ( &addr, &chanAddr ) ) { char acc[64]; pChan->getPIIU(guard)->getHostName ( guard, acc, sizeof ( acc ) ); msgForMultiplyDefinedPV * pMsg = new ( this->mdpvFreeList ) msgForMultiplyDefinedPV ( this->ipToAEngine, *this, pChan->pName ( guard ), acc ); // cac keeps a list of these objects for proper clean-up in ~cac this->msgMultiPVList.add ( *pMsg ); // It is possible for the ioInitiate call below to // call the callback directly if queue quota is exceeded. // This callback takes the callback lock and therefore we // must release the primary mutex here to avoid a lock // hierarchy inversion. epicsGuardRelease < epicsMutex > unguard ( guard ); pMsg->ioInitiate ( addr ); } return; } caServerID servID ( addr.ia, pChan->getPriority(guard) ); tcpiiu * piiu = this->serverTable.lookup ( servID ); bool newIIU = findOrCreateVirtCircuit ( guard, addr, pChan->getPriority(guard), piiu, minorVersionNumber ); // must occur before moving to new iiu pChan->getPIIU(guard)->uninstallChanDueToSuccessfulSearchResponse ( guard, *pChan, currentTime ); if ( piiu ) { piiu->installChannel ( guard, *pChan, sid, typeCode, count ); if ( newIIU ) { piiu->start ( guard ); } } } void cac::destroyChannel ( epicsGuard < epicsMutex > & guard, nciu & chan ) { guard.assertIdenticalMutex ( this->mutex ); // uninstall channel so that recv threads // will not start a new callback for this channel's IO. if ( this->chanTable.remove ( chan ) != & chan ) { throw std::logic_error ( "Invalid channel identifier" ); } chan.~nciu (); this->channelFreeList.release ( & chan ); } void cac::disconnectAllIO ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard, nciu & chan, tsDLList < baseNMIU > & ioList ) { cbGuard.assertIdenticalMutex ( this->cbMutex ); guard.assertIdenticalMutex ( this->mutex ); char buf[128]; chan.getHostName ( guard, buf, sizeof ( buf ) ); tsDLIter < baseNMIU > pNetIO = ioList.firstIter(); while ( pNetIO.valid () ) { tsDLIter < baseNMIU > pNext = pNetIO; pNext++; if ( ! pNetIO->isSubscription() ) { this->ioTable.remove ( pNetIO->getId () ); } pNetIO->exception ( guard, *this, ECA_DISCONN, buf ); pNetIO = pNext; } } int cac :: printFormated ( epicsGuard < epicsMutex > & callbackControl, const char * pformat, ... ) const { va_list theArgs; va_start ( theArgs, pformat ); int status = this->varArgsPrintFormated ( callbackControl, pformat, theArgs ); va_end ( theArgs ); return status; } netWriteNotifyIO & cac::writeNotifyRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, privateInterfaceForIO & icni, unsigned type, arrayElementCount nElem, const void * pValue, cacWriteNotify & notifyIn ) { guard.assertIdenticalMutex ( this->mutex ); autoPtrRecycle < netWriteNotifyIO > pIO ( guard, this->ioTable, *this, netWriteNotifyIO::factory ( this->freeListWriteNotifyIO, icni, notifyIn ) ); this->ioTable.idAssignAdd ( *pIO ); chan.getPIIU(guard)->writeNotifyRequest ( guard, chan, *pIO, type, nElem, pValue ); return *pIO.release(); } netReadNotifyIO & cac::readNotifyRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, privateInterfaceForIO & icni, unsigned type, arrayElementCount nElem, cacReadNotify & notifyIn ) { guard.assertIdenticalMutex ( this->mutex ); autoPtrRecycle < netReadNotifyIO > pIO ( guard, this->ioTable, *this, netReadNotifyIO::factory ( this->freeListReadNotifyIO, icni, notifyIn ) ); this->ioTable.idAssignAdd ( *pIO ); chan.getPIIU(guard)->readNotifyRequest ( guard, chan, *pIO, type, nElem ); return *pIO.release(); } bool cac::destroyIO ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & guard, const cacChannel::ioid & idIn, nciu & chan ) { guard.assertIdenticalMutex ( this->mutex ); baseNMIU * pIO = this->ioTable.remove ( idIn ); if ( pIO ) { class netSubscription * pSubscr = pIO->isSubscription (); if ( pSubscr ) { pSubscr->unsubscribeIfRequired ( guard, chan ); } // this uninstalls from the list and destroys the IO pIO->exception ( guard, *this, ECA_CHANDESTROY, chan.pName ( guard ) ); return true; } return false; } void cac::ioShow ( epicsGuard < epicsMutex > & guard, const cacChannel::ioid & idIn, unsigned level ) const { baseNMIU * pmiu = this->ioTable.lookup ( idIn ); if ( pmiu ) { pmiu->show ( guard, level ); } } void cac::ioExceptionNotify ( unsigned idIn, int status, const char * pContext, unsigned type, arrayElementCount count ) { epicsGuard < epicsMutex > guard ( this->mutex ); baseNMIU * pmiu = this->ioTable.lookup ( idIn ); if ( pmiu ) { pmiu->exception ( guard, *this, status, pContext, type, count ); } } void cac::ioExceptionNotifyAndUninstall ( unsigned idIn, int status, const char * pContext, unsigned type, arrayElementCount count ) { epicsGuard < epicsMutex > guard ( this->mutex ); baseNMIU * pmiu = this->ioTable.remove ( idIn ); if ( pmiu ) { pmiu->exception ( guard, *this, status, pContext, type, count ); } } void cac::recycleReadNotifyIO ( epicsGuard < epicsMutex > & guard, netReadNotifyIO & io ) { guard.assertIdenticalMutex ( this->mutex ); this->freeListReadNotifyIO.release ( & io ); } void cac::recycleWriteNotifyIO ( epicsGuard < epicsMutex > & guard, netWriteNotifyIO & io ) { guard.assertIdenticalMutex ( this->mutex ); this->freeListWriteNotifyIO.release ( & io ); } void cac::recycleSubscription ( epicsGuard < epicsMutex > & guard, netSubscription & io ) { guard.assertIdenticalMutex ( this->mutex ); this->freeListSubscription.release ( & io ); } netSubscription & cac::subscriptionRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, privateInterfaceForIO & privChan, unsigned type, arrayElementCount nElem, unsigned mask, cacStateNotify & notifyIn, bool chanIsInstalled ) { guard.assertIdenticalMutex ( this->mutex ); autoPtrRecycle < netSubscription > pIO ( guard, this->ioTable, *this, netSubscription::factory ( this->freeListSubscription, privChan, type, nElem, mask, notifyIn ) ); this->ioTable.idAssignAdd ( *pIO ); if ( chanIsInstalled ) { pIO->subscribeIfRequired ( guard, chan ); } return *pIO.release (); } bool cac::versionAction ( callbackManager &, tcpiiu & iiu, const epicsTime &, const caHdrLargeArray & msg, void * ) { iiu.versionRespNotify ( msg ); return true; } bool cac::echoRespAction ( callbackManager & mgr, tcpiiu & iiu, const epicsTime & /* current */, const caHdrLargeArray &, void * ) { iiu.probeResponseNotify ( mgr.cbGuard ); return true; } bool cac::writeNotifyRespAction ( callbackManager &, tcpiiu &, const epicsTime &, const caHdrLargeArray & hdr, void * ) { epicsGuard < epicsMutex > guard ( this->mutex ); baseNMIU * pmiu = this->ioTable.remove ( hdr.m_available ); if ( pmiu ) { if ( hdr.m_cid == ECA_NORMAL ) { pmiu->completion ( guard, *this ); } else { pmiu->exception ( guard, *this, hdr.m_cid, "write notify request rejected" ); } } return true; } bool cac::readNotifyRespAction ( callbackManager &, tcpiiu & iiu, const epicsTime &, const caHdrLargeArray & hdr, void * pMsgBdy ) { epicsGuard < epicsMutex > guard ( this->mutex ); /* * the channel id field is abused for * read notify status starting with CA V4.1 */ int caStatus; if ( iiu.ca_v41_ok ( guard ) ) { caStatus = hdr.m_cid; } else { caStatus = ECA_NORMAL; } baseNMIU * pmiu = this->ioTable.remove ( hdr.m_available ); // // The IO destroy routines take the call back mutex // when uninstalling and deleting the baseNMIU so there is // no need to worry here about the baseNMIU being deleted while // it is in use here. // if ( pmiu ) { // if its a circuit-becomes-responsive subscription update // then we need to reinstall the IO into the table netSubscription * pSubscr = pmiu->isSubscription (); if ( pSubscr ) { // this does *not* assign a new resource id this->ioTable.add ( *pmiu ); } if ( caStatus == ECA_NORMAL ) { /* * convert the data buffer from net * format to host format */ caStatus = caNetConvert ( hdr.m_dataType, pMsgBdy, pMsgBdy, false, hdr.m_count ); } if ( caStatus == ECA_NORMAL ) { pmiu->completion ( guard, *this, hdr.m_dataType, hdr.m_count, pMsgBdy ); } else { pmiu->exception ( guard, *this, caStatus, "read failed", hdr.m_dataType, hdr.m_count ); } } return true; } bool cac::searchRespAction ( callbackManager &, tcpiiu & iiu, const epicsTime & currentTime, const caHdrLargeArray & msg, void * /* pMsgBdy */ ) { assert ( this->pudpiiu ); iiu.searchRespNotify ( currentTime, msg ); return true; } bool cac::eventRespAction ( callbackManager &, tcpiiu &iiu, const epicsTime &, const caHdrLargeArray & hdr, void * pMsgBdy ) { int caStatus; /* * m_postsize = 0 used to be a subscription cancel confirmation, * but is now a noop because the IO block is immediately deleted */ if ( ! hdr.m_postsize ) { return true; } epicsGuard < epicsMutex > guard ( this->mutex ); /* * the channel id field is abused for * read notify status starting with CA V4.1 */ if ( iiu.ca_v41_ok ( guard ) ) { caStatus = hdr.m_cid; } else { caStatus = ECA_NORMAL; } // // The IO destroy routines take the call back mutex // when uninstalling and deleting the baseNMIU so there is // no need to worry here about the baseNMIU being deleted while // it is in use here. // baseNMIU * pmiu = this->ioTable.lookup ( hdr.m_available ); if ( pmiu ) { /* * convert the data buffer from net format to host format */ if ( caStatus == ECA_NORMAL ) { caStatus = caNetConvert ( hdr.m_dataType, pMsgBdy, pMsgBdy, false, hdr.m_count ); } if ( caStatus == ECA_NORMAL ) { pmiu->completion ( guard, *this, hdr.m_dataType, hdr.m_count, pMsgBdy ); } else { pmiu->exception ( guard, *this, caStatus, "subscription update read failed", hdr.m_dataType, hdr.m_count ); } } return true; } bool cac::readRespAction ( callbackManager &, tcpiiu &, const epicsTime &, const caHdrLargeArray & hdr, void * pMsgBdy ) { epicsGuard < epicsMutex > guard ( this->mutex ); baseNMIU * pmiu = this->ioTable.remove ( hdr.m_available ); // // The IO destroy routines take the call back mutex // when uninstalling and deleting the baseNMIU so there is // no need to worry here about the baseNMIU being deleted while // it is in use here. // if ( pmiu ) { pmiu->completion ( guard, *this, hdr.m_dataType, hdr.m_count, pMsgBdy ); } return true; } bool cac::clearChannelRespAction ( callbackManager &, tcpiiu &, const epicsTime &, const caHdrLargeArray &, void * /* pMsgBody */ ) { return true; // currently a noop } bool cac::defaultExcep ( callbackManager &, tcpiiu & iiu, const caHdrLargeArray &, const char * pCtx, unsigned status ) { epicsGuard < epicsMutex > guard ( this->mutex ); char buf[512]; char hostName[64]; iiu.getHostName ( guard, hostName, sizeof ( hostName ) ); sprintf ( buf, "host=%s ctx=%.400s", hostName, pCtx ); this->notify.exception ( guard, status, buf, 0, 0u ); return true; } void cac::exception ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard, int status, const char * pContext, const char * pFileName, unsigned lineNo ) { cbGuard.assertIdenticalMutex ( this->cbMutex ); guard.assertIdenticalMutex ( this->mutex ); this->notify.exception ( guard, status, pContext, pFileName, lineNo ); } bool cac::eventAddExcep ( callbackManager &, tcpiiu &, const caHdrLargeArray &hdr, const char *pCtx, unsigned status ) { this->ioExceptionNotify ( hdr.m_available, status, pCtx, hdr.m_dataType, hdr.m_count ); return true; } bool cac::readExcep ( callbackManager &, tcpiiu &, const caHdrLargeArray & hdr, const char * pCtx, unsigned status ) { this->ioExceptionNotifyAndUninstall ( hdr.m_available, status, pCtx, hdr.m_dataType, hdr.m_count ); return true; } bool cac::writeExcep ( callbackManager & mgr, tcpiiu &, const caHdrLargeArray & hdr, const char * pCtx, unsigned status ) { epicsGuard < epicsMutex > guard ( this->mutex ); nciu * pChan = this->chanTable.lookup ( hdr.m_available ); if ( pChan ) { pChan->writeException ( mgr.cbGuard, guard, status, pCtx, hdr.m_dataType, hdr.m_count ); } return true; } bool cac::readNotifyExcep ( callbackManager &, tcpiiu &, const caHdrLargeArray &hdr, const char *pCtx, unsigned status ) { this->ioExceptionNotifyAndUninstall ( hdr.m_available, status, pCtx, hdr.m_dataType, hdr.m_count ); return true; } bool cac::writeNotifyExcep ( callbackManager &, tcpiiu &, const caHdrLargeArray &hdr, const char *pCtx, unsigned status ) { this->ioExceptionNotifyAndUninstall ( hdr.m_available, status, pCtx, hdr.m_dataType, hdr.m_count ); return true; } bool cac::exceptionRespAction ( callbackManager & cbMutexIn, tcpiiu & iiu, const epicsTime &, const caHdrLargeArray & hdr, void * pMsgBdy ) { const caHdr * pReq = reinterpret_cast < const caHdr * > ( pMsgBdy ); unsigned bytesSoFar = sizeof ( *pReq ); if ( hdr.m_postsize < bytesSoFar ) { return false; } caHdrLargeArray req; req.m_cmmd = AlignedWireRef < const epicsUInt16 > ( pReq->m_cmmd ); req.m_postsize = AlignedWireRef < const epicsUInt16 > ( pReq->m_postsize ); req.m_dataType = AlignedWireRef < const epicsUInt16 > ( pReq->m_dataType ); req.m_count = AlignedWireRef < const epicsUInt16 > ( pReq->m_count ); req.m_cid = AlignedWireRef < const epicsUInt32 > ( pReq->m_cid ); req.m_available = AlignedWireRef < const epicsUInt32 > ( pReq->m_available ); const ca_uint32_t * pLW = reinterpret_cast < const ca_uint32_t * > ( pReq + 1 ); if ( req.m_postsize == 0xffff ) { static const unsigned annexSize = sizeof ( req.m_postsize ) + sizeof ( req.m_count ); bytesSoFar += annexSize; if ( hdr.m_postsize < bytesSoFar ) { return false; } req.m_postsize = AlignedWireRef < const epicsUInt32 > ( pLW[0] ); req.m_count = AlignedWireRef < const epicsUInt32 > ( pLW[1] ); pLW += 2u; } // execute the exception message pExcepProtoStubTCP pStub; if ( hdr.m_cmmd >= NELEMENTS ( cac::tcpExcepJumpTableCAC ) ) { pStub = &cac::defaultExcep; } else { pStub = cac::tcpExcepJumpTableCAC [req.m_cmmd]; } const char *pCtx = reinterpret_cast < const char * > ( pLW ); return ( this->*pStub ) ( cbMutexIn, iiu, req, pCtx, hdr.m_available ); } bool cac::accessRightsRespAction ( callbackManager & mgr, tcpiiu &, const epicsTime &, const caHdrLargeArray & hdr, void * /* pMsgBody */ ) { epicsGuard < epicsMutex > guard ( this->mutex ); nciu * pChan = this->chanTable.lookup ( hdr.m_cid ); if ( pChan ) { unsigned ar = hdr.m_available; caAccessRights accessRights ( ( ar & CA_PROTO_ACCESS_RIGHT_READ ) ? true : false, ( ar & CA_PROTO_ACCESS_RIGHT_WRITE ) ? true : false); pChan->accessRightsStateChange ( accessRights, mgr.cbGuard, guard ); } return true; } bool cac::createChannelRespAction ( callbackManager & mgr, tcpiiu & iiu, const epicsTime &, const caHdrLargeArray & hdr, void * /* pMsgBody */ ) { epicsGuard < epicsMutex > guard ( this->mutex ); nciu * pChan = this->chanTable.lookup ( hdr.m_cid ); if ( pChan ) { unsigned sidTmp; if ( iiu.ca_v44_ok ( guard ) ) { sidTmp = hdr.m_available; } else { sidTmp = pChan->getSID (guard); } bool wasExpected = iiu.connectNotify ( guard, *pChan ); if ( wasExpected ) { pChan->connect ( hdr.m_dataType, hdr.m_count, sidTmp, mgr.cbGuard, guard ); } else { errlogPrintf ( "CA Client Library: Ignored duplicate create channel " "response from CA server?\n" ); } } else if ( iiu.ca_v44_ok ( guard ) ) { // this indicates a claim response for a resource that does // not exist in the client - so just remove it from the server iiu.clearChannelRequest ( guard, hdr.m_available, hdr.m_cid ); } return true; } bool cac::verifyAndDisconnectChan ( callbackManager & mgr, tcpiiu &, const epicsTime &, const caHdrLargeArray & hdr, void * /* pMsgBody */ ) { epicsGuard < epicsMutex > guard ( this->mutex ); nciu * pChan = this->chanTable.lookup ( hdr.m_cid ); if ( ! pChan ) { return true; } this->disconnectChannel ( mgr.cbGuard, guard, *pChan ); return true; } void cac::disconnectChannel ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard, nciu & chan ) { guard.assertIdenticalMutex ( this->mutex ); assert ( this->pudpiiu ); chan.disconnectAllIO ( cbGuard, guard ); chan.getPIIU(guard)->uninstallChan ( guard, chan ); this->pudpiiu->installDisconnectedChannel ( guard, chan ); chan.unresponsiveCircuitNotify ( cbGuard, guard ); } bool cac::badTCPRespAction ( callbackManager &, tcpiiu & iiu, const epicsTime &, const caHdrLargeArray & hdr, void * /* pMsgBody */ ) { epicsGuard < epicsMutex > guard ( this->mutex ); char hostName[64]; iiu.getHostName ( guard, hostName, sizeof ( hostName ) ); errlogPrintf ( "CAC: Undecipherable TCP message ( bad response type %u ) from %s\n", hdr.m_cmmd, hostName ); return false; } bool cac::executeResponse ( callbackManager & mgr, tcpiiu & iiu, const epicsTime & currentTime, caHdrLargeArray & hdr, char * pMshBody ) { // execute the response message pProtoStubTCP pStub; if ( hdr.m_cmmd >= NELEMENTS ( cac::tcpJumpTableCAC ) ) { pStub = &cac::badTCPRespAction; } else { pStub = cac::tcpJumpTableCAC [hdr.m_cmmd]; } return ( this->*pStub ) ( mgr, iiu, currentTime, hdr, pMshBody ); } void cac::selfTest ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); this->chanTable.verify (); this->ioTable.verify (); this->beaconTable.verify (); } void cac::destroyIIU ( tcpiiu & iiu ) { { callbackManager mgr ( this->notify, this->cbMutex ); epicsGuard < epicsMutex > guard ( this->mutex ); if ( iiu.channelCount ( guard ) ) { char hostNameTmp[64]; iiu.getHostName ( guard, hostNameTmp, sizeof ( hostNameTmp ) ); genLocalExcep ( mgr.cbGuard, guard, *this, ECA_DISCONN, hostNameTmp ); } osiSockAddr addr = iiu.getNetworkAddress ( guard ); if ( addr.sa.sa_family == AF_INET ) { inetAddrID tmp ( addr.ia ); bhe * pBHE = this->beaconTable.lookup ( tmp ); if ( pBHE ) { pBHE->unregisterIIU ( guard, iiu ); } } assert ( this->pudpiiu ); iiu.disconnectAllChannels ( mgr.cbGuard, guard, *this->pudpiiu ); this->serverTable.remove ( iiu ); this->circuitList.remove ( iiu ); } // this destroys a timer that takes the primary mutex // so we must not hold the primary mutex here // // this waits for send/recv threads to exit // this also uses the cac free lists so cac must wait // for this to finish before it shuts down iiu.~tcpiiu (); { epicsGuard < epicsMutex > guard ( this->mutex ); this->freeListVirtualCircuit.release ( & iiu ); this->iiuExistenceCount--; // signal iiu uninstall event so that cac can properly shut down this->iiuUninstall.signal(); } // do not touch "this" after lock is released above } double cac::beaconPeriod ( epicsGuard < epicsMutex > & guard, const nciu & chan ) const { const netiiu * pIIU = chan.getConstPIIU ( guard ); if ( pIIU ) { osiSockAddr addr = pIIU->getNetworkAddress ( guard ); if ( addr.sa.sa_family == AF_INET ) { inetAddrID tmp ( addr.ia ); bhe *pBHE = this->beaconTable.lookup ( tmp ); if ( pBHE ) { return pBHE->period ( guard ); } } } return - DBL_MAX; } void cac::initiateConnect ( epicsGuard < epicsMutex > & guard, nciu & chan, netiiu * & piiu ) { guard.assertIdenticalMutex ( this->mutex ); assert ( this->pudpiiu ); this->pudpiiu->installNewChannel ( guard, chan, piiu ); } void *cacComBufMemoryManager::allocate ( size_t size ) { return this->freeList.allocate ( size ); } void cacComBufMemoryManager::release ( void * pCadaver ) { this->freeList.release ( pCadaver ); } void cac::pvMultiplyDefinedNotify ( msgForMultiplyDefinedPV & mfmdpv, const char * pChannelName, const char * pAcc, const char * pRej ) { char buf[256]; sprintf ( buf, "Channel: \"%.64s\", Connecting to: %.64s, Ignored: %.64s", pChannelName, pAcc, pRej ); { callbackManager mgr ( this->notify, this->cbMutex ); epicsGuard < epicsMutex > guard ( this->mutex ); this->exception ( mgr.cbGuard, guard, ECA_DBLCHNL, buf, __FILE__, __LINE__ ); // remove from the list under lock this->msgMultiPVList.remove ( mfmdpv ); } // delete msg object mfmdpv.~msgForMultiplyDefinedPV (); this->mdpvFreeList.release ( & mfmdpv ); } void cac::registerSearchDest ( epicsGuard < epicsMutex > & guard, SearchDest & req ) { guard.assertIdenticalMutex ( this->mutex ); this->searchDestList.add ( req ); } base-7.0.3.1/modules/ca/src/client/cac.h0000664000577000060420000003567713557101274016434 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author: Jeff Hill * */ #ifndef cach #define cach #ifdef epicsExportSharedSymbols # define cach_restore_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "compilerDependencies.h" #include "ipAddrToAsciiAsynchronous.h" #include "msgForMultiplyDefinedPV.h" #include "epicsTimer.h" #include "epicsEvent.h" #include "freeList.h" #include "localHostName.h" #ifdef cach_restore_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif #include "nciu.h" #include "comBuf.h" #include "bhe.h" #include "cacIO.h" #include "netIO.h" #include "localHostName.h" #include "virtualCircuit.h" class netWriteNotifyIO; class netReadNotifyIO; class netSubscription; class tcpiiu; // used to control access to cac's recycle routines which // should only be indirectly invoked by CAC when its lock // is applied class cacRecycle { public: virtual void recycleReadNotifyIO ( epicsGuard < epicsMutex > &, netReadNotifyIO &io ) = 0; virtual void recycleWriteNotifyIO ( epicsGuard < epicsMutex > &, netWriteNotifyIO &io ) = 0; virtual void recycleSubscription ( epicsGuard < epicsMutex > &, netSubscription &io ) = 0; protected: virtual ~cacRecycle() {} }; struct CASG; class inetAddrID; class caServerID; struct caHdrLargeArray; class cacComBufMemoryManager : public comBufMemoryManager { public: cacComBufMemoryManager () {} void * allocate ( size_t ); void release ( void * ); private: tsFreeList < comBuf, 0x20 > freeList; cacComBufMemoryManager ( const cacComBufMemoryManager & ); cacComBufMemoryManager & operator = ( const cacComBufMemoryManager & ); }; class notifyGuard { public: notifyGuard ( cacContextNotify & ); ~notifyGuard (); private: cacContextNotify & notify; notifyGuard ( const notifyGuard & ); notifyGuard & operator = ( const notifyGuard & ); }; class callbackManager : public notifyGuard { public: callbackManager ( cacContextNotify &, epicsMutex & callbackControl ); epicsGuard < epicsMutex > cbGuard; }; class cac : public cacContext, private cacRecycle, private callbackForMultiplyDefinedPV { public: cac ( epicsMutex & mutualExclusion, epicsMutex & callbackControl, cacContextNotify & ); virtual ~cac (); // beacon management void beaconNotify ( const inetAddrID & addr, const epicsTime & currentTime, ca_uint32_t beaconNumber, unsigned protocolRevision ); unsigned beaconAnomaliesSinceProgramStart ( epicsGuard < epicsMutex > & ) const; // IO management void flush ( epicsGuard < epicsMutex > & guard ); bool executeResponse ( callbackManager &, tcpiiu &, const epicsTime & currentTime, caHdrLargeArray &, char *pMsgBody ); // channel routines void transferChanToVirtCircuit ( unsigned cid, unsigned sid, ca_uint16_t typeCode, arrayElementCount count, unsigned minorVersionNumber, const osiSockAddr &, const epicsTime & currentTime ); cacChannel & createChannel ( epicsGuard < epicsMutex > & guard, const char * pChannelName, cacChannelNotify &, cacChannel::priLev ); void destroyChannel ( epicsGuard < epicsMutex > &, nciu & ); void initiateConnect ( epicsGuard < epicsMutex > &, nciu &, netiiu * & ); nciu * lookupChannel ( epicsGuard < epicsMutex > &, const cacChannel::ioid & ); // IO requests netWriteNotifyIO & writeNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, privateInterfaceForIO &, unsigned type, arrayElementCount nElem, const void * pValue, cacWriteNotify & ); netReadNotifyIO & readNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, privateInterfaceForIO &, unsigned type, arrayElementCount nElem, cacReadNotify & ); netSubscription & subscriptionRequest ( epicsGuard < epicsMutex > &, nciu &, privateInterfaceForIO &, unsigned type, arrayElementCount nElem, unsigned mask, cacStateNotify &, bool channelIsInstalled ); bool destroyIO ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & mutualExclusionGuard, const cacChannel::ioid & idIn, nciu & chan ); void disconnectAllIO ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard, nciu &, tsDLList < baseNMIU > & ioList ); void ioShow ( epicsGuard < epicsMutex > & guard, const cacChannel::ioid &id, unsigned level ) const; // exception generation void exception ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard, int status, const char * pContext, const char * pFileName, unsigned lineNo ); // search destination management void registerSearchDest ( epicsGuard < epicsMutex > &, SearchDest & req ); bool findOrCreateVirtCircuit ( epicsGuard < epicsMutex > &, const osiSockAddr &, unsigned, tcpiiu *&, unsigned, SearchDestTCP * pSearchDest = NULL ); // diagnostics unsigned circuitCount ( epicsGuard < epicsMutex > & ) const; void show ( epicsGuard < epicsMutex > &, unsigned level ) const; int printFormated ( epicsGuard < epicsMutex > & callbackControl, const char *pformat, ... ) const; int varArgsPrintFormated ( epicsGuard < epicsMutex > & callbackControl, const char *pformat, va_list args ) const; double connectionTimeout ( epicsGuard < epicsMutex > & ); unsigned maxContiguousFrames ( epicsGuard < epicsMutex > & ) const; // misc const char * userNamePointer () const; unsigned getInitializingThreadsPriority () const; epicsMutex & mutexRef (); void attachToClientCtx (); void selfTest ( epicsGuard < epicsMutex > & ) const; double beaconPeriod ( epicsGuard < epicsMutex > &, const nciu & chan ) const; static unsigned lowestPriorityLevelAbove ( unsigned priority ); static unsigned highestPriorityLevelBelow ( unsigned priority ); void destroyIIU ( tcpiiu & iiu ); const char * pLocalHostName (); private: epicsSingleton < localHostName > :: reference _refLocalHostName; chronIntIdResTable < nciu > chanTable; // // !!!! There is at this point no good reason // !!!! to maintain one IO table for all types of // !!!! IO. It would probably be better to maintain // !!!! an independent table for each IO type. The // !!!! new adaptive sized hash table will not // !!!! waste memory. Making this change will // !!!! avoid virtual function overhead when // !!!! accessing the different types of IO. This // !!!! approach would also probably be safer in // !!!! terms of detecting damaged protocol. // chronIntIdResTable < baseNMIU > ioTable; resTable < bhe, inetAddrID > beaconTable; resTable < tcpiiu, caServerID > serverTable; tsDLList < tcpiiu > circuitList; tsDLList < SearchDest > searchDestList; tsDLList < msgForMultiplyDefinedPV > msgMultiPVList; tsFreeList < class tcpiiu, 32, epicsMutexNOOP > freeListVirtualCircuit; tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > freeListReadNotifyIO; tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > freeListWriteNotifyIO; tsFreeList < class netSubscription, 1024, epicsMutexNOOP > freeListSubscription; tsFreeList < class nciu, 1024, epicsMutexNOOP > channelFreeList; tsFreeList < class msgForMultiplyDefinedPV, 16 > mdpvFreeList; cacComBufMemoryManager comBufMemMgr; bheFreeStore bheFreeList; epicsTime programBeginTime; double connTMO; // **** lock hierarchy **** // 1) callback lock must always be acquired before // the primary mutex if both locks are needed epicsMutex & mutex; epicsMutex & cbMutex; epicsEvent iiuUninstall; ipAddrToAsciiEngine & ipToAEngine; epicsTimerQueueActive & timerQueue; char * pUserName; class udpiiu * pudpiiu; void * tcpSmallRecvBufFreeList; void * tcpLargeRecvBufFreeList; cacContextNotify & notify; epicsThreadId initializingThreadsId; unsigned initializingThreadsPriority; unsigned maxRecvBytesTCP; unsigned maxContigFrames; unsigned beaconAnomalyCount; unsigned short _serverPort; unsigned iiuExistenceCount; bool cacShutdownInProgress; void recycleReadNotifyIO ( epicsGuard < epicsMutex > &, netReadNotifyIO &io ); void recycleWriteNotifyIO ( epicsGuard < epicsMutex > &, netWriteNotifyIO &io ); void recycleSubscription ( epicsGuard < epicsMutex > &, netSubscription &io ); void disconnectChannel ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard, nciu & chan ); void ioExceptionNotify ( unsigned id, int status, const char * pContext, unsigned type, arrayElementCount count ); void ioExceptionNotifyAndUninstall ( unsigned id, int status, const char * pContext, unsigned type, arrayElementCount count ); void pvMultiplyDefinedNotify ( msgForMultiplyDefinedPV & mfmdpv, const char * pChannelName, const char * pAcc, const char * pRej ); // recv protocol stubs bool versionAction ( callbackManager &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); bool echoRespAction ( callbackManager &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); bool writeNotifyRespAction ( callbackManager &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); bool searchRespAction ( callbackManager &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); bool readNotifyRespAction ( callbackManager &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); bool eventRespAction ( callbackManager &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); bool readRespAction ( callbackManager &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); bool clearChannelRespAction ( callbackManager &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); bool exceptionRespAction ( callbackManager &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); bool accessRightsRespAction ( callbackManager &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); bool createChannelRespAction ( callbackManager &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); bool verifyAndDisconnectChan ( callbackManager &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); bool badTCPRespAction ( callbackManager &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); typedef bool ( cac::*pProtoStubTCP ) ( callbackManager &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); static const pProtoStubTCP tcpJumpTableCAC []; bool defaultExcep ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, const char *pCtx, unsigned status ); bool eventAddExcep ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, const char *pCtx, unsigned status ); bool readExcep ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, const char *pCtx, unsigned status ); bool writeExcep ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, const char *pCtx, unsigned status ); bool clearChanExcep ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, const char *pCtx, unsigned status ); bool readNotifyExcep ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, const char *pCtx, unsigned status ); bool writeNotifyExcep ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, const char *pCtx, unsigned status ); typedef bool ( cac::*pExcepProtoStubTCP ) ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, const char *pCtx, unsigned status ); static const pExcepProtoStubTCP tcpExcepJumpTableCAC []; cac ( const cac & ); cac & operator = ( const cac & ); friend class tcpiiu; }; inline const char * cac::userNamePointer () const { return this->pUserName; } inline unsigned cac::getInitializingThreadsPriority () const { return this->initializingThreadsPriority; } inline epicsMutex & cac::mutexRef () { return this->mutex; } inline int cac :: varArgsPrintFormated ( epicsGuard < epicsMutex > & callbackControl, const char *pformat, va_list args ) const { callbackControl.assertIdenticalMutex ( this->cbMutex ); return this->notify.varArgsPrintFormated ( pformat, args ); } inline void cac::attachToClientCtx () { this->notify.attachToClientCtx (); } inline unsigned cac::beaconAnomaliesSinceProgramStart ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); return this->beaconAnomalyCount; } inline notifyGuard::notifyGuard ( cacContextNotify & notifyIn ) : notify ( notifyIn ) { this->notify.callbackProcessingInitiateNotify (); } inline notifyGuard::~notifyGuard () { this->notify.callbackProcessingCompleteNotify (); } inline callbackManager::callbackManager ( cacContextNotify & notify, epicsMutex & callbackControl ) : notifyGuard ( notify ), cbGuard ( callbackControl ) { } inline nciu * cac::lookupChannel ( epicsGuard < epicsMutex > & guard, const cacChannel::ioid & idIn ) { guard.assertIdenticalMutex ( this->mutex ); return this->chanTable.lookup ( idIn ); } inline const char * cac :: pLocalHostName () { return _refLocalHostName->pointer (); } inline unsigned cac :: maxContiguousFrames ( epicsGuard < epicsMutex > & ) const { return maxContigFrames; } inline double cac :: connectionTimeout ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); return this->connTMO; } #endif // ifdef cach base-7.0.3.1/modules/ca/src/client/cacChannel.cpp0000664000577000060420000000710513557101274020241 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #include #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "errlog.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "localHostName.h" #include "cacIO.h" class CACChannelPrivate { public: CACChannelPrivate (); unsigned getHostName ( char * pBuf, unsigned bufLength ); const char * pHostName (); private: epicsSingleton < localHostName > :: reference _refLocalHostName; }; static epicsThreadOnceId cacChannelIdOnce = EPICS_THREAD_ONCE_INIT; const cacChannel::priLev cacChannel::priorityMax = 99u; const cacChannel::priLev cacChannel::priorityMin = 0u; const cacChannel::priLev cacChannel::priorityDefault = priorityMin; const cacChannel::priLev cacChannel::priorityLinksDB = priorityMax; const cacChannel::priLev cacChannel::priorityArchive = ( priorityMax - priorityMin ) / 2; const cacChannel::priLev cacChannel::priorityOPI = priorityMin; cacChannel::~cacChannel () { } caAccessRights cacChannel::accessRights ( epicsGuard < epicsMutex > & ) const { static caAccessRights ar ( true, true ); return ar; } unsigned cacChannel::searchAttempts ( epicsGuard < epicsMutex > & ) const { return 0u; } double cacChannel::beaconPeriod ( epicsGuard < epicsMutex > & ) const { return - DBL_MAX; } double cacChannel::receiveWatchdogDelay ( epicsGuard < epicsMutex > & ) const { return - DBL_MAX; } bool cacChannel::ca_v42_ok ( epicsGuard < epicsMutex > & ) const { return true; } bool cacChannel::connected ( epicsGuard < epicsMutex > & ) const { return true; } CACChannelPrivate :: CACChannelPrivate() : _refLocalHostName ( localHostNameCache.getReference () ) { } inline unsigned CACChannelPrivate :: getHostName ( char * pBuf, unsigned bufLength ) { return _refLocalHostName->getName ( pBuf, bufLength ); } inline const char * CACChannelPrivate :: pHostName () { return _refLocalHostName->pointer (); } static CACChannelPrivate * pCACChannelPrivate = 0; // runs once only for each process extern "C" void cacChannelSetup ( void * ) { pCACChannelPrivate = new CACChannelPrivate (); } // the default is to assume that it is a locally hosted channel unsigned cacChannel::getHostName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLength ) const throw () { if ( bufLength ) { epicsThreadOnce ( & cacChannelIdOnce, cacChannelSetup, 0); return pCACChannelPrivate->getHostName ( pBuf, bufLength ); } return 0u; } // the default is to assume that it is a locally hosted channel const char * cacChannel::pHostName ( epicsGuard < epicsMutex > & ) const throw () { epicsThreadOnce ( & cacChannelIdOnce, cacChannelSetup, 0); return pCACChannelPrivate->pHostName (); } cacContext::~cacContext () {} cacService::~cacService () {} base-7.0.3.1/modules/ca/src/client/cacChannelNotify.cpp0000664000577000060420000000206713557101274021434 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include "iocinf.h" #define epicsExportSharedSymbols #include "cacIO.h" #undef epicsExportSharedSymbols cacChannelNotify::~cacChannelNotify () { } base-7.0.3.1/modules/ca/src/client/cacContextNotify.cpp0000664000577000060420000000206613557101274021507 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #include #include "iocinf.h" #define epicsExportSharedSymbols #include "cacIO.h" #undef epicsExportSharedSymbols cacContextNotify::~cacContextNotify () { } void cacContextNotify::callbackProcessingInitiateNotify () { } void cacContextNotify::callbackProcessingCompleteNotify () { } base-7.0.3.1/modules/ca/src/client/cacIO.h0000664000577000060420000003166513557101274016655 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef cacIOh #define cacIOh // // Open Issues // ----------- // // 1) A status code from the old client side interface is passed // to the exception notify callback. Should we just pass a string? // If so, then how do they detect the type of error and recover. // Perhaps we should call a different vf for each type of exception. // // 2) Some exception types are present here but there is no common // exception base class in use. // // 3) Should I be passing the channel reference in cacChannelNotify? // // 4) Should the code for caAccessRights not be inline so that this // interface is version independent. // // #include #include #ifdef epicsExportSharedSymbols # define cacIOh_restore_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "tsDLList.h" #include "epicsMutex.h" #include "epicsGuard.h" #include "epicsThread.h" #ifdef cacIOh_restore_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif class cacChannel; typedef unsigned long arrayElementCount; // 1) this should not be passing caerr.h status to the exception callback // 2) needless-to-say the data should be passed here using the new data access API class epicsShareClass cacWriteNotify { public: virtual ~cacWriteNotify () = 0; virtual void completion ( epicsGuard < epicsMutex > & ) = 0; // we should probably have a different vf for each type of exception ???? virtual void exception ( epicsGuard < epicsMutex > &, int status, const char * pContext, unsigned type, arrayElementCount count ) = 0; }; // 1) this should not be passing caerr.h status to the exception callback // 2) needless-to-say the data should be passed here using the new data access API class epicsShareClass cacReadNotify { public: virtual ~cacReadNotify () = 0; virtual void completion ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void * pData ) = 0; // we should probably have a different vf for each type of exception ???? virtual void exception ( epicsGuard < epicsMutex > &, int status, const char * pContext, unsigned type, arrayElementCount count ) = 0; }; // 1) this should not be passing caerr.h status to the exception callback // 2) needless-to-say the data should be passed here using the new data access API class epicsShareClass cacStateNotify { public: virtual ~cacStateNotify () = 0; virtual void current ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void * pData ) = 0; // we should probably have a different vf for each type of exception ???? virtual void exception ( epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count ) = 0; }; class caAccessRights { public: caAccessRights ( bool readPermit = false, bool writePermit = false, bool operatorConfirmationRequest = false); void setReadPermit (); void setWritePermit (); void setOperatorConfirmationRequest (); void clrReadPermit (); void clrWritePermit (); void clrOperatorConfirmationRequest (); bool readPermit () const; bool writePermit () const; bool operatorConfirmationRequest () const; private: bool f_readPermit:1; bool f_writePermit:1; bool f_operatorConfirmationRequest:1; }; class epicsShareClass cacChannelNotify { public: virtual ~cacChannelNotify () = 0; virtual void connectNotify ( epicsGuard < epicsMutex > & ) = 0; virtual void disconnectNotify ( epicsGuard < epicsMutex > & ) = 0; virtual void serviceShutdownNotify ( epicsGuard < epicsMutex > & mutualExclusionGuard ) = 0; virtual void accessRightsNotify ( epicsGuard < epicsMutex > &, const caAccessRights & ) = 0; virtual void exception ( epicsGuard < epicsMutex > &, int status, const char *pContext ) = 0; // we should probably have a different vf for each type of exception ???? virtual void readException ( epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count, void *pValue ) = 0; // we should probably have a different vf for each type of exception ???? virtual void writeException ( epicsGuard < epicsMutex > &, int status, const char * pContext, unsigned type, arrayElementCount count ) = 0; }; class CallbackGuard : public epicsGuard < epicsMutex > { public: CallbackGuard ( epicsMutex & mutex ) : epicsGuard < epicsMutex > ( mutex ) {} private: CallbackGuard ( const CallbackGuard & ); CallbackGuard & operator = ( const CallbackGuard & ); }; // // Notes // 1) This interface assumes that when a channel is deleted then all // attached IO is deleted. This is left over from the old interface, // but perhaps is a bad practice that should be eliminated? If so, // then the IO should not store or use a pointer to the channel. // class epicsShareClass cacChannel { public: typedef unsigned priLev; static const priLev priorityMax; static const priLev priorityMin; static const priLev priorityDefault; static const priLev priorityLinksDB; static const priLev priorityArchive; static const priLev priorityOPI; typedef unsigned ioid; enum ioStatus { iosSynch, iosAsynch }; cacChannel ( cacChannelNotify & ); virtual void destroy ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & mutualExclusionGuard ) = 0; cacChannelNotify & notify () const; // required ????? virtual unsigned getName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw () = 0; // !! deprecated, avoid use !! virtual const char * pName ( epicsGuard < epicsMutex > & guard ) const throw () = 0; virtual void show ( epicsGuard < epicsMutex > &, unsigned level ) const = 0; virtual void initiateConnect ( epicsGuard < epicsMutex > & ) = 0; virtual unsigned requestMessageBytesPending ( epicsGuard < epicsMutex > & mutualExclusionGuard ) = 0; virtual void flush ( epicsGuard < epicsMutex > & mutualExclusionGuard ) = 0; virtual ioStatus read ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, cacReadNotify &, ioid * = 0 ) = 0; virtual void write ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void *pValue ) = 0; virtual ioStatus write ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void *pValue, cacWriteNotify &, ioid * = 0 ) = 0; virtual void subscribe ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, unsigned mask, cacStateNotify &, ioid * = 0 ) = 0; // The primary mutex must be released when calling the user's // callback, and therefore a finite interval exists when we are // moving forward with the intent to call the users callback // but the users IO could be deleted during this interval. // To prevent the user's callback from being called after // destroying his IO we must past a guard for the callback // mutex here. virtual void ioCancel ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & mutualExclusionGuard, const ioid & ) = 0; virtual void ioShow ( epicsGuard < epicsMutex > &, const ioid &, unsigned level ) const = 0; virtual short nativeType ( epicsGuard < epicsMutex > & ) const = 0; virtual arrayElementCount nativeElementCount ( epicsGuard < epicsMutex > & ) const = 0; virtual caAccessRights accessRights ( epicsGuard < epicsMutex > & ) const; virtual unsigned searchAttempts ( epicsGuard < epicsMutex > & ) const; virtual double beaconPeriod ( epicsGuard < epicsMutex > & ) const; // negative DBL_MAX if UKN virtual double receiveWatchdogDelay ( epicsGuard < epicsMutex > & ) const; // negative DBL_MAX if UKN virtual bool ca_v42_ok ( epicsGuard < epicsMutex > & ) const; virtual bool connected ( epicsGuard < epicsMutex > & ) const; virtual unsigned getHostName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLength ) const throw (); // !! deprecated, avoid use !! virtual const char * pHostName ( epicsGuard < epicsMutex > & guard ) const throw (); // exceptions class badString {}; class badType {}; class badPriority {}; class outOfBounds {}; class badEventSelection {}; class noWriteAccess {}; class noReadAccess {}; class notConnected {}; class unsupportedByService {}; class msgBodyCacheTooSmall {}; // hopefully this one goes away in the future class requestTimedOut {}; protected: virtual ~cacChannel () = 0; private: cacChannelNotify & callback; cacChannel ( const cacChannel & ); cacChannel & operator = ( const cacChannel & ); }; class epicsShareClass cacContext { public: virtual ~cacContext (); virtual cacChannel & createChannel ( epicsGuard < epicsMutex > &, const char * pChannelName, cacChannelNotify &, cacChannel::priLev = cacChannel::priorityDefault ) = 0; virtual void flush ( epicsGuard < epicsMutex > & ) = 0; virtual unsigned circuitCount ( epicsGuard < epicsMutex > & ) const = 0; virtual void selfTest ( epicsGuard < epicsMutex > & ) const = 0; virtual unsigned beaconAnomaliesSinceProgramStart ( epicsGuard < epicsMutex > & ) const = 0; virtual void show ( epicsGuard < epicsMutex > &, unsigned level ) const = 0; }; class epicsShareClass cacContextNotify { public: virtual ~cacContextNotify () = 0; virtual cacContext & createNetworkContext ( epicsMutex & mutualExclusion, epicsMutex & callbackControl ) = 0; // we should probably have a different vf for each type of exception ???? virtual void exception ( epicsGuard < epicsMutex > &, int status, const char * pContext, const char * pFileName, unsigned lineNo ) = 0; // perhaps this should be phased out in deference to the exception mechanism virtual int varArgsPrintFormated ( const char * pformat, va_list args ) const = 0; // backwards compatibility (from here down) virtual void attachToClientCtx () = 0; virtual void callbackProcessingInitiateNotify () = 0; virtual void callbackProcessingCompleteNotify () = 0; }; // **** Lock Hierarchy **** // callbackControl must be taken before mutualExclusion if both are held at // the same time class epicsShareClass cacService { public: virtual ~cacService () = 0; virtual cacContext & contextCreate ( epicsMutex & mutualExclusion, epicsMutex & callbackControl, cacContextNotify & ) = 0; }; epicsShareFunc void epicsShareAPI caInstallDefaultService ( cacService & service ); epicsShareExtern epicsThreadPrivateId caClientCallbackThreadId; inline cacChannel::cacChannel ( cacChannelNotify & notify ) : callback ( notify ) { } inline cacChannelNotify & cacChannel::notify () const { return this->callback; } inline caAccessRights::caAccessRights ( bool readPermit, bool writePermit, bool operatorConfirmationRequest) : f_readPermit ( readPermit ), f_writePermit ( writePermit ), f_operatorConfirmationRequest ( operatorConfirmationRequest ) {} inline void caAccessRights::setReadPermit () { this->f_readPermit = true; } inline void caAccessRights::setWritePermit () { this->f_writePermit = true; } inline void caAccessRights::setOperatorConfirmationRequest () { this->f_operatorConfirmationRequest = true; } inline void caAccessRights::clrReadPermit () { this->f_readPermit = false; } inline void caAccessRights::clrWritePermit () { this->f_writePermit = false; } inline void caAccessRights::clrOperatorConfirmationRequest () { this->f_operatorConfirmationRequest = false; } inline bool caAccessRights::readPermit () const { return this->f_readPermit; } inline bool caAccessRights::writePermit () const { return this->f_writePermit; } inline bool caAccessRights::operatorConfirmationRequest () const { return this->f_operatorConfirmationRequest; } #endif // ifndef cacIOh base-7.0.3.1/modules/ca/src/client/cacReadNotify.cpp0000664000577000060420000000162613557101274020737 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #include "iocinf.h" #define epicsExportSharedSymbols #include "cacIO.h" #undef epicsExportSharedSymbols cacReadNotify::~cacReadNotify () { } base-7.0.3.1/modules/ca/src/client/cacStateNotify.cpp0000664000577000060420000000163013557101274021137 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #include "iocinf.h" #define epicsExportSharedSymbols #include "cacIO.h" #undef epicsExportSharedSymbols cacStateNotify::~cacStateNotify () { } base-7.0.3.1/modules/ca/src/client/cacWriteNotify.cpp0000664000577000060420000000163013557101274021151 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #include "iocinf.h" #define epicsExportSharedSymbols #include "cacIO.h" #undef epicsExportSharedSymbols cacWriteNotify::~cacWriteNotify () { } base-7.0.3.1/modules/ca/src/client/cadef.h0000664000577000060420000007265013557101274016740 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 * */ #ifndef INCLcadefh #define INCLcadefh /* * done in two ifdef steps so that we will remain compatible with * traditional C */ #ifndef CA_DONT_INCLUDE_STDARGH # include #endif #ifdef epicsExportSharedSymbols # define INCLcadefh_accessh_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "epicsThread.h" #ifdef INCLcadefh_accessh_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif #include "caerr.h" #include "db_access.h" #include "caeventmask.h" #ifdef __cplusplus extern "C" { #endif typedef struct oldChannelNotify *chid; typedef chid chanId; /* for when the structures field name is "chid" */ typedef long chtype; typedef struct oldSubscription *evid; typedef double ca_real; /* arguments passed to user connection handlers */ struct connection_handler_args { chanId chid; /* channel id */ long op; /* one of CA_OP_CONN_UP or CA_OP_CONN_DOWN */ }; typedef void caCh (struct connection_handler_args args); typedef struct ca_access_rights { unsigned read_access:1; unsigned write_access:1; } caar; /* arguments passed to user access rights handlers */ struct access_rights_handler_args { chanId chid; /* channel id */ caar ar; /* new access rights state */ }; typedef void caArh (struct access_rights_handler_args args); /* The conversion routine to call for each type */ #define VALID_TYPE(TYPE) (((unsigned short)TYPE)<=LAST_BUFFER_TYPE) /* * Arguments passed to event handlers and get/put call back handlers. * * The status field below is the CA ECA_XXX status of the requested * operation which is saved from when the operation was attempted in the * server and copied back to the clients call back routine. * If the status is not ECA_NORMAL then the dbr pointer will be NULL * and the requested operation can not be assumed to be successful. */ typedef struct event_handler_args { void *usr; /* user argument supplied with request */ chanId chid; /* channel id */ long type; /* the type of the item returned */ long count; /* the element count of the item returned */ const void *dbr; /* a pointer to the item returned */ int status; /* ECA_XXX status of the requested op from the server */ } evargs; typedef void caEventCallBackFunc (struct event_handler_args); epicsShareFunc void epicsShareAPI ca_test_event ( struct event_handler_args ); /* arguments passed to user exception handlers */ struct exception_handler_args { void *usr; /* user argument supplied when installed */ chanId chid; /* channel id (may be nill) */ long type; /* type requested */ long count; /* count requested */ void *addr; /* user's address to write results of CA_OP_GET */ long stat; /* channel access ECA_XXXX status code */ long op; /* CA_OP_GET, CA_OP_PUT, ..., CA_OP_OTHER */ const char *ctx; /* a character string containing context info */ const char *pFile; /* source file name (may be NULL) */ unsigned lineNo; /* source file line number (may be zero) */ }; typedef unsigned CA_SYNC_GID; /* * External OP codes for CA operations */ #define CA_OP_GET 0 #define CA_OP_PUT 1 #define CA_OP_CREATE_CHANNEL 2 #define CA_OP_ADD_EVENT 3 #define CA_OP_CLEAR_EVENT 4 #define CA_OP_OTHER 5 /* * used with connection_handler_args */ #define CA_OP_CONN_UP 6 #define CA_OP_CONN_DOWN 7 /* depricated */ #define CA_OP_SEARCH 2 /* * provides efficient test and display of channel access errors */ #define SEVCHK(CA_ERROR_CODE, MESSAGE_STRING) \ { \ int ca_unique_status_name = (CA_ERROR_CODE); \ if(!(ca_unique_status_name & CA_M_SUCCESS)) \ ca_signal_with_file_and_lineno( \ ca_unique_status_name, \ (MESSAGE_STRING), \ __FILE__, \ __LINE__); \ } #define TYPENOTCONN (-1) /* the channel's native type when disconnected */ epicsShareFunc short epicsShareAPI ca_field_type (chid chan); epicsShareFunc unsigned long epicsShareAPI ca_element_count (chid chan); epicsShareFunc const char * epicsShareAPI ca_name (chid chan); epicsShareFunc void epicsShareAPI ca_set_puser (chid chan, void *puser); epicsShareFunc void * epicsShareAPI ca_puser (chid chan); epicsShareFunc unsigned epicsShareAPI ca_read_access (chid chan); epicsShareFunc unsigned epicsShareAPI ca_write_access (chid chan); /* * cs_ - `channel state' * * cs_never_conn valid chid, IOC not found * cs_prev_conn valid chid, IOC was found, but unavailable * cs_conn valid chid, IOC was found, still available * cs_closed channel deleted by user */ enum channel_state {cs_never_conn, cs_prev_conn, cs_conn, cs_closed}; epicsShareFunc enum channel_state epicsShareAPI ca_state (chid chan); /************************************************************************/ /* Perform Library Initialization */ /* */ /* Must be called once before calling any of the other routines */ /************************************************************************/ epicsShareFunc int epicsShareAPI ca_task_initialize (void); enum ca_preemptive_callback_select { ca_disable_preemptive_callback, ca_enable_preemptive_callback }; epicsShareFunc int epicsShareAPI ca_context_create (enum ca_preemptive_callback_select select); epicsShareFunc void epicsShareAPI ca_detach_context (); /************************************************************************/ /* Remove CA facility from your task */ /* */ /* Normally called automatically at task exit */ /************************************************************************/ epicsShareFunc int epicsShareAPI ca_task_exit (void); epicsShareFunc void epicsShareAPI ca_context_destroy (void); typedef unsigned capri; #define CA_PRIORITY_MAX 99 #define CA_PRIORITY_MIN 0 #define CA_PRIORITY_DEFAULT CA_PRIORITY_MIN #define CA_PRIORITY_DB_LINKS 80 #define CA_PRIORITY_ARCHIVE 20 #define CA_PRIORITY_OPI 0 /* * ca_create_channel () * * pChanName R channel name string * pConnStateCallback R address of connection state change * callback function * pUserPrivate R placed in the channel's user private field * o can be fetched later by ca_puser(CHID) * o passed as void * arg to *pConnectCallback above * priority R priority level in the server 0 - 100 * pChanID RW channel id written here */ epicsShareFunc int epicsShareAPI ca_create_channel ( const char *pChanName, caCh *pConnStateCallback, void *pUserPrivate, capri priority, chid *pChanID ); /* * ca_change_connection_event() * * chan R channel identifier * pfunc R address of connection call-back function */ epicsShareFunc int epicsShareAPI ca_change_connection_event ( chid chan, caCh * pfunc ); /* * ca_replace_access_rights_event () * * chan R channel identifier * pfunc R address of access rights call-back function */ epicsShareFunc int epicsShareAPI ca_replace_access_rights_event ( chid chan, caArh *pfunc ); /* * ca_add_exception_event () * * replace the default exception handler * * pfunc R address of exception call-back function * pArg R copy of this pointer passed to exception * call-back function */ typedef void caExceptionHandler (struct exception_handler_args); epicsShareFunc int epicsShareAPI ca_add_exception_event ( caExceptionHandler *pfunc, void *pArg ); /* * ca_clear_channel() * - deallocate resources reserved for a channel * * chanId R channel ID */ epicsShareFunc int epicsShareAPI ca_clear_channel ( chid chanId ); /************************************************************************/ /* Write a value to a channel */ /************************************************************************/ /* * ca_bput() * * WARNING: this copies the new value from a string (dbr_string_t) * (and not as an integer) * * chan R channel identifier * pValue R new channel value string copied from this location */ #define ca_bput(chan, pValue) \ ca_array_put(DBR_STRING, 1u, chan, (const dbr_string_t *) (pValue)) /* * ca_rput() * * WARNING: this copies the new value from a dbr_float_t * * chan R channel identifier * pValue R new channel value copied from this location */ #define ca_rput(chan,pValue) \ ca_array_put(DBR_FLOAT, 1u, chan, (const dbr_float_t *) pValue) /* * ca_put() * * type R data type from db_access.h * chan R channel identifier * pValue R new channel value copied from this location */ #define ca_put(type, chan, pValue) ca_array_put (type, 1u, chan, pValue) /* * ca_array_put() * * type R data type from db_access.h * count R array element count * chan R channel identifier * pValue R new channel value copied from this location */ epicsShareFunc int epicsShareAPI ca_array_put ( chtype type, unsigned long count, chid chanId, const void * pValue ); /* * ca_array_put_callback() * * This routine functions identically to the original ca put request * with the addition of a callback to the user supplied function * after recod processing completes in the IOC. The arguments * to the user supplied callback function are declared in * the structure event_handler_args and include the pointer * sized user argument supplied when ca_array_put_callback() is called. * * type R data type from db_access.h * count R array element count * chan R channel identifier * pValue R new channel value copied from this location * pFunc R pointer to call-back function * pArg R copy of this pointer passed to pFunc */ epicsShareFunc int epicsShareAPI ca_array_put_callback ( chtype type, unsigned long count, chid chanId, const void * pValue, caEventCallBackFunc * pFunc, void * pArg ); #define ca_put_callback(type, chan, pValue, pFunc, pArg) \ ca_array_put_callback(type, 1u, chan, pValue, pFunc, pArg) /************************************************************************/ /* Read a value from a channel */ /************************************************************************/ /* * ca_bget() * * WARNING: this copies the new value into a string (dbr_string_t) * (and not into an integer) * * chan R channel identifier * pValue W channel value copied to this location */ #define ca_bget(chan, pValue) \ ca_array_get(DBR_STRING, 1u, chan, (dbr_string_t *)(pValue)) /* * ca_rget() * * WARNING: this copies the new value into a 32 bit float (dbr_float_t) * * chan R channel identifier * pValue W channel value copied to this location */ #define ca_rget(chan, pValue) \ ca_array_get(DBR_FLOAT, 1u, chan, (dbr_float_t *)(pValue)) /* * ca_rget() * * type R data type from db_access.h * chan R channel identifier * pValue W channel value copied to this location */ #define ca_get(type, chan, pValue) ca_array_get(type, 1u, chan, pValue) /* * ca_array_get() * * type R data type from db_access.h * count R array element count * chan R channel identifier * pValue W channel value copied to this location */ epicsShareFunc int epicsShareAPI ca_array_get ( chtype type, unsigned long count, chid chanId, void * pValue ); /************************************************************************/ /* Read a value from a channel and run a callback when the value */ /* returns */ /* */ /* */ /************************************************************************/ /* * ca_bget_callback() * * WARNING: this returns the new value as a string (dbr_string_t) * (and not as an integer) * * chan R channel identifier * pFunc R pointer to call-back function * pArg R copy of this pointer passed to pFunc */ #define ca_bget_callback(chan, pFunc, pArg)\ ca_array_get_callback (DBR_STRING, 1u, chan, pFunc, pArg) /* * ca_rget_callback() * * WARNING: this returns the new value as a float (dbr_float_t) * * chan R channel identifier * pFunc R pointer to call-back function * pArg R copy of this pointer passed to pFunc */ #define ca_rget_callback(chan, pFunc, pArg)\ ca_array_get_callback (DBR_FLOAT, 1u, chan, pFunc, pArg) /* * ca_get_callback() * * type R data type from db_access.h * chan R channel identifier * pFunc R pointer to call-back function * pArg R copy of this pointer passed to pFunc */ #define ca_get_callback(type, chan, pFunc, pArg)\ ca_array_get_callback (type, 1u, chan, pFunc, pArg) /* * ca_array_get_callback() * * type R data type from db_access.h * count R array element count * chan R channel identifier * pFunc R pointer to call-back function * pArg R copy of this pointer passed to pFunc */ epicsShareFunc int epicsShareAPI ca_array_get_callback ( chtype type, unsigned long count, chid chanId, caEventCallBackFunc * pFunc, void * pArg ); /************************************************************************/ /* Specify a function to be executed whenever significant changes */ /* occur to a channel. */ /* NOTES: */ /* 1) Evid may be omited by passing a NULL pointer */ /* */ /* 2) An array count of zero specifies the native db count */ /* */ /************************************************************************/ /* * ca_create_subscription () * * type R data type from db_access.h * count R array element count * chan R channel identifier * mask R event mask - one of {DBE_VALUE, DBE_ALARM, DBE_LOG} * pFunc R pointer to call-back function * pArg R copy of this pointer passed to pFunc * pEventID W event id written at specified address */ epicsShareFunc int epicsShareAPI ca_create_subscription ( chtype type, unsigned long count, chid chanId, long mask, caEventCallBackFunc * pFunc, void * pArg, evid * pEventID ); /************************************************************************/ /* Remove a function from a list of those specified to run */ /* whenever significant changes occur to a channel */ /* */ /************************************************************************/ /* * ca_clear_subscription() * * eventID R event id */ epicsShareFunc int epicsShareAPI ca_clear_subscription ( evid eventID ); epicsShareFunc chid epicsShareAPI ca_evid_to_chid ( evid id ); /************************************************************************/ /* */ /* Requested data is not necessarily stable prior to */ /* return from called subroutine. Call ca_pend_io() */ /* to guarantee that requested data is stable. Call the routine */ /* ca_flush_io() to force all outstanding requests to be */ /* sent out over the network. Significant increases in */ /* performance have been measured when batching several remote */ /* requests together into one message. Additional */ /* improvements can be obtained by performing local processing */ /* in parallel with outstanding remote processing. */ /* */ /* FLOW OF TYPICAL APPLICATION */ /* */ /* search() ! Obtain Channel ids */ /* . ! " */ /* . ! " */ /* pend_io ! wait for channels to connect */ /* */ /* get() ! several requests for remote info */ /* get() ! " */ /* add_event() ! " */ /* get() ! " */ /* . */ /* . */ /* . */ /* flush_io() ! send get requests */ /* ! optional parallel processing */ /* . ! " */ /* . ! " */ /* pend_io() ! wait for replies from get requests */ /* . ! access to requested data */ /* . ! " */ /* pend_event() ! wait for requested events */ /* */ /************************************************************************/ /************************************************************************/ /* These routines wait for channel subscription events and call the */ /* functions specified with add_event when events occur. If the */ /* timeout is specified as 0 an infinite timeout is assumed. */ /* ca_flush_io() is called by this routine. If ca_pend_io () */ /* is called when no IO is outstanding then it will return immediately */ /* without processing. */ /************************************************************************/ /* * ca_pend_event() * * timeOut R wait for this delay in seconds */ epicsShareFunc int epicsShareAPI ca_pend_event (ca_real timeOut); #define ca_poll() ca_pend_event(1e-12) /* * ca_pend_io() * * timeOut R wait for this delay in seconds but return early * if all get requests (or search requests with null * connection handler pointer have completed) */ epicsShareFunc int epicsShareAPI ca_pend_io (ca_real timeOut); /* calls ca_pend_io() if early is true otherwise ca_pend_event() is called */ epicsShareFunc int epicsShareAPI ca_pend (ca_real timeout, int early); /* * ca_test_io() * * returns TRUE when get requests (or search requests with null * connection handler pointer) are outstanding */ epicsShareFunc int epicsShareAPI ca_test_io (void); /************************************************************************/ /* Send out all outstanding messages in the send queue */ /************************************************************************/ /* * ca_flush_io() */ epicsShareFunc int epicsShareAPI ca_flush_io (void); /* * ca_signal() * * errorCode R status returned from channel access function * pCtxStr R context string included with error print out */ epicsShareFunc void epicsShareAPI ca_signal ( long errorCode, const char *pCtxStr ); /* * ca_signal_with_file_and_lineno() * errorCode R status returned from channel access function * pCtxStr R context string included with error print out * pFileStr R file name string included with error print out * lineNo R line number included with error print out * */ epicsShareFunc void epicsShareAPI ca_signal_with_file_and_lineno ( long errorCode, const char *pCtxStr, const char *pFileStr, int lineNo ); /* * ca_signal_formated() * errorCode R status returned from channel access function * pFileStr R file name string included with error print out * lineNo R line number included with error print out * pFormat R printf dtyle format string (and optional arguments) * */ epicsShareFunc void epicsShareAPI ca_signal_formated (long ca_status, const char *pfilenm, int lineno, const char *pFormat, ...); /* * ca_host_name_function() * * channel R channel identifier * * !!!! this function is _not_ thread safe !!!! */ epicsShareFunc const char * epicsShareAPI ca_host_name (chid channel); /* thread safe version */ epicsShareFunc unsigned epicsShareAPI ca_get_host_name ( chid pChan, char *pBuf, unsigned bufLength ); /* * CA_ADD_FD_REGISTRATION * * call their function with their argument whenever * a new fd is added or removed * (for use with a manager of the select system call under UNIX) * * if (opened) then fd was created * if (!opened) then fd was deleted * */ typedef void CAFDHANDLER (void *parg, int fd, int opened); /* * ca_add_fd_registration() * * pHandler R pointer to function which is to be called * when an fd is created or deleted * pArg R argument passed to above function */ epicsShareFunc int epicsShareAPI ca_add_fd_registration ( CAFDHANDLER *pHandler, void *pArg ); /* * CA synch groups * * This facility will allow the programmer to create * any number of synchronization groups. The programmer might then * interleave IO requests within any of the groups. Once The * IO operations are initiated then the programmer is free to * block for IO completion within any one of the groups as needed. */ /* * ca_sg_create() * * create a sync group * * pgid W pointer to sync group id that will be written */ epicsShareFunc int epicsShareAPI ca_sg_create (CA_SYNC_GID * pgid); /* * ca_sg_delete() * * delete a sync group * * gid R sync group id */ epicsShareFunc int epicsShareAPI ca_sg_delete (const CA_SYNC_GID gid); /* * ca_sg_block() * * block for IO performed within a sync group to complete * * gid R sync group id * timeout R wait for this duration prior to timing out * and returning ECA_TIMEOUT */ epicsShareFunc int epicsShareAPI ca_sg_block (const CA_SYNC_GID gid, ca_real timeout); /* * ca_sg_test() * * test for sync group IO operations in progress * * gid R sync group id * * returns one of ECA_BADSYNCGRP, ECA_IOINPROGRESS, ECA_IODONE */ epicsShareFunc int epicsShareAPI ca_sg_test (const CA_SYNC_GID gid); /* * ca_sg_reset * * gid R sync group id */ epicsShareFunc int epicsShareAPI ca_sg_reset(const CA_SYNC_GID gid); /* * ca_sg_array_get() * * initiate a get within a sync group * (essentially a ca_array_get() with a sync group specified) * * gid R sync group id * type R data type from db_access.h * count R array element count * chan R channel identifier * pValue W channel value copied to this location */ epicsShareFunc int epicsShareAPI ca_sg_array_get ( const CA_SYNC_GID gid, chtype type, unsigned long count, chid chan, void *pValue ); #define ca_sg_get(gid, type, chan, pValue) \ ca_sg_array_get (gid, type, 1u, chan, pValue) /* * ca_sg_array_put() * * initiate a put within a sync group * (essentially a ca_array_put() with a sync group specified) * * gid R sync group id * type R data type from db_access.h * count R array element count * chan R channel identifier * pValue R new channel value copied from this location */ epicsShareFunc int epicsShareAPI ca_sg_array_put ( const CA_SYNC_GID gid, chtype type, unsigned long count, chid chan, const void *pValue ); #define ca_sg_put(gid, type, chan, pValue) \ ca_sg_array_put (gid, type, 1u, chan, pValue) /* * ca_sg_stat() * * print status of a sync group * * gid R sync group id */ epicsShareFunc int epicsShareAPI ca_sg_stat (CA_SYNC_GID gid); epicsShareFunc void epicsShareAPI ca_dump_dbr (chtype type, unsigned count, const void * pbuffer); /* * ca_v42_ok() * * Put call back is available if the CA server is on version is 4.2 * or higher. * * chan R channel identifier * * (returns true or false) */ epicsShareFunc int epicsShareAPI ca_v42_ok (chid chan); /* * ca_version() * * returns the CA version string */ epicsShareFunc const char * epicsShareAPI ca_version (void); /* * ca_replace_printf_handler () * * for apps that want to change where ca formatted * text output goes * * use two ifdef's for trad C compatibility * * ca_printf_func R pointer to new function called when * CA prints an error message */ #ifndef CA_DONT_INCLUDE_STDARGH typedef int caPrintfFunc (const char *pformat, va_list args); epicsShareFunc int epicsShareAPI ca_replace_printf_handler ( caPrintfFunc *ca_printf_func ); #endif /*CA_DONT_INCLUDE_STDARGH*/ /* * (for testing purposes only) */ epicsShareFunc unsigned epicsShareAPI ca_get_ioc_connection_count (void); epicsShareFunc int epicsShareAPI ca_preemtive_callback_is_enabled (void); epicsShareFunc void epicsShareAPI ca_self_test (void); epicsShareFunc unsigned epicsShareAPI ca_beacon_anomaly_count (void); epicsShareFunc unsigned epicsShareAPI ca_search_attempts (chid chan); epicsShareFunc double epicsShareAPI ca_beacon_period (chid chan); epicsShareFunc double epicsShareAPI ca_receive_watchdog_delay (chid chan); /* * used when an auxillary thread needs to join a CA client context started * by another thread */ epicsShareFunc struct ca_client_context * epicsShareAPI ca_current_context (); epicsShareFunc int epicsShareAPI ca_attach_context ( struct ca_client_context * context ); epicsShareFunc int epicsShareAPI ca_client_status ( unsigned level ); epicsShareFunc int epicsShareAPI ca_context_status ( struct ca_client_context *, unsigned level ); /* * deprecated */ #define ca_build_channel(NAME,XXXXX,CHIDPTR,YYYYY)\ ca_build_and_connect(NAME, XXXXX, 1, CHIDPTR, YYYYY, 0, 0) #define ca_array_build(NAME,XXXXX, ZZZZZZ, CHIDPTR,YYYYY)\ ca_build_and_connect(NAME, XXXXX, ZZZZZZ, CHIDPTR, YYYYY, 0, 0) epicsShareFunc int epicsShareAPI ca_build_and_connect ( const char *pChanName, chtype, unsigned long, chid * pChanID, void *, caCh * pFunc, void * pArg ); #define ca_search(pChanName, pChanID)\ ca_search_and_connect (pChanName, pChanID, 0, 0) epicsShareFunc int epicsShareAPI ca_search_and_connect ( const char * pChanName, chid * pChanID, caCh *pFunc, void * pArg ); epicsShareFunc int epicsShareAPI ca_channel_status (epicsThreadId tid); epicsShareFunc int epicsShareAPI ca_clear_event ( evid eventID ); #define ca_add_event(type,chan,pFunc,pArg,pEventID)\ ca_add_array_event(type,1u,chan,pFunc,pArg,0.0,0.0,0.0,pEventID) #define ca_add_delta_event(TYPE,CHID,ENTRY,ARG,DELTA,EVID)\ ca_add_array_event(TYPE,1,CHID,ENTRY,ARG,DELTA,DELTA,0.0,EVID) #define ca_add_general_event(TYPE,CHID,ENTRY,ARG,P_DELTA,N_DELTA,TO,EVID)\ ca_add_array_event(TYPE,1,CHID,ENTRY,ARG,P_DELTA,N_DELTA,TO,EVID) #define ca_add_array_event(TYPE,COUNT,CHID,ENTRY,ARG,P_DELTA,N_DELTA,TO,EVID)\ ca_add_masked_array_event(TYPE,COUNT,CHID,ENTRY,ARG,P_DELTA,N_DELTA,TO,EVID, DBE_VALUE | DBE_ALARM) epicsShareFunc int epicsShareAPI ca_add_masked_array_event ( chtype type, unsigned long count, chid chanId, caEventCallBackFunc * pFunc, void * pArg, ca_real p_delta, ca_real n_delta, ca_real timeout, evid * pEventID, long mask ); /* * defunct */ epicsShareFunc int epicsShareAPI ca_modify_user_name ( const char *pUserName ); epicsShareFunc int epicsShareAPI ca_modify_host_name ( const char *pHostName ); #ifdef __cplusplus } #endif /* * no additions below this endif */ #endif /* ifndef INCLcadefh */ base-7.0.3.1/modules/ca/src/client/caerr.h0000664000577000060420000001477213557101274016773 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeffrey O. Hill * */ #ifndef INCLcaerrh #define INCLcaerrh #ifdef epicsExportSharedSymbols # define INCLcaerrh_accessh_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif # include "epicsTypes.h" #ifdef INCLcaerrh_accessh_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif /* CA Status Code Definitions */ #define CA_K_INFO 3 /* successful */ #define CA_K_ERROR 2 /* failed- continue */ #define CA_K_SUCCESS 1 /* successful */ #define CA_K_WARNING 0 /* unsuccessful */ #define CA_K_SEVERE 4 /* failed- quit */ #define CA_K_FATAL CA_K_ERROR | CA_K_SEVERE #define CA_M_MSG_NO 0x0000FFF8 #define CA_M_SEVERITY 0x00000007 #define CA_M_LEVEL 0x00000003 #define CA_M_SUCCESS 0x00000001 #define CA_M_ERROR 0x00000002 #define CA_M_SEVERE 0x00000004 #define CA_S_MSG_NO 0x0D #define CA_S_SEVERITY 0x03 #define CA_V_MSG_NO 0x03 #define CA_V_SEVERITY 0x00 #define CA_V_SUCCESS 0x00 /* Define MACROS to extract/insert individual fields from a status value */ #define CA_EXTRACT_MSG_NO(code)\ ( ( (code) & CA_M_MSG_NO ) >> CA_V_MSG_NO ) #define CA_EXTRACT_SEVERITY(code)\ ( ( (code) & CA_M_SEVERITY ) >> CA_V_SEVERITY ) #define CA_EXTRACT_SUCCESS(code)\ ( ( (code) & CA_M_SUCCESS ) >> CA_V_SUCCESS ) #define CA_INSERT_MSG_NO(code)\ ( ((code)<< CA_V_MSG_NO) & CA_M_MSG_NO ) #define CA_INSERT_SEVERITY(code)\ ( ((code)<< CA_V_SEVERITY)& CA_M_SEVERITY ) #define CA_INSERT_SUCCESS(code)\ ( ((code)<< CA_V_SUCCESS) & CA_M_SUCCESS ) #define DEFMSG(SEVERITY,NUMBER)\ (CA_INSERT_MSG_NO(NUMBER) | CA_INSERT_SEVERITY(SEVERITY)) /* * In the lines below "defunct" indicates that current release * servers and client library will not return this error code, but * servers on earlier releases that communicate with current clients * might still generate exceptions with these error constants */ #define ECA_NORMAL DEFMSG(CA_K_SUCCESS, 0) /* success */ #define ECA_MAXIOC DEFMSG(CA_K_ERROR, 1) /* defunct */ #define ECA_UKNHOST DEFMSG(CA_K_ERROR, 2) /* defunct */ #define ECA_UKNSERV DEFMSG(CA_K_ERROR, 3) /* defunct */ #define ECA_SOCK DEFMSG(CA_K_ERROR, 4) /* defunct */ #define ECA_CONN DEFMSG(CA_K_WARNING, 5) /* defunct */ #define ECA_ALLOCMEM DEFMSG(CA_K_WARNING, 6) #define ECA_UKNCHAN DEFMSG(CA_K_WARNING, 7) /* defunct */ #define ECA_UKNFIELD DEFMSG(CA_K_WARNING, 8) /* defunct */ #define ECA_TOLARGE DEFMSG(CA_K_WARNING, 9) #define ECA_TIMEOUT DEFMSG(CA_K_WARNING, 10) #define ECA_NOSUPPORT DEFMSG(CA_K_WARNING, 11) /* defunct */ #define ECA_STRTOBIG DEFMSG(CA_K_WARNING, 12) /* defunct */ #define ECA_DISCONNCHID DEFMSG(CA_K_ERROR, 13) /* defunct */ #define ECA_BADTYPE DEFMSG(CA_K_ERROR, 14) #define ECA_CHIDNOTFND DEFMSG(CA_K_INFO, 15) /* defunct */ #define ECA_CHIDRETRY DEFMSG(CA_K_INFO, 16) /* defunct */ #define ECA_INTERNAL DEFMSG(CA_K_FATAL, 17) #define ECA_DBLCLFAIL DEFMSG(CA_K_WARNING, 18) /* defunct */ #define ECA_GETFAIL DEFMSG(CA_K_WARNING, 19) #define ECA_PUTFAIL DEFMSG(CA_K_WARNING, 20) #define ECA_ADDFAIL DEFMSG(CA_K_WARNING, 21) /* defunct */ #define ECA_BADCOUNT DEFMSG(CA_K_WARNING, 22) #define ECA_BADSTR DEFMSG(CA_K_ERROR, 23) #define ECA_DISCONN DEFMSG(CA_K_WARNING, 24) #define ECA_DBLCHNL DEFMSG(CA_K_WARNING, 25) #define ECA_EVDISALLOW DEFMSG(CA_K_ERROR, 26) #define ECA_BUILDGET DEFMSG(CA_K_WARNING, 27) /* defunct */ #define ECA_NEEDSFP DEFMSG(CA_K_WARNING, 28) /* defunct */ #define ECA_OVEVFAIL DEFMSG(CA_K_WARNING, 29) /* defunct */ #define ECA_BADMONID DEFMSG(CA_K_ERROR, 30) #define ECA_NEWADDR DEFMSG(CA_K_WARNING, 31) /* defunct */ #define ECA_NEWCONN DEFMSG(CA_K_INFO, 32) /* defunct */ #define ECA_NOCACTX DEFMSG(CA_K_WARNING, 33) /* defunct */ #define ECA_DEFUNCT DEFMSG(CA_K_FATAL, 34) /* defunct */ #define ECA_EMPTYSTR DEFMSG(CA_K_WARNING, 35) /* defunct */ #define ECA_NOREPEATER DEFMSG(CA_K_WARNING, 36) /* defunct */ #define ECA_NOCHANMSG DEFMSG(CA_K_WARNING, 37) /* defunct */ #define ECA_DLCKREST DEFMSG(CA_K_WARNING, 38) /* defunct */ #define ECA_SERVBEHIND DEFMSG(CA_K_WARNING, 39) /* defunct */ #define ECA_NOCAST DEFMSG(CA_K_WARNING, 40) /* defunct */ #define ECA_BADMASK DEFMSG(CA_K_ERROR, 41) #define ECA_IODONE DEFMSG(CA_K_INFO, 42) #define ECA_IOINPROGRESS DEFMSG(CA_K_INFO, 43) #define ECA_BADSYNCGRP DEFMSG(CA_K_ERROR, 44) #define ECA_PUTCBINPROG DEFMSG(CA_K_ERROR, 45) #define ECA_NORDACCESS DEFMSG(CA_K_WARNING, 46) #define ECA_NOWTACCESS DEFMSG(CA_K_WARNING, 47) #define ECA_ANACHRONISM DEFMSG(CA_K_ERROR, 48) #define ECA_NOSEARCHADDR DEFMSG(CA_K_WARNING, 49) #define ECA_NOCONVERT DEFMSG(CA_K_WARNING, 50) #define ECA_BADCHID DEFMSG(CA_K_ERROR, 51) #define ECA_BADFUNCPTR DEFMSG(CA_K_ERROR, 52) #define ECA_ISATTACHED DEFMSG(CA_K_WARNING, 53) #define ECA_UNAVAILINSERV DEFMSG(CA_K_WARNING, 54) #define ECA_CHANDESTROY DEFMSG(CA_K_WARNING, 55) #define ECA_BADPRIORITY DEFMSG(CA_K_ERROR, 56) #define ECA_NOTTHREADED DEFMSG(CA_K_ERROR, 57) #define ECA_16KARRAYCLIENT DEFMSG(CA_K_WARNING, 58) #define ECA_CONNSEQTMO DEFMSG(CA_K_WARNING, 59) #define ECA_UNRESPTMO DEFMSG(CA_K_WARNING, 60) #ifdef __cplusplus extern "C" { #endif epicsShareFunc const char * epicsShareAPI ca_message(long ca_status); epicsShareExtern const char * ca_message_text []; #ifdef __cplusplus } #endif #endif base-7.0.3.1/modules/ca/src/client/caeventmask.h0000664000577000060420000000265613557101274020176 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #ifndef INCLcaeventmaskh #define INCLcaeventmaskh /* event selections (If any more than 8 of these are needed then update the select field in the event_block struct in db_event.c from unsigned char to unsigned short) DBE_VALUE Trigger an event when a significant change in the channel's value occurs. Relies on the monitor deadband field under DCT. DBE_ARCHIVE (DBE_LOG) Trigger an event when an archive significant change in the channel's value occurs. Relies on the archiver monitor deadband field under DCT. DBE_ALARM Trigger an event when the alarm state changes DBE_PROPERTY Trigger an event when a property change (control limit, graphical limit, status string, enum string ...) occurs. */ #define DBE_VALUE (1<<0) #define DBE_ARCHIVE (1<<1) #define DBE_LOG DBE_ARCHIVE #define DBE_ALARM (1<<2) #define DBE_PROPERTY (1<<3) #endif base-7.0.3.1/modules/ca/src/client/casw.cpp0000664000577000060420000002473413557101274017166 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "envDefs.h" #include "errlog.h" #include "osiWireFormat.h" #include "bhe.h" #include "udpiiu.h" #include "inetAddrID.h" // using a wrapper class around the free list avoids // Tornado 2.0.1 GNU compiler bugs class bheFreeStoreMgr : public bheMemoryManager { public: bheFreeStoreMgr () {} void * allocate ( size_t ); void release ( void * ); private: tsFreeList < class bhe, 0x100 > freeList; bheFreeStoreMgr ( const bheFreeStoreMgr & ); bheFreeStoreMgr & operator = ( const bheFreeStoreMgr & ); }; void * bheFreeStoreMgr::allocate ( size_t size ) { return freeList.allocate ( size ); } void bheFreeStoreMgr::release ( void * pCadaver ) { freeList.release ( pCadaver ); } int main ( int argc, char ** argv ) { epicsMutex mutex; epicsGuard < epicsMutex > guard ( mutex ); bheFreeStoreMgr bheFreeList; epicsTime programBeginTime = epicsTime::getMonotonic (); bool validCommandLine = false; unsigned interest = 0u; SOCKET sock; osiSockAddr addr; osiSocklen_t addrSize; char buf [0x4000]; const char *pCurBuf; const caHdr *pCurMsg; ca_uint16_t serverPort; ca_uint16_t repeaterPort; int status; if ( argc == 1 ) { validCommandLine = true; } else if ( argc == 2 ) { status = sscanf ( argv[1], " -i%u ", & interest ); if ( status == 1 ) { validCommandLine = true; } } else if ( argc == 3 ) { if ( strcmp ( argv[1], "-i" ) == 0 ) { status = sscanf ( argv[2], " %u ", & interest ); if ( status == 1 ) { validCommandLine = true; } } } if ( ! validCommandLine ) { printf ( "usage: casw <-i interestLevel>\n" ); return 0; } serverPort = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT, static_cast (CA_SERVER_PORT) ); repeaterPort = envGetInetPortConfigParam ( &EPICS_CA_REPEATER_PORT, static_cast (CA_REPEATER_PORT) ); caStartRepeaterIfNotInstalled ( repeaterPort ); sock = epicsSocketCreate ( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if ( sock == INVALID_SOCKET ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ("casw: unable to create datagram socket because = \"%s\"\n", sockErrBuf ); return -1; } memset ( (char *) &addr, 0 , sizeof (addr) ); addr.ia.sin_family = AF_INET; addr.ia.sin_addr.s_addr = htonl ( INADDR_ANY ); addr.ia.sin_port = htons ( 0 ); // any port status = bind ( sock, &addr.sa, sizeof (addr) ); if ( status < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); epicsSocketDestroy ( sock ); errlogPrintf ( "casw: unable to bind to an unconstrained address because = \"%s\"\n", sockErrBuf ); return -1; } osiSockIoctl_t yes = true; status = socket_ioctl ( sock, FIONBIO, &yes ); if ( status < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); epicsSocketDestroy ( sock ); errlogPrintf ( "casw: unable to set socket to nonblocking state because \"%s\"\n", sockErrBuf ); return -1; } unsigned attemptNumber = 0u; while ( true ) { caRepeaterRegistrationMessage ( sock, repeaterPort, attemptNumber ); epicsThreadSleep ( 0.1 ); addrSize = ( osiSocklen_t ) sizeof ( addr ); status = recvfrom ( sock, buf, sizeof ( buf ), 0, &addr.sa, &addrSize ); if ( status >= static_cast ( sizeof ( *pCurMsg ) ) ) { pCurMsg = reinterpret_cast < caHdr * > ( buf ); epicsUInt16 cmmd = AlignedWireRef < const epicsUInt16 > ( pCurMsg->m_cmmd ); if ( cmmd == REPEATER_CONFIRM ) { break; } } attemptNumber++; if ( attemptNumber > 100 ) { epicsSocketDestroy ( sock ); errlogPrintf ( "casw: unable to register with the CA repeater\n" ); return -1; } } osiSockIoctl_t no = false; status = socket_ioctl ( sock, FIONBIO, &no ); if ( status < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); epicsSocketDestroy ( sock ); errlogPrintf ( "casw: unable to set socket to blocking state because \"%s\"\n", sockErrBuf ); return -1; } resTable < bhe, inetAddrID > beaconTable; while ( true ) { addrSize = ( osiSocklen_t ) sizeof ( addr ); status = recvfrom ( sock, buf, sizeof ( buf ), 0, &addr.sa, &addrSize ); if ( status <= 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); epicsSocketDestroy ( sock ); errlogPrintf ("casw: error from recv was = \"%s\"\n", sockErrBuf ); return -1; } if ( addr.sa.sa_family != AF_INET ) { continue; } unsigned byteCount = static_cast ( status ); pCurMsg = reinterpret_cast < const caHdr * > ( ( pCurBuf = buf ) ); while ( byteCount ) { AlignedWireRef < const epicsUInt16 > pstSize ( pCurMsg->m_postsize ); size_t msgSize = pstSize + sizeof ( *pCurMsg ) ; if ( msgSize > byteCount ) { errlogPrintf ( "CASW: udp input protocol violation\n" ); break; } epicsUInt16 cmmd = AlignedWireRef < const epicsUInt16 > ( pCurMsg->m_cmmd ); if ( cmmd == CA_PROTO_RSRV_IS_UP ) { bool anomaly = false; epicsTime previousTime; struct sockaddr_in ina; /* * this allows a fan-out server to potentially * insert the true address of the CA server * * old servers: * 1) set this field to one of the ip addresses of the host _or_ * 2) set this field to INADDR_ANY * new servers: * always set this field to INADDR_ANY * * clients always assume that if this * field is set to something that isnt INADDR_ANY * then it is the overriding IP address of the server. */ ina.sin_family = AF_INET; ina.sin_addr.s_addr = pCurMsg->m_available; if ( pCurMsg->m_count != 0 ) { ina.sin_port = pCurMsg->m_count; } else { /* * old servers dont supply this and the * default port must be assumed */ ina.sin_port = htons ( serverPort ); } ca_uint32_t beaconNumber = ntohl ( pCurMsg->m_cid ); unsigned protocolRevision = ntohs ( pCurMsg->m_dataType ); epicsTime currentTime = epicsTime::getMonotonic(); /* * look for it in the hash table */ bhe *pBHE = beaconTable.lookup ( ina ); if ( pBHE ) { previousTime = pBHE->updateTime ( guard ); anomaly = pBHE->updatePeriod ( guard, programBeginTime, currentTime, beaconNumber, protocolRevision ); } else { /* * This is the first beacon seen from this server. * Wait until 2nd beacon is seen before deciding * if it is a new server (or just the first * time that we have seen a server's beacon * shortly after the program started up) */ pBHE = new ( bheFreeList ) bhe ( mutex, currentTime, beaconNumber, ina ); if ( pBHE ) { if ( beaconTable.add ( *pBHE ) < 0 ) { pBHE->~bhe (); bheFreeList.release ( pBHE ); } } } if ( anomaly || interest > 1 ) { char date[64]; currentTime.strftime ( date, sizeof ( date ), "%Y-%m-%d %H:%M:%S.%09f"); char host[64]; ipAddrToA ( &ina, host, sizeof ( host ) ); const char * pPrefix = ""; if ( interest > 1 ) { if ( anomaly ) { pPrefix = "* "; } else { pPrefix = " "; } } printf ( "%s%-40s %s\n", pPrefix, host, date ); if ( anomaly && interest > 0 ) { printf ( "\testimate=%f current=%f\n", pBHE->period ( guard ), currentTime - previousTime ); } fflush(stdout); } } pCurBuf += msgSize; pCurMsg = reinterpret_cast < const caHdr * > ( pCurBuf ); byteCount -= msgSize; } } } base-7.0.3.1/modules/ca/src/client/catime.c0000664000577000060420000004207713557101274017133 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * CA performance test * * History * joh 09-12-89 Initial release * joh 12-20-94 portability * * */ #include #include #include #include #include #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "epicsAssert.h" #include "epicsTime.h" #include "cadef.h" #include "caProto.h" #include "caDiagnostics.h" #ifndef NULL #define NULL 0 #endif #define WAIT_FOR_ACK typedef struct testItem { chid chix; char name[128]; int type; int count; void * pValue; } ti; typedef void tf ( ti *pItems, unsigned iterations, unsigned *pInlineIter ); /* * test_pend() */ static void test_pend( ti *pItems, unsigned iterations, unsigned *pInlineIter ) { unsigned i; int status; for (i=0; itype, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); } #ifdef WAIT_FOR_ACK status = ca_array_get (DBR_INT, 1, pItems[0].chix, &val); SEVCHK (status, NULL); ca_pend_io(100.0); #endif status = ca_array_put( pItems[0].type, pItems[0].count, pItems[0].chix, pItems[0].pValue); SEVCHK (status, NULL); status = ca_flush_io(); SEVCHK (status, NULL); *pInlineIter = 10; } /* * test_get () */ static void test_get( ti *pItems, unsigned iterations, unsigned *pInlineIter ) { ti *pi; int status; for (pi=pItems; pi<&pItems[iterations]; pi++) { status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); } status = ca_pend_io(1e20); SEVCHK (status, NULL); *pInlineIter = 10; } /* * test_wait () */ static void test_wait ( ti *pItems, unsigned iterations, unsigned *pInlineIter ) { ti *pi; int status; for (pi=pItems; pi<&pItems[iterations]; pi++) { status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_pend_io(100.0); SEVCHK (status, NULL); } *pInlineIter = 1; } /* * measure_get_latency */ static void measure_get_latency (ti *pItems, unsigned iterations) { epicsTimeStamp end_time; epicsTimeStamp start_time; double delay; double X = 0u; double XX = 0u; double max = DBL_MIN; double min = DBL_MAX; double mean; double stdDev; ti *pi; int status; for ( pi = pItems; pi < &pItems[iterations]; pi++ ) { epicsTimeGetCurrent ( &start_time ); status = ca_array_get ( pi->type, pi->count, pi->chix, pi->pValue ); SEVCHK ( status, NULL ); status = ca_pend_io ( 100.0 ); SEVCHK ( status, NULL ); epicsTimeGetCurrent ( &end_time ); delay = epicsTimeDiffInSeconds ( &end_time,&start_time ); X += delay; XX += delay*delay; if ( delay > max ) { max = delay; } if ( delay < min ) { min = delay; } } mean = X/iterations; stdDev = sqrt ( XX/iterations - mean*mean ); printf ( "Get Latency - " "mean = %3.1f uS, " "std dev = %3.1f uS, " "min = %3.1f uS " "max = %3.1f uS\n", mean * 1e6, stdDev * 1e6, min * 1e6, max * 1e6 ); } /* * printSearchStat() */ static void printSearchStat ( const ti * pi, unsigned iterations ) { unsigned i; double X = 0u; double XX = 0u; double max = DBL_MIN; double min = DBL_MAX; double mean; double stdDev; for ( i = 0; i < iterations; i++ ) { double retry = ca_search_attempts ( pi[i].chix ); X += retry; XX += retry * retry; if ( retry > max ) { max = retry; } if ( retry < min ) { min = retry; } } mean = X / iterations; stdDev = sqrt( XX / iterations - mean * mean ); printf ( "Search tries per chan - " "mean = %3.1f " "std dev = %3.1f " "min = %3.1f " "max = %3.1f\n", mean, stdDev, min, max); } /* * timeIt () */ void timeIt ( tf *pfunc, ti *pItems, unsigned iterations, unsigned nBytesSent, unsigned nBytesRecv ) { epicsTimeStamp end_time; epicsTimeStamp start_time; double delay; unsigned inlineIter; epicsTimeGetCurrent ( &start_time ); (*pfunc) ( pItems, iterations, &inlineIter ); epicsTimeGetCurrent ( &end_time ); delay = epicsTimeDiffInSeconds ( &end_time, &start_time ); if ( delay > 0.0 ) { double freq = ( iterations * inlineIter ) / delay; printf ( "Per Op, %8.4f uS ( %8.4f MHz )", 1e6 / freq, freq / 1e6 ); if ( pItems != NULL ) { printf(", %8.4f snd Mbps, %8.4f rcv Mbps\n", (inlineIter*nBytesSent*CHAR_BIT)/(delay*1e6), (inlineIter*nBytesRecv*CHAR_BIT)/(delay*1e6) ); } else { printf ("\n"); } } } /* * test () */ static void test ( ti *pItems, unsigned iterations ) { unsigned payloadSize, dblPayloadSize; unsigned nBytesSent, nBytesRecv; payloadSize = dbr_size_n ( pItems[0].type, pItems[0].count ); payloadSize = CA_MESSAGE_ALIGN ( payloadSize ); dblPayloadSize = dbr_size [ DBR_DOUBLE ]; dblPayloadSize = CA_MESSAGE_ALIGN ( dblPayloadSize ); if ( payloadSize > dblPayloadSize ) { unsigned factor = payloadSize / dblPayloadSize; while ( factor ) { if ( iterations > 10 * factor ) { iterations /= factor; break; } factor /= 2; } } printf ( "\t### async put test ###\n"); nBytesSent = sizeof ( caHdr ) + CA_MESSAGE_ALIGN( payloadSize ); nBytesRecv = 0u; timeIt ( test_put, pItems, iterations, nBytesSent * iterations, nBytesRecv * iterations ); printf ( "\t### async get test ###\n"); nBytesSent = sizeof ( caHdr ); nBytesRecv = sizeof ( caHdr ) + CA_MESSAGE_ALIGN ( payloadSize ); timeIt ( test_get, pItems, iterations, nBytesSent * ( iterations ), nBytesRecv * ( iterations ) ); printf ("\t### synch get test ###\n"); nBytesSent = sizeof ( caHdr ); nBytesRecv = sizeof ( caHdr ) + CA_MESSAGE_ALIGN ( payloadSize ); if ( iterations > 100 ) { iterations /= 100; } else if ( iterations > 10 ) { iterations /= 10; } timeIt ( test_wait, pItems, iterations, nBytesSent * iterations, nBytesRecv * iterations ); } /* * catime () */ int catime ( const char * channelName, unsigned channelCount, enum appendNumberFlag appNF ) { unsigned i; int j; unsigned strsize; unsigned nBytesSent, nBytesRecv; ti *pItemList; if ( channelCount == 0 ) { printf ( "channel count was zero\n" ); return 0; } pItemList = calloc ( channelCount, sizeof (ti) ); if ( ! pItemList ) { return -1; } SEVCHK ( ca_context_create ( ca_disable_preemptive_callback ), "Unable to initialize" ); if ( appNF == appendNumber ) { printf ( "Testing with %u channels named %snnn\n", channelCount, channelName ); } else { printf ( "Testing with %u channels named %s\n", channelCount, channelName ); } strsize = sizeof ( pItemList[0].name ) - 1; nBytesSent = 0; nBytesRecv = 0; for ( i=0; i < channelCount; i++ ) { if ( appNF == appendNumber ) { sprintf ( pItemList[i].name,"%.*s%.6u", (int) (strsize - 15u), channelName, i ); } else { strncpy ( pItemList[i].name, channelName, strsize); } pItemList[i].name[strsize]= '\0'; pItemList[i].count = 0; pItemList[i].pValue = 0; nBytesSent += 2 * ( CA_MESSAGE_ALIGN ( strlen ( pItemList[i].name ) ) + sizeof (caHdr) ); nBytesRecv += 2 * sizeof (caHdr); } printf ( "Channel Connect Test\n" ); printf ( "--------------------\n" ); timeIt ( test_search, pItemList, channelCount, nBytesSent, nBytesRecv ); printSearchStat ( pItemList, channelCount ); for ( i = 0; i < channelCount; i++ ) { size_t count = ca_element_count ( pItemList[i].chix ); size_t size = sizeof ( dbr_string_t ) * count; pItemList[i].count = count; pItemList[i].pValue = malloc ( size ); assert ( pItemList[i].pValue ); } printf ( "channel name=%s, native type=%d, native count=%u\n", ca_name (pItemList[0].chix), ca_field_type (pItemList[0].chix), pItemList[0].count ); printf ("Pend Event Test\n"); printf ( "----------------\n" ); timeIt ( test_pend, NULL, 100, 0, 0 ); for ( i = 0; i < channelCount; i++ ) { dbr_float_t * pFltVal = ( dbr_float_t * ) pItemList[i].pValue; double val = i; val /= channelCount; for ( j = 0; j < pItemList[i].count; j++ ) { pFltVal[j] = (dbr_float_t) val; } pItemList[i].type = DBR_FLOAT; } printf ( "DBR_FLOAT Test\n" ); printf ( "--------------\n" ); test ( pItemList, channelCount ); for ( i = 0; i < channelCount; i++ ) { dbr_double_t * pDblVal = ( dbr_double_t * ) pItemList[i].pValue; double val = i; val /= channelCount; for ( j = 0; j < pItemList[i].count; j++ ) { pDblVal[j] = (dbr_double_t) val; } pItemList[i].type = DBR_DOUBLE; } printf ( "DBR_DOUBLE Test\n" ); printf ( "---------------\n" ); test ( pItemList, channelCount ); for ( i = 0; i < channelCount; i++ ) { dbr_string_t * pStrVal = ( dbr_string_t * ) pItemList[i].pValue; double val = i; val /= channelCount; for ( j = 0; j < pItemList[i].count; j++ ) { sprintf ( pStrVal[j], "%f", val ); } pItemList[i].type = DBR_STRING; } printf ( "DBR_STRING Test\n" ); printf ( "---------------\n" ); test ( pItemList, channelCount ); for ( i = 0; i < channelCount; i++ ) { dbr_int_t * pIntVal = ( dbr_int_t * ) pItemList[i].pValue; double val = i; val /= channelCount; for ( j = 0; j < pItemList[i].count; j++ ) { pIntVal[j] = (dbr_int_t) val; } pItemList[i].type = DBR_INT; } printf ( "DBR_INT Test\n" ); printf ( "------------\n" ); test ( pItemList, channelCount ); printf ( "Get Latency Test\n" ); printf ( "----------------\n" ); for ( i = 0; i < channelCount; i++ ) { dbr_double_t * pDblVal = ( dbr_double_t * ) pItemList[i].pValue; for ( j = 0; j < pItemList[i].count; j++ ) { pDblVal[j] = 0; } pItemList[i].type = DBR_DOUBLE; } measure_get_latency ( pItemList, channelCount ); printf ( "Free Channel Test\n" ); printf ( "-----------------\n" ); timeIt ( test_free, pItemList, channelCount, 0, 0 ); SEVCHK ( ca_task_exit (), "Unable to free resources at exit" ); for ( i = 0; i < channelCount; i++ ) { free ( pItemList[i].pValue ); } free ( pItemList ); return CATIME_OK; } base-7.0.3.1/modules/ca/src/client/catimeMain.c0000664000577000060420000000364613557101274017737 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include #include #include "caDiagnostics.h" static const unsigned defaultIterations = 10000u; int main ( int argc, char **argv ) { const char *pUsage = " [ []]"; if ( argc > 1 ) { char *pname = argv[1]; if ( argc > 2 ) { int iterations = atoi (argv[2]); if ( iterations > 0) { if ( argc > 3 ) { if ( argc == 4 ) { int status; unsigned appendNumberBool; status = sscanf ( argv[3], " %u ", &appendNumberBool ); if ( status == 1 ) { if ( appendNumberBool ) { return catime ( pname, (unsigned) iterations, appendNumber ); } else { return catime ( pname, (unsigned) iterations, dontAppendNumber ); } } } } else { return catime ( pname, (unsigned) iterations, dontAppendNumber ); } } } else { return catime ( pname, defaultIterations, dontAppendNumber ); } } printf ( "usage: %s %s\n", argv[0], pUsage); return -1; } base-7.0.3.1/modules/ca/src/client/comBuf.cpp0000664000577000060420000000427713557101274017444 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov */ #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "comBuf.h" #include "errlog.h" bool comBuf::flushToWire ( wireSendAdapter & wire, const epicsTime & currentTime ) { unsigned index = this->nextReadIndex; unsigned finalIndex = this->commitIndex; while ( index < finalIndex ) { unsigned nBytes = wire.sendBytes ( &this->buf[index], finalIndex - index, currentTime ); if ( nBytes == 0u ) { this->nextReadIndex = index; return false; } index += nBytes; } this->nextReadIndex = index; return true; } // throwing the exception from a function that isnt inline // shrinks the GNU compiled object code void comBuf::throwInsufficentBytesException () { throw comBuf::insufficentBytesAvailable (); } void comBuf::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } comBufMemoryManager::~comBufMemoryManager () {} base-7.0.3.1/modules/ca/src/client/comBuf.h0000664000577000060420000002205413557101274017102 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef comBufh #define comBufh #include #include #include "epicsAssert.h" #include "epicsTypes.h" #include "tsFreeList.h" #include "tsDLList.h" #include "osiWireFormat.h" #include "compilerDependencies.h" static const unsigned comBufSize = 0x4000; // this wrapper avoids Tornado 2.0.1 compiler bugs class comBufMemoryManager { public: virtual ~comBufMemoryManager (); virtual void * allocate ( size_t ) = 0; virtual void release ( void * ) = 0; }; class wireSendAdapter { public: virtual unsigned sendBytes ( const void * pBuf, unsigned nBytesInBuf, const class epicsTime & currentTime ) = 0; protected: virtual ~wireSendAdapter() {} }; enum swioCircuitState { swioConnected, swioPeerHangup, swioPeerAbort, swioLinkFailure, swioLocalAbort }; struct statusWireIO { unsigned bytesCopied; swioCircuitState circuitState; }; class wireRecvAdapter { public: virtual void recvBytes ( void * pBuf, unsigned nBytesInBuf, statusWireIO & ) = 0; protected: virtual ~wireRecvAdapter() {} }; class comBuf : public tsDLNode < comBuf > { public: class insufficentBytesAvailable {}; comBuf (); unsigned unoccupiedBytes () const; unsigned occupiedBytes () const; unsigned uncommittedBytes () const; static unsigned capacityBytes (); void clear (); unsigned copyInBytes ( const void *pBuf, unsigned nBytes ); unsigned push ( comBuf & ); template < class T > bool push ( const T & value ); template < class T > unsigned push ( const T * pValue, unsigned nElem ); unsigned push ( const epicsInt8 * pValue, unsigned nElem ); unsigned push ( const epicsUInt8 * pValue, unsigned nElem ); unsigned push ( const epicsOldString * pValue, unsigned nElem ); void commitIncomming (); void clearUncommittedIncomming (); bool copyInAllBytes ( const void *pBuf, unsigned nBytes ); unsigned copyOutBytes ( void *pBuf, unsigned nBytes ); bool copyOutAllBytes ( void *pBuf, unsigned nBytes ); unsigned removeBytes ( unsigned nBytes ); bool flushToWire ( wireSendAdapter &, const epicsTime & currentTime ); void fillFromWire ( wireRecvAdapter &, statusWireIO & ); struct popStatus { bool success; bool nowEmpty; }; template < class T > popStatus pop ( T & ); static void throwInsufficentBytesException (); void * operator new ( size_t size, comBufMemoryManager & ); epicsPlacementDeleteOperator (( void *, comBufMemoryManager & )) private: unsigned commitIndex; unsigned nextWriteIndex; unsigned nextReadIndex; epicsUInt8 buf [ comBufSize ]; void operator delete ( void * ); template < class T > bool push ( const T * ); // disabled }; inline void * comBuf::operator new ( size_t size, comBufMemoryManager & mgr ) { return mgr.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void comBuf::operator delete ( void * pCadaver, comBufMemoryManager & mgr ) { mgr.release ( pCadaver ); } #endif inline comBuf::comBuf () : commitIndex ( 0u ), nextWriteIndex ( 0u ), nextReadIndex ( 0u ) { } inline void comBuf :: clear () { this->commitIndex = 0u; this->nextWriteIndex = 0u; this->nextReadIndex = 0u; } inline unsigned comBuf :: unoccupiedBytes () const { return sizeof ( this->buf ) - this->nextWriteIndex; } inline unsigned comBuf :: occupiedBytes () const { return this->commitIndex - this->nextReadIndex; } inline unsigned comBuf :: uncommittedBytes () const { return this->nextWriteIndex - this->commitIndex; } inline unsigned comBuf :: push ( comBuf & bufIn ) { unsigned nBytes = this->copyInBytes ( & bufIn.buf[ bufIn.nextReadIndex ], bufIn.commitIndex - bufIn.nextReadIndex ); bufIn.nextReadIndex += nBytes; return nBytes; } inline unsigned comBuf :: capacityBytes () { return comBufSize; } inline void comBuf :: fillFromWire ( wireRecvAdapter & wire, statusWireIO & stat ) { wire.recvBytes ( & this->buf[this->nextWriteIndex], sizeof ( this->buf ) - this->nextWriteIndex, stat ); if ( stat.circuitState == swioConnected ) { this->nextWriteIndex += stat.bytesCopied; } } template < class T > inline bool comBuf :: push ( const T & value ) { unsigned index = this->nextWriteIndex; unsigned available = sizeof ( this->buf ) - index; if ( sizeof ( value ) > available ) { return false; } WireSet ( value, & this->buf[index] ); this->nextWriteIndex = index + sizeof ( value ); return true; } inline unsigned comBuf :: push ( const epicsInt8 *pValue, unsigned nElem ) { return copyInBytes ( pValue, nElem ); } inline unsigned comBuf :: push ( const epicsUInt8 *pValue, unsigned nElem ) { return copyInBytes ( pValue, nElem ); } inline unsigned comBuf :: push ( const epicsOldString * pValue, unsigned nElem ) { unsigned index = this->nextWriteIndex; unsigned available = sizeof ( this->buf ) - index; unsigned nBytes = sizeof ( *pValue ) * nElem; if ( nBytes > available ) { nElem = available / sizeof ( *pValue ); nBytes = nElem * sizeof ( *pValue ); } memcpy ( &this->buf[ index ], pValue, nBytes ); this->nextWriteIndex = index + nBytes; return nElem; } template < class T > unsigned comBuf :: push ( const T * pValue, unsigned nElem ) { unsigned index = this->nextWriteIndex; unsigned available = sizeof ( this->buf ) - index; unsigned nBytes = sizeof ( *pValue ) * nElem; if ( nBytes > available ) { nElem = available / sizeof ( *pValue ); } for ( unsigned i = 0u; i < nElem; i++ ) { // allow native floating point formats to be converted to IEEE WireSet( pValue[i], &this->buf[index] ); index += sizeof ( *pValue ); } this->nextWriteIndex = index; return nElem; } inline void comBuf :: commitIncomming () { this->commitIndex = this->nextWriteIndex; } inline void comBuf :: clearUncommittedIncomming () { this->nextWriteIndex = this->commitIndex; } inline bool comBuf :: copyInAllBytes ( const void *pBuf, unsigned nBytes ) { unsigned index = this->nextWriteIndex; unsigned available = sizeof ( this->buf ) - index; if ( nBytes <= available ) { memcpy ( & this->buf[index], pBuf, nBytes ); this->nextWriteIndex = index + nBytes; return true; } return false; } inline unsigned comBuf :: copyInBytes ( const void * pBuf, unsigned nBytes ) { unsigned index = this->nextWriteIndex; unsigned available = sizeof ( this->buf ) - index; if ( nBytes > available ) { nBytes = available; } memcpy ( & this->buf[index], pBuf, nBytes ); this->nextWriteIndex = index + nBytes; return nBytes; } inline bool comBuf :: copyOutAllBytes ( void * pBuf, unsigned nBytes ) { unsigned index = this->nextReadIndex; unsigned occupied = this->commitIndex - index; if ( nBytes <= occupied ) { memcpy ( pBuf, &this->buf[index], nBytes); this->nextReadIndex = index + nBytes; return true; } return false; } inline unsigned comBuf :: copyOutBytes ( void *pBuf, unsigned nBytes ) { unsigned index = this->nextReadIndex; unsigned occupied = this->commitIndex - index; if ( nBytes > occupied ) { nBytes = occupied; } memcpy ( pBuf, &this->buf[index], nBytes); this->nextReadIndex = index + nBytes; return nBytes; } inline unsigned comBuf :: removeBytes ( unsigned nBytes ) { unsigned index = this->nextReadIndex; unsigned occupied = this->commitIndex - index; if ( nBytes > occupied ) { nBytes = occupied; } this->nextReadIndex = index + nBytes; return nBytes; } template < class T > comBuf :: popStatus comBuf :: pop ( T & returnVal ) { unsigned nrIndex = this->nextReadIndex; unsigned popIndex = nrIndex + sizeof ( returnVal ); unsigned cIndex = this->commitIndex; popStatus status; status.success = true; status.nowEmpty = false; if ( popIndex >= cIndex ) { if ( popIndex == cIndex ) { status.nowEmpty = true; } else { status.success = false; return status; } } WireGet ( & this->buf[ nrIndex ], returnVal ); this->nextReadIndex = popIndex; return status; } #endif // ifndef comBufh base-7.0.3.1/modules/ca/src/client/comQueRecv.cpp0000664000577000060420000001744313557101274020301 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov */ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "iocinf.h" #include "virtualCircuit.h" comQueRecv::comQueRecv ( comBufMemoryManager & comBufMemoryManagerIn ): comBufMemMgr ( comBufMemoryManagerIn ), nBytesPending ( 0u ) { } comQueRecv::~comQueRecv () { this->clear (); } void comQueRecv::clear () { comBuf *pBuf; while ( ( pBuf = this->bufs.get () ) ) { pBuf->~comBuf (); this->comBufMemMgr.release ( pBuf ); } this->nBytesPending = 0u; } unsigned comQueRecv::copyOutBytes ( epicsInt8 *pBuf, unsigned nBytes ) { unsigned totalBytes = 0u; do { comBuf * pComBuf = this->bufs.first (); if ( ! pComBuf ) { this->nBytesPending -= totalBytes; return totalBytes; } totalBytes += pComBuf->copyOutBytes ( &pBuf[totalBytes], nBytes - totalBytes ); if ( pComBuf->occupiedBytes () == 0u ) { this->bufs.remove ( *pComBuf ); pComBuf->~comBuf (); this->comBufMemMgr.release ( pComBuf ); } } while ( totalBytes < nBytes ); this->nBytesPending -= totalBytes; return totalBytes; } unsigned comQueRecv::removeBytes ( unsigned nBytes ) { unsigned totalBytes = 0u; unsigned bytesLeft = nBytes; while ( bytesLeft ) { comBuf * pComBuf = this->bufs.first (); if ( ! pComBuf ) { this->nBytesPending -= totalBytes; return totalBytes; } unsigned nBytesThisTime = pComBuf->removeBytes ( bytesLeft ); if ( pComBuf->occupiedBytes () == 0u ) { this->bufs.remove ( *pComBuf ); pComBuf->~comBuf (); this->comBufMemMgr.release ( pComBuf ); } if ( nBytesThisTime == 0u) { break; } totalBytes += nBytesThisTime; bytesLeft = nBytes - totalBytes; } this->nBytesPending -= totalBytes; return totalBytes; } void comQueRecv::popString ( epicsOldString *pStr ) { for ( unsigned i = 0u; i < sizeof ( *pStr ); i++ ) { pStr[0][i] = this->popInt8 (); } } void comQueRecv::pushLastComBufReceived ( comBuf & bufIn ) { bufIn.commitIncomming (); comBuf * pComBuf = this->bufs.last (); if ( pComBuf ) { if ( pComBuf->unoccupiedBytes() ) { this->nBytesPending += pComBuf->push ( bufIn ); pComBuf->commitIncomming (); } } unsigned bufBytes = bufIn.occupiedBytes(); if ( bufBytes ) { this->nBytesPending += bufBytes; this->bufs.add ( bufIn ); } else { bufIn.~comBuf (); this->comBufMemMgr.release ( & bufIn ); } } // 1) split between buffers expected to run slower // 2) using canonical unsigned tmp avoids ANSI C conversions to int // 3) cast required because sizeof(unsigned) >= sizeof(epicsUInt32) epicsUInt16 comQueRecv::multiBufferPopUInt16 () { epicsUInt16 tmp; if ( this->occupiedBytes() >= sizeof (tmp) ) { unsigned byte1 = this->popUInt8 (); unsigned byte2 = this->popUInt8 (); tmp = static_cast ( ( byte1 << 8u ) | byte2 ); } else { comBuf::throwInsufficentBytesException (); tmp = 0u; } return tmp; } // 1) split between buffers expected to run slower // 2) using canonical unsigned temporary avoids ANSI C conversions to int // 3) cast required because sizeof(unsigned) >= sizeof(epicsUInt32) epicsUInt32 comQueRecv::multiBufferPopUInt32 () { epicsUInt32 tmp; if ( this->occupiedBytes() >= sizeof (tmp) ) { // 1) split between buffers expected to run slower // 2) using canonical unsigned temporary avoids ANSI C conversions to int // 3) cast required because sizeof(unsigned) >= sizeof(epicsUInt32) unsigned byte1 = this->popUInt8(); unsigned byte2 = this->popUInt8(); unsigned byte3 = this->popUInt8(); unsigned byte4 = this->popUInt8(); tmp = static_cast ( ( byte1 << 24u ) | ( byte2 << 16u ) | ( byte3 << 8u ) | byte4 ); } else { comBuf::throwInsufficentBytesException (); tmp = 0u; // avoid compiler warnings } return tmp; } void comQueRecv::removeAndDestroyBuf ( comBuf & buf ) { this->bufs.remove ( buf ); buf.~comBuf (); this->comBufMemMgr.release ( & buf ); } epicsUInt8 comQueRecv::popUInt8 () { comBuf * pComBuf = this->bufs.first (); if ( ! pComBuf ) { comBuf::throwInsufficentBytesException (); } epicsUInt8 tmp = '\0'; comBuf::popStatus status = pComBuf->pop ( tmp ); if ( ! status.success ) { comBuf::throwInsufficentBytesException (); } if ( status.nowEmpty ) { this->removeAndDestroyBuf ( *pComBuf ); } this->nBytesPending--; return tmp; } epicsUInt16 comQueRecv::popUInt16 () { comBuf * pComBuf = this->bufs.first (); if ( ! pComBuf ) { comBuf::throwInsufficentBytesException (); } // try first for all in one buffer efficent version epicsUInt16 tmp = 0; comBuf::popStatus status = pComBuf->pop ( tmp ); if ( status.success ) { this->nBytesPending -= sizeof ( epicsUInt16 ); if ( status.nowEmpty ) { this->removeAndDestroyBuf ( *pComBuf ); } return tmp; } return this->multiBufferPopUInt16 (); } epicsUInt32 comQueRecv::popUInt32 () { comBuf *pComBuf = this->bufs.first (); if ( ! pComBuf ) { comBuf::throwInsufficentBytesException (); } // try first for all in one buffer efficent version epicsUInt32 tmp = 0; comBuf::popStatus status = pComBuf->pop ( tmp ); if ( status.success ) { this->nBytesPending -= sizeof ( epicsUInt32 ); if ( status.nowEmpty ) { this->removeAndDestroyBuf ( *pComBuf ); } return tmp; } return this->multiBufferPopUInt32 (); } bool comQueRecv::popOldMsgHeader ( caHdrLargeArray & msg ) { // try first for all in one buffer efficent version comBuf * pComBuf = this->bufs.first (); if ( ! pComBuf ) { return false; } unsigned avail = pComBuf->occupiedBytes (); if ( avail >= sizeof ( caHdr ) ) { pComBuf->pop ( msg.m_cmmd ); ca_uint16_t smallPostsize = 0; pComBuf->pop ( smallPostsize ); msg.m_postsize = smallPostsize; pComBuf->pop ( msg.m_dataType ); ca_uint16_t smallCount = 0; pComBuf->pop ( smallCount ); msg.m_count = smallCount; pComBuf->pop ( msg.m_cid ); pComBuf->pop ( msg.m_available ); this->nBytesPending -= sizeof ( caHdr ); if ( avail == sizeof ( caHdr ) ) { this->removeAndDestroyBuf ( *pComBuf ); } return true; } else if ( this->occupiedBytes () >= sizeof ( caHdr ) ) { msg.m_cmmd = this->popUInt16 (); msg.m_postsize = this->popUInt16 (); msg.m_dataType = this->popUInt16 (); msg.m_count = this->popUInt16 (); msg.m_cid = this->popUInt32 (); msg.m_available = this->popUInt32 (); return true; } else { return false; } } base-7.0.3.1/modules/ca/src/client/comQueRecv.h0000664000577000060420000000637613557101274017751 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef comQueRecvh #define comQueRecvh #include "comBuf.h" class comQueRecv { public: comQueRecv ( comBufMemoryManager & ); ~comQueRecv (); unsigned occupiedBytes () const; unsigned copyOutBytes ( epicsInt8 *pBuf, unsigned nBytes ); unsigned removeBytes ( unsigned nBytes ); void pushLastComBufReceived ( comBuf & ); void clear (); bool popOldMsgHeader ( struct caHdrLargeArray & ); epicsInt8 popInt8 (); epicsUInt8 popUInt8 (); epicsInt16 popInt16 (); epicsUInt16 popUInt16 (); epicsInt32 popInt32 (); epicsUInt32 popUInt32 (); epicsFloat32 popFloat32 (); epicsFloat64 popFloat64 (); void popString ( epicsOldString * ); private: tsDLList < comBuf > bufs; comBufMemoryManager & comBufMemMgr; unsigned nBytesPending; epicsUInt16 multiBufferPopUInt16 (); epicsUInt32 multiBufferPopUInt32 (); void removeAndDestroyBuf ( comBuf & ); comQueRecv ( const comQueRecv & ); comQueRecv & operator = ( const comQueRecv & ); }; inline unsigned comQueRecv::occupiedBytes () const { return this->nBytesPending; } inline epicsInt8 comQueRecv::popInt8 () { return static_cast < epicsInt8 > ( this->popUInt8() ); } inline epicsInt16 comQueRecv::popInt16 () { return static_cast < epicsInt16 > ( this->popUInt16() ); } inline epicsInt32 comQueRecv::popInt32 () { return static_cast < epicsInt32 > ( this->popUInt32() ); } // this has been optimized to aligned convert, maybe more could be done, // but since it is currently not used ... inline epicsFloat32 comQueRecv::popFloat32 () { union { epicsUInt8 _wire[ sizeof ( epicsFloat32 ) ]; epicsFloat32 _fp; } tmp; // optimizer will unroll this loop for ( unsigned i = 0u; i < sizeof ( tmp._wire ); i++ ) { tmp._wire[i] = this->popUInt8 (); } return AlignedWireRef < epicsFloat32 > ( tmp._fp ); } // this has been optimized to aligned convert, maybe more could be done, // but since it is currently not used ... inline epicsFloat64 comQueRecv::popFloat64 () { union { epicsUInt8 _wire[ sizeof ( epicsFloat64 ) ]; epicsFloat64 _fp; } tmp; // optimizer will unroll this loop for ( unsigned i = 0u; i < sizeof ( tmp._wire ); i++ ) { tmp._wire[i] = this->popUInt8 (); } return AlignedWireRef < epicsFloat64 > ( tmp._fp ); } #endif // ifndef comQueRecvh base-7.0.3.1/modules/ca/src/client/comQueSend.cpp0000664000577000060420000003523013557101274020265 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov */ // // Requirements: // 1) Allow sufficent headroom so that users will be able to perform // a reasonable amount of IO within CA callbacks without experiencing // a push/pull deadlock. If a potential push/pull deadlock situation // occurs then detect and avoid it and provide diagnotic to the user // via special status. // 2) Return status to the user when there is insufficent memory to // queue a complete message. // 3) return status to the user when a message cant be flushed because // a connection dropped. // 4) Do not allocate too much memory in exception situatons (such as // after a circuit disconnect). // 5) Avoid allocating more memory than is absolutely necessary to meet // the above requirements. // 6) Message fragments must never be sent to the IOC when there isnt // enough memory to queue part of a message (we also must not force // a disconnect because the client is starved for memory). // 7) avoid the need to check status for each byte pushed into the // protocol stream. // // Implementation: // 1) When queuing a complete message, first test to see if a flush is // required. If it is a receive thread scheduals the flush with the // send thread, and otherwise directly execute the system call. The // send thread must run at a higher priority than the receive thread // if we are to minimize memory consumption. // 2) Preallocate space for the entire message prior to copying in the // message so that message fragments are not flushed out just prior // to detecting that memory is unavailable. // 3) Return a special error constant when the following situations // are detected when the user is attempting to queue a request // from within a user callback executed by a receive thread: // a) A user is queuing more requests that demand a response from a // callback than are removed by the response that initiated the // callback, and this situation persists for many callbacks until // all buffering in the system is exausted. // b) A user is queuing many requests that demand a response from one // callback until all buffering in the system is exausted. // c) Some combination of both (a) nad (b). // // #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #define epicsExportSharedSymbols #include "iocinf.h" #include "virtualCircuit.h" #include "db_access.h" // for dbr_short_t etc // nill message alignment pad bytes const char cacNillBytes [] = { 0, 0, 0, 0, 0, 0, 0, 0 }; comQueSend::comQueSend ( wireSendAdapter & wireIn, comBufMemoryManager & comBufMemMgrIn ): comBufMemMgr ( comBufMemMgrIn ), wire ( wireIn ), nBytesPending ( 0u ) { } comQueSend::~comQueSend () { this->clear (); } void comQueSend::clear () { comBuf *pBuf; while ( ( pBuf = this->bufs.get () ) ) { this->nBytesPending -= pBuf->occupiedBytes (); pBuf->~comBuf (); this->comBufMemMgr.release ( pBuf ); } this->pFirstUncommited = tsDLIter < comBuf > (); assert ( this->nBytesPending == 0 ); } void comQueSend::copy_dbr_string ( const void * pValue ) { this->push ( static_cast < const char * > ( pValue ), MAX_STRING_SIZE ); } void comQueSend::copy_dbr_short ( const void * pValue ) { this->push ( * static_cast ( pValue ) ); } void comQueSend::copy_dbr_float ( const void * pValue ) { this->push ( * static_cast ( pValue ) ); } void comQueSend::copy_dbr_char ( const void * pValue ) { this->push ( * static_cast ( pValue ) ); } void comQueSend::copy_dbr_long ( const void * pValue ) { this->push ( * static_cast ( pValue ) ); } void comQueSend::copy_dbr_double ( const void * pValue ) { this->push ( * static_cast ( pValue ) ); } void comQueSend::copy_dbr_invalid ( const void * ) { throw cacChannel::badType (); } const comQueSend::copyScalarFunc_t comQueSend::dbrCopyScalar [39] = { &comQueSend::copy_dbr_string, &comQueSend::copy_dbr_short, &comQueSend::copy_dbr_float, &comQueSend::copy_dbr_short, // DBR_ENUM &comQueSend::copy_dbr_char, &comQueSend::copy_dbr_long, &comQueSend::copy_dbr_double, &comQueSend::copy_dbr_invalid, // DBR_STS_SHORT &comQueSend::copy_dbr_invalid, // DBR_STS_FLOAT &comQueSend::copy_dbr_invalid, // DBR_STS_ENUM &comQueSend::copy_dbr_invalid, // DBR_STS_CHAR &comQueSend::copy_dbr_invalid, // DBR_STS_LONG &comQueSend::copy_dbr_invalid, // DBR_STS_DOUBLE &comQueSend::copy_dbr_invalid, // DBR_TIME_STRING &comQueSend::copy_dbr_invalid, // DBR_TIME_INT &comQueSend::copy_dbr_invalid, // DBR_TIME_SHORT &comQueSend::copy_dbr_invalid, // DBR_TIME_FLOAT &comQueSend::copy_dbr_invalid, // DBR_TIME_ENUM &comQueSend::copy_dbr_invalid, // DBR_TIME_CHAR &comQueSend::copy_dbr_invalid, // DBR_TIME_LONG &comQueSend::copy_dbr_invalid, // DBR_TIME_DOUBLE &comQueSend::copy_dbr_invalid, // DBR_GR_STRING &comQueSend::copy_dbr_invalid, // DBR_GR_SHORT &comQueSend::copy_dbr_invalid, // DBR_GR_FLOAT &comQueSend::copy_dbr_invalid, // DBR_GR_ENUM &comQueSend::copy_dbr_invalid, // DBR_GR_CHAR &comQueSend::copy_dbr_invalid, // DBR_GR_LONG &comQueSend::copy_dbr_invalid, // DBR_GR_DOUBLE &comQueSend::copy_dbr_invalid, // DBR_CTRL_STRING &comQueSend::copy_dbr_invalid, // DBR_CTRL_SHORT &comQueSend::copy_dbr_invalid, // DBR_CTRL_FLOAT &comQueSend::copy_dbr_invalid, // DBR_CTRL_ENUM &comQueSend::copy_dbr_invalid, // DBR_CTRL_CHAR &comQueSend::copy_dbr_invalid, // DBR_CTRL_LONG &comQueSend::copy_dbr_invalid, // DBR_CTRL_DOUBLE &comQueSend::copy_dbr_short, // DBR_PUT_ACKT &comQueSend::copy_dbr_short, // DBR_PUT_ACKS &comQueSend::copy_dbr_invalid, // DBR_STSACK_STRING &comQueSend::copy_dbr_invalid // DBR_CLASS_NAME }; void comQueSend::copy_dbr_string ( const void *pValue, unsigned nElem ) { this->push ( static_cast < const char * > ( pValue ), nElem * MAX_STRING_SIZE ); } void comQueSend::copy_dbr_short ( const void *pValue, unsigned nElem ) { this->push ( static_cast ( pValue ), nElem ); } void comQueSend::copy_dbr_float ( const void *pValue, unsigned nElem ) { this->push ( static_cast ( pValue ), nElem ); } void comQueSend::copy_dbr_char ( const void *pValue, unsigned nElem ) { this->push ( static_cast ( pValue ), nElem ); } void comQueSend::copy_dbr_long ( const void *pValue, unsigned nElem ) { this->push ( static_cast ( pValue ), nElem ); } void comQueSend::copy_dbr_double ( const void *pValue, unsigned nElem ) { this->push ( static_cast ( pValue ), nElem ); } void comQueSend::copy_dbr_invalid ( const void *, unsigned ) { throw cacChannel::badType (); } const comQueSend::copyVectorFunc_t comQueSend::dbrCopyVector [39] = { &comQueSend::copy_dbr_string, &comQueSend::copy_dbr_short, &comQueSend::copy_dbr_float, &comQueSend::copy_dbr_short, // DBR_ENUM &comQueSend::copy_dbr_char, &comQueSend::copy_dbr_long, &comQueSend::copy_dbr_double, &comQueSend::copy_dbr_invalid, // DBR_STS_SHORT &comQueSend::copy_dbr_invalid, // DBR_STS_FLOAT &comQueSend::copy_dbr_invalid, // DBR_STS_ENUM &comQueSend::copy_dbr_invalid, // DBR_STS_CHAR &comQueSend::copy_dbr_invalid, // DBR_STS_LONG &comQueSend::copy_dbr_invalid, // DBR_STS_DOUBLE &comQueSend::copy_dbr_invalid, // DBR_TIME_STRING &comQueSend::copy_dbr_invalid, // DBR_TIME_INT &comQueSend::copy_dbr_invalid, // DBR_TIME_SHORT &comQueSend::copy_dbr_invalid, // DBR_TIME_FLOAT &comQueSend::copy_dbr_invalid, // DBR_TIME_ENUM &comQueSend::copy_dbr_invalid, // DBR_TIME_CHAR &comQueSend::copy_dbr_invalid, // DBR_TIME_LONG &comQueSend::copy_dbr_invalid, // DBR_TIME_DOUBLE &comQueSend::copy_dbr_invalid, // DBR_GR_STRING &comQueSend::copy_dbr_invalid, // DBR_GR_SHORT &comQueSend::copy_dbr_invalid, // DBR_GR_FLOAT &comQueSend::copy_dbr_invalid, // DBR_GR_ENUM &comQueSend::copy_dbr_invalid, // DBR_GR_CHAR &comQueSend::copy_dbr_invalid, // DBR_GR_LONG &comQueSend::copy_dbr_invalid, // DBR_GR_DOUBLE &comQueSend::copy_dbr_invalid, // DBR_CTRL_STRING &comQueSend::copy_dbr_invalid, // DBR_CTRL_SHORT &comQueSend::copy_dbr_invalid, // DBR_CTRL_FLOAT &comQueSend::copy_dbr_invalid, // DBR_CTRL_ENUM &comQueSend::copy_dbr_invalid, // DBR_CTRL_CHAR &comQueSend::copy_dbr_invalid, // DBR_CTRL_LONG &comQueSend::copy_dbr_invalid, // DBR_CTRL_DOUBLE &comQueSend::copy_dbr_short, // DBR_PUT_ACKT &comQueSend::copy_dbr_short, // DBR_PUT_ACKS &comQueSend::copy_dbr_invalid, // DBR_STSACK_STRING &comQueSend::copy_dbr_invalid // DBR_CLASS_NAME }; comBuf * comQueSend::popNextComBufToSend () { comBuf *pBuf = this->bufs.get (); if ( pBuf ) { unsigned nBytesThisBuf = pBuf->occupiedBytes (); if ( nBytesThisBuf ) { assert ( this->nBytesPending >= nBytesThisBuf ); this->nBytesPending -= nBytesThisBuf; } else { this->bufs.push ( *pBuf ); pBuf = 0; } } else { assert ( this->nBytesPending == 0u ); } return pBuf; } void comQueSend::insertRequestHeader ( ca_uint16_t request, ca_uint32_t payloadSize, ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, ca_uint32_t requestDependent, bool v49Ok ) { if ( payloadSize < 0xffff && nElem < 0xffff ) { comBuf * pComBuf = this->bufs.last (); if ( ! pComBuf || pComBuf->unoccupiedBytes() < 16u ) { pComBuf = newComBuf (); this->pushComBuf ( *pComBuf ); } pComBuf->push ( request ); pComBuf->push ( static_cast < ca_uint16_t > ( payloadSize ) ); pComBuf->push ( dataType ); pComBuf->push ( static_cast < ca_uint16_t > ( nElem ) ); pComBuf->push ( cid ); pComBuf->push ( requestDependent ); } else if ( v49Ok ) { comBuf * pComBuf = this->bufs.last (); if ( ! pComBuf || pComBuf->unoccupiedBytes() < 24u ) { pComBuf = newComBuf (); this->pushComBuf ( *pComBuf ); } pComBuf->push ( request ); pComBuf->push ( static_cast < ca_uint16_t > ( 0xffff ) ); pComBuf->push ( dataType ); pComBuf->push ( static_cast < ca_uint16_t > ( 0u ) ); pComBuf->push ( cid ); pComBuf->push ( requestDependent ); pComBuf->push ( payloadSize ); pComBuf->push ( nElem ); } else { throw cacChannel::outOfBounds (); } } void comQueSend::insertRequestWithPayLoad ( ca_uint16_t request, unsigned dataType, arrayElementCount nElem, ca_uint32_t cid, ca_uint32_t requestDependent, const void * pPayload, bool v49Ok ) { if ( INVALID_DB_REQ ( dataType ) ) { throw cacChannel::badType (); } if ( dataType >= comQueSendCopyDispatchSize ) { throw cacChannel::badType(); } ca_uint32_t size = 0u; ca_uint32_t payloadSize = 0u; if ( nElem == 1 ) { if ( dataType == DBR_STRING ) { const char * pStr = static_cast < const char * > ( pPayload ); size = strlen ( pStr ) + 1u; if ( size > MAX_STRING_SIZE ) { throw cacChannel::outOfBounds(); } payloadSize = CA_MESSAGE_ALIGN ( size ); this->insertRequestHeader ( request, payloadSize, static_cast ( dataType ), nElem, cid, requestDependent, v49Ok ); this->pushString ( pStr, size ); } else { size = dbr_size[dataType]; payloadSize = CA_MESSAGE_ALIGN ( size ); this->insertRequestHeader ( request, payloadSize, static_cast ( dataType ), nElem, cid, requestDependent, v49Ok ); ( this->*dbrCopyScalar [dataType] ) ( pPayload ); } } else { arrayElementCount maxBytes; if ( v49Ok ) { maxBytes = 0xffffffff; } else { maxBytes = MAX_TCP - sizeof ( caHdr ); } arrayElementCount maxElem = ( maxBytes - sizeof (dbr_double_t) - dbr_size[dataType] ) / dbr_value_size[dataType]; if ( nElem >= maxElem ) { throw cacChannel::outOfBounds(); } // the above checks verify that the total size // is lest that 0xffffffff size = static_cast < ca_uint32_t > ( dbr_size_n ( dataType, nElem ) ); payloadSize = CA_MESSAGE_ALIGN ( size ); this->insertRequestHeader ( request, payloadSize, static_cast ( dataType ), static_cast < ca_uint32_t > ( nElem ), cid, requestDependent, v49Ok ); ( this->*dbrCopyVector [dataType] ) ( pPayload, nElem ); } // set pad bytes to nill unsigned padSize = payloadSize - size; if ( padSize ) { this->pushString ( cacNillBytes, payloadSize - size ); } } void comQueSend::commitMsg () { while ( this->pFirstUncommited.valid() ) { this->nBytesPending += this->pFirstUncommited->uncommittedBytes (); this->pFirstUncommited->commitIncomming (); this->pFirstUncommited++; } // printf ( "NBP: %u\n", this->nBytesPending ); } void comQueSend::clearUncommitedMsg () { while ( this->pFirstUncommited.valid() ) { tsDLIter < comBuf > next = this->pFirstUncommited; next++; this->pFirstUncommited->clearUncommittedIncomming (); if ( this->pFirstUncommited->occupiedBytes() == 0u ) { this->bufs.remove ( *this->pFirstUncommited ); this->pFirstUncommited->~comBuf (); this->comBufMemMgr.release ( this->pFirstUncommited.pointer() ); } this->pFirstUncommited = next; } } base-7.0.3.1/modules/ca/src/client/comQueSend.h0000664000577000060420000001543313557101274017735 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef comQueSendh #define comQueSendh #include #include "tsDLList.h" #include "comBuf.h" #define comQueSendCopyDispatchSize 39 class epicsMutex; template < class T > class epicsGuard; class comQueSendMsgMinder { public: comQueSendMsgMinder ( class comQueSend &, epicsGuard < epicsMutex > & ); ~comQueSendMsgMinder (); void commit (); private: class comQueSend * pSendQue; }; // // Notes. // o calling popNextComBufToSend() will clear any uncommitted bytes // class comQueSend { public: comQueSend ( wireSendAdapter &, comBufMemoryManager & ); ~comQueSend (); void clear (); unsigned occupiedBytes () const; bool flushEarlyThreshold ( unsigned nBytesThisMsg ) const; bool flushBlockThreshold () const; void pushUInt16 ( const ca_uint16_t value ); void pushUInt32 ( const ca_uint32_t value ); void pushFloat32 ( const ca_float32_t value ); void pushString ( const char *pVal, unsigned nChar ); void insertRequestHeader ( ca_uint16_t request, ca_uint32_t payloadSize, ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, ca_uint32_t requestDependent, bool v49Ok ); void insertRequestWithPayLoad ( ca_uint16_t request, unsigned dataType, arrayElementCount nElem, ca_uint32_t cid, ca_uint32_t requestDependent, const void * pPayload, bool v49Ok ); comBuf * popNextComBufToSend (); private: comBufMemoryManager & comBufMemMgr; tsDLList < comBuf > bufs; tsDLIter < comBuf > pFirstUncommited; wireSendAdapter & wire; unsigned nBytesPending; typedef void ( comQueSend::*copyScalarFunc_t ) ( const void * pValue ); static const copyScalarFunc_t dbrCopyScalar [comQueSendCopyDispatchSize]; void copy_dbr_string ( const void * pValue ); void copy_dbr_short ( const void * pValue ); void copy_dbr_float ( const void * pValue ); void copy_dbr_char ( const void * pValue ); void copy_dbr_long ( const void * pValue ); void copy_dbr_double ( const void * pValue ); void copy_dbr_invalid ( const void * pValue ); typedef void ( comQueSend::*copyVectorFunc_t ) ( const void * pValue, unsigned nElem ); static const copyVectorFunc_t dbrCopyVector [comQueSendCopyDispatchSize]; void copy_dbr_string ( const void *pValue, unsigned nElem ); void copy_dbr_short ( const void *pValue, unsigned nElem ); void copy_dbr_float ( const void *pValue, unsigned nElem ); void copy_dbr_char ( const void *pValue, unsigned nElem ); void copy_dbr_long ( const void *pValue, unsigned nElem ); void copy_dbr_double ( const void *pValue, unsigned nElem ); void copy_dbr_invalid ( const void * pValue, unsigned nElem ); void pushComBuf ( comBuf & ); comBuf * newComBuf (); void beginMsg (); void commitMsg (); void clearUncommitedMsg (); friend class comQueSendMsgMinder; // // visual C++ versions 6 & 7 do not allow out of // class member template function definition // template < class T > inline void push ( const T *pVal, const unsigned nElem ) { comBuf * pLastBuf = this->bufs.last (); unsigned nCopied; if ( pLastBuf ) { nCopied = pLastBuf->push ( pVal, nElem ); } else { nCopied = 0u; } while ( nElem > nCopied ) { comBuf * pComBuf = newComBuf (); nCopied += pComBuf->push ( &pVal[nCopied], nElem - nCopied ); this->pushComBuf ( *pComBuf ); } } // // visual C++ versions 6 and 7 do not allow out of // class member template function definition // template < class T > inline void push ( const T & val ) { comBuf * pComBuf = this->bufs.last (); if ( pComBuf && pComBuf->push ( val ) ) { return; } pComBuf = newComBuf (); bool success = pComBuf->push ( val ); assert ( success ); this->pushComBuf ( *pComBuf ); } template < class T > inline void push ( const T * ); // disabled comQueSend ( const comQueSend & ); comQueSend & operator = ( const comQueSend & ); }; extern const char cacNillBytes[]; inline comQueSendMsgMinder::comQueSendMsgMinder ( class comQueSend & sendQueIn, epicsGuard < epicsMutex > & ) : pSendQue ( & sendQueIn ) { sendQueIn.beginMsg (); } inline comQueSendMsgMinder::~comQueSendMsgMinder () { if ( this->pSendQue ) { this->pSendQue->clearUncommitedMsg (); } } inline void comQueSendMsgMinder::commit () { if ( this->pSendQue ) { this->pSendQue->commitMsg (); this->pSendQue = 0; } } inline void comQueSend::beginMsg () { this->pFirstUncommited = this->bufs.lastIter (); } inline void comQueSend::pushUInt16 ( const ca_uint16_t value ) { this->push ( value ); } inline void comQueSend::pushUInt32 ( const ca_uint32_t value ) { this->push ( value ); } inline void comQueSend::pushFloat32 ( const ca_float32_t value ) { this->push ( value ); } inline void comQueSend::pushString ( const char *pVal, unsigned nChar ) { this->push ( pVal, nChar ); } inline void comQueSend::pushComBuf ( comBuf & cb ) { this->bufs.add ( cb ); if ( ! this->pFirstUncommited.valid() ) { this->pFirstUncommited = this->bufs.lastIter (); } } inline unsigned comQueSend::occupiedBytes () const { return this->nBytesPending; } inline bool comQueSend::flushBlockThreshold () const { return ( this->nBytesPending > 16 * comBuf::capacityBytes () ); } inline bool comQueSend::flushEarlyThreshold ( unsigned nBytesThisMsg ) const { return ( this->nBytesPending + nBytesThisMsg > 4 * comBuf::capacityBytes () ); } // wrapping this with a function avoids WRS T2.2 Cygnus GNU compiler bugs inline comBuf * comQueSend::newComBuf () { return new ( this->comBufMemMgr ) comBuf; } #endif // ifndef comQueSendh base-7.0.3.1/modules/ca/src/client/convert.cpp0000664000577000060420000014073413557101274017710 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * C O N V E R T . C * * Author: D. Kersteins * * * NOTES: * * 1) All routines in this file have an encode argument which * determines if we are converting from the standard format to * the local format or vise versa. To date only float and double data * types must be converted differently depending on the encode * argument - joh * */ #include #include "dbDefs.h" #include "osiSock.h" #include "osiWireFormat.h" #define epicsExportSharedSymbols #include "net_convert.h" #include "iocinf.h" #include "caProto.h" #include "caerr.h" /* * NOOP if this isnt required */ #ifdef EPICS_CONVERSION_REQUIRED /* * if hton is true then it is a host to network conversion * otherwise vise-versa * * net format: big endian and IEEE float */ typedef void ( * CACVRTFUNCPTR ) ( const void *pSrc, void *pDest, int hton, arrayElementCount count ); inline void dbr_htond ( const dbr_double_t * pHost, dbr_double_t * pNet ) { AlignedWireRef < epicsFloat64 > tmp ( *pNet ); tmp = *pHost; } inline void dbr_ntohd ( const dbr_double_t * pNet, dbr_double_t * pHost ) { *pHost = AlignedWireRef < const epicsFloat64 > ( *pNet ); } inline void dbr_htonf ( const dbr_float_t * pHost, dbr_float_t * pNet ) { AlignedWireRef < epicsFloat32 > tmp ( *pNet ); tmp = *pHost; } inline void dbr_ntohf ( const dbr_float_t * pNet, dbr_float_t * pHost ) { *pHost = AlignedWireRef < const epicsFloat32 > ( *pNet ); } inline epicsUInt16 dbr_ntohs( const epicsUInt16 & net ) { return AlignedWireRef < const epicsUInt16 > ( net ); } inline epicsUInt16 dbr_htons ( const epicsUInt16 & host ) { epicsUInt16 tmp; AlignedWireRef < epicsUInt16 > awr ( tmp ); awr = host; return tmp; } inline epicsUInt32 dbr_ntohl( const epicsUInt32 & net ) { return AlignedWireRef < const epicsUInt32 > ( net ); } inline epicsUInt32 dbr_htonl ( const epicsUInt32 & host ) { epicsUInt32 tmp; AlignedWireRef < epicsUInt32 > awr ( tmp ); awr = host; return tmp; } /* * if hton is true then it is a host to network conversion * otherwise vise-versa * * net format: big endian and IEEE float * */ /* * CVRT_STRING() */ static void cvrt_string( const void *s, /* source */ void *d, /* destination */ int /* encode */, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { char *pSrc = (char *) s; char *pDest = (char *) d; /* convert "in place" -> nothing to do */ if (s == d) return; memcpy ( pDest, pSrc, num*MAX_STRING_SIZE ); } /* * CVRT_SHORT() */ static void cvrt_short( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { dbr_short_t *pSrc = (dbr_short_t *) s; dbr_short_t *pDest = (dbr_short_t *) d; if(encode){ for(arrayElementCount i=0; i nothing to do */ if (s == d) return; for( arrayElementCount i=0; istatus = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); /* convert "in place" -> nothing else to do */ if (s == d) return; memcpy ( pDest->value, pSrc->value, (MAX_STRING_SIZE * num) ); } /**************************************************************************** ** cvrt_sts_short(s,d) ** struct dbr_sts_int *s pointer to source struct ** struct dbr_sts_int *d pointer to destination struct ** int encode; boolean, if true vax to ieee ** else ieee to vax ** ** converts fields ofstruct in HOST format to ieee format ** or ** converts fields of struct in NET format to fields with HOST ** format ****************************************************************************/ static void cvrt_sts_short( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_sts_int *pSrc = (struct dbr_sts_int *) s; struct dbr_sts_int *pDest = (struct dbr_sts_int *) d; /* convert vax to ieee or ieee to vax format -- same code*/ pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); if (num == 1) /* single value */ pDest->value = dbr_ntohs(pSrc->value); else /* array chan-- multiple pts */ { cvrt_short(&pSrc->value, &pDest->value, encode, num); } } /**************************************************************************** ** cvrt_sts_float(s,d) ** struct dbr_sts_float *s pointer to source struct ** struct dbr_sts_float *d pointer to destination struct ** int encode; boolean, if true vax to ieee ** else ieee to vax ** ** if encode ** converts struct in HOST format to ieee format ** else ** converts fields of struct in NET format to fields with HOST ** format; ****************************************************************************/ static void cvrt_sts_float( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_sts_float *pSrc = (struct dbr_sts_float *) s; struct dbr_sts_float *pDest = (struct dbr_sts_float *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); cvrt_float(&pSrc->value, &pDest->value, encode, num); } /**************************************************************************** ** cvrt_sts_double(s,d) ** ** if encode ** converts struct in HOST format to ieee format ** else ** converts fields of struct in NET format to fields with HOST ** format; ****************************************************************************/ static void cvrt_sts_double( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_sts_double *pSrc = (struct dbr_sts_double *) s; struct dbr_sts_double *pDest = (struct dbr_sts_double *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); cvrt_double(&pSrc->value, &pDest->value, encode, num); } /**************************************************************************** ** cvrt_sts_enum(s,d) ** struct dbr_sts_enum *s pointer to source struct ** struct dbr_sts_enum *d pointer to destination struct ** int encode; boolean, if true vax to ieee ** else ieee to vax ** ** converts fields of struct in NET format to fields with HOST format ** or ** converts fields of struct in HOST format to fields with NET format ** ****************************************************************************/ static void cvrt_sts_enum( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_sts_enum *pSrc = (struct dbr_sts_enum *) s; struct dbr_sts_enum *pDest = (struct dbr_sts_enum *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); if (num == 1) pDest->value = dbr_ntohs(pSrc->value); else { cvrt_enum(&pSrc->value,&pDest->value,encode,num); } } /**************************************************************************** ** cvrt_gr_short() ** ** converts fields of struct in NET format to fields with HOST format ** or ** converts fields of struct in HOST format to fields with NET format ** ****************************************************************************/ static void cvrt_gr_short( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_gr_int *pSrc = (struct dbr_gr_int *) s; struct dbr_gr_int *pDest = (struct dbr_gr_int *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); if ( s != d ) { memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); } pDest->upper_disp_limit = dbr_ntohs(pSrc->upper_disp_limit); pDest->lower_disp_limit = dbr_ntohs(pSrc->lower_disp_limit); pDest->upper_alarm_limit = dbr_ntohs(pSrc->upper_alarm_limit); pDest->upper_warning_limit = dbr_ntohs(pSrc->upper_warning_limit); pDest->lower_alarm_limit = dbr_ntohs(pSrc->lower_alarm_limit); pDest->lower_warning_limit = dbr_ntohs(pSrc->lower_warning_limit); if (num == 1) pDest->value = dbr_ntohs(pSrc->value); else { cvrt_short(&pSrc->value, &pDest->value, encode,num); } } /**************************************************************************** ** cvrt_gr_char() ** ** converts fields of struct in NET format to fields with HOST format ** or ** converts fields of struct in HOST format to fields with NET format ** ****************************************************************************/ static void cvrt_gr_char( const void *s, /* source */ void *d, /* destination */ int /*encode*/, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_gr_char *pSrc = (struct dbr_gr_char *) s; struct dbr_gr_char *pDest = (struct dbr_gr_char *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); if (s == d) /* source == dest -> no more conversions */ return; memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); pDest->upper_disp_limit = pSrc->upper_disp_limit; pDest->lower_disp_limit = pSrc->lower_disp_limit; pDest->upper_alarm_limit = pSrc->upper_alarm_limit; pDest->upper_warning_limit = pSrc->upper_warning_limit; pDest->lower_alarm_limit = pSrc->lower_alarm_limit; pDest->lower_warning_limit = pSrc->lower_warning_limit; if (num == 1) pDest->value = pSrc->value; else { memcpy((char *)&pDest->value, (char *)&pSrc->value, num); } } /**************************************************************************** ** cvrt_gr_long() ** ** converts fields of struct in NET format to fields with HOST format ** or ** converts fields of struct in HOST format to fields with NET format ** ****************************************************************************/ static void cvrt_gr_long( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_gr_long *pSrc = (struct dbr_gr_long *) s; struct dbr_gr_long *pDest = (struct dbr_gr_long *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); if ( s != d ) { memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); } pDest->upper_disp_limit = dbr_ntohl(pSrc->upper_disp_limit); pDest->lower_disp_limit = dbr_ntohl(pSrc->lower_disp_limit); pDest->upper_alarm_limit = dbr_ntohl(pSrc->upper_alarm_limit); pDest->upper_warning_limit = dbr_ntohl(pSrc->upper_warning_limit); pDest->lower_alarm_limit = dbr_ntohl(pSrc->lower_alarm_limit); pDest->lower_warning_limit = dbr_ntohl(pSrc->lower_warning_limit); if (num == 1) pDest->value = dbr_ntohl(pSrc->value); else { cvrt_long(&pSrc->value, &pDest->value, encode, num); } } /**************************************************************************** ** cvrt_gr_enum(s,d) ** ** if encode ** converts struct in HOST format to ieee format ** else ** converts fields of struct in NET format to fields with HOST ** format; ****************************************************************************/ static void cvrt_gr_enum( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_gr_enum *pSrc = (struct dbr_gr_enum *) s; struct dbr_gr_enum *pDest = (struct dbr_gr_enum *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); pDest->no_str = dbr_ntohs(pSrc->no_str); if ( s != d ) { memcpy((void *)pDest->strs,(void *)pSrc->strs,sizeof(pSrc->strs)); } if (num == 1) /* single value */ pDest->value = dbr_ntohs(pSrc->value); else /* array chan-- multiple pts */ { cvrt_enum(&(pSrc->value), &(pDest->value), encode, num); } } /**************************************************************************** ** cvrt_gr_double(s,d) ** ** if encode ** converts struct in HOST format to ieee format ** else ** converts fields of struct in NET format to fields with HOST ** format; ****************************************************************************/ static void cvrt_gr_double( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_gr_double *pSrc = (struct dbr_gr_double *) s; struct dbr_gr_double *pDest = (struct dbr_gr_double *) d; /* these are same for vax to ieee or ieee to vax */ pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); pDest->precision = dbr_ntohs(pSrc->precision); if ( s != d ) { memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); } if (encode) /* vax to ieee convert */ { if (num == 1){ dbr_htond(&pSrc->value, &pDest->value); } else { cvrt_double(&pSrc->value, &pDest->value, encode,num); } dbr_htond(&pSrc->upper_disp_limit,&pDest->upper_disp_limit); dbr_htond(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); dbr_htond(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); dbr_htond(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); dbr_htond(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); dbr_htond(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); } else /* ieee to vax convert */ { if (num == 1){ dbr_ntohd(&pSrc->value, &pDest->value); } else { cvrt_double(&pSrc->value, &pDest->value, encode,num); } dbr_ntohd(&pSrc->upper_disp_limit,&pDest->upper_disp_limit); dbr_ntohd(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); dbr_ntohd(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); dbr_ntohd(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); dbr_ntohd(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); dbr_ntohd(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); } } /**************************************************************************** ** cvrt_gr_float(s,d) ** struct dbr_gr_float *d pointer to destination struct ** int encode; boolean, if true vax to ieee ** else ieee to vax ** ** if encode ** converts struct in HOST format to ieee format ** else ** converts fields of struct in NET format to fields with HOST ** format; ****************************************************************************/ static void cvrt_gr_float( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_gr_float *pSrc = (struct dbr_gr_float *) s; struct dbr_gr_float *pDest = (struct dbr_gr_float *) d; /* these are same for vax to ieee or ieee to vax */ pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); pDest->precision = dbr_ntohs(pSrc->precision); if ( s != d ) { memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); } if (encode) /* vax to ieee convert */ { if (num == 1){ dbr_htonf(&pSrc->value, &pDest->value); } else { cvrt_float(&pSrc->value, &pDest->value, encode,num); } dbr_htonf(&pSrc->upper_disp_limit,&pDest->upper_disp_limit); dbr_htonf(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); dbr_htonf(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); dbr_htonf(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); dbr_htonf(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); dbr_htonf(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); } else /* ieee to vax convert */ { if (num == 1){ dbr_ntohf(&pSrc->value, &pDest->value); } else { cvrt_float(&pSrc->value, &pDest->value, encode,num); } dbr_ntohf(&pSrc->upper_disp_limit,&pDest->upper_disp_limit); dbr_ntohf(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); dbr_ntohf(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); dbr_ntohf(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); dbr_ntohf(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); dbr_ntohf(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); } } /**************************************************************************** ** cvrt_ctrl_short(s,d) ** struct dbr_gr_int *s pointer to source struct ** struct dbr_gr_int *d pointer to destination struct ** int encode; boolean, if true vax to ieee ** else ieee to vax ** ** converts fields of struct in NET format to fields with HOST format ** or ** converts fields of struct in HOST format to fields with NET format ** ****************************************************************************/ static void cvrt_ctrl_short( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_ctrl_int *pSrc = (struct dbr_ctrl_int *) s; struct dbr_ctrl_int *pDest = (struct dbr_ctrl_int *) d; /* vax to ieee or ieee to vax -- same code */ pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); if ( s != d ) { memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); } pDest->upper_disp_limit = dbr_ntohs(pSrc->upper_disp_limit); pDest->lower_disp_limit = dbr_ntohs(pSrc->lower_disp_limit); pDest->upper_alarm_limit = dbr_ntohs(pSrc->upper_alarm_limit); pDest->upper_warning_limit = dbr_ntohs(pSrc->upper_warning_limit); pDest->lower_alarm_limit = dbr_ntohs(pSrc->lower_alarm_limit); pDest->lower_warning_limit = dbr_ntohs(pSrc->lower_warning_limit); pDest->lower_ctrl_limit = dbr_ntohs(pSrc->lower_ctrl_limit); pDest->upper_ctrl_limit = dbr_ntohs(pSrc->upper_ctrl_limit); if (num == 1) pDest->value = dbr_ntohs(pSrc->value); else { cvrt_short(&pSrc->value, &pDest->value, encode, num); } } /**************************************************************************** ** cvrt_ctrl_long(s,d) ** ** converts fields of struct in NET format to fields with HOST format ** or ** converts fields of struct in HOST format to fields with NET format ** ****************************************************************************/ static void cvrt_ctrl_long( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_ctrl_long *pSrc = (struct dbr_ctrl_long*) s; struct dbr_ctrl_long *pDest = (struct dbr_ctrl_long *) d; /* vax to ieee or ieee to vax -- same code */ pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); if ( s != d ) { memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); } pDest->upper_disp_limit = dbr_ntohl(pSrc->upper_disp_limit); pDest->lower_disp_limit = dbr_ntohl(pSrc->lower_disp_limit); pDest->upper_alarm_limit = dbr_ntohl(pSrc->upper_alarm_limit); pDest->upper_warning_limit = dbr_ntohl(pSrc->upper_warning_limit); pDest->lower_alarm_limit = dbr_ntohl(pSrc->lower_alarm_limit); pDest->lower_warning_limit = dbr_ntohl(pSrc->lower_warning_limit); pDest->lower_ctrl_limit = dbr_ntohl(pSrc->lower_ctrl_limit); pDest->upper_ctrl_limit = dbr_ntohl(pSrc->upper_ctrl_limit); if (num == 1) pDest->value = dbr_ntohl(pSrc->value); else { cvrt_long(&pSrc->value, &pDest->value, encode, num); } } /**************************************************************************** ** cvrt_ctrl_short(s,d) ** ** converts fields of struct in NET format to fields with HOST format ** or ** converts fields of struct in HOST format to fields with NET format ** ****************************************************************************/ static void cvrt_ctrl_char( const void *s, /* source */ void *d, /* destination */ int /*encode*/, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_ctrl_char *pSrc = (struct dbr_ctrl_char *) s; struct dbr_ctrl_char *pDest = (struct dbr_ctrl_char *) d; /* vax to ieee or ieee to vax -- same code */ pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); if ( s == d ) return; pDest->upper_disp_limit = pSrc->upper_disp_limit; pDest->lower_disp_limit = pSrc->lower_disp_limit; pDest->upper_alarm_limit = pSrc->upper_alarm_limit; pDest->upper_warning_limit = pSrc->upper_warning_limit; pDest->lower_ctrl_limit = pSrc->lower_ctrl_limit; pDest->upper_ctrl_limit = pSrc->upper_ctrl_limit; if (num == 1) pDest->value = pSrc->value; else { memcpy((void *)&pDest->value, (void *)&pSrc->value, num); } } /**************************************************************************** ** cvrt_ctrl_double(s,d) ** ** if encode ** converts struct in HOST format to ieee format ** else ** converts fields of struct in NET format to fields with HOST ** format; ****************************************************************************/ static void cvrt_ctrl_double( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_ctrl_double *pSrc = (struct dbr_ctrl_double *) s; struct dbr_ctrl_double *pDest = (struct dbr_ctrl_double *) d; /* these are the same for ieee to vax or vax to ieee */ pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); pDest->precision = dbr_ntohs(pSrc->precision); if ( s != d ) { memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); } if (encode) /* vax to ieee convert */ { if (num == 1){ dbr_htond(&pSrc->value, &pDest->value); } else { cvrt_double(&pSrc->value, &pDest->value, encode, num); } dbr_htond(&pSrc->upper_disp_limit,&pDest->upper_disp_limit); dbr_htond(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); dbr_htond(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); dbr_htond(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); dbr_htond(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); dbr_htond(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); dbr_htond(&pSrc->lower_ctrl_limit, &pDest->lower_ctrl_limit); dbr_htond(&pSrc->upper_ctrl_limit, &pDest->upper_ctrl_limit); } else /* ieee to vax convert */ { if (num == 1){ dbr_ntohd(&pSrc->value, &pDest->value); } else { cvrt_double(&pSrc->value, &pDest->value, encode, num); } dbr_ntohd(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); dbr_ntohd(&pSrc->upper_disp_limit, &pDest->upper_disp_limit); dbr_ntohd(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); dbr_ntohd(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); dbr_ntohd(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); dbr_ntohd(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); dbr_ntohd(&pSrc->lower_ctrl_limit, &pDest->lower_ctrl_limit); dbr_ntohd(&pSrc->upper_ctrl_limit, &pDest->upper_ctrl_limit); } } /**************************************************************************** ** cvrt_ctrl_float(s,d) ** ** if encode ** converts struct in HOST format to ieee format ** else ** converts fields of struct in NET format to fields with HOST ** format; ****************************************************************************/ static void cvrt_ctrl_float( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_ctrl_float *pSrc = (struct dbr_ctrl_float *) s; struct dbr_ctrl_float *pDest = (struct dbr_ctrl_float *) d; /* these are the same for ieee to vaax or vax to ieee */ pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); pDest->precision = dbr_ntohs(pSrc->precision); if ( s != d ) { memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); } if (encode) /* vax to ieee convert */ { if (num == 1){ dbr_htonf(&pSrc->value, &pDest->value); } else { cvrt_float(&pSrc->value, &pDest->value, encode, num); } dbr_htonf(&pSrc->upper_disp_limit,&pDest->upper_disp_limit); dbr_htonf(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); dbr_htonf(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); dbr_htonf(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); dbr_htonf(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); dbr_htonf(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); dbr_htonf(&pSrc->lower_ctrl_limit, &pDest->lower_ctrl_limit); dbr_htonf(&pSrc->upper_ctrl_limit, &pDest->upper_ctrl_limit); } else /* ieee to vax convert */ { if (num == 1){ dbr_ntohf(&pSrc->value, &pDest->value); } else { cvrt_float(&pSrc->value, &pDest->value, encode, num); } dbr_ntohf(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); dbr_ntohf(&pSrc->upper_disp_limit, &pDest->upper_disp_limit); dbr_ntohf(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); dbr_ntohf(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); dbr_ntohf(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); dbr_ntohf(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); dbr_ntohf(&pSrc->lower_ctrl_limit, &pDest->lower_ctrl_limit); dbr_ntohf(&pSrc->upper_ctrl_limit, &pDest->upper_ctrl_limit); } } /**************************************************************************** ** cvrt_ctrl_enum(s,d) ** ** if encode ** converts struct in HOST format to ieee format ** else ** converts fields of struct in NET format to fields with HOST ** format; ****************************************************************************/ static void cvrt_ctrl_enum( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_ctrl_enum *pSrc = (struct dbr_ctrl_enum *) s; struct dbr_ctrl_enum *pDest = (struct dbr_ctrl_enum *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); pDest->no_str = dbr_ntohs(pSrc->no_str); if ( s != d ) { memcpy((void *)pDest->strs,(void *)pSrc->strs,sizeof(pSrc->strs)); } if (num == 1) /* single value */ pDest->value = dbr_ntohs(pSrc->value); else /* array chan-- multiple pts */ { cvrt_enum(&(pSrc->value), &(pDest->value), encode, num); } } /**************************************************************************** ** cvrt_sts_char(s,d) ** struct dbr_sts_int *s pointer to source struct ** struct dbr_sts_int *d pointer to destination struct ** int encode; boolean, if true vax to ieee ** else ieee to vax ** ** converts fields ofstruct in HOST format to ieee format ** or ** converts fields of struct in NET format to fields with HOST ** format ****************************************************************************/ static void cvrt_sts_char( const void *s, /* source */ void *d, /* destination */ int /*encode*/, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_sts_char *pSrc = (struct dbr_sts_char *) s; struct dbr_sts_char *pDest = (struct dbr_sts_char *) d; /* convert vax to ieee or ieee to vax format -- same code*/ pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); if ( s == d ) return; if (num == 1) /* single value */ pDest->value = pSrc->value; else /* array chan-- multiple pts */ { memcpy((void *)&pDest->value, (void *)&pSrc->value, num); } } /**************************************************************************** ** cvrt_sts_long(s,d) ** ** converts fields ofstruct in HOST format to ieee format ** or ** converts fields of struct in NET format to fields with HOST ** format ****************************************************************************/ static void cvrt_sts_long( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_sts_long *pSrc = (struct dbr_sts_long *) s; struct dbr_sts_long *pDest = (struct dbr_sts_long *) d; /* convert vax to ieee or ieee to vax format -- same code*/ pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); if (num == 1) /* single value */ pDest->value = dbr_ntohl(pSrc->value); else /* array chan-- multiple pts */ { cvrt_long(&pDest->value, &pSrc->value, encode, num); } } /**************************************************************************** ** cvrt_time_string(s,d) ** ** converts fields of struct in HOST format to NET format ** or ** converts fields of struct in NET format to fields with HOST ** format; ****************************************************************************/ static void cvrt_time_string( const void *s, /* source */ void *d, /* destination */ int /*encode*/, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_time_string *pSrc = (struct dbr_time_string *) s; struct dbr_time_string *pDest = (struct dbr_time_string *) d; /* convert ieee to vax format or vax to ieee */ pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); pDest->stamp.secPastEpoch = dbr_ntohl(pSrc->stamp.secPastEpoch); pDest->stamp.nsec = dbr_ntohl(pSrc->stamp.nsec); if ( s != d ) { memcpy(pDest->value, pSrc->value, (MAX_STRING_SIZE * num)); } } /**************************************************************************** ** cvrt_time_short(s,d) ** ** converts fields ofstruct in HOST format to ieee format ** or ** converts fields of struct in NET format to fields with HOST ** format ****************************************************************************/ static void cvrt_time_short( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_time_short *pSrc = (struct dbr_time_short *) s; struct dbr_time_short *pDest = (struct dbr_time_short *) d; /* convert vax to ieee or ieee to vax format -- same code*/ pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); pDest->stamp.secPastEpoch = dbr_ntohl(pSrc->stamp.secPastEpoch); pDest->stamp.nsec = dbr_ntohl(pSrc->stamp.nsec); if (num == 1) /* single value */ pDest->value = dbr_ntohs(pSrc->value); else /* array chan-- multiple pts */ { cvrt_short(&pSrc->value, &pDest->value, encode, num); } } /**************************************************************************** ** cvrt_time_float(s,d) ** ** if encode ** converts struct in HOST format to ieee format ** else ** converts fields of struct in NET format to fields with HOST ** format; ****************************************************************************/ static void cvrt_time_float( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_time_float *pSrc = (struct dbr_time_float *) s; struct dbr_time_float *pDest = (struct dbr_time_float *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); pDest->stamp.secPastEpoch = dbr_ntohl(pSrc->stamp.secPastEpoch); pDest->stamp.nsec = dbr_ntohl(pSrc->stamp.nsec); cvrt_float(&pSrc->value, &pDest->value, encode, num); } /**************************************************************************** ** cvrt_time_double(s,d) ** ** if encode ** converts struct in HOST format to ieee format ** else ** converts fields of struct in NET format to fields with HOST ** format; ****************************************************************************/ static void cvrt_time_double( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_time_double *pSrc = (struct dbr_time_double *) s; struct dbr_time_double *pDest = (struct dbr_time_double *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); pDest->stamp.secPastEpoch = dbr_ntohl(pSrc->stamp.secPastEpoch); pDest->stamp.nsec = dbr_ntohl(pSrc->stamp.nsec); cvrt_double(&pSrc->value, &pDest->value, encode, num); } /**************************************************************************** ** cvrt_time_enum(s,d) ** ** converts fields of struct in NET format to fields with HOST format ** or ** converts fields of struct in HOST format to fields with NET format ** ****************************************************************************/ static void cvrt_time_enum( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_time_enum *pSrc = (struct dbr_time_enum *) s; struct dbr_time_enum *pDest = (struct dbr_time_enum *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); pDest->stamp.secPastEpoch = dbr_ntohl(pSrc->stamp.secPastEpoch); pDest->stamp.nsec = dbr_ntohl(pSrc->stamp.nsec); if (num == 1) pDest->value = dbr_ntohs(pSrc->value); else { cvrt_enum(&pSrc->value,&pDest->value,encode,num); } } /**************************************************************************** ** cvrt_sts_char(s,d) ** ** converts fields ofstruct in HOST format to ieee format ** or ** converts fields of struct in NET format to fields with HOST ** format ****************************************************************************/ static void cvrt_time_char( const void *s, /* source */ void *d, /* destination */ int /*encode*/, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_time_char *pSrc = (struct dbr_time_char *) s; struct dbr_time_char *pDest = (struct dbr_time_char *) d; /* convert vax to ieee or ieee to vax format -- same code*/ pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); pDest->stamp.secPastEpoch = dbr_ntohl(pSrc->stamp.secPastEpoch); pDest->stamp.nsec = dbr_ntohl(pSrc->stamp.nsec); if ( s == d ) return; if (num == 1) /* single value */ pDest->value = pSrc->value; else /* array chan-- multiple pts */ { memcpy((void *)&pDest->value, (void *)&pSrc->value, num); } } /**************************************************************************** ** cvrt_time_long(s,d) ** ** converts fields ofstruct in HOST format to ieee format ** or ** converts fields of struct in NET format to fields with HOST ** format ****************************************************************************/ static void cvrt_time_long( const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { struct dbr_time_long *pSrc = (struct dbr_time_long *) s; struct dbr_time_long *pDest = (struct dbr_time_long *) d; /* convert vax to ieee or ieee to vax format -- same code*/ pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); pDest->stamp.secPastEpoch = dbr_ntohl(pSrc->stamp.secPastEpoch); pDest->stamp.nsec = dbr_ntohl(pSrc->stamp.nsec); if (num == 1) /* single value */ pDest->value = dbr_ntohl(pSrc->value); else /* array chan-- multiple pts */ { cvrt_long(&pDest->value, &pSrc->value, encode, num); } } /* * cvrt_put_ackt() * * * * */ static void cvrt_put_ackt( const void *s, /* source */ void *d, /* destination */ int /*encode*/, /* cvrt HOST to NET if T */ arrayElementCount num /* number of values */ ) { dbr_put_ackt_t *pSrc = (dbr_put_ackt_t *) s; dbr_put_ackt_t *pDest = (dbr_put_ackt_t *) d; arrayElementCount i; for(i=0; istatus = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); pDest->ackt = dbr_ntohs(pSrc->ackt); pDest->acks = dbr_ntohs(pSrc->acks); /* convert "in place" -> nothing else to do */ if (s == d) return; memcpy(pDest->value, pSrc->value, (MAX_STRING_SIZE * num)); } /* cvrt is (array of) (pointer to) (function returning) int */ static CACVRTFUNCPTR cac_dbr_cvrt[] = { cvrt_string, cvrt_short, cvrt_float, cvrt_enum, cvrt_char, cvrt_long, cvrt_double, cvrt_sts_string, cvrt_sts_short, cvrt_sts_float, cvrt_sts_enum, cvrt_sts_char, cvrt_sts_long, cvrt_sts_double, cvrt_time_string, cvrt_time_short, cvrt_time_float, cvrt_time_enum, cvrt_time_char, cvrt_time_long, cvrt_time_double, cvrt_sts_string, /* DBR_GR_STRING identical to dbr_sts_string */ cvrt_gr_short, cvrt_gr_float, cvrt_gr_enum, cvrt_gr_char, cvrt_gr_long, cvrt_gr_double, cvrt_sts_string, /* DBR_CTRL_STRING identical to dbr_sts_string */ cvrt_ctrl_short, cvrt_ctrl_float, cvrt_ctrl_enum, cvrt_ctrl_char, cvrt_ctrl_long, cvrt_ctrl_double, cvrt_put_ackt, cvrt_put_ackt, /* DBR_PUT_ACKS identical to DBR_PUT_ACKT */ cvrt_stsack_string, cvrt_string }; #endif /* EPICS_CONVERSION_REQUIRED */ int caNetConvert ( unsigned type, const void *pSrc, void *pDest, int hton, arrayElementCount count ) { # ifdef EPICS_CONVERSION_REQUIRED if ( type >= NELEMENTS ( cac_dbr_cvrt ) ) { return ECA_BADTYPE; } ( * cac_dbr_cvrt [ type ] ) ( pSrc, pDest, hton, count ); # else if ( INVALID_DB_REQ ( type ) ) { return ECA_BADTYPE; } if ( pSrc != pDest ) { memcpy ( pDest, pSrc, dbr_size_n ( type, count ) ); } # endif return ECA_NORMAL; } base-7.0.3.1/modules/ca/src/client/db_access.h0000664000577000060420000006557513557101274017614 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* base/include/db_access.h */ /* Author: Bob Dalesio * Date: 4-4-88 */ #ifndef INCLdb_accessh #define INCLdb_accessh #include #ifdef epicsExportSharedSymbols # define INCLdb_accessh_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "epicsTypes.h" #include "epicsTime.h" #ifdef INCLdb_accessh_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif #ifdef __cplusplus extern "C" { #endif #define MAX_UNITS_SIZE 8 #define MAX_ENUM_STRING_SIZE 26 #define MAX_ENUM_STATES 16 /* * architecture independent types * * (so far this is sufficient for all archs we have ported to) */ typedef epicsOldString dbr_string_t; typedef epicsUInt8 dbr_char_t; typedef epicsInt16 dbr_short_t; typedef epicsUInt16 dbr_ushort_t; typedef epicsInt16 dbr_int_t; typedef epicsUInt16 dbr_enum_t; typedef epicsInt32 dbr_long_t; typedef epicsUInt32 dbr_ulong_t; typedef epicsFloat32 dbr_float_t; typedef epicsFloat64 dbr_double_t; typedef epicsUInt16 dbr_put_ackt_t; typedef epicsUInt16 dbr_put_acks_t; typedef epicsOldString dbr_stsack_string_t; typedef epicsOldString dbr_class_name_t; #ifndef db_accessHFORdb_accessC /* database field types */ #define DBF_STRING 0 #define DBF_INT 1 #define DBF_SHORT 1 #define DBF_FLOAT 2 #define DBF_ENUM 3 #define DBF_CHAR 4 #define DBF_LONG 5 #define DBF_DOUBLE 6 #define DBF_NO_ACCESS 7 #define LAST_TYPE DBF_DOUBLE #define VALID_DB_FIELD(x) ((x >= 0) && (x <= LAST_TYPE)) #define INVALID_DB_FIELD(x) ((x < 0) || (x > LAST_TYPE)) /* data request buffer types */ #define DBR_STRING DBF_STRING #define DBR_INT DBF_INT #define DBR_SHORT DBF_INT #define DBR_FLOAT DBF_FLOAT #define DBR_ENUM DBF_ENUM #define DBR_CHAR DBF_CHAR #define DBR_LONG DBF_LONG #define DBR_DOUBLE DBF_DOUBLE #define DBR_STS_STRING 7 #define DBR_STS_SHORT 8 #define DBR_STS_INT DBR_STS_SHORT #define DBR_STS_FLOAT 9 #define DBR_STS_ENUM 10 #define DBR_STS_CHAR 11 #define DBR_STS_LONG 12 #define DBR_STS_DOUBLE 13 #define DBR_TIME_STRING 14 #define DBR_TIME_INT 15 #define DBR_TIME_SHORT 15 #define DBR_TIME_FLOAT 16 #define DBR_TIME_ENUM 17 #define DBR_TIME_CHAR 18 #define DBR_TIME_LONG 19 #define DBR_TIME_DOUBLE 20 #define DBR_GR_STRING 21 #define DBR_GR_SHORT 22 #define DBR_GR_INT DBR_GR_SHORT #define DBR_GR_FLOAT 23 #define DBR_GR_ENUM 24 #define DBR_GR_CHAR 25 #define DBR_GR_LONG 26 #define DBR_GR_DOUBLE 27 #define DBR_CTRL_STRING 28 #define DBR_CTRL_SHORT 29 #define DBR_CTRL_INT DBR_CTRL_SHORT #define DBR_CTRL_FLOAT 30 #define DBR_CTRL_ENUM 31 #define DBR_CTRL_CHAR 32 #define DBR_CTRL_LONG 33 #define DBR_CTRL_DOUBLE 34 #define DBR_PUT_ACKT DBR_CTRL_DOUBLE + 1 #define DBR_PUT_ACKS DBR_PUT_ACKT + 1 #define DBR_STSACK_STRING DBR_PUT_ACKS + 1 #define DBR_CLASS_NAME DBR_STSACK_STRING + 1 #define LAST_BUFFER_TYPE DBR_CLASS_NAME #define VALID_DB_REQ(x) ((x >= 0) && (x <= LAST_BUFFER_TYPE)) #define INVALID_DB_REQ(x) ((x < 0) || (x > LAST_BUFFER_TYPE)) /* * The enumeration "epicsType" is an index to this array * of type DBR types. In some cases we select the a * larger type to avoid loss of information */ epicsShareExtern const int epicsTypeToDBR_XXXX [lastEpicsType+1]; /* * The DBR_XXXX types are indicies into this array */ epicsShareExtern const epicsType DBR_XXXXToEpicsType [LAST_BUFFER_TYPE+1]; /* values returned for each field type * DBR_STRING returns a NULL terminated string * DBR_SHORT returns an unsigned short * DBR_INT returns an unsigned short * DBR_FLOAT returns an IEEE floating point value * DBR_ENUM returns an unsigned short which is the enum item * DBR_CHAR returns an unsigned char * DBR_LONG returns an unsigned long * DBR_DOUBLE returns a double precision floating point number * DBR_STS_STRING returns a string status structure (dbr_sts_string) * DBR_STS_SHORT returns a short status structure (dbr_sts_short) * DBR_STS_INT returns a short status structure (dbr_sts_int) * DBR_STS_FLOAT returns a float status structure (dbr_sts_float) * DBR_STS_ENUM returns an enum status structure (dbr_sts_enum) * DBR_STS_CHAR returns a char status structure (dbr_sts_char) * DBR_STS_LONG returns a long status structure (dbr_sts_long) * DBR_STS_DOUBLE returns a double status structure (dbr_sts_double) * DBR_TIME_STRING returns a string time structure (dbr_time_string) * DBR_TIME_SHORT returns a short time structure (dbr_time_short) * DBR_TIME_INT returns a short time structure (dbr_time_short) * DBR_TIME_FLOAT returns a float time structure (dbr_time_float) * DBR_TIME_ENUM returns an enum time structure (dbr_time_enum) * DBR_TIME_CHAR returns a char time structure (dbr_time_char) * DBR_TIME_LONG returns a long time structure (dbr_time_long) * DBR_TIME_DOUBLE returns a double time structure (dbr_time_double) * DBR_GR_STRING returns a graphic string structure (dbr_gr_string) * DBR_GR_SHORT returns a graphic short structure (dbr_gr_short) * DBR_GR_INT returns a graphic short structure (dbr_gr_int) * DBR_GR_FLOAT returns a graphic float structure (dbr_gr_float) * DBR_GR_ENUM returns a graphic enum structure (dbr_gr_enum) * DBR_GR_CHAR returns a graphic char structure (dbr_gr_char) * DBR_GR_LONG returns a graphic long structure (dbr_gr_long) * DBR_GR_DOUBLE returns a graphic double structure (dbr_gr_double) * DBR_CTRL_STRING returns a control string structure (dbr_ctrl_int) * DBR_CTRL_SHORT returns a control short structure (dbr_ctrl_short) * DBR_CTRL_INT returns a control short structure (dbr_ctrl_int) * DBR_CTRL_FLOAT returns a control float structure (dbr_ctrl_float) * DBR_CTRL_ENUM returns a control enum structure (dbr_ctrl_enum) * DBR_CTRL_CHAR returns a control char structure (dbr_ctrl_char) * DBR_CTRL_LONG returns a control long structure (dbr_ctrl_long) * DBR_CTRL_DOUBLE returns a control double structure (dbr_ctrl_double) */ #endif /*db_accessHFORdb_accessC*/ /* VALUES WITH STATUS STRUCTURES */ /* structure for a string status field */ struct dbr_sts_string { dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ dbr_string_t value; /* current value */ }; /* structure for a string status and ack field */ struct dbr_stsack_string{ dbr_ushort_t status; /* status of value */ dbr_ushort_t severity; /* severity of alarm */ dbr_ushort_t ackt; /* ack transient? */ dbr_ushort_t acks; /* ack severity */ dbr_string_t value; /* current value */ }; /* structure for an short status field */ struct dbr_sts_int{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ dbr_short_t value; /* current value */ }; struct dbr_sts_short{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ dbr_short_t value; /* current value */ }; /* structure for a float status field */ struct dbr_sts_float{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ dbr_float_t value; /* current value */ }; /* structure for a enum status field */ struct dbr_sts_enum{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ dbr_enum_t value; /* current value */ }; /* structure for a char status field */ struct dbr_sts_char{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ dbr_char_t RISC_pad; /* RISC alignment */ dbr_char_t value; /* current value */ }; /* structure for a long status field */ struct dbr_sts_long{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ dbr_long_t value; /* current value */ }; /* structure for a double status field */ struct dbr_sts_double{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ dbr_long_t RISC_pad; /* RISC alignment */ dbr_double_t value; /* current value */ }; /* VALUES WITH STATUS AND TIME STRUCTURES */ /* structure for a string time field */ struct dbr_time_string{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ epicsTimeStamp stamp; /* time stamp */ dbr_string_t value; /* current value */ }; /* structure for an short time field */ struct dbr_time_short{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ epicsTimeStamp stamp; /* time stamp */ dbr_short_t RISC_pad; /* RISC alignment */ dbr_short_t value; /* current value */ }; /* structure for a float time field */ struct dbr_time_float{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ epicsTimeStamp stamp; /* time stamp */ dbr_float_t value; /* current value */ }; /* structure for a enum time field */ struct dbr_time_enum{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ epicsTimeStamp stamp; /* time stamp */ dbr_short_t RISC_pad; /* RISC alignment */ dbr_enum_t value; /* current value */ }; /* structure for a char time field */ struct dbr_time_char{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ epicsTimeStamp stamp; /* time stamp */ dbr_short_t RISC_pad0; /* RISC alignment */ dbr_char_t RISC_pad1; /* RISC alignment */ dbr_char_t value; /* current value */ }; /* structure for a long time field */ struct dbr_time_long{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ epicsTimeStamp stamp; /* time stamp */ dbr_long_t value; /* current value */ }; /* structure for a double time field */ struct dbr_time_double{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ epicsTimeStamp stamp; /* time stamp */ dbr_long_t RISC_pad; /* RISC alignment */ dbr_double_t value; /* current value */ }; /* VALUES WITH STATUS AND GRAPHIC STRUCTURES */ /* structure for a graphic string */ /* not implemented; use struct_dbr_sts_string */ /* structure for a graphic short field */ struct dbr_gr_int{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ char units[MAX_UNITS_SIZE]; /* units of value */ dbr_short_t upper_disp_limit; /* upper limit of graph */ dbr_short_t lower_disp_limit; /* lower limit of graph */ dbr_short_t upper_alarm_limit; dbr_short_t upper_warning_limit; dbr_short_t lower_warning_limit; dbr_short_t lower_alarm_limit; dbr_short_t value; /* current value */ }; struct dbr_gr_short{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ char units[MAX_UNITS_SIZE]; /* units of value */ dbr_short_t upper_disp_limit; /* upper limit of graph */ dbr_short_t lower_disp_limit; /* lower limit of graph */ dbr_short_t upper_alarm_limit; dbr_short_t upper_warning_limit; dbr_short_t lower_warning_limit; dbr_short_t lower_alarm_limit; dbr_short_t value; /* current value */ }; /* structure for a graphic floating point field */ struct dbr_gr_float{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ dbr_short_t precision; /* number of decimal places */ dbr_short_t RISC_pad0; /* RISC alignment */ char units[MAX_UNITS_SIZE]; /* units of value */ dbr_float_t upper_disp_limit; /* upper limit of graph */ dbr_float_t lower_disp_limit; /* lower limit of graph */ dbr_float_t upper_alarm_limit; dbr_float_t upper_warning_limit; dbr_float_t lower_warning_limit; dbr_float_t lower_alarm_limit; dbr_float_t value; /* current value */ }; /* structure for a graphic enumeration field */ struct dbr_gr_enum{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ dbr_short_t no_str; /* number of strings */ char strs[MAX_ENUM_STATES][MAX_ENUM_STRING_SIZE]; /* state strings */ dbr_enum_t value; /* current value */ }; /* structure for a graphic char field */ struct dbr_gr_char{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ char units[MAX_UNITS_SIZE]; /* units of value */ dbr_char_t upper_disp_limit; /* upper limit of graph */ dbr_char_t lower_disp_limit; /* lower limit of graph */ dbr_char_t upper_alarm_limit; dbr_char_t upper_warning_limit; dbr_char_t lower_warning_limit; dbr_char_t lower_alarm_limit; dbr_char_t RISC_pad; /* RISC alignment */ dbr_char_t value; /* current value */ }; /* structure for a graphic long field */ struct dbr_gr_long{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ char units[MAX_UNITS_SIZE]; /* units of value */ dbr_long_t upper_disp_limit; /* upper limit of graph */ dbr_long_t lower_disp_limit; /* lower limit of graph */ dbr_long_t upper_alarm_limit; dbr_long_t upper_warning_limit; dbr_long_t lower_warning_limit; dbr_long_t lower_alarm_limit; dbr_long_t value; /* current value */ }; /* structure for a graphic double field */ struct dbr_gr_double{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ dbr_short_t precision; /* number of decimal places */ dbr_short_t RISC_pad0; /* RISC alignment */ char units[MAX_UNITS_SIZE]; /* units of value */ dbr_double_t upper_disp_limit; /* upper limit of graph */ dbr_double_t lower_disp_limit; /* lower limit of graph */ dbr_double_t upper_alarm_limit; dbr_double_t upper_warning_limit; dbr_double_t lower_warning_limit; dbr_double_t lower_alarm_limit; dbr_double_t value; /* current value */ }; /* VALUES WITH STATUS, GRAPHIC and CONTROL STRUCTURES */ /* structure for a control string */ /* not implemented; use struct_dbr_sts_string */ /* structure for a control integer */ struct dbr_ctrl_int{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ char units[MAX_UNITS_SIZE]; /* units of value */ dbr_short_t upper_disp_limit; /* upper limit of graph */ dbr_short_t lower_disp_limit; /* lower limit of graph */ dbr_short_t upper_alarm_limit; dbr_short_t upper_warning_limit; dbr_short_t lower_warning_limit; dbr_short_t lower_alarm_limit; dbr_short_t upper_ctrl_limit; /* upper control limit */ dbr_short_t lower_ctrl_limit; /* lower control limit */ dbr_short_t value; /* current value */ }; struct dbr_ctrl_short{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ char units[MAX_UNITS_SIZE]; /* units of value */ dbr_short_t upper_disp_limit; /* upper limit of graph */ dbr_short_t lower_disp_limit; /* lower limit of graph */ dbr_short_t upper_alarm_limit; dbr_short_t upper_warning_limit; dbr_short_t lower_warning_limit; dbr_short_t lower_alarm_limit; dbr_short_t upper_ctrl_limit; /* upper control limit */ dbr_short_t lower_ctrl_limit; /* lower control limit */ dbr_short_t value; /* current value */ }; /* structure for a control floating point field */ struct dbr_ctrl_float{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ dbr_short_t precision; /* number of decimal places */ dbr_short_t RISC_pad; /* RISC alignment */ char units[MAX_UNITS_SIZE]; /* units of value */ dbr_float_t upper_disp_limit; /* upper limit of graph */ dbr_float_t lower_disp_limit; /* lower limit of graph */ dbr_float_t upper_alarm_limit; dbr_float_t upper_warning_limit; dbr_float_t lower_warning_limit; dbr_float_t lower_alarm_limit; dbr_float_t upper_ctrl_limit; /* upper control limit */ dbr_float_t lower_ctrl_limit; /* lower control limit */ dbr_float_t value; /* current value */ }; /* structure for a control enumeration field */ struct dbr_ctrl_enum{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ dbr_short_t no_str; /* number of strings */ char strs[MAX_ENUM_STATES][MAX_ENUM_STRING_SIZE]; /* state strings */ dbr_enum_t value; /* current value */ }; /* structure for a control char field */ struct dbr_ctrl_char{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ char units[MAX_UNITS_SIZE]; /* units of value */ dbr_char_t upper_disp_limit; /* upper limit of graph */ dbr_char_t lower_disp_limit; /* lower limit of graph */ dbr_char_t upper_alarm_limit; dbr_char_t upper_warning_limit; dbr_char_t lower_warning_limit; dbr_char_t lower_alarm_limit; dbr_char_t upper_ctrl_limit; /* upper control limit */ dbr_char_t lower_ctrl_limit; /* lower control limit */ dbr_char_t RISC_pad; /* RISC alignment */ dbr_char_t value; /* current value */ }; /* structure for a control long field */ struct dbr_ctrl_long{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ char units[MAX_UNITS_SIZE]; /* units of value */ dbr_long_t upper_disp_limit; /* upper limit of graph */ dbr_long_t lower_disp_limit; /* lower limit of graph */ dbr_long_t upper_alarm_limit; dbr_long_t upper_warning_limit; dbr_long_t lower_warning_limit; dbr_long_t lower_alarm_limit; dbr_long_t upper_ctrl_limit; /* upper control limit */ dbr_long_t lower_ctrl_limit; /* lower control limit */ dbr_long_t value; /* current value */ }; /* structure for a control double field */ struct dbr_ctrl_double{ dbr_short_t status; /* status of value */ dbr_short_t severity; /* severity of alarm */ dbr_short_t precision; /* number of decimal places */ dbr_short_t RISC_pad0; /* RISC alignment */ char units[MAX_UNITS_SIZE]; /* units of value */ dbr_double_t upper_disp_limit; /* upper limit of graph */ dbr_double_t lower_disp_limit; /* lower limit of graph */ dbr_double_t upper_alarm_limit; dbr_double_t upper_warning_limit; dbr_double_t lower_warning_limit; dbr_double_t lower_alarm_limit; dbr_double_t upper_ctrl_limit; /* upper control limit */ dbr_double_t lower_ctrl_limit; /* lower control limit */ dbr_double_t value; /* current value */ }; #define dbr_size_n(TYPE,COUNT)\ ((unsigned)((COUNT)<=0?dbr_size[TYPE]:dbr_size[TYPE]+((COUNT)-1)*dbr_value_size[TYPE])) /* size for each type - array indexed by the DBR_ type code */ epicsShareExtern const unsigned short dbr_size[]; /* size for each type's value - array indexed by the DBR_ type code */ epicsShareExtern const unsigned short dbr_value_size[]; #ifndef db_accessHFORdb_accessC /* class for each type's value */ enum dbr_value_class { dbr_class_int, dbr_class_float, dbr_class_string, dbr_class_max}; epicsShareExtern const enum dbr_value_class dbr_value_class[LAST_BUFFER_TYPE+1]; /* * ptr to value given a pointer to the structure and the DBR type */ #define dbr_value_ptr(PDBR, DBR_TYPE) \ ((void *)(((char *)PDBR)+dbr_value_offset[DBR_TYPE])) /* * ptr to value given a pointer to the structure and the structure declaration */ #define dbr_value_ptr_from_structure(PDBR, STRUCTURE)\ ((void *)(((char *)PDBR)+BYTE_OS(STRUCTURE, value))) epicsShareExtern const unsigned short dbr_value_offset[LAST_BUFFER_TYPE+1]; /* union for each fetch buffers */ union db_access_val{ dbr_string_t strval; /* string max size */ dbr_short_t shrtval; /* short */ dbr_short_t intval; /* short */ dbr_float_t fltval; /* IEEE Float */ dbr_enum_t enmval; /* item number */ dbr_char_t charval; /* character */ dbr_long_t longval; /* long */ dbr_double_t doubleval; /* double */ struct dbr_sts_string sstrval; /* string field with status */ struct dbr_sts_short sshrtval; /* short field with status */ struct dbr_sts_float sfltval; /* float field with status */ struct dbr_sts_enum senmval; /* item number with status */ struct dbr_sts_char schrval; /* char field with status */ struct dbr_sts_long slngval; /* long field with status */ struct dbr_sts_double sdblval; /* double field with time */ struct dbr_time_string tstrval; /* string field with time */ struct dbr_time_short tshrtval; /* short field with time */ struct dbr_time_float tfltval; /* float field with time */ struct dbr_time_enum tenmval; /* item number with time */ struct dbr_time_char tchrval; /* char field with time */ struct dbr_time_long tlngval; /* long field with time */ struct dbr_time_double tdblval; /* double field with time */ struct dbr_sts_string gstrval; /* graphic string info */ struct dbr_gr_short gshrtval; /* graphic short info */ struct dbr_gr_float gfltval; /* graphic float info */ struct dbr_gr_enum genmval; /* graphic item info */ struct dbr_gr_char gchrval; /* graphic char info */ struct dbr_gr_long glngval; /* graphic long info */ struct dbr_gr_double gdblval; /* graphic double info */ struct dbr_sts_string cstrval; /* control string info */ struct dbr_ctrl_short cshrtval; /* control short info */ struct dbr_ctrl_float cfltval; /* control float info */ struct dbr_ctrl_enum cenmval; /* control item info */ struct dbr_ctrl_char cchrval; /* control char info */ struct dbr_ctrl_long clngval; /* control long info */ struct dbr_ctrl_double cdblval; /* control double info */ dbr_put_ackt_t putackt; /* item number */ dbr_put_acks_t putacks; /* item number */ struct dbr_sts_string sastrval; /* string field with status */ dbr_string_t classname; /* string max size */ }; /*---------------------------------------------------------------------------- * repository for some useful PV database constants and utilities * * item dimensions * db_strval_dim dimension for string values * db_units_dim dimension for record units text * db_desc_dim dimension for record description text * db_name_dim dimension for channel names (record.field\0) * db_state_dim number of states possible in a state table * db_state_text_dim dimension for a state text string * usage: char state_table[db_state_dim][db_state_text_dim] * * type checking macros -- return non-zero if condition is true, zero otherwise * * int dbf_type_is_valid(type) type is a valid DBF_xxx * int dbr_type_is_valid(type) type is a valid DBR_xxx * int dbr_type_is_plain(type) type is a valid plain DBR_xxx * int dbr_type_is_STS(type) type is a valid DBR_STS_xxx * int dbr_type_is_TIME(type) type is a valid DBR_TIME_xxx * int dbr_type_is_GR(type) type is a valid DBR_GR_xxx * int dbr_type_is_CTRL(type) type is a valid DBR_CTRL_xxx * int dbr_type_is_STRING(type) type is a valid DBR_STRING_xxx * int dbr_type_is_SHORT(type) type is a valid DBR_SHORT_xxx * int dbr_type_is_FLOAT(type) type is a valid DBR_FLOAT_xxx * int dbr_type_is_ENUM(type) type is a valid DBR_ENUM_xxx * int dbr_type_is_CHAR(type) type is a valid DBR_CHAR_xxx * int dbr_type_is_LONG(type) type is a valid DBR_LONG_xxx * int dbr_type_is_DOUBLE(type) type is a valid DBR_DOUBLE_xxx * * type conversion macros * * char *dbf_type_to_text(type) returns text matching DBF_xxx * void dbf_text_to_type(text, type) finds DBF_xxx matching text * int dbf_type_to_DBR(type) returns DBR_xxx matching DBF_xxx * int dbf_type_to_DBR_TIME(type) returns DBR_TIME_xxx matching DBF_xxx * int dbf_type_to_DBR_GR(type) returns DBR_GR_xxx matching DBF_xxx * int dbf_type_to_DBR_CTRL(type) returns DBR_CTRL_xxx matching DBF_xxx * char *dbr_type_to_text(type) returns text matching DBR_xxx * void dbr_text_to_type(text, type) finds DBR_xxx matching text *---------------------------------------------------------------------------*/ #define db_strval_dim MAX_STRING_SIZE #define db_units_dim MAX_UNITS_SIZE #define db_desc_dim 24 #define db_name_dim 36 #define db_state_dim MAX_ENUM_STATES #define db_state_text_dim MAX_ENUM_STRING_SIZE #define dbf_type_is_valid(type) ((type) >= 0 && (type) <= LAST_TYPE) #define dbr_type_is_valid(type) ((type) >= 0 && (type) <= LAST_BUFFER_TYPE) #define dbr_type_is_plain(type) \ ((type) >= DBR_STRING && (type) <= DBR_DOUBLE) #define dbr_type_is_STS(type) \ ((type) >= DBR_STS_STRING && (type) <= DBR_STS_DOUBLE) #define dbr_type_is_TIME(type) \ ((type) >= DBR_TIME_STRING && (type) <= DBR_TIME_DOUBLE) #define dbr_type_is_GR(type) \ ((type) >= DBR_GR_STRING && (type) <= DBR_GR_DOUBLE) #define dbr_type_is_CTRL(type) \ ((type) >= DBR_CTRL_STRING && (type) <= DBR_CTRL_DOUBLE) #define dbr_type_is_STRING(type) \ ((type) >= 0 && (type) <= LAST_BUFFER_TYPE && \ (type)%(LAST_TYPE+1) == DBR_STRING) #define dbr_type_is_SHORT(type) \ ((type) >= 0 && (type) <= LAST_BUFFER_TYPE && \ (type)%(LAST_TYPE+1) == DBR_SHORT) #define dbr_type_is_FLOAT(type) \ ((type) >= 0 && (type) <= LAST_BUFFER_TYPE && \ (type)%(LAST_TYPE+1) == DBR_FLOAT) #define dbr_type_is_ENUM(type) \ ((type) >= 0 && (type) <= LAST_BUFFER_TYPE && \ (type)%(LAST_TYPE+1) == DBR_ENUM) #define dbr_type_is_CHAR(type) \ ((type) >= 0 && (type) <= LAST_BUFFER_TYPE && \ (type)%(LAST_TYPE+1) == DBR_CHAR) #define dbr_type_is_LONG(type) \ ((type) >= 0 && (type) <= LAST_BUFFER_TYPE && \ (type)%(LAST_TYPE+1) == DBR_LONG) #define dbr_type_is_DOUBLE(type) \ ((type) >= 0 && (type) <= LAST_BUFFER_TYPE && \ (type)%(LAST_TYPE+1) == DBR_DOUBLE) #define dbf_type_to_text(type) \ ( ((type) >= -1 && (type) < dbf_text_dim-2) ? \ dbf_text[type+1] : dbf_text_invalid ) #define dbf_text_to_type(text, type) \ for (type=dbf_text_dim-3; type>=0; type--) { \ if (strcmp(text, dbf_text[type+1]) == 0) \ break; \ } #define dbr_type_to_text(type) \ ( ((type) >= 0 && (type) < dbr_text_dim) ? \ dbr_text[(type)] : dbr_text_invalid ) #define dbr_text_to_type(text, type) \ for (type=dbr_text_dim-2; type>=0; type--) { \ if (strcmp(text, dbr_text[type]) == 0) \ break; \ } #define dbf_type_to_DBR(type) \ (((type) >= 0 && (type) <= dbf_text_dim-3) ? \ (type) : -1 ) #define dbf_type_to_DBR_STS(type) \ (((type) >= 0 && (type) <= dbf_text_dim-3) ? \ (type) + (dbf_text_dim-2) : -1 ) #define dbf_type_to_DBR_TIME(type) \ (((type) >= 0 && (type) <= dbf_text_dim-3) ? \ (type) + 2*(dbf_text_dim-2) : -1 ) #define dbf_type_to_DBR_GR(type) \ (((type) >= 0 && (type) <= dbf_text_dim-3) ? \ (type) + 3*(dbf_text_dim-2) : -1 ) #define dbf_type_to_DBR_CTRL(type) \ (((type) >= 0 && (type) <= dbf_text_dim-3) ? \ (type) + 4*(dbf_text_dim-2) : -1 ) epicsShareExtern const char *dbf_text[LAST_TYPE+3]; epicsShareExtern const short dbf_text_dim; epicsShareExtern const char *dbf_text_invalid; epicsShareExtern const char *dbr_text[LAST_BUFFER_TYPE+1]; epicsShareExtern const short dbr_text_dim; epicsShareExtern const char *dbr_text_invalid; #endif /*db_accessHFORdb_accessC*/ #ifdef __cplusplus } #endif #endif /* INCLdb_accessh */ base-7.0.3.1/modules/ca/src/client/disconnectGovernorTimer.cpp0000664000577000060420000000637513557101274023106 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ // // // L O S A L A M O S // Los Alamos National Laboratory // Los Alamos, New Mexico 87545 // // Copyright, 1986, The Regents of the University of California. // // Author: Jeff Hill // #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #define epicsExportSharedSymbols #include "disconnectGovernorTimer.h" #include "udpiiu.h" #include "nciu.h" static const double disconnectGovernorPeriod = 10.0; // sec disconnectGovernorTimer::disconnectGovernorTimer ( disconnectGovernorNotify & iiuIn, epicsTimerQueue & queueIn, epicsMutex & mutexIn ) : mutex ( mutexIn ), timer ( queueIn.createTimer () ), iiu ( iiuIn ) { } disconnectGovernorTimer::~disconnectGovernorTimer () { this->timer.destroy (); } void disconnectGovernorTimer:: start () { this->timer.start ( *this, disconnectGovernorPeriod ); } void disconnectGovernorTimer::shutdown ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ) { { epicsGuardRelease < epicsMutex > unguard ( guard ); { epicsGuardRelease < epicsMutex > cbUnguard ( cbGuard ); this->timer.cancel (); } } while ( nciu * pChan = this->chanList.get () ) { pChan->channelNode::listMember = channelNode::cs_none; pChan->serviceShutdownNotify ( cbGuard, guard ); } } epicsTimerNotify::expireStatus disconnectGovernorTimer::expire ( const epicsTime & /* currentTime */ ) { epicsGuard < epicsMutex > guard ( this->mutex ); while ( nciu * pChan = chanList.get () ) { pChan->channelNode::listMember = channelNode::cs_none; this->iiu.govExpireNotify ( guard, *pChan ); } return expireStatus ( restart, disconnectGovernorPeriod ); } void disconnectGovernorTimer::show ( unsigned level ) const { epicsGuard < epicsMutex > guard ( this->mutex ); ::printf ( "disconnect governor timer: with %u channels pending\n", this->chanList.count () ); if ( level > 0u ) { tsDLIterConst < nciu > pChan = this->chanList.firstIter (); while ( pChan.valid () ) { pChan->show ( level - 1u ); pChan++; } } } void disconnectGovernorTimer::installChan ( epicsGuard < epicsMutex > & guard, nciu & chan ) { guard.assertIdenticalMutex ( this->mutex ); this->chanList.add ( chan ); chan.channelNode::listMember = channelNode::cs_disconnGov; } void disconnectGovernorTimer::uninstallChan ( epicsGuard < epicsMutex > & guard, nciu & chan ) { guard.assertIdenticalMutex ( this->mutex ); this->chanList.remove ( chan ); chan.channelNode::listMember = channelNode::cs_none; } disconnectGovernorNotify::~disconnectGovernorNotify () {} base-7.0.3.1/modules/ca/src/client/disconnectGovernorTimer.h0000664000577000060420000000461713557101274022550 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ // // // // L O S A L A M O S // Los Alamos National Laboratory // Los Alamos, New Mexico 87545 // // Copyright, 1986, The Regents of the University of California. // // // Author Jeffrey O. Hill // johill@lanl.gov // 505 665 1831 // #ifndef disconnectGovernorTimerh #define disconnectGovernorTimerh #ifdef epicsExportSharedSymbols # define searchTimerh_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "epicsMutex.h" #include "epicsGuard.h" #include "epicsTimer.h" #ifdef searchTimerh_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif #include "caProto.h" #include "netiiu.h" class disconnectGovernorNotify { public: virtual ~disconnectGovernorNotify () = 0; virtual void govExpireNotify ( epicsGuard < epicsMutex > &, nciu & ) = 0; }; class disconnectGovernorTimer : private epicsTimerNotify { public: disconnectGovernorTimer ( class disconnectGovernorNotify &, epicsTimerQueue &, epicsMutex & ); virtual ~disconnectGovernorTimer (); void start (); void shutdown ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); void installChan ( epicsGuard < epicsMutex > &, nciu & ); void uninstallChan ( epicsGuard < epicsMutex > &, nciu & ); void show ( unsigned level ) const; private: tsDLList < nciu > chanList; epicsMutex & mutex; epicsTimer & timer; class disconnectGovernorNotify & iiu; epicsTimerNotify::expireStatus expire ( const epicsTime & currentTime ); disconnectGovernorTimer ( const disconnectGovernorTimer & ); disconnectGovernorTimer & operator = ( const disconnectGovernorTimer & ); }; #endif // ifdef disconnectGovernorTimerh base-7.0.3.1/modules/ca/src/client/evtime.c0000664000577000060420000000363113557101274017153 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include #include "dbDefs.h" #include "epicsTime.h" #include "cadef.h" void event_handler (struct event_handler_args args); int evtime (char *pname); static unsigned iteration_count; static epicsUInt32 last_time; #ifndef iocCore int main(int argc, char **argv) { char *pname; if(argc == 2){ pname = argv[1]; evtime(pname); } else{ printf("usage: %s ", argv[0]); } return(0); } #endif /* * evtime() */ int evtime(char *pname) { chid chan; int status; status = ca_search(pname, &chan); SEVCHK(status, NULL); status = ca_pend_io(10.0); if(status != ECA_NORMAL){ printf("%s not found\n", pname); return 0; } status = ca_add_event( DBR_FLOAT, chan, event_handler, NULL, NULL); SEVCHK(status, __FILE__); status = ca_pend_event(0.0); SEVCHK(status, NULL); } /* * event_handler() * */ void event_handler(struct event_handler_args args) { epicsUInt32 current_time; # define COUNT 0x8000 double interval; double delay; epicsTimeStamp ts; if(iteration_count%COUNT == 0){ epicsTimeGetCurrent(&ts); current_time = ts.secPastEpoch; if(last_time != 0){ interval = current_time - last_time; delay = interval/COUNT; printf("Delay = %f sec per event\n", delay); } last_time = current_time; } iteration_count++; } base-7.0.3.1/modules/ca/src/client/future_work.txt0000664000577000060420000000246113557101274020633 0ustar anjaesctl Potential upgrades to Channel Access o generalized access to abstract data o use multicast for broadcasting and ax the repeater o improve client API o improve security o name service (wildcard queries) o PV name prefix (for all PVs) o client access to process passive and for maximize severity o detect name conflicts at boot time o multi priority connections (quality of service) o reduce protocol overhead o compressed protocol o If there is a beacon anomaly then this indicates that the server is _not_ available. Perhaps we should only reset the search timer interval when we see a beacon anomaly transition into a sure steady beacon. o make certain that a monitor callback canceling itself does not deadlock. o the free list library does not now cause exceptions to occur (it uses new ( nothrow )), and therefore we should change the new handlers to be nothrow also if this is the design goal. o test the library when an IOC is running low on memory. o The new CA interface should be multi-threaded by default. We should continue to support a single-threaded interface, but this would should be restricted so that it always runs with preemptive callback disable and no threads may join in. Alternatively, the new CA interface could be 100% preemptive multi-threaded and the old interface could be layered on this. base-7.0.3.1/modules/ca/src/client/getCallback.cpp0000664000577000060420000000636613557101274020426 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include #include "errlog.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "oldAccess.h" getCallback::getCallback ( oldChannelNotify & chanIn, caEventCallBackFunc *pFuncIn, void *pPrivateIn ) : chan ( chanIn ), pFunc ( pFuncIn ), pPrivate ( pPrivateIn ) { } getCallback::~getCallback () { } void getCallback::completion ( epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount count, const void *pData ) { struct event_handler_args args; args.usr = this->pPrivate; args.chid = & this->chan; args.type = type; args.count = count; args.status = ECA_NORMAL; args.dbr = pData; caEventCallBackFunc * pFuncTmp = this->pFunc; // fetch client context and destroy prior to releasing // the lock and calling cb in case they destroy channel there this->chan.getClientCtx().destroyGetCallback ( guard, *this ); if ( pFuncTmp ) { epicsGuardRelease < epicsMutex > unguard ( guard ); pFuncTmp ( args ); } } void getCallback::exception ( epicsGuard < epicsMutex > & guard, int status, const char * /* pContext */, unsigned type, arrayElementCount count ) { if ( status != ECA_CHANDESTROY ) { struct event_handler_args args; args.usr = this->pPrivate; args.chid = & this->chan; args.type = type; args.count = count; args.status = status; args.dbr = 0; caEventCallBackFunc * pFuncTmp = this->pFunc; // fetch client context and destroy prior to releasing // the lock and calling cb in case they destroy channel there this->chan.getClientCtx().destroyGetCallback ( guard, *this ); { epicsGuardRelease < epicsMutex > unguard ( guard ); ( *pFuncTmp ) ( args ); } } else { this->chan.getClientCtx().destroyGetCallback ( guard, *this ); } } void getCallback::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } base-7.0.3.1/modules/ca/src/client/getCopy.cpp0000664000577000060420000000774213557101274017643 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include #include "errlog.h" #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #define epicsExportSharedSymbols #include "iocinf.h" #include "oldAccess.h" #include "cac.h" getCopy::getCopy ( epicsGuard < epicsMutex > & guard, ca_client_context & cacCtxIn, oldChannelNotify & chanIn, unsigned typeIn, arrayElementCount countIn, void * pValueIn ) : count ( countIn ), cacCtx ( cacCtxIn ), chan ( chanIn ), pValue ( pValueIn ), ioSeqNo ( 0 ), type ( typeIn ) { this->ioSeqNo = cacCtxIn.sequenceNumberOfOutstandingIO ( guard ); cacCtxIn.incrementOutstandingIO ( guard, this->ioSeqNo ); } getCopy::~getCopy () { } void getCopy::cancel () { epicsGuard < epicsMutex > guard ( this->cacCtx.mutexRef () ); this->cacCtx.decrementOutstandingIO ( guard, this->ioSeqNo ); } void getCopy::completion ( epicsGuard < epicsMutex > & guard, unsigned typeIn, arrayElementCount countIn, const void *pDataIn ) { if ( this->type == typeIn ) { unsigned size = dbr_size_n ( typeIn, countIn ); memcpy ( this->pValue, pDataIn, size ); this->cacCtx.decrementOutstandingIO ( guard, this->ioSeqNo ); this->cacCtx.destroyGetCopy ( guard, *this ); // this object destroyed by preceding function call } else { this->exception ( guard, ECA_INTERNAL, "bad data type match in get copy back response", typeIn, countIn); // this object destroyed by preceding function call } } void getCopy::exception ( epicsGuard < epicsMutex > & guard, int status, const char *pContext, unsigned /* typeIn */, arrayElementCount /* countIn */ ) { oldChannelNotify & chanTmp ( this->chan ); unsigned typeTmp ( this->type ); arrayElementCount countTmp ( this->count ); ca_client_context & caClientCtx ( this->cacCtx ); // fetch client context and destroy prior to releasing // the lock and calling cb in case they destroy channel there this->cacCtx.destroyGetCopy ( guard, *this ); if ( status != ECA_CHANDESTROY ) { caClientCtx.exception ( guard, status, pContext, __FILE__, __LINE__, chanTmp, typeTmp, countTmp, CA_OP_GET ); } } void getCopy::show ( unsigned level ) const { int tmpType = static_cast ( this->type ); ::printf ( "read copy IO at %p, type %s, element count %lu\n", static_cast ( this ), dbf_type_to_text ( tmpType ), this->count ); if ( level > 0u ) { ::printf ( "\tIO sequence number %u, user's storage %p\n", this->ioSeqNo, static_cast ( this->pValue ) ); } } void getCopy::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } base-7.0.3.1/modules/ca/src/client/hostNameCache.cpp0000664000577000060420000000573013557101274020726 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov */ #include #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "hostNameCache.h" #include "epicsGuard.h" hostNameCache::hostNameCache ( const osiSockAddr & addr, ipAddrToAsciiEngine & engine ) : dnsTransaction ( engine.createTransaction() ), nameLength ( 0 ) { sockAddrToDottedIP ( &addr.sa, hostNameBuf, sizeof ( hostNameBuf ) ); hostNameBuf[ sizeof ( hostNameBuf ) - 1 ] = '\0'; nameLength = strlen ( hostNameBuf ); this->dnsTransaction.ipAddrToAscii ( addr, *this ); } void hostNameCache::destroy () { delete this; } hostNameCache::~hostNameCache () { this->dnsTransaction.release (); } void hostNameCache::transactionComplete ( const char * pHostNameIn ) { epicsGuard < epicsMutex > guard ( this->mutex ); // a few legacy clients have a direct pointer to this buffer so we // set the entrire string to nill terminators before we start copying // in the name (this reduces the chance that another thread will see // garbage characters). size_t newNameLen = strlen ( pHostNameIn ); if ( newNameLen > sizeof ( this->hostNameBuf ) - 1u ) { newNameLen = sizeof ( this->hostNameBuf ) - 1u; } strncpy ( this->hostNameBuf, "", sizeof ( this->hostNameBuf ) ); strncpy ( this->hostNameBuf, pHostNameIn, sizeof ( this->hostNameBuf ) - 1 ); this->nameLength = newNameLen; } unsigned hostNameCache::getName ( char * pBuf, unsigned bufSize ) const { if ( bufSize == 0u ) { return 0u; } epicsGuard < epicsMutex > guard ( this->mutex ); if ( this->nameLength > 0u ) { if ( this->nameLength < bufSize ) { strcpy ( pBuf, this->hostNameBuf ); return this->nameLength; } else { unsigned reducedSize = bufSize - 1u; strncpy ( pBuf, this->hostNameBuf, reducedSize ); pBuf [ reducedSize ] = '\0'; return reducedSize; } } else { osiSockAddr tmpAddr = this->dnsTransaction.address (); return sockAddrToDottedIP ( &tmpAddr.sa, pBuf, bufSize ); } } base-7.0.3.1/modules/ca/src/client/hostNameCache.h0000664000577000060420000000350213557101274020366 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef hostNameCacheh #define hostNameCacheh #ifdef epicsExportSharedSymbols # define hostNameCache_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "ipAddrToAsciiAsynchronous.h" #include "epicsMutex.h" #ifdef hostNameCache_epicsExportSharedSymbols # define epicsExportSharedSymbols #endif class hostNameCache : public ipAddrToAsciiCallBack { public: hostNameCache ( const osiSockAddr & addr, ipAddrToAsciiEngine & engine ); ~hostNameCache (); void destroy (); void transactionComplete ( const char * pHostName ); unsigned getName ( char *pBuf, unsigned bufLength ) const; const char * pointer () const; private: char hostNameBuf [128]; mutable epicsMutex mutex; ipAddrToAsciiTransaction & dnsTransaction; unsigned nameLength; }; inline const char * hostNameCache::pointer () const { return this->hostNameBuf; } #endif // #ifndef hostNameCacheh base-7.0.3.1/modules/ca/src/client/inetAddrID.h0000664000577000060420000000373613557101274017644 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #ifndef inetAddrIDh #define inetAddrIDh #include "osiSock.h" #include "resourceLib.h" class inetAddrID { public: inetAddrID ( const struct sockaddr_in & addrIn ); bool operator == ( const inetAddrID & ) const; resTableIndex hash () const; void name ( char *pBuf, unsigned bufSize ) const; private: struct sockaddr_in addr; }; inline inetAddrID::inetAddrID ( const struct sockaddr_in & addrIn ) : addr ( addrIn ) { } inline bool inetAddrID::operator == ( const inetAddrID &rhs ) const { if ( this->addr.sin_addr.s_addr == rhs.addr.sin_addr.s_addr ) { if ( this->addr.sin_port == rhs.addr.sin_port ) { return true; } } return false; } inline resTableIndex inetAddrID::hash () const { const unsigned inetAddrMinIndexBitWidth = 8u; const unsigned inetAddrMaxIndexBitWidth = 32u; unsigned index; index = this->addr.sin_addr.s_addr; index ^= this->addr.sin_port; index ^= this->addr.sin_port >> 8u; return integerHash ( inetAddrMinIndexBitWidth, inetAddrMaxIndexBitWidth, index ); } inline void inetAddrID::name ( char *pBuf, unsigned bufSize ) const { ipAddrToDottedIP ( &this->addr, pBuf, bufSize ); } #endif // ifdef inetAddrID base-7.0.3.1/modules/ca/src/client/iocinf.cpp0000664000577000060420000001664613557101274017503 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include #include #include #include #include "envDefs.h" #include "epicsAssert.h" #include "epicsStdioRedirect.h" #include "errlog.h" #include "osiWireFormat.h" #define epicsExportSharedSymbols #include "addrList.h" #undef epicsExportSharedSymbols #include "iocinf.h" /* * getToken() */ static char *getToken ( const char **ppString, char *pBuf, unsigned bufSIze ) { bool tokenFound = false; const char *pToken; unsigned i; pToken = *ppString; while ( isspace (*pToken) && *pToken ){ pToken++; } for ( i=0u; iname); fprintf ( stderr, "\tBad internet address or host name: '%s'\n", pToken); continue; } if ( ignoreNonDefaultPort && ntohs ( addr.sin_port ) != port ) { continue; } pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode)); if (pNewNode==NULL) { fprintf ( stderr, "addAddrToChannelAccessAddressList(): no memory available for configuration\n"); break; } pNewNode->addr.ia = addr; /* * LOCK applied externally */ ellAdd (pList, &pNewNode->node); ret = 0; /* success if anything is added to the list */ } return ret; } /* * removeDuplicateAddresses () */ extern "C" void epicsShareAPI removeDuplicateAddresses ( ELLLIST *pDestList, ELLLIST *pSrcList, int silent ) { ELLNODE *pRawNode; while ( (pRawNode = ellGet ( pSrcList ) ) ) { STATIC_ASSERT ( offsetof (osiSockAddrNode, node) == 0 ); osiSockAddrNode *pNode = reinterpret_cast ( pRawNode ); osiSockAddrNode *pTmpNode; if ( pNode->addr.sa.sa_family == AF_INET ) { pTmpNode = (osiSockAddrNode *) ellFirst (pDestList); while ( pTmpNode ) { if (pTmpNode->addr.sa.sa_family == AF_INET) { if ( pNode->addr.ia.sin_addr.s_addr == pTmpNode->addr.ia.sin_addr.s_addr && pNode->addr.ia.sin_port == pTmpNode->addr.ia.sin_port ) { if ( ! silent ) { char buf[64]; ipAddrToDottedIP ( &pNode->addr.ia, buf, sizeof (buf) ); fprintf ( stderr, "Warning: Duplicate EPICS CA Address list entry \"%s\" discarded\n", buf ); } free (pNode); pNode = NULL; break; } } pTmpNode = (osiSockAddrNode *) ellNext (&pTmpNode->node); } if (pNode) { ellAdd (pDestList, &pNode->node); } } else { ellAdd (pDestList, &pNode->node); } } } /* * forcePort () */ static void forcePort ( ELLLIST *pList, unsigned short port ) { osiSockAddrNode *pNode; pNode = ( osiSockAddrNode * ) ellFirst ( pList ); while ( pNode ) { if ( pNode->addr.sa.sa_family == AF_INET ) { pNode->addr.ia.sin_port = htons ( port ); } pNode = ( osiSockAddrNode * ) ellNext ( &pNode->node ); } } /* * configureChannelAccessAddressList () */ extern "C" void epicsShareAPI configureChannelAccessAddressList ( ELLLIST *pList, SOCKET sock, unsigned short port ) { ELLLIST tmpList; char *pstr; char yesno[32u]; int yes; /* * dont load the list twice */ assert ( ellCount (pList) == 0 ); ellInit ( &tmpList ); /* * Check to see if the user has disabled * initializing the search b-cast list * from the interfaces found. */ yes = true; pstr = envGetConfigParam ( &EPICS_CA_AUTO_ADDR_LIST, sizeof (yesno), yesno ); if ( pstr ) { if ( strstr ( pstr, "no" ) || strstr ( pstr, "NO" ) ) { yes = false; } } /* * LOCK is for piiu->destAddr list * (lock outside because this is used by the server also) */ if (yes) { ELLLIST bcastList; osiSockAddr addr; ellInit ( &bcastList ); addr.ia.sin_family = AF_UNSPEC; osiSockDiscoverBroadcastAddresses ( &bcastList, sock, &addr ); forcePort ( &bcastList, port ); removeDuplicateAddresses ( &tmpList, &bcastList, 1 ); if ( ellCount ( &tmpList ) == 0 ) { osiSockAddrNode *pNewNode; pNewNode = (osiSockAddrNode *) calloc ( 1, sizeof (*pNewNode) ); if ( pNewNode ) { /* * if no interfaces found then look for local channels * with the loop back interface */ pNewNode->addr.ia.sin_family = AF_INET; pNewNode->addr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK ); pNewNode->addr.ia.sin_port = htons ( port ); ellAdd ( &tmpList, &pNewNode->node ); } else { errlogPrintf ( "configureChannelAccessAddressList(): no memory available for configuration\n" ); } } } addAddrToChannelAccessAddressList ( &tmpList, &EPICS_CA_ADDR_LIST, port, false ); removeDuplicateAddresses ( pList, &tmpList, 0 ); } /* * printChannelAccessAddressList () */ extern "C" void epicsShareAPI printChannelAccessAddressList ( const ELLLIST *pList ) { osiSockAddrNode *pNode; ::printf ( "Channel Access Address List\n" ); pNode = (osiSockAddrNode *) ellFirst ( pList ); while (pNode) { char buf[64]; ipAddrToA ( &pNode->addr.ia, buf, sizeof ( buf ) ); ::printf ( "%s\n", buf ); pNode = (osiSockAddrNode *) ellNext ( &pNode->node ); } } base-7.0.3.1/modules/ca/src/client/iocinf.h0000664000577000060420000000431413557101274017135 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef INCiocinfh #define INCiocinfh #ifdef DEBUG # define debugPrintf(argsInParen) ::printf argsInParen #else # define debugPrintf(argsInParen) #endif #if defined ( CLOCKS_PER_SEC ) # define CAC_SIGNIFICANT_DELAY ( 1.0 / CLOCKS_PER_SEC ) #else # define CAC_SIGNIFICANT_DELAY (1.0 / 1000000u) #endif /* * these two control the period of connection verifies * (echo requests) - CA_CONN_VERIFY_PERIOD - and how * long we will wait for an echo reply before we * give up and flag the connection for disconnect * - CA_ECHO_TIMEOUT. * * CA_CONN_VERIFY_PERIOD is normally obtained from an * EPICS environment variable. */ static const double CA_ECHO_TIMEOUT = 5.0; /* (sec) disconn no echo reply tmo */ static const double CA_CONN_VERIFY_PERIOD = 30.0; /* (sec) how often to request echo */ /* * this determines the number of messages received * without a delay in between before we go into * monitor flow control * * turning this down effects maximum throughput * because we dont get an optimal number of bytes * per network frame */ static const unsigned contiguousMsgCountWhichTriggersFlowControl = 10u; /* * CA internal functions */ #define genLocalExcep( CBGUARD, GUARD, CAC, STAT, PCTX ) \ (CAC).exception ( CBGUARD, GUARD, STAT, PCTX, __FILE__, __LINE__ ) #endif // ifdef INCiocinfh base-7.0.3.1/modules/ca/src/client/localHostName.cpp0000664000577000060420000000353213557101274020753 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #include "osiSock.h" #include "localHostName.h" epicsSingleton < localHostName > localHostNameCache; localHostName::localHostName () : attachedToSockLib ( osiSockAttach () != 0 ), length ( 0u ) { const char * pErrStr = ""; int status = -1; if ( this->attachedToSockLib ) { status = gethostname ( this->cache, sizeof ( this->cache ) ); } if ( status ) { strncpy ( this->cache, pErrStr, sizeof ( this->cache ) ); } this->cache [ sizeof ( this->cache ) - 1u ] = '\0'; this->length = strlen ( this->cache ); } localHostName::~localHostName () { if ( this->attachedToSockLib ) { osiSockRelease (); } } unsigned localHostName::getName ( char * pBuf, unsigned bufLength ) const { if ( bufLength ) { strncpy ( pBuf, this->cache, bufLength ); if ( this->length < bufLength ) { return this->length; } else { unsigned reducedSize = bufLength - 1; pBuf [ reducedSize ] = '\0'; return reducedSize; } } return 0u; } base-7.0.3.1/modules/ca/src/client/localHostName.h0000664000577000060420000000316613557101274020423 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #ifndef localHostNameh #define localHostNameh #include #ifdef epicsExportSharedSymbols # define localHostNameh_restore_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "epicsSingleton.h" #ifdef localHostNameh_restore_epicsExportSharedSymbols # define epicsExportSharedSymbols #endif class localHostName { public: localHostName (); ~localHostName (); const char * pointer () const; unsigned getName ( char * pBuf, unsigned bufLength ) const; unsigned nameLength () const; private: bool attachedToSockLib; unsigned length; char cache [128]; }; extern epicsSingleton < localHostName > localHostNameCache; inline unsigned localHostName::nameLength () const { return this->length; } inline const char * localHostName::pointer () const { return this->cache; } #endif // ifndef localHostNameh base-7.0.3.1/modules/ca/src/client/msgForMultiplyDefinedPV.cpp0000664000577000060420000000603613557101274022746 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "errlog.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "msgForMultiplyDefinedPV.h" #include "cac.h" #include "caerr.h" // for ECA_DBLCHNL msgForMultiplyDefinedPV::msgForMultiplyDefinedPV ( ipAddrToAsciiEngine & engine, callbackForMultiplyDefinedPV & cbIn, const char * pChannelName, const char * pAcc ) : dnsTransaction ( engine.createTransaction () ), cb ( cbIn ) { strncpy ( this->acc, pAcc, sizeof ( this->acc ) ); this->acc[ sizeof ( this->acc ) - 1 ] = '\0'; strncpy ( this->channel, pChannelName, sizeof ( this->channel ) ); this->channel[ sizeof ( this->channel ) - 1 ] = '\0'; } msgForMultiplyDefinedPV::~msgForMultiplyDefinedPV () { this->dnsTransaction.release (); } void msgForMultiplyDefinedPV::transactionComplete ( const char * pHostNameRej ) { // calls into cac for the notification // the msg object (= this) is being deleted as part of the notification this->cb.pvMultiplyDefinedNotify ( *this, this->channel, this->acc, pHostNameRej ); // !! dont touch 'this' pointer after this point because object has been deleted !! } void * msgForMultiplyDefinedPV::operator new ( size_t size, tsFreeList < class msgForMultiplyDefinedPV, 16 > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE void msgForMultiplyDefinedPV::operator delete ( void *pCadaver, tsFreeList < class msgForMultiplyDefinedPV, 16 > & freeList ) { freeList.release ( pCadaver, sizeof ( msgForMultiplyDefinedPV ) ); } #endif void msgForMultiplyDefinedPV::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } callbackForMultiplyDefinedPV::~callbackForMultiplyDefinedPV () { } base-7.0.3.1/modules/ca/src/client/msgForMultiplyDefinedPV.h0000664000577000060420000000526613557101274022417 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef msgForMultiplyDefinedPVh #define msgForMultiplyDefinedPVh #ifdef epicsExportSharedSymbols # define msgForMultiplyDefinedPVh_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "ipAddrToAsciiAsynchronous.h" #include "tsFreeList.h" #include "tsDLList.h" #include "compilerDependencies.h" #ifdef msgForMultiplyDefinedPVh_epicsExportSharedSymbols # define epicsExportSharedSymbols #endif class callbackForMultiplyDefinedPV { public: virtual ~callbackForMultiplyDefinedPV () = 0; virtual void pvMultiplyDefinedNotify ( class msgForMultiplyDefinedPV &, const char * pChannelName, const char * pAcc, const char * pRej ) = 0; }; class msgForMultiplyDefinedPV : public ipAddrToAsciiCallBack, public tsDLNode < msgForMultiplyDefinedPV > { public: msgForMultiplyDefinedPV ( ipAddrToAsciiEngine & engine, callbackForMultiplyDefinedPV &, const char * pChannelName, const char * pAcc ); virtual ~msgForMultiplyDefinedPV (); void ioInitiate ( const osiSockAddr & rej ); void * operator new ( size_t size, tsFreeList < class msgForMultiplyDefinedPV, 16 > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < class msgForMultiplyDefinedPV, 16 > & )) private: char acc[64]; char channel[64]; ipAddrToAsciiTransaction & dnsTransaction; callbackForMultiplyDefinedPV & cb; void transactionComplete ( const char * pHostName ); msgForMultiplyDefinedPV ( const msgForMultiplyDefinedPV & ); msgForMultiplyDefinedPV & operator = ( const msgForMultiplyDefinedPV & ); void operator delete ( void * ); }; inline void msgForMultiplyDefinedPV::ioInitiate ( const osiSockAddr & rej ) { this->dnsTransaction.ipAddrToAscii ( rej, *this ); } #endif // ifdef msgForMultiplyDefinedPVh base-7.0.3.1/modules/ca/src/client/nciu.cpp0000664000577000060420000004446213557101274017167 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill * */ #include #include #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "epicsAlgorithm.h" #include "errlog.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "cac.h" #include "osiWireFormat.h" #include "udpiiu.h" #include "virtualCircuit.h" #include "cadef.h" #include "db_access.h" // for INVALID_DB_REQ #include "noopiiu.h" nciu::nciu ( cac & cacIn, netiiu & iiuIn, cacChannelNotify & chanIn, const char *pNameIn, cacChannel::priLev pri ) : cacChannel ( chanIn ), cacCtx ( cacIn ), piiu ( & iiuIn ), sid ( UINT_MAX ), count ( 0 ), retry ( 0u ), nameLength ( 0u ), typeCode ( USHRT_MAX ), priority ( static_cast ( pri ) ) { size_t nameLengthTmp = strlen ( pNameIn ) + 1; // second constraint is imposed by size field in protocol header if ( nameLengthTmp > MAX_UDP_SEND - sizeof ( caHdr ) || nameLengthTmp > USHRT_MAX ) { throw cacChannel::badString (); } if ( pri > 0xff ) { throw cacChannel::badPriority (); } this->nameLength = static_cast ( nameLengthTmp ); this->pNameStr = new char [ this->nameLength ]; strcpy ( this->pNameStr, pNameIn ); } nciu::~nciu () { delete [] this->pNameStr; } // channels are created by the user, and only destroyed by the user // using this routine void nciu::destroy ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & mutualExcusionGuard ) { while ( baseNMIU * pNetIO = this->eventq.first () ) { bool success = this->cacCtx.destroyIO ( callbackGuard, mutualExcusionGuard, pNetIO->getId (), *this ); assert ( success ); } // if the claim reply has not returned yet then we will issue // the clear channel request to the server when the claim reply // arrives and there is no matching nciu in the client if ( this->channelNode::isInstalledInServer ( mutualExcusionGuard ) ) { this->getPIIU(mutualExcusionGuard)->clearChannelRequest ( mutualExcusionGuard, this->sid, this->id ); } this->piiu->uninstallChan ( mutualExcusionGuard, *this ); this->cacCtx.destroyChannel ( mutualExcusionGuard, *this ); } void nciu::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } void nciu::initiateConnect ( epicsGuard < epicsMutex > & guard ) { this->cacCtx.initiateConnect ( guard, *this, this->piiu ); } void nciu::connect ( unsigned nativeType, unsigned nativeCount, unsigned sidIn, epicsGuard < epicsMutex > & /* cbGuard */, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); if ( ! dbf_type_is_valid ( nativeType ) ) { throw std::logic_error ( "Ignored conn resp with bad native data type" ); } this->typeCode = static_cast < unsigned short > ( nativeType ); this->count = nativeCount; this->sid = sidIn; /* * if less than v4.1 then the server will never * send access rights and there will always be access */ bool v41Ok = this->piiu->ca_v41_ok ( guard ); if ( ! v41Ok ) { this->accessRightState.setReadPermit(); this->accessRightState.setWritePermit(); } /* * if less than v4.1 then the server will never * send access rights and we know that there * will always be access and also need to call * their call back here */ if ( ! v41Ok ) { this->notify().accessRightsNotify ( guard, this->accessRightState ); } // channel uninstal routine grabs the callback lock so // a channel will not be deleted while a call back is // in progress // // the callback lock is also taken when a channel // disconnects to prevent a race condition with the // code below - ie we hold the callback lock here // so a chanel cant be destroyed out from under us. this->notify().connectNotify ( guard ); } void nciu::unresponsiveCircuitNotify ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ) { ioid tmpId = this->getId (); cac & caRefTmp = this->cacCtx; guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); this->cacCtx.disconnectAllIO ( cbGuard, guard, *this, this->eventq ); this->notify().disconnectNotify ( guard ); // if they destroy the channel in their disconnect // handler then we have to be very careful to not // touch this object if it has been destroyed nciu * pChan = caRefTmp.lookupChannel ( guard, tmpId ); if ( pChan ) { caAccessRights noRights; pChan->notify().accessRightsNotify ( guard, noRights ); // likewise, they might destroy the channel in their access rights // handler so we have to be very careful to not touch this // object from here on down } } void nciu::setServerAddressUnknown ( netiiu & newiiu, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); this->piiu = & newiiu; this->retry = 0; this->typeCode = USHRT_MAX; this->count = 0u; this->sid = UINT_MAX; this->accessRightState.clrReadPermit(); this->accessRightState.clrWritePermit(); } void nciu::accessRightsStateChange ( const caAccessRights & arIn, epicsGuard < epicsMutex > & /* cbGuard */, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); this->accessRightState = arIn; // // the channel delete routine takes the call back lock so // that this will not be called when the channel is being // deleted. // this->notify().accessRightsNotify ( guard, this->accessRightState ); } /* * nciu::searchMsg () */ bool nciu::searchMsg ( epicsGuard < epicsMutex > & guard ) { bool success = this->piiu->searchMsg ( guard, this->getId (), this->pNameStr, this->nameLength ); if ( success ) { if ( this->retry < UINT_MAX ) { this->retry++; } } return success; } const char *nciu::pName ( epicsGuard < epicsMutex > & guard ) const throw () { guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); return this->pNameStr; } unsigned nciu::getName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw () { if ( bufLen == 0u ) { return 0u; } if ( this->nameLength < bufLen ) { strcpy ( pBuf, this->pNameStr ); return this->nameLength; } else { unsigned reducedSize = bufLen - 1u; strncpy ( pBuf, this->pNameStr, bufLen ); pBuf[reducedSize] = '\0'; return reducedSize; } } unsigned nciu::nameLen ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); return this->nameLength; } unsigned nciu::requestMessageBytesPending ( epicsGuard < epicsMutex > & guard ) { return piiu->requestMessageBytesPending ( guard ); } void nciu::flush ( epicsGuard < epicsMutex > & guard ) { piiu->flush ( guard ); } cacChannel::ioStatus nciu::read ( epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount countIn, cacReadNotify ¬ify, ioid *pId ) { guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); if ( ! this->connected ( guard ) ) { throw cacChannel::notConnected (); } if ( ! this->accessRightState.readPermit () ) { throw cacChannel::noReadAccess (); } if ( countIn > this->count ) { throw cacChannel::outOfBounds (); } // // fail out if their arguments are invalid // if ( INVALID_DB_REQ ( type ) ) { throw cacChannel::badType (); } netReadNotifyIO & io = this->cacCtx.readNotifyRequest ( guard, *this, *this, type, countIn, notify ); if ( pId ) { *pId = io.getId (); } this->eventq.add ( io ); return cacChannel::iosAsynch; } void nciu::stringVerify ( const char *pStr, const unsigned count ) { for ( unsigned i = 0; i < count; i++ ) { unsigned int strsize = 0; while ( pStr[strsize++] != '\0' ) { if ( strsize >= MAX_STRING_SIZE ) { throw badString(); } } pStr += MAX_STRING_SIZE; } } void nciu::write ( epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount countIn, const void * pValue ) { guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); // make sure that they get this and not "no write access" // if disconnected if ( ! this->connected ( guard ) ) { throw cacChannel::notConnected(); } if ( ! this->accessRightState.writePermit() ) { throw cacChannel::noWriteAccess(); } if ( countIn > this->count || countIn == 0 ) { throw cacChannel::outOfBounds(); } if ( type == DBR_STRING ) { nciu::stringVerify ( (char *) pValue, countIn ); } this->piiu->writeRequest ( guard, *this, type, countIn, pValue ); } cacChannel::ioStatus nciu::write ( epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount countIn, const void * pValue, cacWriteNotify & notify, ioid * pId ) { // make sure that they get this and not "no write access" // if disconnected if ( ! this->connected ( guard ) ) { throw cacChannel::notConnected(); } if ( ! this->accessRightState.writePermit() ) { throw cacChannel::noWriteAccess(); } if ( countIn > this->count || countIn == 0 ) { throw cacChannel::outOfBounds(); } if ( type == DBR_STRING ) { nciu::stringVerify ( (char *) pValue, countIn ); } netWriteNotifyIO & io = this->cacCtx.writeNotifyRequest ( guard, *this, *this, type, countIn, pValue, notify ); if ( pId ) { *pId = io.getId (); } this->eventq.add ( io ); return cacChannel::iosAsynch; } void nciu::subscribe ( epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount nElem, unsigned mask, cacStateNotify & notify, ioid *pId ) { netSubscription & io = this->cacCtx.subscriptionRequest ( guard, *this, *this, type, nElem, mask, notify, this->channelNode::isInstalledInServer ( guard ) ); this->eventq.add ( io ); if ( pId ) { *pId = io.getId (); } } void nciu::ioCancel ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & mutualExclusionGuard, const ioid & idIn ) { this->cacCtx.destroyIO ( callbackGuard, mutualExclusionGuard, idIn, *this ); } void nciu::ioShow ( epicsGuard < epicsMutex > & guard, const ioid &idIn, unsigned level ) const { this->cacCtx.ioShow ( guard, idIn, level ); } unsigned nciu::getHostName ( epicsGuard < epicsMutex > & guard, char *pBuf, unsigned bufLength ) const throw () { return this->piiu->getHostName ( guard, pBuf, bufLength ); } const char * nciu::pHostName ( epicsGuard < epicsMutex > & guard ) const throw () { return this->piiu->pHostName ( guard ); } bool nciu::ca_v42_ok ( epicsGuard < epicsMutex > & guard ) const { return this->piiu->ca_v42_ok ( guard ); } short nciu::nativeType ( epicsGuard < epicsMutex > & guard ) const { short type = TYPENOTCONN; if ( this->connected ( guard ) ) { if ( this->typeCode < SHRT_MAX ) { type = static_cast ( this->typeCode ); } } return type; } arrayElementCount nciu::nativeElementCount ( epicsGuard < epicsMutex > & guard ) const { arrayElementCount countOut = 0ul; if ( this->connected ( guard ) ) { countOut = this->count; } return countOut; } caAccessRights nciu::accessRights ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); return this->accessRightState; } unsigned nciu::searchAttempts ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); return this->retry; } double nciu::beaconPeriod ( epicsGuard < epicsMutex > & guard ) const { return this->cacCtx.beaconPeriod ( guard, *this ); } double nciu::receiveWatchdogDelay ( epicsGuard < epicsMutex > & guard ) const { return this->piiu->receiveWatchdogDelay ( guard ); } bool nciu::connected ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); return this->channelNode::isConnected ( guard ); } void nciu::show ( unsigned level ) const { epicsGuard < epicsMutex > guard ( this->cacCtx.mutexRef() ); this->show ( guard, level ); } void nciu::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { if ( this->connected ( guard ) ) { char hostNameTmp [256]; this->getHostName ( guard, hostNameTmp, sizeof ( hostNameTmp ) ); ::printf ( "Channel \"%s\", connected to server %s", this->pNameStr, hostNameTmp ); if ( level > 1u ) { int tmpTypeCode = static_cast < int > ( this->typeCode ); ::printf ( ", native type %s, native element count %u", dbf_type_to_text ( tmpTypeCode ), this->count ); ::printf ( ", %sread access, %swrite access", this->accessRightState.readPermit() ? "" : "no ", this->accessRightState.writePermit() ? "" : "no "); } ::printf ( "\n" ); } else { ::printf ( "Channel \"%s\" is disconnected\n", this->pNameStr ); } if ( level > 2u ) { ::printf ( "\tnetwork IO pointer = %p\n", static_cast ( this->piiu ) ); ::printf ( "\tserver identifier %u\n", this->sid ); ::printf ( "\tsearch retry number=%u\n", this->retry ); ::printf ( "\tname length=%u\n", this->nameLength ); } } void nciu::ioCompletionNotify ( epicsGuard < epicsMutex > &, class baseNMIU & io ) { this->eventq.remove ( io ); } void nciu::resubscribe ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); tsDLIter < baseNMIU > pNetIO = this->eventq.firstIter (); while ( pNetIO.valid () ) { tsDLIter < baseNMIU > next = pNetIO; next++; class netSubscription * pSubscr = pNetIO->isSubscription (); // Its normal for other types of IO to exist after the channel connects, // but before all of the resubscription requests go out. We must ignore // them here. if ( pSubscr ) { try { pSubscr->subscribeIfRequired ( guard, *this ); } catch ( ... ) { errlogPrintf ( "CAC: failed to send subscription request " "during channel connect\n" ); } } pNetIO = next; } } void nciu::sendSubscriptionUpdateRequests ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); tsDLIter < baseNMIU > pNetIO = this->eventq.firstIter (); while ( pNetIO.valid () ) { tsDLIter < baseNMIU > next = pNetIO; next++; try { pNetIO->forceSubscriptionUpdate ( guard, *this ); } catch ( ... ) { errlogPrintf ( "CAC: failed to send subscription update request " "during channel connect\n" ); } pNetIO = next; } } void nciu::disconnectAllIO ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ) { this->cacCtx.disconnectAllIO ( cbGuard, guard, *this, this->eventq ); } void nciu::serviceShutdownNotify ( epicsGuard < epicsMutex > & callbackControlGuard, epicsGuard < epicsMutex > & mutualExclusionGuard ) { this->setServerAddressUnknown ( noopIIU, mutualExclusionGuard ); this->notify().serviceShutdownNotify ( mutualExclusionGuard ); } void channelNode::setRespPendingState ( epicsGuard < epicsMutex > &, unsigned index ) { this->listMember = static_cast < channelNode::channelState > ( channelNode::cs_searchRespPending0 + index ); if ( this->listMember > cs_searchRespPending17 ) { throw std::runtime_error ( "resp search timer index out of bounds" ); } } void channelNode::setReqPendingState ( epicsGuard < epicsMutex > &, unsigned index ) { this->listMember = static_cast < channelNode::channelState > ( channelNode::cs_searchReqPending0 + index ); if ( this->listMember > cs_searchReqPending17 ) { throw std::runtime_error ( "req search timer index out of bounds" ); } } unsigned channelNode::getMaxSearchTimerCount () { return epicsMin ( cs_searchReqPending17 - cs_searchReqPending0, cs_searchRespPending17 - cs_searchRespPending0 ) + 1u; } unsigned channelNode::getSearchTimerIndex ( epicsGuard < epicsMutex > & ) { channelNode::channelState chanState = this->listMember; unsigned index = 0u; if ( chanState >= cs_searchReqPending0 && chanState <= cs_searchReqPending17 ) { index = chanState - cs_searchReqPending0; } else if ( chanState >= cs_searchRespPending0 && chanState <= cs_searchRespPending17 ) { index = chanState - cs_searchRespPending0; } else { throw std::runtime_error ( "channel was expected to be in a search timer, but wasnt" );; } return index; } base-7.0.3.1/modules/ca/src/client/nciu.h0000664000577000060420000003013113557101274016620 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef nciuh #define nciuh #ifdef epicsExportSharedSymbols # define nciuh_restore_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "resourceLib.h" #include "tsDLList.h" #include "tsFreeList.h" #include "epicsMutex.h" #include "compilerDependencies.h" #ifdef nciuh_restore_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif #define CA_MINOR_PROTOCOL_REVISION 13 #include "caProto.h" #include "cacIO.h" class cac; class netiiu; // The node and the state which tracks the list membership // are in the channel, but belong to the circuit. // Protected by the callback mutex class channelNode : public tsDLNode < class nciu > { protected: channelNode (); bool isInstalledInServer ( epicsGuard < epicsMutex > & ) const; bool isConnected ( epicsGuard < epicsMutex > & ) const; public: static unsigned getMaxSearchTimerCount (); private: enum channelState { cs_none, cs_disconnGov, // note: indexing is used here // so these must be contiguous cs_searchReqPending0, cs_searchReqPending1, cs_searchReqPending2, cs_searchReqPending3, cs_searchReqPending4, cs_searchReqPending5, cs_searchReqPending6, cs_searchReqPending7, cs_searchReqPending8, cs_searchReqPending9, cs_searchReqPending10, cs_searchReqPending11, cs_searchReqPending12, cs_searchReqPending13, cs_searchReqPending14, cs_searchReqPending15, cs_searchReqPending16, cs_searchReqPending17, // note: indexing is used here // so these must be contiguous cs_searchRespPending0, cs_searchRespPending1, cs_searchRespPending2, cs_searchRespPending3, cs_searchRespPending4, cs_searchRespPending5, cs_searchRespPending6, cs_searchRespPending7, cs_searchRespPending8, cs_searchRespPending9, cs_searchRespPending10, cs_searchRespPending11, cs_searchRespPending12, cs_searchRespPending13, cs_searchRespPending14, cs_searchRespPending15, cs_searchRespPending16, cs_searchRespPending17, cs_createReqPend, cs_createRespPend, cs_v42ConnCallbackPend, cs_subscripReqPend, cs_connected, cs_unrespCircuit, cs_subscripUpdateReqPend } listMember; void setRespPendingState ( epicsGuard < epicsMutex > &, unsigned index ); void setReqPendingState ( epicsGuard < epicsMutex > &, unsigned index ); unsigned getSearchTimerIndex ( epicsGuard < epicsMutex > & ); friend class tcpiiu; friend class udpiiu; friend class tcpSendThread; friend class searchTimer; friend class disconnectGovernorTimer; }; class privateInterfaceForIO { public: virtual void ioCompletionNotify ( epicsGuard < epicsMutex > &, class baseNMIU & ) = 0; virtual arrayElementCount nativeElementCount ( epicsGuard < epicsMutex > & ) const = 0; virtual bool connected ( epicsGuard < epicsMutex > & ) const = 0; protected: virtual ~privateInterfaceForIO() {} }; class nciu : public cacChannel, public chronIntIdRes < nciu >, public channelNode, private privateInterfaceForIO { public: nciu ( cac &, netiiu &, cacChannelNotify &, const char * pNameIn, cacChannel::priLev ); ~nciu (); void connect ( unsigned nativeType, unsigned nativeCount, unsigned sid, epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); void connect ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); void unresponsiveCircuitNotify ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); void circuitHangupNotify ( class udpiiu &, epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); void setServerAddressUnknown ( netiiu & newiiu, epicsGuard < epicsMutex > & guard ); bool searchMsg ( epicsGuard < epicsMutex > & ); void serviceShutdownNotify ( epicsGuard < epicsMutex > & callbackControlGuard, epicsGuard < epicsMutex > & mutualExclusionGuard ); void accessRightsStateChange ( const caAccessRights &, epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); ca_uint32_t getSID ( epicsGuard < epicsMutex > & ) const; ca_uint32_t getCID ( epicsGuard < epicsMutex > & ) const; netiiu * getPIIU ( epicsGuard < epicsMutex > & ); const netiiu * getConstPIIU ( epicsGuard < epicsMutex > & ) const; cac & getClient (); void searchReplySetUp ( netiiu &iiu, unsigned sidIn, ca_uint16_t typeIn, arrayElementCount countIn, epicsGuard < epicsMutex > & ); void show ( unsigned level ) const; void show ( epicsGuard < epicsMutex > &, unsigned level ) const; unsigned getName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw (); const char * pName ( epicsGuard < epicsMutex > & ) const throw (); unsigned nameLen ( epicsGuard < epicsMutex > & ) const; unsigned getHostName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw (); void writeException ( epicsGuard < epicsMutex > &, epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count ); cacChannel::priLev getPriority ( epicsGuard < epicsMutex > & ) const; void * operator new ( size_t size, tsFreeList < class nciu, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator ( ( void *, tsFreeList < class nciu, 1024, epicsMutexNOOP > & )) //arrayElementCount nativeElementCount ( epicsGuard < epicsMutex > & ) const; void resubscribe ( epicsGuard < epicsMutex > & ); void sendSubscriptionUpdateRequests ( epicsGuard < epicsMutex > & ); void disconnectAllIO ( epicsGuard < epicsMutex > &, epicsGuard < epicsMutex > & ); bool connected ( epicsGuard < epicsMutex > & ) const; unsigned getcount() const { return count; } private: tsDLList < class baseNMIU > eventq; caAccessRights accessRightState; cac & cacCtx; char * pNameStr; netiiu * piiu; ca_uint32_t sid; // server id unsigned count; unsigned retry; // search retry number unsigned short nameLength; // channel name length ca_uint16_t typeCode; ca_uint8_t priority; virtual void destroy ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & mutualExclusionGuard ); void initiateConnect ( epicsGuard < epicsMutex > & ); unsigned requestMessageBytesPending ( epicsGuard < epicsMutex > & mutualExclusionGuard ); void flush ( epicsGuard < epicsMutex > & mutualExclusionGuard ); ioStatus read ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, cacReadNotify &, ioid * ); void write ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void *pValue ); ioStatus write ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void *pValue, cacWriteNotify &, ioid * ); void subscribe ( epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount nElem, unsigned mask, cacStateNotify ¬ify, ioid * ); // The primary mutex must be released when calling the user's // callback, and therefore a finite interval exists when we are // moving forward with the intent to call the users callback // but the users IO could be deleted during this interval. // To prevent the user's callback from being called after // destroying his IO we must past a guard for the callback // mutex here. virtual void ioCancel ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & mutualExclusionGuard, const ioid & ); void ioShow ( epicsGuard < epicsMutex > &, const ioid &, unsigned level ) const; short nativeType ( epicsGuard < epicsMutex > & ) const; caAccessRights accessRights ( epicsGuard < epicsMutex > & ) const; unsigned searchAttempts ( epicsGuard < epicsMutex > & ) const; double beaconPeriod ( epicsGuard < epicsMutex > & ) const; double receiveWatchdogDelay ( epicsGuard < epicsMutex > & ) const; bool ca_v42_ok ( epicsGuard < epicsMutex > & ) const; arrayElementCount nativeElementCount ( epicsGuard < epicsMutex > & ) const; static void stringVerify ( const char *pStr, const unsigned count ); void ioCompletionNotify ( epicsGuard < epicsMutex > &, class baseNMIU & ); const char * pHostName ( epicsGuard < epicsMutex > & guard ) const throw (); nciu ( const nciu & ); nciu & operator = ( const nciu & ); void operator delete ( void * ); }; inline void * nciu::operator new ( size_t size, tsFreeList < class nciu, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void nciu::operator delete ( void * pCadaver, tsFreeList < class nciu, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver, sizeof ( nciu ) ); } #endif inline ca_uint32_t nciu::getSID ( epicsGuard < epicsMutex > & ) const { return this->sid; } inline ca_uint32_t nciu::getCID ( epicsGuard < epicsMutex > & ) const { return this->id; } // this is to only be used by early protocol revisions inline void nciu::connect ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ) { this->connect ( this->typeCode, this->count, this->sid, cbGuard, guard ); } inline void nciu::searchReplySetUp ( netiiu &iiu, unsigned sidIn, ca_uint16_t typeIn, arrayElementCount countIn, epicsGuard < epicsMutex > & ) { this->piiu = & iiu; this->typeCode = typeIn; this->count = countIn; this->sid = sidIn; } inline netiiu * nciu::getPIIU ( epicsGuard < epicsMutex > & ) { return this->piiu; } inline void nciu::writeException ( epicsGuard < epicsMutex > & /* cbGuard */, epicsGuard < epicsMutex > & guard, int status, const char * pContext, unsigned typeIn, arrayElementCount countIn ) { this->notify().writeException ( guard, status, pContext, typeIn, countIn ); } inline const netiiu * nciu::getConstPIIU ( epicsGuard < epicsMutex > & ) const { return this->piiu; } inline cac & nciu::getClient () { return this->cacCtx; } inline cacChannel::priLev nciu::getPriority ( epicsGuard < epicsMutex > & ) const { return this->priority; } inline channelNode::channelNode () : listMember ( cs_none ) { } inline bool channelNode::isConnected ( epicsGuard < epicsMutex > & ) const { return this->listMember == cs_connected || this->listMember == cs_subscripReqPend || this->listMember == cs_subscripUpdateReqPend; } inline bool channelNode::isInstalledInServer ( epicsGuard < epicsMutex > & ) const { return this->listMember == cs_connected || this->listMember == cs_subscripReqPend || this->listMember == cs_unrespCircuit || this->listMember == cs_subscripUpdateReqPend; } #endif // ifdef nciuh base-7.0.3.1/modules/ca/src/client/netIO.h0000664000577000060420000002555113557101274016712 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef netIOh #define netIOh #include "nciu.h" #include "compilerDependencies.h" // SUN PRO generates multiply defined symbols if the baseNMIU // destructor is virtual (therefore it is protected). // I assume that SUNPRO will fix this in future versions. // With other compilers we get warnings (and // potential problems) if we dont make the baseNMIU // destructor virtual. #if defined ( __SUNPRO_CC ) && ( __SUNPRO_CC <= 0x540 ) # define NETIO_VIRTUAL_DESTRUCTOR #else # define NETIO_VIRTUAL_DESTRUCTOR virtual #endif class privateInterfaceForIO; class baseNMIU : public tsDLNode < baseNMIU >, public chronIntIdRes < baseNMIU > { public: virtual void destroy ( epicsGuard < epicsMutex > &, class cacRecycle & ) = 0; // only called by cac virtual void completion ( epicsGuard < epicsMutex > &, cacRecycle & ) = 0; virtual void exception ( epicsGuard < epicsMutex > &, cacRecycle &, int status, const char * pContext ) = 0; virtual void exception ( epicsGuard < epicsMutex > &, cacRecycle &, int status, const char * pContext, unsigned type, arrayElementCount count ) = 0; virtual void completion ( epicsGuard < epicsMutex > &, cacRecycle &, unsigned type, arrayElementCount count, const void * pData ) = 0; virtual void forceSubscriptionUpdate ( epicsGuard < epicsMutex > & guard, nciu & chan ) = 0; virtual class netSubscription * isSubscription () = 0; virtual void show ( unsigned level ) const = 0; virtual void show ( epicsGuard < epicsMutex > &, unsigned level ) const = 0; protected: NETIO_VIRTUAL_DESTRUCTOR ~baseNMIU (); }; class netSubscription : public baseNMIU { public: static netSubscription * factory ( tsFreeList < class netSubscription, 1024, epicsMutexNOOP > &, class privateInterfaceForIO &, unsigned type, arrayElementCount count, unsigned mask, cacStateNotify & ); void show ( unsigned level ) const; void show ( epicsGuard < epicsMutex > &, unsigned level ) const; arrayElementCount getCount ( epicsGuard < epicsMutex > &, bool allow_zero ) const; unsigned getType ( epicsGuard < epicsMutex > & ) const; unsigned getMask ( epicsGuard < epicsMutex > & ) const; void subscribeIfRequired ( epicsGuard < epicsMutex > & guard, nciu & chan ); void unsubscribeIfRequired ( epicsGuard < epicsMutex > & guard, nciu & chan ); protected: netSubscription ( class privateInterfaceForIO &, unsigned type, arrayElementCount count, unsigned mask, cacStateNotify & ); ~netSubscription (); private: const arrayElementCount count; class privateInterfaceForIO & privateChanForIO; cacStateNotify & notify; const unsigned type; const unsigned mask; bool subscribed; class netSubscription * isSubscription (); void operator delete ( void * ); void * operator new ( size_t, tsFreeList < class netSubscription, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < class netSubscription, 1024, epicsMutexNOOP > & )) void destroy ( epicsGuard < epicsMutex > &, class cacRecycle & ); void completion ( epicsGuard < epicsMutex > &, cacRecycle & ); void exception ( epicsGuard < epicsMutex > &, cacRecycle &, int status, const char * pContext ); void completion ( epicsGuard < epicsMutex > &, cacRecycle &, unsigned type, arrayElementCount count, const void * pData ); void exception ( epicsGuard < epicsMutex > &, cacRecycle &, int status, const char * pContext, unsigned type, arrayElementCount count ); void forceSubscriptionUpdate ( epicsGuard < epicsMutex > & guard, nciu & chan ); netSubscription ( const netSubscription & ); netSubscription & operator = ( const netSubscription & ); }; class netReadNotifyIO : public baseNMIU { public: static netReadNotifyIO * factory ( tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > &, privateInterfaceForIO &, cacReadNotify & ); void show ( unsigned level ) const; void show ( epicsGuard < epicsMutex > &, unsigned level ) const; protected: netReadNotifyIO ( privateInterfaceForIO &, cacReadNotify & ); ~netReadNotifyIO (); private: cacReadNotify & notify; class privateInterfaceForIO & privateChanForIO; void operator delete ( void * ); void * operator new ( size_t, tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & )) void destroy ( epicsGuard < epicsMutex > &, class cacRecycle & ); void completion ( epicsGuard < epicsMutex > &, cacRecycle & ); void exception ( epicsGuard < epicsMutex > &, cacRecycle &, int status, const char * pContext ); void completion ( epicsGuard < epicsMutex > &, cacRecycle &, unsigned type, arrayElementCount count, const void * pData ); void exception ( epicsGuard < epicsMutex > &, cacRecycle &, int status, const char * pContext, unsigned type, arrayElementCount count ); class netSubscription * isSubscription (); void forceSubscriptionUpdate ( epicsGuard < epicsMutex > & guard, nciu & chan ); netReadNotifyIO ( const netReadNotifyIO & ); netReadNotifyIO & operator = ( const netReadNotifyIO & ); }; class netWriteNotifyIO : public baseNMIU { public: static netWriteNotifyIO * factory ( tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > &, privateInterfaceForIO &, cacWriteNotify & ); void show ( unsigned level ) const; void show ( epicsGuard < epicsMutex > &, unsigned level ) const; protected: netWriteNotifyIO ( privateInterfaceForIO &, cacWriteNotify & ); ~netWriteNotifyIO (); private: cacWriteNotify & notify; privateInterfaceForIO & privateChanForIO; void operator delete ( void * ); void * operator new ( size_t, tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > & )) class netSubscription * isSubscription (); void destroy ( epicsGuard < epicsMutex > &, class cacRecycle & ); void completion ( epicsGuard < epicsMutex > &, cacRecycle & ); void exception ( epicsGuard < epicsMutex > &, cacRecycle &, int status, const char * pContext ); void completion ( epicsGuard < epicsMutex > &, cacRecycle &, unsigned type, arrayElementCount count, const void * pData ); void exception ( epicsGuard < epicsMutex > &, cacRecycle &, int status, const char * pContext, unsigned type, arrayElementCount count ); void forceSubscriptionUpdate ( epicsGuard < epicsMutex > & guard, nciu & chan ); netWriteNotifyIO ( const netWriteNotifyIO & ); netWriteNotifyIO & operator = ( const netWriteNotifyIO & ); }; inline void * netSubscription::operator new ( size_t size, tsFreeList < class netSubscription, 1024, epicsMutexNOOP > &freeList ) { return freeList.allocate ( size ); } #if defined ( CXX_PLACEMENT_DELETE ) inline void netSubscription::operator delete ( void *pCadaver, tsFreeList < class netSubscription, 1024, epicsMutexNOOP > &freeList ) { freeList.release ( pCadaver ); } #endif inline netSubscription * netSubscription::factory ( tsFreeList < class netSubscription, 1024, epicsMutexNOOP > & freeList, class privateInterfaceForIO & chan, unsigned type, arrayElementCount count, unsigned mask, cacStateNotify ¬ify ) { return new ( freeList ) netSubscription ( chan, type, count, mask, notify ); } inline arrayElementCount netSubscription::getCount ( epicsGuard < epicsMutex > & guard, bool allow_zero ) const { //guard.assertIdenticalMutex ( this->mutex ); arrayElementCount nativeCount = this->privateChanForIO.nativeElementCount ( guard ); if ( (this->count == 0u && !allow_zero) || this->count > nativeCount ) { return nativeCount; } else { return this->count; } } inline unsigned netSubscription::getType ( epicsGuard < epicsMutex > & ) const { return this->type; } inline unsigned netSubscription::getMask ( epicsGuard < epicsMutex > & ) const { return this->mask; } inline netReadNotifyIO * netReadNotifyIO::factory ( tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & freeList, privateInterfaceForIO & ioComplNotifIntf, cacReadNotify & notify ) { return new ( freeList ) netReadNotifyIO ( ioComplNotifIntf, notify ); } inline void * netReadNotifyIO::operator new ( size_t size, tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #if defined ( CXX_PLACEMENT_DELETE ) inline void netReadNotifyIO::operator delete ( void *pCadaver, tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif inline netWriteNotifyIO * netWriteNotifyIO::factory ( tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > & freeList, privateInterfaceForIO & ioComplNotifyIntf, cacWriteNotify & notify ) { return new ( freeList ) netWriteNotifyIO ( ioComplNotifyIntf, notify ); } inline void * netWriteNotifyIO::operator new ( size_t size, tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #if defined ( CXX_PLACEMENT_DELETE ) inline void netWriteNotifyIO::operator delete ( void *pCadaver, tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif #endif // ifdef netIOh base-7.0.3.1/modules/ca/src/client/netReadNotifyIO.cpp0000664000577000060420000000753413557101274021233 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #include #include #include "errlog.h" #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "iocinf.h" #include "nciu.h" #include "cac.h" netReadNotifyIO::netReadNotifyIO ( privateInterfaceForIO & ioComplIntfIn, cacReadNotify & notify ) : notify ( notify ), privateChanForIO ( ioComplIntfIn ) { } netReadNotifyIO::~netReadNotifyIO () { } void netReadNotifyIO::show ( unsigned /* level */ ) const { ::printf ( "netReadNotifyIO at %p\n", static_cast < const void * > ( this ) ); } void netReadNotifyIO::show ( epicsGuard < epicsMutex > &, unsigned level ) const { this->show ( level ); } void netReadNotifyIO::destroy ( epicsGuard < epicsMutex > & guard, cacRecycle & recycle ) { this->~netReadNotifyIO (); recycle.recycleReadNotifyIO ( guard, *this ); } void netReadNotifyIO::completion ( epicsGuard < epicsMutex > & guard, cacRecycle & recycle, unsigned type, arrayElementCount count, const void * pData ) { //guard.assertIdenticalMutex ( this->mutex ); this->privateChanForIO.ioCompletionNotify ( guard, *this ); this->notify.completion ( guard, type, count, pData ); this->~netReadNotifyIO (); recycle.recycleReadNotifyIO ( guard, *this ); } void netReadNotifyIO::completion ( epicsGuard < epicsMutex > & guard, cacRecycle & recycle ) { //guard.assertIdenticalMutex ( this->mutex ); //this->chan.getClient().printf ( "Read response w/o data ?\n" ); this->privateChanForIO.ioCompletionNotify ( guard, *this ); this->~netReadNotifyIO (); recycle.recycleReadNotifyIO ( guard, *this ); } void netReadNotifyIO::exception ( epicsGuard < epicsMutex > & guard, cacRecycle & recycle, int status, const char *pContext ) { //guard.assertIdenticalMutex ( this->mutex ); this->privateChanForIO.ioCompletionNotify ( guard, *this ); this->notify.exception ( guard, status, pContext, UINT_MAX, 0u ); this->~netReadNotifyIO (); recycle.recycleReadNotifyIO ( guard, *this ); } void netReadNotifyIO::exception ( epicsGuard < epicsMutex > & guard, cacRecycle & recycle, int status, const char *pContext, unsigned type, arrayElementCount count ) { //guard.assertIdenticalMutex ( this->mutex ) this->privateChanForIO.ioCompletionNotify ( guard, *this ); this->notify.exception ( guard, status, pContext, type, count ); this->~netReadNotifyIO (); recycle.recycleReadNotifyIO ( guard, *this ); } class netSubscription * netReadNotifyIO::isSubscription () { return 0; } void netReadNotifyIO::forceSubscriptionUpdate ( epicsGuard < epicsMutex > &, nciu & ) { } void netReadNotifyIO::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } base-7.0.3.1/modules/ca/src/client/netSubscription.cpp0000664000577000060420000001256013557101274021416 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #include #include #include "errlog.h" #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #define epicsExportSharedSymbols #include "iocinf.h" #include "nciu.h" #include "cac.h" #include "db_access.h" // for dbf_type_to_text #include "caerr.h" netSubscription::netSubscription ( privateInterfaceForIO & chanIn, unsigned typeIn, arrayElementCount countIn, unsigned maskIn, cacStateNotify & notifyIn ) : count ( countIn ), privateChanForIO ( chanIn ), notify ( notifyIn ), type ( typeIn ), mask ( maskIn ), subscribed ( false ) { if ( ! dbr_type_is_valid ( typeIn ) ) { throw cacChannel::badType (); } if ( this->mask == 0u ) { throw cacChannel::badEventSelection (); } } netSubscription::~netSubscription () { } void netSubscription::destroy ( epicsGuard < epicsMutex > & guard, cacRecycle & recycle ) { this->~netSubscription (); recycle.recycleSubscription ( guard, *this ); } class netSubscription * netSubscription::isSubscription () { return this; } void netSubscription::show ( unsigned /* level */ ) const { ::printf ( "event subscription IO at %p, type %s, element count %lu, mask %u\n", static_cast < const void * > ( this ), dbf_type_to_text ( static_cast < int > ( this->type ) ), this->count, this->mask ); } void netSubscription::show ( epicsGuard < epicsMutex > &, unsigned level ) const { this->show ( level ); } void netSubscription::completion ( epicsGuard < epicsMutex > &, cacRecycle & ) { errlogPrintf ( "subscription update w/o data ?\n" ); } void netSubscription::exception ( epicsGuard < epicsMutex > & guard, cacRecycle & recycle, int status, const char * pContext ) { if ( status == ECA_DISCONN ) { this->subscribed = false; } if ( status == ECA_CHANDESTROY ) { this->privateChanForIO.ioCompletionNotify ( guard, *this ); this->notify.exception ( guard, status, pContext, UINT_MAX, 0 ); this->~netSubscription (); recycle.recycleSubscription ( guard, *this ); } else { // guard.assertIdenticalMutex ( this->mutex ); if ( this->privateChanForIO.connected ( guard ) ) { this->notify.exception ( guard, status, pContext, UINT_MAX, 0 ); } } } void netSubscription::exception ( epicsGuard < epicsMutex > & guard, cacRecycle & recycle, int status, const char * pContext, unsigned typeIn, arrayElementCount countIn ) { if ( status == ECA_DISCONN ) { this->subscribed = false; } if ( status == ECA_CHANDESTROY ) { this->privateChanForIO.ioCompletionNotify ( guard, *this ); this->notify.exception ( guard, status, pContext, UINT_MAX, 0 ); this->~netSubscription (); recycle.recycleSubscription ( guard, *this ); } else { //guard.assertIdenticalMutex ( this->mutex ); if ( this->privateChanForIO.connected ( guard ) ) { this->notify.exception ( guard, status, pContext, typeIn, countIn ); } } } void netSubscription::completion ( epicsGuard < epicsMutex > & guard, cacRecycle &, unsigned typeIn, arrayElementCount countIn, const void * pDataIn ) { // guard.assertIdenticalMutex ( this->mutex ); if ( this->privateChanForIO.connected ( guard ) ) { this->notify.current ( guard, typeIn, countIn, pDataIn ); } } void netSubscription::subscribeIfRequired ( epicsGuard < epicsMutex > & guard, nciu & chan ) { if ( ! this->subscribed ) { chan.getPIIU(guard)->subscriptionRequest ( guard, chan, *this ); this->subscribed = true; } } void netSubscription::unsubscribeIfRequired ( epicsGuard < epicsMutex > & guard, nciu & chan ) { if ( this->subscribed ) { chan.getPIIU(guard)->subscriptionCancelRequest ( guard, chan, *this ); this->subscribed = false; } } void netSubscription::forceSubscriptionUpdate ( epicsGuard < epicsMutex > & guard, nciu & chan ) { chan.getPIIU(guard)->subscriptionUpdateRequest ( guard, chan, *this ); } void netSubscription::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } base-7.0.3.1/modules/ca/src/client/netWriteNotifyIO.cpp0000664000577000060420000000726713557101274021455 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #include #include #include "errlog.h" #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "iocinf.h" #include "nciu.h" #include "cac.h" netWriteNotifyIO::netWriteNotifyIO ( privateInterfaceForIO & ioComplIntf, cacWriteNotify & notifyIn ) : notify ( notifyIn ), privateChanForIO ( ioComplIntf ) { } netWriteNotifyIO::~netWriteNotifyIO () { } void netWriteNotifyIO::show ( unsigned /* level */ ) const { ::printf ( "read write notify IO at %p\n", static_cast < const void * > ( this ) ); } void netWriteNotifyIO::show ( epicsGuard < epicsMutex > &, unsigned level ) const { this->show ( level ); } void netWriteNotifyIO::destroy ( epicsGuard < epicsMutex > & guard, cacRecycle & recycle ) { this->~netWriteNotifyIO (); recycle.recycleWriteNotifyIO ( guard, *this ); } void netWriteNotifyIO::completion ( epicsGuard < epicsMutex > & guard, cacRecycle & recycle ) { this->privateChanForIO.ioCompletionNotify ( guard, *this ); this->notify.completion ( guard ); this->~netWriteNotifyIO (); recycle.recycleWriteNotifyIO ( guard, *this ); } void netWriteNotifyIO::completion ( epicsGuard < epicsMutex > & guard, cacRecycle & recycle, unsigned /* type */, arrayElementCount /* count */, const void * /* pData */ ) { //this->chan.getClient().printf ( "Write response with data ?\n" ); this->privateChanForIO.ioCompletionNotify ( guard, *this ); this->~netWriteNotifyIO (); recycle.recycleWriteNotifyIO ( guard, *this ); } void netWriteNotifyIO::exception ( epicsGuard < epicsMutex > & guard, cacRecycle & recycle, int status, const char * pContext ) { this->privateChanForIO.ioCompletionNotify ( guard, *this ); this->notify.exception ( guard, status, pContext, UINT_MAX, 0u ); this->~netWriteNotifyIO (); recycle.recycleWriteNotifyIO ( guard, *this ); } void netWriteNotifyIO::exception ( epicsGuard < epicsMutex > & guard, cacRecycle & recycle, int status, const char *pContext, unsigned type, arrayElementCount count ) { this->privateChanForIO.ioCompletionNotify ( guard, *this ); this->notify.exception ( guard, status, pContext, type, count ); this->~netWriteNotifyIO (); recycle.recycleWriteNotifyIO ( guard, *this ); } class netSubscription * netWriteNotifyIO::isSubscription () { return 0; } void netWriteNotifyIO::forceSubscriptionUpdate ( epicsGuard < epicsMutex > &, nciu & ) { } void netWriteNotifyIO::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } base-7.0.3.1/modules/ca/src/client/net_convert.h0000664000577000060420000000164613557101274020221 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * Author: J. Hill * */ #ifndef _NET_CONVERT_H #define _NET_CONVERT_H #include "db_access.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif typedef unsigned long arrayElementCount; epicsShareFunc int caNetConvert ( unsigned type, const void *pSrc, void *pDest, int hton, arrayElementCount count ); #ifdef __cplusplus } #endif #endif /* define _NET_CONVERT_H */ base-7.0.3.1/modules/ca/src/client/netiiu.cpp0000664000577000060420000000745213557101274017524 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #include #include // vxWorks 6.0 requires this include #include #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "iocinf.h" #include "cac.h" #include "netiiu.h" netiiu::~netiiu () { } bool netiiu::ca_v42_ok ( epicsGuard < epicsMutex > & ) const { return false; } bool netiiu::ca_v41_ok ( epicsGuard < epicsMutex > & ) const { return false; } void netiiu::writeRequest ( epicsGuard < epicsMutex > &, nciu &, unsigned, arrayElementCount, const void * ) { throw cacChannel::notConnected(); } void netiiu::writeNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, netWriteNotifyIO &, unsigned, arrayElementCount, const void * ) { throw cacChannel::notConnected(); } void netiiu::readNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, netReadNotifyIO &, unsigned, arrayElementCount ) { throw cacChannel::notConnected(); } void netiiu::clearChannelRequest ( epicsGuard < epicsMutex > &, ca_uint32_t, ca_uint32_t ) { } void netiiu::subscriptionRequest ( epicsGuard < epicsMutex > &, nciu &, netSubscription & ) { } void netiiu::subscriptionCancelRequest ( epicsGuard < epicsMutex > &, nciu &, netSubscription & ) { } void netiiu::subscriptionUpdateRequest ( epicsGuard < epicsMutex > &, nciu &, netSubscription & ) { } static const char * const pHostNameNetIIU = ""; unsigned netiiu::getHostName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw () { if ( bufLen ) { unsigned len = strlen ( pHostNameNetIIU ); strncpy ( pBuf, pHostNameNetIIU, bufLen ); if ( len < bufLen ) { return len; } else { unsigned reducedSize = bufLen - 1u; pBuf[reducedSize] = '\0'; return reducedSize; } } return 0u; } const char * netiiu::pHostName ( epicsGuard < epicsMutex > & ) const throw () { return pHostNameNetIIU; } osiSockAddr netiiu::getNetworkAddress ( epicsGuard < epicsMutex > & ) const { osiSockAddr addr; addr.sa.sa_family = AF_UNSPEC; return addr; } void netiiu::flushRequest ( epicsGuard < epicsMutex > & ) { } unsigned netiiu::requestMessageBytesPending ( epicsGuard < epicsMutex > & ) { return 0u; } void netiiu::flush ( epicsGuard < epicsMutex > & ) { } void netiiu::requestRecvProcessPostponedFlush ( epicsGuard < epicsMutex > & ) { } void netiiu::uninstallChan ( epicsGuard < epicsMutex > &, nciu & ) { throw cacChannel::notConnected(); } double netiiu::receiveWatchdogDelay ( epicsGuard < epicsMutex > & ) const { return - DBL_MAX; } void netiiu::uninstallChanDueToSuccessfulSearchResponse ( epicsGuard < epicsMutex > &, nciu &, const epicsTime & ) { throw std::runtime_error ( "search response occured when not attached to udpiiu?" ); } bool netiiu::searchMsg ( epicsGuard < epicsMutex > &, ca_uint32_t /* id */, const char * /* pName */, unsigned /* nameLength */ ) { return false; } base-7.0.3.1/modules/ca/src/client/netiiu.h0000664000577000060420000000674213557101274017172 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef netiiuh #define netiiuh #include "cacIO.h" #include "caProto.h" class netWriteNotifyIO; class netReadNotifyIO; class netSubscription; union osiSockAddr; class cac; class nciu; class netiiu { public: virtual ~netiiu () = 0; virtual unsigned getHostName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLength ) const throw () = 0; virtual const char * pHostName ( epicsGuard < epicsMutex > & ) const throw () = 0; virtual bool ca_v41_ok ( epicsGuard < epicsMutex > & ) const = 0; virtual bool ca_v42_ok ( epicsGuard < epicsMutex > & ) const = 0; virtual unsigned requestMessageBytesPending ( epicsGuard < epicsMutex > & mutualExclusionGuard ) = 0; virtual void flush ( epicsGuard < epicsMutex > & mutualExclusionGuard ) = 0; virtual void writeRequest ( epicsGuard < epicsMutex > &, nciu &, unsigned type, arrayElementCount nElem, const void *pValue ) = 0; virtual void writeNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, netWriteNotifyIO &, unsigned type, arrayElementCount nElem, const void *pValue ) = 0; virtual void readNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, netReadNotifyIO &, unsigned type, arrayElementCount nElem ) = 0; virtual void clearChannelRequest ( epicsGuard < epicsMutex > &, ca_uint32_t sid, ca_uint32_t cid ) = 0; virtual void subscriptionRequest ( epicsGuard < epicsMutex > &, nciu &, netSubscription & ) = 0; virtual void subscriptionUpdateRequest ( epicsGuard < epicsMutex > &, nciu &, netSubscription & ) = 0; virtual void subscriptionCancelRequest ( epicsGuard < epicsMutex > &, nciu & chan, netSubscription & subscr ) = 0; virtual void flushRequest ( epicsGuard < epicsMutex > & ) = 0; virtual void requestRecvProcessPostponedFlush ( epicsGuard < epicsMutex > & ) = 0; virtual osiSockAddr getNetworkAddress ( epicsGuard < epicsMutex > & ) const = 0; virtual void uninstallChan ( epicsGuard < epicsMutex > &, nciu & ) = 0; virtual void uninstallChanDueToSuccessfulSearchResponse ( epicsGuard < epicsMutex > &, nciu &, const class epicsTime & currentTime ) = 0; virtual double receiveWatchdogDelay ( epicsGuard < epicsMutex > & ) const = 0; virtual bool searchMsg ( epicsGuard < epicsMutex > &, ca_uint32_t id, const char * pName, unsigned nameLength ) = 0; }; #endif // netiiuh base-7.0.3.1/modules/ca/src/client/noopiiu.cpp0000664000577000060420000001057113557101274017705 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include "osiSock.h" #define epicsExportSharedSymbols #include "noopiiu.h" noopiiu noopIIU; noopiiu::~noopiiu () { } unsigned noopiiu::getHostName ( epicsGuard < epicsMutex > & cacGuard, char * pBuf, unsigned bufLength ) const throw () { return netiiu::getHostName ( cacGuard, pBuf, bufLength ); } const char * noopiiu::pHostName ( epicsGuard < epicsMutex > & cacGuard ) const throw () { return netiiu::pHostName ( cacGuard ); } bool noopiiu::ca_v42_ok ( epicsGuard < epicsMutex > & cacGuard ) const { return netiiu::ca_v42_ok ( cacGuard ); } bool noopiiu::ca_v41_ok ( epicsGuard < epicsMutex > & cacGuard ) const { return netiiu::ca_v41_ok ( cacGuard ); } void noopiiu::writeRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, unsigned type, arrayElementCount nElem, const void * pValue ) { netiiu::writeRequest ( guard, chan, type, nElem, pValue ); } void noopiiu::writeNotifyRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netWriteNotifyIO & io, unsigned type, arrayElementCount nElem, const void *pValue ) { netiiu::writeNotifyRequest ( guard, chan, io, type, nElem, pValue ); } void noopiiu::readNotifyRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netReadNotifyIO & io, unsigned type, arrayElementCount nElem ) { netiiu::readNotifyRequest ( guard, chan, io, type, nElem ); } void noopiiu::clearChannelRequest ( epicsGuard < epicsMutex > & guard, ca_uint32_t sid, ca_uint32_t cid ) { netiiu::clearChannelRequest ( guard, sid, cid ); } void noopiiu::subscriptionRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netSubscription & subscr ) { netiiu::subscriptionRequest ( guard, chan, subscr ); } void noopiiu::subscriptionUpdateRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netSubscription & subscr ) { netiiu::subscriptionUpdateRequest ( guard, chan, subscr ); } void noopiiu::subscriptionCancelRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netSubscription & subscr ) { netiiu::subscriptionCancelRequest ( guard, chan, subscr ); } void noopiiu::flushRequest ( epicsGuard < epicsMutex > & guard ) { netiiu::flushRequest ( guard ); } unsigned noopiiu::requestMessageBytesPending ( epicsGuard < epicsMutex > & guard ) { return netiiu::requestMessageBytesPending ( guard ); } void noopiiu::flush ( epicsGuard < epicsMutex > & guard ) { netiiu::flush ( guard ); } void noopiiu::requestRecvProcessPostponedFlush ( epicsGuard < epicsMutex > & guard ) { netiiu::requestRecvProcessPostponedFlush ( guard ); } osiSockAddr noopiiu::getNetworkAddress ( epicsGuard < epicsMutex > & guard ) const { return netiiu::getNetworkAddress ( guard ); } double noopiiu::receiveWatchdogDelay ( epicsGuard < epicsMutex > & guard ) const { return netiiu::receiveWatchdogDelay ( guard ); } void noopiiu::uninstallChan ( epicsGuard < epicsMutex > &, nciu & ) { // intentionally does not call default in netiiu } void noopiiu::uninstallChanDueToSuccessfulSearchResponse ( epicsGuard < epicsMutex > & guard, nciu & chan, const class epicsTime & currentTime ) { netiiu::uninstallChanDueToSuccessfulSearchResponse ( guard, chan, currentTime ); } bool noopiiu::searchMsg ( epicsGuard < epicsMutex > & guard, ca_uint32_t id, const char * pName, unsigned nameLength ) { return netiiu::searchMsg ( guard, id, pName, nameLength ); } base-7.0.3.1/modules/ca/src/client/noopiiu.h0000664000577000060420000000624613557101274017356 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef noopiiuh #define noopiiuh #include "netiiu.h" class noopiiu : public netiiu { public: ~noopiiu (); unsigned getHostName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLength ) const throw (); const char * pHostName ( epicsGuard < epicsMutex > & ) const throw (); bool ca_v41_ok ( epicsGuard < epicsMutex > & ) const; bool ca_v42_ok ( epicsGuard < epicsMutex > & ) const; unsigned requestMessageBytesPending ( epicsGuard < epicsMutex > & mutualExclusionGuard ); void flush ( epicsGuard < epicsMutex > & mutualExclusionGuard ); void writeRequest ( epicsGuard < epicsMutex > &, nciu &, unsigned type, arrayElementCount nElem, const void *pValue ); void writeNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, netWriteNotifyIO &, unsigned type, arrayElementCount nElem, const void *pValue ); void readNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, netReadNotifyIO &, unsigned type, arrayElementCount nElem ); void clearChannelRequest ( epicsGuard < epicsMutex > &, ca_uint32_t sid, ca_uint32_t cid ); void subscriptionRequest ( epicsGuard < epicsMutex > &, nciu &, netSubscription & ); void subscriptionUpdateRequest ( epicsGuard < epicsMutex > &, nciu &, netSubscription & ); void subscriptionCancelRequest ( epicsGuard < epicsMutex > &, nciu & chan, netSubscription & subscr ); void flushRequest ( epicsGuard < epicsMutex > & ); void requestRecvProcessPostponedFlush ( epicsGuard < epicsMutex > & ); osiSockAddr getNetworkAddress ( epicsGuard < epicsMutex > & ) const; void uninstallChan ( epicsGuard < epicsMutex > & mutex, nciu & ); void uninstallChanDueToSuccessfulSearchResponse ( epicsGuard < epicsMutex > &, nciu &, const class epicsTime & currentTime ); double receiveWatchdogDelay ( epicsGuard < epicsMutex > & ) const; bool searchMsg ( epicsGuard < epicsMutex > &, ca_uint32_t id, const char * pName, unsigned nameLength ); }; extern noopiiu noopIIU; #endif // ifndef noopiiuh base-7.0.3.1/modules/ca/src/client/oldAccess.h0000664000577000060420000005342013557101274017570 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef oldAccessh #define oldAccessh #include #ifdef epicsExportSharedSymbols # define oldAccessh_restore_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "tsFreeList.h" #include "compilerDependencies.h" #include "osiSock.h" #ifdef oldAccessh_restore_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif #include "caProto.h" #include "cacIO.h" #include "cadef.h" #include "syncGroup.h" struct oldChannelNotify : private cacChannelNotify { public: oldChannelNotify ( epicsGuard < epicsMutex > &, struct ca_client_context &, const char * pName, caCh * pConnCallBackIn, void * pPrivateIn, capri priority ); void destructor ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & mutexGuard ); // legacy C API friend unsigned epicsShareAPI ca_get_host_name ( chid pChan, char * pBuf, unsigned bufLength ); friend const char * epicsShareAPI ca_host_name ( chid pChan ); friend const char * epicsShareAPI ca_name ( chid pChan ); friend void epicsShareAPI ca_set_puser ( chid pChan, void * puser ); friend void * epicsShareAPI ca_puser ( chid pChan ); friend int epicsShareAPI ca_change_connection_event ( chid pChan, caCh * pfunc ); friend int epicsShareAPI ca_replace_access_rights_event ( chid pChan, caArh *pfunc ); friend int epicsShareAPI ca_array_get ( chtype type, arrayElementCount count, chid pChan, void * pValue ); friend int epicsShareAPI ca_array_get_callback ( chtype type, arrayElementCount count, chid pChan, caEventCallBackFunc *pfunc, void *arg ); friend int epicsShareAPI ca_array_put ( chtype type, arrayElementCount count, chid pChan, const void * pValue ); friend int epicsShareAPI ca_array_put_callback ( chtype type, arrayElementCount count, chid pChan, const void *pValue, caEventCallBackFunc *pfunc, void *usrarg ); friend double epicsShareAPI ca_beacon_period ( chid pChan ); friend unsigned epicsShareAPI ca_search_attempts ( chid pChan ); friend unsigned epicsShareAPI ca_write_access ( chid pChan ); friend unsigned epicsShareAPI ca_read_access ( chid pChan ); friend short epicsShareAPI ca_field_type ( chid pChan ); friend arrayElementCount epicsShareAPI ca_element_count ( chid pChan ); friend int epicsShareAPI ca_v42_ok ( chid pChan ); friend int epicsShareAPI ca_create_subscription ( chtype type, arrayElementCount count, chid pChan, long mask, caEventCallBackFunc * pCallBack, void * pCallBackArg, evid * monixptr ); friend enum channel_state epicsShareAPI ca_state ( chid pChan ); friend double epicsShareAPI ca_receive_watchdog_delay ( chid pChan ); unsigned getName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw (); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; void initiateConnect ( epicsGuard < epicsMutex > & ); void read ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, cacReadNotify ¬ify, cacChannel::ioid *pId = 0 ); void write ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void *pValue, cacWriteNotify &, cacChannel::ioid *pId = 0 ); void ioCancel ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & mutualExclusionGuard, const cacChannel::ioid & ); void ioShow ( epicsGuard < epicsMutex > & guard, const cacChannel::ioid &, unsigned level ) const; ca_client_context & getClientCtx (); void eliminateExcessiveSendBacklog ( epicsGuard < epicsMutex > & ); void * operator new ( size_t size, tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void * , tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > & )) protected: ~oldChannelNotify (); private: ca_client_context & cacCtx; cacChannel & io; caCh * pConnCallBack; void * pPrivate; caArh * pAccessRightsFunc; unsigned ioSeqNo; bool currentlyConnected; bool prevConnected; void connectNotify ( epicsGuard < epicsMutex > & ); void disconnectNotify ( epicsGuard < epicsMutex > & ); void serviceShutdownNotify ( epicsGuard < epicsMutex > & mutualExclusionGuard ); void accessRightsNotify ( epicsGuard < epicsMutex > &, const caAccessRights & ); void exception ( epicsGuard < epicsMutex > &, int status, const char * pContext ); void readException ( epicsGuard < epicsMutex > &, int status, const char * pContext, unsigned type, arrayElementCount count, void *pValue ); void writeException ( epicsGuard < epicsMutex > &, int status, const char * pContext, unsigned type, arrayElementCount count ); oldChannelNotify ( const oldChannelNotify & ); oldChannelNotify & operator = ( const oldChannelNotify & ); void operator delete ( void * ); }; class getCopy : public cacReadNotify { public: getCopy ( epicsGuard < epicsMutex > & guard, ca_client_context & cacCtx, oldChannelNotify &, unsigned type, arrayElementCount count, void *pValue ); ~getCopy (); void show ( unsigned level ) const; void cancel (); void * operator new ( size_t size, tsFreeList < class getCopy, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < class getCopy, 1024, epicsMutexNOOP > & )) private: arrayElementCount count; ca_client_context & cacCtx; oldChannelNotify & chan; void * pValue; unsigned ioSeqNo; unsigned type; void completion ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void *pData ); void exception ( epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count ); getCopy ( const getCopy & ); getCopy & operator = ( const getCopy & ); void operator delete ( void * ); }; class getCallback : public cacReadNotify { public: getCallback ( oldChannelNotify & chanIn, caEventCallBackFunc *pFunc, void *pPrivate ); ~getCallback (); void * operator new ( size_t size, tsFreeList < class getCallback, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < class getCallback, 1024, epicsMutexNOOP > & )) private: oldChannelNotify & chan; caEventCallBackFunc * pFunc; void * pPrivate; void completion ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void *pData); void exception ( epicsGuard < epicsMutex > &, int status, const char * pContext, unsigned type, arrayElementCount count ); getCallback ( const getCallback & ); getCallback & operator = ( const getCallback & ); void operator delete ( void * ); }; class putCallback : public cacWriteNotify { public: putCallback ( oldChannelNotify &, caEventCallBackFunc *pFunc, void *pPrivate ); ~putCallback (); void * operator new ( size_t size, tsFreeList < class putCallback, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < class putCallback, 1024, epicsMutexNOOP > & )) private: oldChannelNotify & chan; caEventCallBackFunc * pFunc; void *pPrivate; void completion ( epicsGuard < epicsMutex > & ); void exception ( epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count ); putCallback ( const putCallback & ); putCallback & operator = ( const putCallback & ); void operator delete ( void * ); }; struct oldSubscription : private cacStateNotify { public: oldSubscription ( epicsGuard < epicsMutex > & guard, oldChannelNotify & chanIn, cacChannel & io, unsigned type, arrayElementCount nElem, unsigned mask, caEventCallBackFunc * pFuncIn, void * pPrivateIn, evid * ); ~oldSubscription (); oldChannelNotify & channel () const; // The primary mutex must be released when calling the user's // callback, and therefore a finite interval exists when we are // moving forward with the intent to call the users callback // but the users IO could be deleted during this interval. // To prevent the user's callback from being called after // destroying his IO we must past a guard for the callback // mutex here. void cancel ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & mutualExclusionGuard ); void * operator new ( size_t size, tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > & )) private: oldChannelNotify & chan; cacChannel::ioid id; caEventCallBackFunc * pFunc; void * pPrivate; void current ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void *pData ); void exception ( epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count ); oldSubscription ( const oldSubscription & ); oldSubscription & operator = ( const oldSubscription & ); void operator delete ( void * ); }; extern "C" void cacOnceFunc ( void * ); struct ca_client_context : public cacContextNotify { public: ca_client_context ( bool enablePreemptiveCallback = false ); virtual ~ca_client_context (); void changeExceptionEvent ( caExceptionHandler * pfunc, void * arg ); void registerForFileDescriptorCallBack ( CAFDHANDLER * pFunc, void * pArg ); void replaceErrLogHandler ( caPrintfFunc * ca_printf_func ); cacChannel & createChannel ( epicsGuard < epicsMutex > &, const char * pChannelName, cacChannelNotify &, cacChannel::priLev pri ); void flush ( epicsGuard < epicsMutex > & ); void eliminateExcessiveSendBacklog ( epicsGuard < epicsMutex > &, cacChannel & ); int pendIO ( const double & timeout ); int pendEvent ( const double & timeout ); bool ioComplete () const; void show ( unsigned level ) const; unsigned circuitCount () const; unsigned sequenceNumberOfOutstandingIO ( epicsGuard < epicsMutex > & ) const; unsigned beaconAnomaliesSinceProgramStart () const; void incrementOutstandingIO ( epicsGuard < epicsMutex > &, unsigned ioSeqNo ); void decrementOutstandingIO ( epicsGuard < epicsMutex > &, unsigned ioSeqNo ); void exception ( epicsGuard < epicsMutex > &, int status, const char * pContext, const char * pFileName, unsigned lineNo ); void exception ( epicsGuard < epicsMutex > &, int status, const char * pContext, const char * pFileName, unsigned lineNo, oldChannelNotify & chan, unsigned type, arrayElementCount count, unsigned op ); void blockForEventAndEnableCallbacks ( epicsEvent & event, const double & timeout ); CASG * lookupCASG ( epicsGuard < epicsMutex > &, unsigned id ); static void installDefaultService ( cacService & ); void installCASG ( epicsGuard < epicsMutex > &, CASG & ); void uninstallCASG ( epicsGuard < epicsMutex > &, CASG & ); void selfTest () const; // perhaps these should be eliminated in deference to the exception mechanism int printFormated ( const char * pformat, ... ) const; int varArgsPrintFormated ( const char * pformat, va_list args ) const; void signal ( int ca_status, const char * pfilenm, int lineno, const char * pFormat, ... ); void vSignal ( int ca_status, const char * pfilenm, int lineno, const char *pFormat, va_list args ); bool preemptiveCallbakIsEnabled () const; void destroyGetCopy ( epicsGuard < epicsMutex > &, getCopy & ); void destroyGetCallback ( epicsGuard < epicsMutex > &, getCallback & ); void destroyPutCallback ( epicsGuard < epicsMutex > &, putCallback & ); void destroySubscription ( epicsGuard < epicsMutex > &, oldSubscription & ); epicsMutex & mutexRef () const; template < class T > void whenThereIsAnExceptionDestroySyncGroupIO ( epicsGuard < epicsMutex > &, T & ); // legacy C API friend int epicsShareAPI ca_create_channel ( const char * name_str, caCh * conn_func, void * puser, capri priority, chid * chanptr ); friend int epicsShareAPI ca_clear_channel ( chid pChan ); friend int epicsShareAPI ca_array_get ( chtype type, arrayElementCount count, chid pChan, void * pValue ); friend int epicsShareAPI ca_array_get_callback ( chtype type, arrayElementCount count, chid pChan, caEventCallBackFunc *pfunc, void *arg ); friend int epicsShareAPI ca_array_put ( chtype type, arrayElementCount count, chid pChan, const void * pValue ); friend int epicsShareAPI ca_array_put_callback ( chtype type, arrayElementCount count, chid pChan, const void * pValue, caEventCallBackFunc *pfunc, void *usrarg ); friend int epicsShareAPI ca_create_subscription ( chtype type, arrayElementCount count, chid pChan, long mask, caEventCallBackFunc * pCallBack, void * pCallBackArg, evid *monixptr ); friend int epicsShareAPI ca_flush_io (); friend int epicsShareAPI ca_clear_subscription ( evid pMon ); friend int epicsShareAPI ca_sg_create ( CA_SYNC_GID * pgid ); friend int epicsShareAPI ca_sg_delete ( const CA_SYNC_GID gid ); friend int epicsShareAPI ca_sg_block ( const CA_SYNC_GID gid, ca_real timeout ); friend int epicsShareAPI ca_sg_reset ( const CA_SYNC_GID gid ); friend int epicsShareAPI ca_sg_test ( const CA_SYNC_GID gid ); friend int epicsShareAPI ca_sg_array_get ( const CA_SYNC_GID gid, chtype type, arrayElementCount count, chid pChan, void *pValue ); friend int epicsShareAPI ca_sg_array_put ( const CA_SYNC_GID gid, chtype type, arrayElementCount count, chid pChan, const void *pValue ); friend int ca_sync_group_destroy ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard, ca_client_context & cac, const CA_SYNC_GID gid ); friend void sync_group_reset ( ca_client_context & client, CASG & sg ); // exceptions class noSocket {}; private: chronIntIdResTable < CASG > sgTable; tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > oldChannelNotifyFreeList; tsFreeList < class getCopy, 1024, epicsMutexNOOP > getCopyFreeList; tsFreeList < class getCallback, 1024, epicsMutexNOOP > getCallbackFreeList; tsFreeList < class putCallback, 1024, epicsMutexNOOP > putCallbackFreeList; tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > subscriptionFreeList; tsFreeList < struct CASG, 128, epicsMutexNOOP > casgFreeList; mutable epicsMutex mutex; mutable epicsMutex cbMutex; epicsEvent ioDone; epicsEvent callbackThreadActivityComplete; epicsThreadId createdByThread; std::auto_ptr < CallbackGuard > pCallbackGuard; std::auto_ptr < cacContext > pServiceContext; caExceptionHandler * ca_exception_func; void * ca_exception_arg; caPrintfFunc * pVPrintfFunc; CAFDHANDLER * fdRegFunc; void * fdRegArg; SOCKET sock; unsigned pndRecvCnt; unsigned ioSeqNo; unsigned callbackThreadsPending; ca_uint16_t localPort; bool fdRegFuncNeedsToBeCalled; bool noWakeupSincePend; void attachToClientCtx (); void callbackProcessingInitiateNotify (); void callbackProcessingCompleteNotify (); cacContext & createNetworkContext ( epicsMutex & mutualExclusion, epicsMutex & callbackControl ); void _sendWakeupMsg (); ca_client_context ( const ca_client_context & ); ca_client_context & operator = ( const ca_client_context & ); friend void cacOnceFunc ( void * ); static cacService * pDefaultService; static epicsMutex * pDefaultServiceInstallMutex; static const unsigned flushBlockThreshold; }; int fetchClientContext ( ca_client_context * * ppcac ); inline ca_client_context & oldChannelNotify::getClientCtx () { return this->cacCtx; } inline unsigned oldChannelNotify::getName ( epicsGuard < epicsMutex > & guard, char * pBuf, unsigned bufLen ) const throw () { return this->io.getName ( guard, pBuf, bufLen ); } inline void oldChannelNotify::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { this->io.show ( guard, level ); } inline void oldChannelNotify::initiateConnect ( epicsGuard < epicsMutex > & guard ) { this->io.initiateConnect ( guard ); } inline void oldChannelNotify::ioCancel ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & mutualExclusionGuard, const cacChannel::ioid & id ) { this->io.ioCancel ( callbackGuard, mutualExclusionGuard, id ); } inline void oldChannelNotify::ioShow ( epicsGuard < epicsMutex > & guard, const cacChannel::ioid & id, unsigned level ) const { this->io.ioShow ( guard, id, level ); } inline void oldChannelNotify::eliminateExcessiveSendBacklog ( epicsGuard < epicsMutex > & guard ) { this->cacCtx.eliminateExcessiveSendBacklog ( guard, this->io ); } inline void * oldChannelNotify::operator new ( size_t size, tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void oldChannelNotify::operator delete ( void *pCadaver, tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif inline void * oldSubscription::operator new ( size_t size, tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void oldSubscription::operator delete ( void *pCadaver, tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif inline void oldSubscription::cancel ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & mutualExclusionGuard ) { this->chan.ioCancel ( callbackGuard, mutualExclusionGuard, this->id ); } inline oldChannelNotify & oldSubscription::channel () const { return this->chan; } inline void * getCopy::operator new ( size_t size, tsFreeList < class getCopy, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void getCopy::operator delete ( void *pCadaver, tsFreeList < class getCopy, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif inline void * putCallback::operator new ( size_t size, tsFreeList < class putCallback, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void putCallback::operator delete ( void * pCadaver, tsFreeList < class putCallback, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif inline void * getCallback::operator new ( size_t size, tsFreeList < class getCallback, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void getCallback::operator delete ( void * pCadaver, tsFreeList < class getCallback, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif inline bool ca_client_context::preemptiveCallbakIsEnabled () const { return this->pCallbackGuard.get () == 0; } inline bool ca_client_context::ioComplete () const { return ( this->pndRecvCnt == 0u ); } inline unsigned ca_client_context::sequenceNumberOfOutstandingIO ( epicsGuard < epicsMutex > & ) const { // perhaps on SMP systems THERE should be lock/unlock around this return this->ioSeqNo; } template < class T > void ca_client_context :: whenThereIsAnExceptionDestroySyncGroupIO ( epicsGuard < epicsMutex > & guard, T & io ) { if ( this->pCallbackGuard.get() && this->createdByThread == epicsThreadGetIdSelf () ) { io.destroy ( *this->pCallbackGuard.get(), guard ); } else { // dont reverse the lock hierarchy epicsGuardRelease < epicsMutex > guardRelease ( guard ); { // // we will definately stall out here if all of the // following are true // // o user creates non-preemtive mode client library context // o user doesnt periodically call a ca function // o user calls this function from an auxiillary thread // CallbackGuard cbGuard ( this->cbMutex ); epicsGuard < epicsMutex > guard ( this->mutex ); io.destroy ( cbGuard, guard ); } } } #endif // ifndef oldAccessh base-7.0.3.1/modules/ca/src/client/oldChannelNotify.cpp0000664000577000060420000004540213557101274021464 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include #ifdef _MSC_VER # pragma warning(disable:4355) #endif #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "errlog.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "oldAccess.h" #include "cac.h" #include "autoPtrFreeList.h" extern "C" void cacNoopAccesRightsHandler ( struct access_rights_handler_args ) { } oldChannelNotify::oldChannelNotify ( epicsGuard < epicsMutex > & guard, ca_client_context & cacIn, const char *pName, caCh * pConnCallBackIn, void * pPrivateIn, capri priority ) : cacCtx ( cacIn ), io ( cacIn.createChannel ( guard, pName, *this, priority ) ), pConnCallBack ( pConnCallBackIn ), pPrivate ( pPrivateIn ), pAccessRightsFunc ( cacNoopAccesRightsHandler ), ioSeqNo ( 0 ), currentlyConnected ( false ), prevConnected ( false ) { guard.assertIdenticalMutex ( cacIn.mutexRef () ); this->ioSeqNo = cacIn.sequenceNumberOfOutstandingIO ( guard ); if ( pConnCallBackIn == 0 ) { cacIn.incrementOutstandingIO ( guard, this->ioSeqNo ); } } oldChannelNotify::~oldChannelNotify () { } void oldChannelNotify::destructor ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & mutexGuard ) { mutexGuard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); this->io.destroy ( cbGuard, mutexGuard ); // no need to worry about a connect preempting here because // the io (the nciu) has been destroyed above if ( this->pConnCallBack == 0 && ! this->currentlyConnected ) { this->cacCtx.decrementOutstandingIO ( mutexGuard, this->ioSeqNo ); } this->~oldChannelNotify (); } void oldChannelNotify::connectNotify ( epicsGuard < epicsMutex > & guard ) { this->currentlyConnected = true; this->prevConnected = true; if ( this->pConnCallBack ) { struct connection_handler_args args; args.chid = this; args.op = CA_OP_CONN_UP; caCh * pFunc = this->pConnCallBack; { epicsGuardRelease < epicsMutex > unguard ( guard ); ( *pFunc ) ( args ); } } else { this->cacCtx.decrementOutstandingIO ( guard, this->ioSeqNo ); } } void oldChannelNotify::disconnectNotify ( epicsGuard < epicsMutex > & guard ) { this->currentlyConnected = false; if ( this->pConnCallBack ) { struct connection_handler_args args; args.chid = this; args.op = CA_OP_CONN_DOWN; caCh * pFunc = this->pConnCallBack; { epicsGuardRelease < epicsMutex > unguard ( guard ); ( *pFunc ) ( args ); } } else { this->cacCtx.incrementOutstandingIO ( guard, this->ioSeqNo ); } } void oldChannelNotify::serviceShutdownNotify ( epicsGuard < epicsMutex > & guard ) { this->disconnectNotify ( guard ); } void oldChannelNotify::accessRightsNotify ( epicsGuard < epicsMutex > & guard, const caAccessRights & ar ) { struct access_rights_handler_args args; args.chid = this; args.ar.read_access = ar.readPermit(); args.ar.write_access = ar.writePermit(); caArh * pFunc = this->pAccessRightsFunc; { epicsGuardRelease < epicsMutex > unguard ( guard ); ( *pFunc ) ( args ); } } void oldChannelNotify::exception ( epicsGuard < epicsMutex > & guard, int status, const char * pContext ) { this->cacCtx.exception ( guard, status, pContext, __FILE__, __LINE__ ); } void oldChannelNotify::readException ( epicsGuard < epicsMutex > & guard, int status, const char *pContext, unsigned type, arrayElementCount count, void * /* pValue */ ) { this->cacCtx.exception ( guard, status, pContext, __FILE__, __LINE__, *this, type, count, CA_OP_GET ); } void oldChannelNotify::writeException ( epicsGuard < epicsMutex > & guard, int status, const char *pContext, unsigned type, arrayElementCount count ) { this->cacCtx.exception ( guard, status, pContext, __FILE__, __LINE__, *this, type, count, CA_OP_PUT ); } void oldChannelNotify::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } /* * ca_get_host_name () */ unsigned epicsShareAPI ca_get_host_name ( chid pChan, char * pBuf, unsigned bufLength ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef() ); return pChan->io.getHostName ( guard, pBuf, bufLength ); } /* * ca_host_name () * * !!!! not thread safe !!!! * */ const char * epicsShareAPI ca_host_name ( chid pChan ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); return pChan->io.pHostName ( guard ); } /* * ca_set_puser () */ void epicsShareAPI ca_set_puser ( chid pChan, void * puser ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); pChan->pPrivate = puser; } /* * ca_get_puser () */ void * epicsShareAPI ca_puser ( chid pChan ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); return pChan->pPrivate; } /* * Specify an event subroutine to be run for connection events */ int epicsShareAPI ca_change_connection_event ( chid pChan, caCh * pfunc ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); if ( ! pChan->currentlyConnected ) { if ( pfunc ) { if ( ! pChan->pConnCallBack ) { pChan->cacCtx.decrementOutstandingIO ( guard, pChan->ioSeqNo ); } } else { if ( pChan->pConnCallBack ) { pChan->cacCtx.incrementOutstandingIO ( guard, pChan->ioSeqNo ); } } } pChan->pConnCallBack = pfunc; return ECA_NORMAL; } /* * ca_replace_access_rights_event */ int epicsShareAPI ca_replace_access_rights_event ( chid pChan, caArh *pfunc ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); // The order of the following is significant to guarantee that the // access rights handler is always gets called even if the channel connects // while this is running. There is some very small chance that the // handler could be called twice here with the same access rights state, but // that will not upset the application. pChan->pAccessRightsFunc = pfunc ? pfunc : cacNoopAccesRightsHandler; caAccessRights tmp = pChan->io.accessRights ( guard ); if ( pChan->currentlyConnected ) { struct access_rights_handler_args args; args.chid = pChan; args.ar.read_access = tmp.readPermit (); args.ar.write_access = tmp.writePermit (); epicsGuardRelease < epicsMutex > unguard ( guard ); ( *pChan->pAccessRightsFunc ) ( args ); } return ECA_NORMAL; } /* * ca_array_get () */ int epicsShareAPI ca_array_get ( chtype type, arrayElementCount count, chid pChan, void *pValue ) { int caStatus; try { if ( type < 0 ) { return ECA_BADTYPE; } if ( count == 0 ) return ECA_BADCOUNT; unsigned tmpType = static_cast < unsigned > ( type ); epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); pChan->eliminateExcessiveSendBacklog ( guard ); autoPtrFreeList < getCopy, 0x400, epicsMutexNOOP > pNotify ( pChan->getClientCtx().getCopyFreeList, new ( pChan->getClientCtx().getCopyFreeList ) getCopy ( guard, pChan->getClientCtx(), *pChan, tmpType, count, pValue ) ); pChan->io.read ( guard, type, count, *pNotify, 0 ); pNotify.release (); caStatus = ECA_NORMAL; } catch ( cacChannel::badString & ) { caStatus = ECA_BADSTR; } catch ( cacChannel::badType & ) { caStatus = ECA_BADTYPE; } catch ( cacChannel::outOfBounds & ) { caStatus = ECA_BADCOUNT; } catch ( cacChannel::noReadAccess & ) { caStatus = ECA_NORDACCESS; } catch ( cacChannel::notConnected & ) { caStatus = ECA_DISCONN; } catch ( cacChannel::unsupportedByService & ) { caStatus = ECA_UNAVAILINSERV; } catch ( cacChannel::requestTimedOut & ) { caStatus = ECA_TIMEOUT; } catch ( std::bad_alloc & ) { caStatus = ECA_ALLOCMEM; } catch ( cacChannel::msgBodyCacheTooSmall & ) { caStatus = ECA_TOLARGE; } catch ( ... ) { caStatus = ECA_GETFAIL; } return caStatus; } /* * ca_array_get_callback () */ int epicsShareAPI ca_array_get_callback ( chtype type, arrayElementCount count, chid pChan, caEventCallBackFunc *pfunc, void *arg ) { int caStatus; try { if ( type < 0 ) { return ECA_BADTYPE; } if ( pfunc == NULL ) { return ECA_BADFUNCPTR; } unsigned tmpType = static_cast < unsigned > ( type ); epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); pChan->eliminateExcessiveSendBacklog ( guard ); autoPtrFreeList < getCallback, 0x400, epicsMutexNOOP > pNotify ( pChan->getClientCtx().getCallbackFreeList, new ( pChan->getClientCtx().getCallbackFreeList ) getCallback ( *pChan, pfunc, arg ) ); pChan->io.read ( guard, tmpType, count, *pNotify, 0 ); pNotify.release (); caStatus = ECA_NORMAL; } catch ( cacChannel::badString & ) { caStatus = ECA_BADSTR; } catch ( cacChannel::badType & ) { caStatus = ECA_BADTYPE; } catch ( cacChannel::outOfBounds & ) { caStatus = ECA_BADCOUNT; } catch ( cacChannel::noReadAccess & ) { caStatus = ECA_NORDACCESS; } catch ( cacChannel::notConnected & ) { caStatus = ECA_DISCONN; } catch ( cacChannel::unsupportedByService & ) { caStatus = ECA_UNAVAILINSERV; } catch ( cacChannel::requestTimedOut & ) { caStatus = ECA_TIMEOUT; } catch ( std::bad_alloc & ) { caStatus = ECA_ALLOCMEM; } catch ( cacChannel::msgBodyCacheTooSmall & ) { caStatus = ECA_TOLARGE; } catch ( ... ) { caStatus = ECA_GETFAIL; } return caStatus; } void oldChannelNotify::read ( epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount count, cacReadNotify & notify, cacChannel::ioid * pId ) { this->io.read ( guard, type, count, notify, pId ); } /* * ca_array_put_callback () */ int epicsShareAPI ca_array_put_callback ( chtype type, arrayElementCount count, chid pChan, const void *pValue, caEventCallBackFunc *pfunc, void *usrarg ) { int caStatus; try { if ( type < 0 ) { return ECA_BADTYPE; } if ( pfunc == NULL ) { return ECA_BADFUNCPTR; } epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); pChan->eliminateExcessiveSendBacklog ( guard ); unsigned tmpType = static_cast < unsigned > ( type ); autoPtrFreeList < putCallback, 0x400, epicsMutexNOOP > pNotify ( pChan->getClientCtx().putCallbackFreeList, new ( pChan->getClientCtx().putCallbackFreeList ) putCallback ( *pChan, pfunc, usrarg ) ); pChan->io.write ( guard, tmpType, count, pValue, *pNotify, 0 ); pNotify.release (); caStatus = ECA_NORMAL; } catch ( cacChannel::badString & ) { caStatus = ECA_BADSTR; } catch ( cacChannel::badType & ) { caStatus = ECA_BADTYPE; } catch ( cacChannel::outOfBounds & ) { caStatus = ECA_BADCOUNT; } catch ( cacChannel::noWriteAccess & ) { caStatus = ECA_NOWTACCESS; } catch ( cacChannel::notConnected & ) { caStatus = ECA_DISCONN; } catch ( cacChannel::unsupportedByService & ) { caStatus = ECA_UNAVAILINSERV; } catch ( cacChannel::requestTimedOut & ) { caStatus = ECA_TIMEOUT; } catch ( std::bad_alloc & ) { caStatus = ECA_ALLOCMEM; } catch ( ... ) { caStatus = ECA_PUTFAIL; } return caStatus; } /* * ca_array_put () */ int epicsShareAPI ca_array_put ( chtype type, arrayElementCount count, chid pChan, const void * pValue ) { if ( type < 0 ) { return ECA_BADTYPE; } unsigned tmpType = static_cast < unsigned > ( type ); int caStatus; try { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); pChan->eliminateExcessiveSendBacklog ( guard ); pChan->io.write ( guard, tmpType, count, pValue ); caStatus = ECA_NORMAL; } catch ( cacChannel::badString & ) { caStatus = ECA_BADSTR; } catch ( cacChannel::badType & ) { caStatus = ECA_BADTYPE; } catch ( cacChannel::outOfBounds & ) { caStatus = ECA_BADCOUNT; } catch ( cacChannel::noWriteAccess & ) { caStatus = ECA_NOWTACCESS; } catch ( cacChannel::notConnected & ) { caStatus = ECA_DISCONN; } catch ( cacChannel::unsupportedByService & ) { caStatus = ECA_UNAVAILINSERV; } catch ( cacChannel::requestTimedOut & ) { caStatus = ECA_TIMEOUT; } catch ( std::bad_alloc & ) { caStatus = ECA_ALLOCMEM; } catch ( ... ) { caStatus = ECA_PUTFAIL; } return caStatus; } int epicsShareAPI ca_create_subscription ( chtype type, arrayElementCount count, chid pChan, long mask, caEventCallBackFunc * pCallBack, void * pCallBackArg, evid * monixptr ) { if ( type < 0 ) { return ECA_BADTYPE; } unsigned tmpType = static_cast < unsigned > ( type ); if ( INVALID_DB_REQ (type) ) { return ECA_BADTYPE; } if ( pCallBack == NULL ) { return ECA_BADFUNCPTR; } static const long maskMask = 0xffff; if ( ( mask & maskMask ) == 0) { return ECA_BADMASK; } if ( mask & ~maskMask ) { return ECA_BADMASK; } try { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); try { // if this stalls out on a live circuit then an exception // can be forthcoming which we must ignore (this is a // special case preserving legacy ca_create_subscription // behavior) pChan->eliminateExcessiveSendBacklog ( guard ); } catch ( cacChannel::notConnected & ) { // intentionally ignored (its ok to subscribe when not connected) } new ( pChan->getClientCtx().subscriptionFreeList ) oldSubscription ( guard, *pChan, pChan->io, tmpType, count, mask, pCallBack, pCallBackArg, monixptr ); // dont touch object created after above new because // the first callback might have canceled, and therefore // destroyed, it return ECA_NORMAL; } catch ( cacChannel::badType & ) { return ECA_BADTYPE; } catch ( cacChannel::outOfBounds & ) { return ECA_BADCOUNT; } catch ( cacChannel::badEventSelection & ) { return ECA_BADMASK; } catch ( cacChannel::noReadAccess & ) { return ECA_NORDACCESS; } catch ( cacChannel::unsupportedByService & ) { return ECA_UNAVAILINSERV; } catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } catch ( cacChannel::msgBodyCacheTooSmall & ) { return ECA_TOLARGE; } catch ( ... ) { return ECA_INTERNAL; } } void oldChannelNotify::write ( epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount count, const void * pValue, cacWriteNotify & notify, cacChannel::ioid * pId ) { this->io.write ( guard, type, count, pValue, notify, pId ); } /* * ca_field_type() */ short epicsShareAPI ca_field_type ( chid pChan ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); return pChan->io.nativeType ( guard ); } /* * ca_element_count () */ arrayElementCount epicsShareAPI ca_element_count ( chid pChan ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); return pChan->io.nativeElementCount ( guard ); } /* * ca_state () */ enum channel_state epicsShareAPI ca_state ( chid pChan ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); if ( pChan->io.connected ( guard ) ) { return cs_conn; } else if ( pChan->prevConnected ){ return cs_prev_conn; } else { return cs_never_conn; } } /* * ca_read_access () */ unsigned epicsShareAPI ca_read_access ( chid pChan ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); return pChan->io.accessRights(guard).readPermit(); } /* * ca_write_access () */ unsigned epicsShareAPI ca_write_access ( chid pChan ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); return pChan->io.accessRights(guard).writePermit(); } /* * ca_name () */ const char * epicsShareAPI ca_name ( chid pChan ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); return pChan->io.pName ( guard ); } unsigned epicsShareAPI ca_search_attempts ( chid pChan ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); return pChan->io.searchAttempts ( guard ); } double epicsShareAPI ca_beacon_period ( chid pChan ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); return pChan->io.beaconPeriod ( guard ); } double epicsShareAPI ca_receive_watchdog_delay ( chid pChan ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); return pChan->io.receiveWatchdogDelay ( guard ); } /* * ca_v42_ok(chid chan) */ int epicsShareAPI ca_v42_ok ( chid pChan ) { epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); return pChan->io.ca_v42_ok ( guard ); } base-7.0.3.1/modules/ca/src/client/oldSubscription.cpp0000664000577000060420000000657113557101274021413 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #include #include "errlog.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "oldAccess.h" oldSubscription::oldSubscription ( epicsGuard < epicsMutex > & guard, oldChannelNotify & chanIn, cacChannel & io, unsigned type, arrayElementCount nElem, unsigned mask, caEventCallBackFunc * pFuncIn, void * pPrivateIn, evid * pEventId ) : chan ( chanIn ), id ( UINT_MAX ), pFunc ( pFuncIn ), pPrivate ( pPrivateIn ) { // The users event id *must* be set prior to potentially // calling his callback from within subscribe. if ( pEventId ) { *pEventId = this; } io.subscribe ( guard, type, nElem, mask, *this, &this->id ); // Dont touch this pointer after this point because the // 1st update callback might cancel the subscription and // thereby destroy this object. } oldSubscription::~oldSubscription () { } void oldSubscription::current ( epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount count, const void * pData ) { struct event_handler_args args; args.usr = this->pPrivate; args.chid = & this->chan; args.type = static_cast < long > ( type ); args.count = static_cast < long > ( count ); args.status = ECA_NORMAL; args.dbr = pData; caEventCallBackFunc * pFuncTmp = this->pFunc; { epicsGuardRelease < epicsMutex > unguard ( guard ); ( *pFuncTmp ) ( args ); } } void oldSubscription::exception ( epicsGuard < epicsMutex > & guard, int status, const char * /* pContext */, unsigned type, arrayElementCount count ) { if ( status == ECA_CHANDESTROY ) { ca_client_context & cac = this->chan.getClientCtx (); cac.destroySubscription ( guard, *this ); } else if ( status != ECA_DISCONN ) { struct event_handler_args args; args.usr = this->pPrivate; args.chid = & this->chan; args.type = type; args.count = count; args.status = status; args.dbr = 0; caEventCallBackFunc * pFuncTmp = this->pFunc; { epicsGuardRelease < epicsMutex > unguard ( guard ); ( *pFuncTmp ) ( args ); } } } void oldSubscription::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } base-7.0.3.1/modules/ca/src/client/putCallback.cpp0000664000577000060420000000627413557101274020455 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include #include "errlog.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "oldAccess.h" putCallback::putCallback ( oldChannelNotify & chanIn, caEventCallBackFunc * pFuncIn, void * pPrivateIn ) : chan ( chanIn ), pFunc ( pFuncIn ), pPrivate ( pPrivateIn ) { } putCallback::~putCallback () { } void putCallback::completion ( epicsGuard < epicsMutex > & guard ) { struct event_handler_args args; args.usr = this->pPrivate; args.chid = & this->chan; args.type = TYPENOTCONN; args.count = 0; args.status = ECA_NORMAL; args.dbr = 0; caEventCallBackFunc * pFuncTmp = this->pFunc; // fetch client context and destroy prior to releasing // the lock and calling cb in case they destroy channel there this->chan.getClientCtx().destroyPutCallback ( guard, *this ); if ( pFuncTmp ) { epicsGuardRelease < epicsMutex > unguard ( guard ); pFuncTmp ( args ); } } void putCallback::exception ( epicsGuard < epicsMutex > & guard, int status, const char * /* pContext */, unsigned type, arrayElementCount count ) { if ( status != ECA_CHANDESTROY ) { struct event_handler_args args; args.usr = this->pPrivate; args.chid = & this->chan; args.type = type; args.count = count; args.status = status; args.dbr = 0; caEventCallBackFunc * pFuncTmp = this->pFunc; // fetch client context and destroy prior to releasing // the lock and calling cb in case they destroy channel there this->chan.getClientCtx().destroyPutCallback ( guard, *this ); { epicsGuardRelease < epicsMutex > unguard ( guard ); ( *pFuncTmp ) ( args ); } } else { this->chan.getClientCtx().destroyPutCallback ( guard, *this ); } } void putCallback::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } base-7.0.3.1/modules/ca/src/client/repeater.cpp0000664000577000060420000004176013557101274020036 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * * REPEATER.cpp * * CA broadcast repeater * * Author: Jeff Hill * Date: 3-27-90 * * PURPOSE: * Broadcasts fan out over the LAN, but old IP kernels do not allow * two processes on the same machine to get the same broadcast * (and modern IP kernels do not allow two processes on the same machine * to receive the same unicast). * * This code fans out UDP messages sent to the CA repeater port * to all CA client processes that have subscribed. * */ /* * It would be preferable to avoid using the repeater on multicast enhanced * IP kernels, but this is not going to work in all situations because * (according to Steven's TCP/IP illustrated volume I) if a broadcast is * received it goes to all sockets on the same port, but if a unicast is * received it goes to only one of the sockets on the same port (we can only * guess at which one it will be). * * I have observed this behavior under winsock II: * o only one of the sockets on the same port receives the message if we * send to the loopback address * o both of the sockets on the same port receives the message if we send * to the broadcast address */ /* verifyClients() Mechanism * * This is required because Solaris and HPUX have half baked versions * of sockets. * * As written, the repeater should be robust against situations where the * IP kernel doesn't implement UDP disconnect on receiving ICMP port * unreachable errors from the destination process. As I recall, this * change was required in the repeater code when we ported from sunos4 to * Solaris. To avoid unreasonable overhead, I decided at the time to check * the validity of all existing connections only when a new client * registers with the repeater (and not when fanning out each beacon * received). -- Jeff */ #include #include #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "tsDLList.h" #include "envDefs.h" #include "tsFreeList.h" #include "osiWireFormat.h" #include "taskwd.h" #include "errlog.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "caProto.h" #include "udpiiu.h" #include "repeaterClient.h" /* * these can be external since there is only one instance * per machine so we dont care about reentrancy */ static tsDLList < repeaterClient > client_list; static const unsigned short PORT_ANY = 0u; /* * makeSocket() */ static int makeSocket ( unsigned short port, bool reuseAddr, SOCKET * pSock ) { SOCKET sock = epicsSocketCreate ( AF_INET, SOCK_DGRAM, 0 ); if ( sock == INVALID_SOCKET ) { *pSock = sock; return SOCKERRNO; } /* * no need to bind if unconstrained */ if ( port != PORT_ANY ) { int status; union { struct sockaddr_in ia; struct sockaddr sa; } bd; memset ( (char *) &bd, 0, sizeof (bd) ); bd.ia.sin_family = AF_INET; bd.ia.sin_addr.s_addr = htonl ( INADDR_ANY ); bd.ia.sin_port = htons ( port ); status = bind ( sock, &bd.sa, (int) sizeof(bd) ); if ( status < 0 ) { status = SOCKERRNO; epicsSocketDestroy ( sock ); return status; } if ( reuseAddr ) { epicsSocketEnableAddressReuseDuringTimeWaitState ( sock ); } } *pSock = sock; return 0; } repeaterClient::repeaterClient ( const osiSockAddr &fromIn ) : from ( fromIn ), sock ( INVALID_SOCKET ) { #ifdef DEBUG unsigned port = ntohs ( from.ia.sin_port ); debugPrintf ( ( "new client %u\n", port ) ); #endif } bool repeaterClient::connect () { int status; if ( int sockerrno = makeSocket ( PORT_ANY, false, & this->sock ) ) { char sockErrBuf[64]; epicsSocketConvertErrorToString ( sockErrBuf, sizeof ( sockErrBuf ), sockerrno ); fprintf ( stderr, "%s: no client sock because \"%s\"\n", __FILE__, sockErrBuf ); return false; } status = ::connect ( this->sock, &this->from.sa, sizeof ( this->from.sa ) ); if ( status < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf ( stderr, "%s: unable to connect client sock because \"%s\"\n", __FILE__, sockErrBuf ); return false; } return true; } bool repeaterClient::sendConfirm () { int status; caHdr confirm; memset ( (char *) &confirm, '\0', sizeof (confirm) ); AlignedWireRef < epicsUInt16 > ( confirm.m_cmmd ) = REPEATER_CONFIRM; confirm.m_available = this->from.ia.sin_addr.s_addr; status = send ( this->sock, (char *) &confirm, sizeof (confirm), 0 ); if ( status >= 0 ) { assert ( status == sizeof ( confirm ) ); return true; } else if ( SOCKERRNO == SOCK_ECONNREFUSED ) { return false; } else { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); debugPrintf ( ( "CA Repeater: confirm req err was \"%s\"\n", sockErrBuf) ); return false; } } bool repeaterClient::sendMessage ( const void *pBuf, unsigned bufSize ) { int status; status = send ( this->sock, (char *) pBuf, bufSize, 0 ); if ( status >= 0 ) { assert ( static_cast ( status ) == bufSize ); #ifdef DEBUG epicsUInt16 port = ntohs ( this->from.ia.sin_port ); debugPrintf ( ("Sent to %u\n", port ) ); #endif return true; } else { int errnoCpy = SOCKERRNO; if ( errnoCpy == SOCK_ECONNREFUSED ) { #ifdef DEBUG epicsUInt16 port = ntohs ( this->from.ia.sin_port ); debugPrintf ( ("Client refused message %u\n", port ) ); #endif } else { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); debugPrintf ( ( "CA Repeater: UDP send err was \"%s\"\n", sockErrBuf) ); } return false; } } repeaterClient::~repeaterClient () { if ( this->sock != INVALID_SOCKET ) { epicsSocketDestroy ( this->sock ); } #ifdef DEBUG epicsUInt16 port = ntohs ( this->from.ia.sin_port ); debugPrintf ( ( "Deleted client %u\n", port ) ); #endif } void repeaterClient::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } void * repeaterClient::operator new ( size_t size, tsFreeList < repeaterClient, 0x20 > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE void repeaterClient::operator delete ( void *pCadaver, tsFreeList < repeaterClient, 0x20 > & freeList ) { freeList.release ( pCadaver ); } #endif inline unsigned short repeaterClient::port () const { return ntohs ( this->from.ia.sin_port ); } inline bool repeaterClient::identicalAddress ( const osiSockAddr &fromIn ) { if ( fromIn.sa.sa_family == this->from.sa.sa_family ) { if ( fromIn.ia.sin_port == this->from.ia.sin_port) { if ( fromIn.ia.sin_addr.s_addr == this->from.ia.sin_addr.s_addr ) { return true; } } } return false; } inline bool repeaterClient::identicalPort ( const osiSockAddr &fromIn ) { if ( fromIn.sa.sa_family == this->from.sa.sa_family ) { if ( fromIn.ia.sin_port == this->from.ia.sin_port) { return true; } } return false; } bool repeaterClient::verify () { SOCKET tmpSock; int sockerrno = makeSocket ( this->port (), false, & tmpSock ); if ( sockerrno == SOCK_EADDRINUSE ) { // Normal result, client using port return true; } if ( sockerrno == 0 ) { // Client went away, released port epicsSocketDestroy ( tmpSock ); } else { char sockErrBuf[64]; epicsSocketConvertErrorToString ( sockErrBuf, sizeof ( sockErrBuf ), sockerrno ); fprintf ( stderr, "CA Repeater: Bind test error \"%s\"\n", sockErrBuf ); } return false; } /* * verifyClients() */ static void verifyClients ( tsFreeList < repeaterClient, 0x20 > & freeList ) { static tsDLList < repeaterClient > theClients; repeaterClient *pclient; while ( ( pclient = client_list.get () ) ) { if ( pclient->verify () ) { theClients.add ( *pclient ); } else { pclient->~repeaterClient (); freeList.release ( pclient ); } } client_list.add ( theClients ); } /* * fanOut() */ static void fanOut ( const osiSockAddr & from, const void * pMsg, unsigned msgSize, tsFreeList < repeaterClient, 0x20 > & freeList ) { static tsDLList < repeaterClient > theClients; repeaterClient *pclient; while ( ( pclient = client_list.get () ) ) { theClients.add ( *pclient ); /* Dont reflect back to sender */ if ( pclient->identicalAddress ( from ) ) { continue; } if ( ! pclient->sendMessage ( pMsg, msgSize ) ) { if ( ! pclient->verify () ) { theClients.remove ( *pclient ); pclient->~repeaterClient (); freeList.release ( pclient ); } } } client_list.add ( theClients ); } /* * register_new_client() */ static void register_new_client ( osiSockAddr & from, tsFreeList < repeaterClient, 0x20 > & freeList ) { bool newClient = false; int status; if ( from.sa.sa_family != AF_INET ) { return; } /* * the repeater and its clients must be on the same host */ if ( INADDR_LOOPBACK != ntohl ( from.ia.sin_addr.s_addr ) ) { static SOCKET testSock = INVALID_SOCKET; static bool init = false; if ( ! init ) { SOCKET sock; if ( int sockerrno = makeSocket ( PORT_ANY, true, & sock ) ) { char sockErrBuf[64]; epicsSocketConvertErrorToString ( sockErrBuf, sizeof ( sockErrBuf ), sockerrno ); fprintf ( stderr, "%s: Unable to create repeater bind test socket because \"%s\"\n", __FILE__, sockErrBuf ); } else { testSock = sock; } init = true; } /* * Unfortunately on 3.13 beta 11 and before the * repeater would not always allow the loopback address * as a local client address so current clients alternate * between the address of the first non-loopback interface * found and the loopback addresss when subscribing with * the CA repeater until all CA repeaters have been updated * to current code. */ if ( testSock != INVALID_SOCKET ) { osiSockAddr addr; addr = from; addr.ia.sin_port = PORT_ANY; /* we can only bind to a local address */ status = bind ( testSock, &addr.sa, sizeof ( addr ) ); if ( status ) { return; } } else { return; } } tsDLIter < repeaterClient > pclient = client_list.firstIter (); while ( pclient.valid () ) { if ( pclient->identicalPort ( from ) ) { break; } pclient++; } repeaterClient *pNewClient; if ( pclient.valid () ) { pNewClient = pclient.pointer (); } else { pNewClient = new ( freeList ) repeaterClient ( from ); if ( ! pNewClient ) { fprintf ( stderr, "%s: no memory for new client\n", __FILE__ ); return; } if ( ! pNewClient->connect () ) { pNewClient->~repeaterClient (); freeList.release ( pNewClient ); return; } client_list.add ( *pNewClient ); newClient = true; } if ( ! pNewClient->sendConfirm () ) { client_list.remove ( *pNewClient ); pNewClient->~repeaterClient (); freeList.release ( pNewClient ); # ifdef DEBUG epicsUInt16 port = ntohs ( from.ia.sin_port ); debugPrintf ( ( "Deleted repeater client=%u (error while sending ack)\n", port ) ); # endif } /* * send a noop message to all other clients so that we dont * accumulate sockets when there are no beacons */ caHdr noop; memset ( (char *) &noop, '\0', sizeof ( noop ) ); AlignedWireRef < epicsUInt16 > ( noop.m_cmmd ) = CA_PROTO_VERSION; fanOut ( from, &noop, sizeof ( noop ), freeList ); if ( newClient ) { /* * For HPUX and Solaris we need to verify that the clients * have not gone away - because an ICMP error return does not * get through to send(), which returns no error code. * * This is done each time that a new client is created. * See also the note in the file header. * * This is done here in order to avoid deleting a client * prior to sending its confirm message. */ verifyClients ( freeList ); } } /* * ca_repeater () */ void ca_repeater () { tsFreeList < repeaterClient, 0x20 > freeList; int size; SOCKET sock; osiSockAddr from; unsigned short port; char * pBuf; pBuf = new char [MAX_UDP_RECV]; { bool success = osiSockAttach(); assert ( success ); } port = envGetInetPortConfigParam ( & EPICS_CA_REPEATER_PORT, static_cast (CA_REPEATER_PORT) ); if ( int sockerrno = makeSocket ( port, true, & sock ) ) { /* * test for server was already started */ if ( sockerrno == SOCK_EADDRINUSE ) { osiSockRelease (); debugPrintf ( ( "CA Repeater: exiting because a repeater is already running\n" ) ); delete [] pBuf; return; } char sockErrBuf[64]; epicsSocketConvertErrorToString ( sockErrBuf, sizeof ( sockErrBuf ), sockerrno ); fprintf ( stderr, "%s: Unable to create repeater socket because \"%s\" - fatal\n", __FILE__, sockErrBuf ); osiSockRelease (); delete [] pBuf; return; } debugPrintf ( ( "CA Repeater: Attached and initialized\n" ) ); while ( true ) { osiSocklen_t from_size = sizeof ( from ); size = recvfrom ( sock, pBuf, MAX_UDP_RECV, 0, &from.sa, &from_size ); if ( size < 0 ) { int errnoCpy = SOCKERRNO; // Avoid spurious ECONNREFUSED bug in linux if ( errnoCpy == SOCK_ECONNREFUSED ) { continue; } // Avoid ECONNRESET from connected socket in windows if ( errnoCpy == SOCK_ECONNRESET ) { continue; } char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf ( stderr, "CA Repeater: unexpected UDP recv err: %s\n", sockErrBuf ); continue; } caHdr * pMsg = ( caHdr * ) pBuf; /* * both zero length message and a registration message * will register a new client */ if ( ( (size_t) size) >= sizeof (*pMsg) ) { if ( AlignedWireRef < epicsUInt16 > ( pMsg->m_cmmd ) == REPEATER_REGISTER ) { register_new_client ( from, freeList ); /* * strip register client message */ pMsg++; size -= sizeof ( *pMsg ); if ( size==0 ) { continue; } } else if ( AlignedWireRef < epicsUInt16 > ( pMsg->m_cmmd ) == CA_PROTO_RSRV_IS_UP ) { if ( pMsg->m_available == 0u ) { pMsg->m_available = from.ia.sin_addr.s_addr; } } } else if ( size == 0 ) { register_new_client ( from, freeList ); continue; } fanOut ( from, pMsg, size, freeList ); } } /* * caRepeaterThread () */ extern "C" void caRepeaterThread ( void * /* pDummy */ ) { taskwdInsert ( epicsThreadGetIdSelf(), NULL, NULL ); ca_repeater (); } base-7.0.3.1/modules/ca/src/client/repeaterClient.h0000664000577000060420000000415313557101274020635 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef repeaterClienth #define repeaterClienth #ifdef epicsExportSharedSymbols # define repeaterClienth_restore_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "tsDLList.h" #include "tsFreeList.h" #include "compilerDependencies.h" #ifdef repeaterClienth_restore_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif union osiSockAddr; /* * one socket per client so we will get the ECONNREFUSED * error code (and then delete the client) */ class repeaterClient : public tsDLNode < repeaterClient > { public: repeaterClient ( const osiSockAddr & from ); ~repeaterClient (); bool connect (); bool sendConfirm (); bool sendMessage ( const void *pBuf, unsigned bufSize ); bool verify (); bool identicalAddress ( const osiSockAddr &from ); bool identicalPort ( const osiSockAddr &from ); void * operator new ( size_t size, tsFreeList < repeaterClient, 0x20 > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < repeaterClient, 0x20 > & )) private: osiSockAddr from; SOCKET sock; unsigned short port () const; void operator delete ( void * ); }; #endif // repeaterClienth base-7.0.3.1/modules/ca/src/client/repeaterSubscribeTimer.cpp0000664000577000060420000000657713557101274022710 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill * */ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "cac.h" #include "iocinf.h" #include "repeaterSubscribeTimer.h" #define epicsExportSharedSymbols #include "udpiiu.h" #undef epicsExportSharedSymbols static const double repeaterSubscribeTimerInitialPeriod = 10.0; // sec static const double repeaterSubscribeTimerPeriod = 1.0; // sec repeaterSubscribeTimer::repeaterSubscribeTimer ( repeaterTimerNotify & iiuIn, epicsTimerQueue & queueIn, epicsMutex & cbMutexIn, cacContextNotify & ctxNotifyIn ) : timer ( queueIn.createTimer () ), iiu ( iiuIn ), cbMutex ( cbMutexIn ),ctxNotify ( ctxNotifyIn ), stateMutex(__FILE__, __LINE__), attempts ( 0 ), registered ( false ), once ( false ) { } repeaterSubscribeTimer::~repeaterSubscribeTimer () { this->timer.destroy (); } void repeaterSubscribeTimer::start () { this->timer.start ( *this, repeaterSubscribeTimerInitialPeriod ); } void repeaterSubscribeTimer::shutdown ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ) { epicsGuardRelease < epicsMutex > unguard ( guard ); { epicsGuardRelease < epicsMutex > cbUnguard ( cbGuard ); this->timer.cancel (); } } epicsTimerNotify::expireStatus repeaterSubscribeTimer:: expire ( const epicsTime & /* currentTime */ ) { epicsGuard < epicsMutex > guard ( this->stateMutex ); static const unsigned nTriesToMsg = 50; if ( this->attempts > nTriesToMsg && ! this->once ) { callbackManager mgr ( this->ctxNotify, this->cbMutex ); this->iiu.printFormated ( mgr.cbGuard, "CA client library is unable to contact CA repeater after %u tries.\n", nTriesToMsg ); this->iiu.printFormated ( mgr.cbGuard, "Silence this message by starting a CA repeater daemon\n") ; this->iiu.printFormated ( mgr.cbGuard, "or by calling ca_pend_event() and or ca_poll() more often.\n" ); this->once = true; } this->iiu.repeaterRegistrationMessage ( this->attempts ); this->attempts++; if ( this->registered ) { return noRestart; } else { return expireStatus ( restart, repeaterSubscribeTimerPeriod ); } } void repeaterSubscribeTimer::show ( unsigned /* level */ ) const { epicsGuard < epicsMutex > guard ( this->stateMutex ); ::printf ( "repeater subscribe timer: attempts=%u registered=%u once=%u\n", this->attempts, this->registered, this->once ); } void repeaterSubscribeTimer::confirmNotify () { epicsGuard < epicsMutex > guard ( this->stateMutex ); this->registered = true; } repeaterTimerNotify::~repeaterTimerNotify () {} base-7.0.3.1/modules/ca/src/client/repeaterSubscribeTimer.h0000664000577000060420000000475113557101274022345 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef repeaterSubscribeTimerh #define repeaterSubscribeTimerh #include "epicsTimer.h" #ifdef epicsExportSharedSymbols # define repeaterSubscribeTimerh_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "epicsTimer.h" #ifdef repeaterSubscribeTimerh_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif class epicsMutex; class cacContextNotify; class repeaterTimerNotify { public: virtual ~repeaterTimerNotify () = 0; virtual void repeaterRegistrationMessage ( unsigned attemptNumber ) = 0; virtual int printFormated ( epicsGuard < epicsMutex > & callbackControl, const char * pformat, ... ) = 0; }; class repeaterSubscribeTimer : private epicsTimerNotify { public: repeaterSubscribeTimer ( repeaterTimerNotify &, epicsTimerQueue &, epicsMutex & cbMutex, cacContextNotify & ctxNotify ); virtual ~repeaterSubscribeTimer (); void start (); void shutdown ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); void confirmNotify (); void show ( unsigned level ) const; private: epicsTimer & timer; repeaterTimerNotify & iiu; epicsMutex & cbMutex; cacContextNotify & ctxNotify; mutable epicsMutex stateMutex; unsigned attempts; bool registered; bool once; expireStatus expire ( const epicsTime & currentTime ); repeaterSubscribeTimer ( const repeaterSubscribeTimer & ); repeaterSubscribeTimer & operator = ( const repeaterSubscribeTimer & ); }; #endif // ifdef repeaterSubscribeTimerh base-7.0.3.1/modules/ca/src/client/searchTimer.cpp0000664000577000060420000003231613557101274020472 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ // // // L O S A L A M O S // Los Alamos National Laboratory // Los Alamos, New Mexico 87545 // // Copyright, 1986, The Regents of the University of California. // // Author: Jeff Hill // #include #include // vxWorks 6.0 requires this include #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "envDefs.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "udpiiu.h" #include "nciu.h" static const unsigned initialTriesPerFrame = 1u; // initial UDP frames per search try static const unsigned maxTriesPerFrame = 64u; // max UDP frames per search try // // searchTimer::searchTimer () // searchTimer::searchTimer ( searchTimerNotify & iiuIn, epicsTimerQueue & queueIn, const unsigned indexIn, epicsMutex & mutexIn, bool boostPossibleIn ) : timeAtLastSend ( epicsTime::getMonotonic () ), timer ( queueIn.createTimer () ), iiu ( iiuIn ), mutex ( mutexIn ), framesPerTry ( initialTriesPerFrame ), framesPerTryCongestThresh ( DBL_MAX ), retry ( 0 ), searchAttempts ( 0u ), searchResponses ( 0u ), index ( indexIn ), dgSeqNoAtTimerExpireBegin ( 0u ), dgSeqNoAtTimerExpireEnd ( 0u ), boostPossible ( boostPossibleIn ), stopped ( false ) { } void searchTimer::start ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->timer.start ( *this, this->period ( guard ) ); } searchTimer::~searchTimer () { assert ( this->chanListReqPending.count() == 0 ); assert ( this->chanListRespPending.count() == 0 ); this->timer.destroy (); } void searchTimer::shutdown ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ) { this->stopped = true; { epicsGuardRelease < epicsMutex > unguard ( guard ); { epicsGuardRelease < epicsMutex > cbUnguard ( cbGuard ); this->timer.cancel (); } } while ( nciu * pChan = this->chanListReqPending.get () ) { pChan->channelNode::listMember = channelNode::cs_none; pChan->serviceShutdownNotify ( cbGuard, guard ); } while ( nciu * pChan = this->chanListRespPending.get () ) { pChan->channelNode::listMember = channelNode::cs_none; pChan->serviceShutdownNotify ( cbGuard, guard ); } } void searchTimer::installChannel ( epicsGuard < epicsMutex > & guard, nciu & chan ) { this->chanListReqPending.add ( chan ); chan.channelNode::setReqPendingState ( guard, this->index ); } void searchTimer::moveChannels ( epicsGuard < epicsMutex > & guard, searchTimer & dest ) { while ( nciu * pChan = this->chanListRespPending.get () ) { if ( this->searchAttempts > 0 ) { this->searchAttempts--; } dest.installChannel ( guard, *pChan ); } while ( nciu * pChan = this->chanListReqPending.get () ) { dest.installChannel ( guard, *pChan ); } } // // searchTimer::expire () // epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTime ) { epicsGuard < epicsMutex > guard ( this->mutex ); while ( nciu * pChan = this->chanListRespPending.get () ) { pChan->channelNode::listMember = channelNode::cs_none; this->iiu.noSearchRespNotify ( guard, *pChan, this->index ); } this->timeAtLastSend = currentTime; // boost search period for channels not recently // searched for if there was some success if ( this->searchResponses && this->boostPossible ) { while ( nciu * pChan = this->chanListReqPending.get () ) { pChan->channelNode::listMember = channelNode::cs_none; this->iiu.boostChannel ( guard, *pChan ); } } if ( this->searchAttempts ) { #if 0 // // dynamically adjust the number of UDP frames per // try depending how many search requests are not // replied to // // The variable this->framesPerTry // determines the number of UDP frames to be sent // each time that expire() is called. // If this value is too high we will waste some // network bandwidth. If it is too low we will // use very little of the incoming UDP message // buffer associated with the server's port and // will therefore take longer to connect. We // initialize this->framesPerTry to a prime number // so that it is less likely that the // same channel is in the last UDP frame // sent every time that this is called (and // potentially discarded by a CA server with // a small UDP input queue). // // increase frames per try only if we see better than // a 93.75% success rate for one pass through the list // if ( this->searchResponses > ( this->searchAttempts - (this->searchAttempts/16u) ) ) { // increase UDP frames per try if we have a good score if ( this->framesPerTry < maxTriesPerFrame ) { // a congestion avoidance threshold similar to TCP is now used if ( this->framesPerTry < this->framesPerTryCongestThresh ) { this->framesPerTry += this->framesPerTry; } else { this->framesPerTry += (this->framesPerTry/8) + 1; } debugPrintf ( ("Increasing frame count to %u t=%u r=%u\n", this->framesPerTry, this->searchAttempts, this->searchResponses) ); } } // if we detect congestion because we have less than a 87.5% success // rate then gradually reduce the frames per try else if ( this->searchResponses < ( this->searchAttempts - (this->searchAttempts/8u) ) ) { if ( this->framesPerTry > 1 ) { this->framesPerTry--; } this->framesPerTryCongestThresh = this->framesPerTry/2 + 1; debugPrintf ( ("Congestion detected - set frames per try to %f t=%u r=%u\n", this->framesPerTry, this->searchAttempts, this->searchResponses) ); } #else if ( this->searchResponses == this->searchAttempts ) { // increase UDP frames per try if we have a good score if ( this->framesPerTry < maxTriesPerFrame ) { // a congestion avoidance threshold similar to TCP is now used if ( this->framesPerTry < this->framesPerTryCongestThresh ) { double doubled = 2 * this->framesPerTry; if ( doubled > this->framesPerTryCongestThresh ) { this->framesPerTry = this->framesPerTryCongestThresh; } else { this->framesPerTry = doubled; } } else { this->framesPerTry += 1.0 / this->framesPerTry; } debugPrintf ( ("Increasing frame count to %g t=%u r=%u\n", this->framesPerTry, this->searchAttempts, this->searchResponses) ); } } else { this->framesPerTryCongestThresh = this->framesPerTry / 2.0; this->framesPerTry = 1u; debugPrintf ( ("Congestion detected - set frames per try to %g t=%u r=%u\n", this->framesPerTry, this->searchAttempts, this->searchResponses) ); } #endif } this->dgSeqNoAtTimerExpireBegin = this->iiu.datagramSeqNumber ( guard ); this->searchAttempts = 0; this->searchResponses = 0; unsigned nFrameSent = 0u; while ( true ) { nciu * pChan = this->chanListReqPending.get (); if ( ! pChan ) { break; } pChan->channelNode::listMember = channelNode::cs_none; bool success = pChan->searchMsg ( guard ); if ( ! success ) { if ( this->iiu.datagramFlush ( guard, currentTime ) ) { nFrameSent++; if ( nFrameSent < this->framesPerTry ) { success = pChan->searchMsg ( guard ); } } if ( ! success ) { this->chanListReqPending.push ( *pChan ); pChan->channelNode::setReqPendingState ( guard, this->index ); break; } } this->chanListRespPending.add ( *pChan ); pChan->channelNode::setRespPendingState ( guard, this->index ); if ( this->searchAttempts < UINT_MAX ) { this->searchAttempts++; } } // flush out the search request buffer if ( this->iiu.datagramFlush ( guard, currentTime ) ) { nFrameSent++; } this->dgSeqNoAtTimerExpireEnd = this->iiu.datagramSeqNumber ( guard ) - 1u; # ifdef DEBUG if ( this->searchAttempts ) { char buf[64]; currentTime.strftime ( buf, sizeof(buf), "%M:%S.%09f"); debugPrintf ( ("sent %u delay sec=%f Rts=%s\n", nFrameSent, this->period(), buf ) ); } # endif return expireStatus ( restart, this->period ( guard ) ); } void searchTimer :: show ( unsigned level ) const { epicsGuard < epicsMutex > guard ( this->mutex ); ::printf ( "searchTimer with period %f\n", this->period ( guard ) ); if ( level > 0 ) { ::printf ( "channels with search request pending = %u\n", this->chanListReqPending.count () ); if ( level > 1u ) { tsDLIterConst < nciu > pChan = this->chanListReqPending.firstIter (); while ( pChan.valid () ) { pChan->show ( level - 2u ); pChan++; } } ::printf ( "channels with search response pending = %u\n", this->chanListRespPending.count () ); if ( level > 1u ) { tsDLIterConst < nciu > pChan = this->chanListRespPending.firstIter (); while ( pChan.valid () ) { pChan->show ( level - 2u ); pChan++; } } } } // // Reset the delay to the next search request if we get // at least one response. However, dont reset this delay if we // get a delayed response to an old search request. // void searchTimer::uninstallChanDueToSuccessfulSearchResponse ( epicsGuard < epicsMutex > & guard, nciu & chan, ca_uint32_t respDatagramSeqNo, bool seqNumberIsValid, const epicsTime & currentTime ) { guard.assertIdenticalMutex ( this->mutex ); this->uninstallChan ( guard, chan ); if ( this->stopped ) { return; } bool validResponse = true; if ( seqNumberIsValid ) { validResponse = this->dgSeqNoAtTimerExpireBegin <= respDatagramSeqNo && this->dgSeqNoAtTimerExpireEnd >= respDatagramSeqNo; } // if we receive a successful response then reset to a // reasonable timer period if ( validResponse ) { double measured = currentTime - this->timeAtLastSend; this->iiu.updateRTTE ( guard, measured ); if ( this->searchResponses < UINT_MAX ) { this->searchResponses++; if ( this->searchResponses == this->searchAttempts ) { if ( this->chanListReqPending.count () ) { // // when we get 100% success immediately // send another search request // debugPrintf ( ( "All requests succesful, set timer delay to zero\n" ) ); this->timer.start ( *this, currentTime ); } } } } } void searchTimer::uninstallChan ( epicsGuard < epicsMutex > & cacGuard, nciu & chan ) { cacGuard.assertIdenticalMutex ( this->mutex ); unsigned ulistmem = static_cast ( chan.channelNode::listMember ); unsigned uReqBase = static_cast ( channelNode::cs_searchReqPending0 ); if ( ulistmem == this->index + uReqBase ) { this->chanListReqPending.remove ( chan ); } else { unsigned uRespBase = static_cast ( channelNode::cs_searchRespPending0 ); if ( ulistmem == this->index + uRespBase ) { this->chanListRespPending.remove ( chan ); } else { throw std::runtime_error ( "uninstalling channel search timer, but channel " "state is wrong" ); } } chan.channelNode::listMember = channelNode::cs_none; } double searchTimer::period ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); return (1 << this->index ) * this->iiu.getRTTE ( guard ); } searchTimerNotify::~searchTimerNotify () {} base-7.0.3.1/modules/ca/src/client/searchTimer.h0000664000577000060420000000726213557101274020141 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ // // // // L O S A L A M O S // Los Alamos National Laboratory // Los Alamos, New Mexico 87545 // // Copyright, 1986, The Regents of the University of California. // // // Author Jeffrey O. Hill // johill@lanl.gov // 505 665 1831 // #ifndef searchTimerh #define searchTimerh #ifdef epicsExportSharedSymbols # define searchTimerh_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "epicsMutex.h" #include "epicsGuard.h" #include "epicsTimer.h" #ifdef searchTimerh_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif #include "caProto.h" #include "netiiu.h" class searchTimerNotify { public: virtual ~searchTimerNotify () = 0; virtual void boostChannel ( epicsGuard < epicsMutex > &, nciu & ) = 0; virtual void noSearchRespNotify ( epicsGuard < epicsMutex > &, nciu &, unsigned ) = 0; virtual double getRTTE ( epicsGuard < epicsMutex > & ) const = 0; virtual void updateRTTE ( epicsGuard < epicsMutex > &, double rtte ) = 0; virtual bool datagramFlush ( epicsGuard < epicsMutex > &, const epicsTime & currentTime ) = 0; virtual ca_uint32_t datagramSeqNumber ( epicsGuard < epicsMutex > & ) const = 0; }; class searchTimer : private epicsTimerNotify { public: searchTimer ( class searchTimerNotify &, epicsTimerQueue &, const unsigned index, epicsMutex &, bool boostPossible ); virtual ~searchTimer (); void start ( epicsGuard < epicsMutex > & ); void shutdown ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); void moveChannels ( epicsGuard < epicsMutex > &, searchTimer & dest ); void installChannel ( epicsGuard < epicsMutex > &, nciu & ); void uninstallChan ( epicsGuard < epicsMutex > &, nciu & ); void uninstallChanDueToSuccessfulSearchResponse ( epicsGuard < epicsMutex > &, nciu &, ca_uint32_t respDatagramSeqNo, bool seqNumberIsValid, const epicsTime & currentTime ); void show ( unsigned level ) const; private: tsDLList < nciu > chanListReqPending; tsDLList < nciu > chanListRespPending; epicsTime timeAtLastSend; epicsTimer & timer; searchTimerNotify & iiu; epicsMutex & mutex; double framesPerTry; /* # of UDP frames per search try */ double framesPerTryCongestThresh; /* one half N tries w congest */ unsigned retry; unsigned searchAttempts; /* num search tries after last timer experation */ unsigned searchResponses; /* num search resp after last timer experation */ const unsigned index; ca_uint32_t dgSeqNoAtTimerExpireBegin; ca_uint32_t dgSeqNoAtTimerExpireEnd; const bool boostPossible; bool stopped; expireStatus expire ( const epicsTime & currentTime ); double period ( epicsGuard < epicsMutex > & ) const; searchTimer ( const searchTimer & ); // not implemented searchTimer & operator = ( const searchTimer & ); // not implemented }; #endif // ifdef searchTimerh base-7.0.3.1/modules/ca/src/client/sgAutoPtr.h0000664000577000060420000000517313557101274017622 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef sgAutoPtrh #define sgAutoPtrh template < class T > class sgAutoPtr { public: sgAutoPtr ( epicsGuard < epicsMutex > &, struct CASG & ); ~sgAutoPtr (); sgAutoPtr < T > & operator = ( T * ); T * operator -> (); T & operator * (); T * get (); T * release (); private: T * pNotify; struct CASG & sg; epicsGuard < epicsMutex > & guard; sgAutoPtr & operator = ( const sgAutoPtr & ); }; template < class T > inline sgAutoPtr < T > :: sgAutoPtr ( epicsGuard < epicsMutex > & guardIn, struct CASG & sgIn ) : pNotify ( 0 ), sg ( sgIn ), guard ( guardIn ) { } template < class T > inline sgAutoPtr < T > :: ~sgAutoPtr () { if ( this->pNotify ) { this->sg.ioPendingList.remove ( *this->pNotify ); this->sg.client. whenThereIsAnExceptionDestroySyncGroupIO ( this->guard, *this->pNotify ); } } template < class T > inline sgAutoPtr < T > & sgAutoPtr < T > :: operator = ( T * pNotifyIn ) { if ( this->pNotify ) { this->sg.ioPendingList.remove ( *this->pNotify ); this->sg.client. whenThereIsAnExceptionDestroySyncGroupIO ( this->guard, *this->pNotify ); } this->pNotify = pNotifyIn; this->sg.ioPendingList.add ( *this->pNotify ); return *this; } template < class T > inline T * sgAutoPtr < T > :: operator -> () { return this->pNotify; } template < class T > inline T & sgAutoPtr < T > :: operator * () { assert ( this->pNotify ); return * this->pNotify; } template < class T > inline T * sgAutoPtr < T > :: release () { T * pTmp = this->pNotify; this->pNotify = 0; return pTmp; } template < class T > inline T * sgAutoPtr < T > :: get () { return this->pNotify; } #endif // sgAutoPtrh base-7.0.3.1/modules/ca/src/client/syncGroup.h0000664000577000060420000002203713557101274017661 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef syncGrouph #define syncGrouph #ifdef epicsExportSharedSymbols # define syncGrouph_restore_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "tsDLList.h" #include "tsFreeList.h" #include "resourceLib.h" #include "epicsEvent.h" #include "compilerDependencies.h" #ifdef syncGrouph_restore_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif #include "cadef.h" #include "cacIO.h" static const unsigned CASG_MAGIC = 0xFAB4CAFE; class syncGroupNotify : public tsDLNode < syncGroupNotify > { public: syncGroupNotify (); virtual void destroy ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ) = 0; virtual bool ioPending ( epicsGuard < epicsMutex > & guard ) = 0; virtual void cancel ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & mutualExclusionGuard ) = 0; virtual void show ( epicsGuard < epicsMutex > &, unsigned level ) const = 0; protected: virtual ~syncGroupNotify (); syncGroupNotify ( const syncGroupNotify & ); syncGroupNotify & operator = ( const syncGroupNotify & ); }; struct CASG; class syncGroupReadNotify : public syncGroupNotify, public cacReadNotify { public: typedef void ( CASG :: * PRecycleFunc ) ( epicsGuard < epicsMutex > &, syncGroupReadNotify & ); static syncGroupReadNotify * factory ( tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > &, CASG &, PRecycleFunc, chid, void *pValueIn ); void destroy ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ); bool ioPending ( epicsGuard < epicsMutex > & guard ); void begin ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count ); void cancel ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; protected: syncGroupReadNotify ( CASG & sgIn, PRecycleFunc, chid, void * pValueIn ); virtual ~syncGroupReadNotify (); private: chid chan; PRecycleFunc pRecycleFunc; CASG & sg; void * pValue; const unsigned magic; cacChannel::ioid id; bool idIsValid; bool ioComplete; void operator delete ( void * ); void * operator new ( size_t, tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > & )) void completion ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void * pData ); void exception ( epicsGuard < epicsMutex > &, int status, const char * pContext, unsigned type, arrayElementCount count ); syncGroupReadNotify ( const syncGroupReadNotify & ); syncGroupReadNotify & operator = ( const syncGroupReadNotify & ); }; class syncGroupWriteNotify : public syncGroupNotify, public cacWriteNotify { public: typedef void ( CASG :: * PRecycleFunc ) ( epicsGuard < epicsMutex > &, syncGroupWriteNotify & ); static syncGroupWriteNotify * factory ( tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > &, CASG &, PRecycleFunc, chid ); void destroy ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ); bool ioPending ( epicsGuard < epicsMutex > & guard ); void begin ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void * pValueIn ); void cancel ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; protected: syncGroupWriteNotify ( struct CASG &, PRecycleFunc, chid ); virtual ~syncGroupWriteNotify (); // allocate only from pool private: chid chan; PRecycleFunc pRecycleFunc; CASG & sg; const unsigned magic; cacChannel::ioid id; bool idIsValid; bool ioComplete; void operator delete ( void * ); void * operator new ( size_t, tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > & )) void completion ( epicsGuard < epicsMutex > & ); void exception ( epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count ); syncGroupWriteNotify ( const syncGroupWriteNotify & ); syncGroupWriteNotify & operator = ( const syncGroupWriteNotify & ); }; struct ca_client_context; template < class T > class sgAutoPtr; struct CASG : public chronIntIdRes < CASG > { public: CASG ( epicsGuard < epicsMutex > &, ca_client_context & cacIn ); void destructor ( CallbackGuard &, epicsGuard < epicsMutex > & guard ); bool ioComplete ( CallbackGuard &, epicsGuard < epicsMutex > & guard ); bool verify ( epicsGuard < epicsMutex > & ) const; int block ( epicsGuard < epicsMutex > * pcbGuard, epicsGuard < epicsMutex > & guard, double timeout ); void reset ( CallbackGuard &, epicsGuard < epicsMutex > & ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; void show ( unsigned level ) const; void get ( epicsGuard < epicsMutex > &, chid pChan, unsigned type, arrayElementCount count, void * pValue ); void put ( epicsGuard < epicsMutex > &, chid pChan, unsigned type, arrayElementCount count, const void * pValue ); void completionNotify ( epicsGuard < epicsMutex > &, syncGroupNotify & ); int printFormated ( const char * pFormat, ... ); void exception ( epicsGuard < epicsMutex > &, int status, const char * pContext, const char * pFileName, unsigned lineNo ); void exception ( epicsGuard < epicsMutex > &, int status, const char * pContext, const char * pFileName, unsigned lineNo, oldChannelNotify & chan, unsigned type, arrayElementCount count, unsigned op ); void * operator new ( size_t size, tsFreeList < struct CASG, 128, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < struct CASG, 128, epicsMutexNOOP > & )) private: tsDLList < syncGroupNotify > ioPendingList; tsDLList < syncGroupNotify > ioCompletedList; epicsEvent sem; ca_client_context & client; unsigned magic; tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > freeListReadOP; tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > freeListWriteOP; void destroyPendingIO ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ); void destroyCompletedIO ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ); void recycleReadNotifyIO ( epicsGuard < epicsMutex > &, syncGroupReadNotify & ); void recycleWriteNotifyIO ( epicsGuard < epicsMutex > &, syncGroupWriteNotify & ); CASG ( const CASG & ); CASG & operator = ( const CASG & ); void operator delete ( void * ); ~CASG (); friend class sgAutoPtr < syncGroupWriteNotify >; friend class sgAutoPtr < syncGroupReadNotify >; }; class boolFlagManager { public: boolFlagManager ( bool & flag ); ~boolFlagManager (); void release (); private: bool * pBool; }; inline boolFlagManager::boolFlagManager ( bool & flagIn ) : pBool ( & flagIn ) { *this->pBool = true; } inline boolFlagManager::~boolFlagManager () { if ( this->pBool ) { *this->pBool = false; } } inline void boolFlagManager::release () { this->pBool = 0; } inline void * CASG::operator new ( size_t size, tsFreeList < struct CASG, 128, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #if defined ( CXX_PLACEMENT_DELETE ) inline void CASG::operator delete ( void * pCadaver, tsFreeList < struct CASG, 128, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif inline bool syncGroupWriteNotify::ioPending ( epicsGuard < epicsMutex > & /* guard */ ) { return ! this->ioComplete; } inline bool syncGroupReadNotify::ioPending ( epicsGuard < epicsMutex > & /* guard */ ) { return ! this->ioComplete; } #endif // ifdef syncGrouph base-7.0.3.1/modules/ca/src/client/syncGroupNotify.cpp0000664000577000060420000000156213557101274021405 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeffrey O. Hill * hill@luke.lanl.gov * (505) 665 1831 */ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #define epicsExportSharedSymbols #include "iocinf.h" #include "syncGroup.h" #include "oldAccess.h" syncGroupNotify::syncGroupNotify () { } syncGroupNotify::~syncGroupNotify () { } base-7.0.3.1/modules/ca/src/client/syncGroupReadNotify.cpp0000664000577000060420000001134513557101274022201 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeffrey O. Hill * hill@luke.lanl.gov * (505) 665 1831 */ #include #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "errlog.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "syncGroup.h" #include "oldAccess.h" syncGroupReadNotify::syncGroupReadNotify ( CASG & sgIn, PRecycleFunc pRecycleFuncIn, chid pChan, void * pValueIn ) : chan ( pChan ), pRecycleFunc ( pRecycleFuncIn ), sg ( sgIn ), pValue ( pValueIn ), magic ( CASG_MAGIC ), id ( 0u ), idIsValid ( false ), ioComplete ( false ) { } void syncGroupReadNotify::begin ( epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount count ) { this->chan->eliminateExcessiveSendBacklog ( guard ); this->ioComplete = false; boolFlagManager mgr ( this->idIsValid ); this->chan->read ( guard, type, count, *this, &this->id ); mgr.release (); } void syncGroupReadNotify::cancel ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & mutualExcusionGuard ) { if ( this->idIsValid ) { this->chan->ioCancel ( callbackGuard, mutualExcusionGuard, this->id ); this->idIsValid = false; } } syncGroupReadNotify * syncGroupReadNotify::factory ( tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > & freeList, struct CASG & sg, PRecycleFunc pRecycleFunc, chid chan, void * pValueIn ) { return new ( freeList ) syncGroupReadNotify ( sg, pRecycleFunc, chan, pValueIn ); } void syncGroupReadNotify::destroy ( CallbackGuard &, epicsGuard < epicsMutex > & guard ) { CASG & sgRef ( this->sg ); this->~syncGroupReadNotify (); ( sgRef.*pRecycleFunc ) ( guard, *this ); } syncGroupReadNotify::~syncGroupReadNotify () { assert ( ! this->idIsValid ); } void syncGroupReadNotify::completion ( epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount count, const void * pData ) { if ( this->magic != CASG_MAGIC ) { this->sg.printFormated ( "cac: sync group io_complete(): bad sync grp op magic number?\n" ); return; } if ( this->pValue ) { size_t size = dbr_size_n ( type, count ); memcpy ( this->pValue, pData, size ); } this->sg.completionNotify ( guard, *this ); this->idIsValid = false; this->ioComplete = true; } void syncGroupReadNotify::exception ( epicsGuard < epicsMutex > & guard, int status, const char * pContext, unsigned type, arrayElementCount count ) { if ( this->magic != CASG_MAGIC ) { this->sg.printFormated ( "cac: sync group io_complete(): bad sync grp op magic number?\n" ); return; } this->idIsValid = false; this->sg.exception ( guard, status, pContext, __FILE__, __LINE__, *this->chan, type, count, CA_OP_GET ); // // This notify is left installed at this point as a place holder indicating that // all requests have not been completed. This notify is not uninstalled until // CASG::block() times out or unit they call CASG::reset(). // } void syncGroupReadNotify::show ( epicsGuard < epicsMutex > &, unsigned level ) const { ::printf ( "pending sg read op: pVal=%p\n", this->pValue ); if ( level > 0u ) { ::printf ( "pending sg op: magic=%u sg=%p\n", this->magic, static_cast < void * > ( & this->sg ) ); } } void syncGroupReadNotify::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } void * syncGroupReadNotify::operator new ( size_t size, tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #if defined ( CXX_PLACEMENT_DELETE ) void syncGroupReadNotify::operator delete ( void *pCadaver, tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > &freeList ) { freeList.release ( pCadaver ); } #endif base-7.0.3.1/modules/ca/src/client/syncGroupWriteNotify.cpp0000664000577000060420000001076713557101274022427 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeffrey O. Hill * hill@luke.lanl.gov * (505) 665 1831 */ #include #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "errlog.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "syncGroup.h" #include "oldAccess.h" syncGroupWriteNotify::syncGroupWriteNotify ( CASG & sgIn, PRecycleFunc pRecycleFuncIn, chid pChan ) : chan ( pChan ), pRecycleFunc ( pRecycleFuncIn ), sg ( sgIn ), magic ( CASG_MAGIC ), id ( 0u ), idIsValid ( false ), ioComplete ( false ) { } void syncGroupWriteNotify::begin ( epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount count, const void * pValueIn ) { this->chan->eliminateExcessiveSendBacklog ( guard ); this->ioComplete = false; boolFlagManager mgr ( this->idIsValid ); this->chan->write ( guard, type, count, pValueIn, *this, &this->id ); mgr.release (); } void syncGroupWriteNotify::cancel ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & mutualExcusionGuard ) { if ( this->idIsValid ) { this->chan->ioCancel ( callbackGuard, mutualExcusionGuard, this->id ); this->idIsValid = false; } } syncGroupWriteNotify * syncGroupWriteNotify::factory ( tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > &freeList, struct CASG & sg, PRecycleFunc pRecycleFunc, chid chan ) { return new ( freeList ) syncGroupWriteNotify ( sg, pRecycleFunc, chan ); } void syncGroupWriteNotify::destroy ( CallbackGuard &, epicsGuard < epicsMutex > & guard ) { CASG & sgRef ( this->sg ); this->~syncGroupWriteNotify (); ( sgRef.*pRecycleFunc ) ( guard, *this ); } syncGroupWriteNotify::~syncGroupWriteNotify () { assert ( ! this->idIsValid ); } void syncGroupWriteNotify::completion ( epicsGuard < epicsMutex > & guard ) { if ( this->magic != CASG_MAGIC ) { this->sg.printFormated ( "cac: sync group io_complete(): bad sync grp op magic number?\n" ); return; } this->sg.completionNotify ( guard, *this ); this->idIsValid = false; this->ioComplete = true; } void syncGroupWriteNotify::exception ( epicsGuard < epicsMutex > & guard, int status, const char *pContext, unsigned type, arrayElementCount count ) { if ( this->magic != CASG_MAGIC ) { this->sg.printFormated ( "cac: sync group io_complete(): bad sync grp op magic number?\n" ); return; } this->sg.exception ( guard, status, pContext, __FILE__, __LINE__, *this->chan, type, count, CA_OP_PUT ); this->idIsValid = false; // // This notify is left installed at this point as a place holder indicating that // all requests have not been completed. This notify is not uninstalled until // CASG::block() times out or unit they call CASG::reset(). // } void syncGroupWriteNotify::show ( epicsGuard < epicsMutex > &, unsigned level ) const { ::printf ( "pending write sg op\n" ); if ( level > 0u ) { ::printf ( "pending sg op: magic=%u sg=%p\n", this->magic, static_cast < void * > ( &this->sg ) ); } } void syncGroupWriteNotify::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } void * syncGroupWriteNotify::operator new ( size_t size, tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #if defined ( CXX_PLACEMENT_DELETE ) void syncGroupWriteNotify::operator delete ( void *pCadaver, tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > &freeList ) { freeList.release ( pCadaver ); } #endif base-7.0.3.1/modules/ca/src/client/syncgrp.cpp0000664000577000060420000002342313557101274017710 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeffrey O. Hill * hill@luke.lanl.gov * (505) 665 1831 */ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #define epicsExportSharedSymbols #include "iocinf.h" #include "oldAccess.h" #include "syncGroup.h" /* * ca_sg_create() */ extern "C" int epicsShareAPI ca_sg_create ( CA_SYNC_GID * pgid ) { ca_client_context * pcac; int caStatus; CASG * pcasg; caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } try { epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); pcasg = new ( pcac->casgFreeList ) CASG ( guard, *pcac ); *pgid = pcasg->getId (); return ECA_NORMAL; } catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } catch ( ... ) { return ECA_INTERNAL; } } int ca_sync_group_destroy ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard, ca_client_context & cac, const CA_SYNC_GID gid ) { int caStatus; CASG * pcasg = cac.lookupCASG ( guard, gid ); if ( pcasg ) { pcasg->destructor ( cbGuard, guard ); cac.casgFreeList.release ( pcasg ); caStatus = ECA_NORMAL; } else { caStatus = ECA_BADSYNCGRP; } return caStatus; } /* * ca_sg_delete() */ extern "C" int epicsShareAPI ca_sg_delete ( const CA_SYNC_GID gid ) { ca_client_context * pcac; int caStatus = fetchClientContext ( & pcac ); if ( caStatus == ECA_NORMAL ) { if ( pcac->pCallbackGuard.get() && pcac->createdByThread == epicsThreadGetIdSelf () ) { epicsGuard < epicsMutex > guard ( pcac->mutex ); caStatus = ca_sync_group_destroy ( *pcac->pCallbackGuard.get(), guard, *pcac, gid ); } else { // // we will definately stall out here if all of the // following are true // // o user creates non-preemtive mode client library context // o user doesnt periodically call a ca function // o user calls this function from an auxiillary thread // CallbackGuard cbGuard ( pcac->cbMutex ); epicsGuard < epicsMutex > guard ( pcac->mutex ); caStatus = ca_sync_group_destroy ( cbGuard, guard, *pcac, gid ); } } return caStatus; } void sync_group_reset ( ca_client_context & client, CASG & sg ) { if ( client.pCallbackGuard.get() && client.createdByThread == epicsThreadGetIdSelf () ) { epicsGuard < epicsMutex > guard ( client.mutex ); sg.reset ( *client.pCallbackGuard.get(), guard ); } else { // // we will definately stall out here if all of the // following are true // // o user creates non-preemtive mode client library context // o user doesnt periodically call a ca function // o user calls this function from an auxiillary thread // CallbackGuard cbGuard ( client.cbMutex ); epicsGuard < epicsMutex > guard ( client.mutex ); sg.reset ( cbGuard, guard ); } } // // ca_sg_block () // // !!!! This routine is only visible in the old interface - or in a new ST interface. // !!!! In the old interface we restrict thread attach so that calls from threads // !!!! other than the initializing thread are not allowed if preemptive callback // !!!! is disabled. This prevents the preemptive callback lock from being released // !!!! by other threads than the one that locked it. // extern "C" int epicsShareAPI ca_sg_block ( const CA_SYNC_GID gid, ca_real timeout ) { ca_client_context *pcac; int status = fetchClientContext ( &pcac ); if ( status == ECA_NORMAL ) { CASG * pcasg; { epicsGuard < epicsMutex > guard ( pcac->mutex ); pcasg = pcac->lookupCASG ( guard, gid ); if ( pcasg ) { status = pcasg->block ( pcac->pCallbackGuard.get (), guard, timeout ); } else { status = ECA_BADSYNCGRP; } } if ( pcasg ) { sync_group_reset ( *pcac, *pcasg ); } } return status; } /* * ca_sg_reset */ extern "C" int epicsShareAPI ca_sg_reset ( const CA_SYNC_GID gid ) { ca_client_context *pcac; int caStatus = fetchClientContext (&pcac); if ( caStatus == ECA_NORMAL ) { CASG * pcasg; { epicsGuard < epicsMutex > guard ( pcac->mutex ); pcasg = pcac->lookupCASG ( guard, gid ); } if ( pcasg ) { sync_group_reset ( *pcac, *pcasg ); caStatus = ECA_NORMAL; } else { caStatus = ECA_BADSYNCGRP; } } return caStatus; } /* * ca_sg_stat */ extern "C" int epicsShareAPI ca_sg_stat ( const CA_SYNC_GID gid ) { ca_client_context * pcac; int caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); CASG * pcasg = pcac->lookupCASG ( guard, gid ); if ( ! pcasg ) { ::printf ( "Bad Sync Group Id\n"); return ECA_BADSYNCGRP; } pcasg->show ( guard, 1000u ); return ECA_NORMAL; } /* * ca_sg_test */ extern "C" int epicsShareAPI ca_sg_test ( const CA_SYNC_GID gid ) { ca_client_context * pcac; int caStatus = fetchClientContext ( &pcac ); if ( caStatus == ECA_NORMAL ) { epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); CASG * pcasg = pcac->lookupCASG ( guard, gid ); if ( pcasg ) { bool isComplete; if ( pcac->pCallbackGuard.get() && pcac->createdByThread == epicsThreadGetIdSelf () ) { epicsGuard < epicsMutex > guard ( pcac->mutex ); isComplete = pcasg->ioComplete ( *pcac->pCallbackGuard.get(), guard ); } else { // // we will definately stall out here if all of the // following are true // // o user creates non-preemtive mode client library context // o user doesnt periodically call a ca function // o user calls this function from an auxiillary thread // CallbackGuard cbGuard ( pcac->cbMutex ); epicsGuard < epicsMutex > guard ( pcac->mutex ); isComplete = pcasg->ioComplete ( cbGuard, guard ); } if ( isComplete ) { caStatus = ECA_IODONE; } else{ caStatus = ECA_IOINPROGRESS; } } else { caStatus = ECA_BADSYNCGRP; } } return caStatus; } /* * ca_sg_array_put() */ extern "C" int epicsShareAPI ca_sg_array_put ( const CA_SYNC_GID gid, chtype type, arrayElementCount count, chid pChan, const void *pValue ) { ca_client_context *pcac; int caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); CASG * const pcasg = pcac->lookupCASG ( guard, gid ); if ( ! pcasg ) { return ECA_BADSYNCGRP; } try { pcasg->put ( guard, pChan, type, static_cast < unsigned > ( count ), pValue ); return ECA_NORMAL; } catch ( cacChannel::badString & ) { return ECA_BADSTR; } catch ( cacChannel::badType & ) { return ECA_BADTYPE; } catch ( cacChannel::outOfBounds & ) { return ECA_BADCOUNT; } catch ( cacChannel::noWriteAccess & ) { return ECA_NOWTACCESS; } catch ( cacChannel::notConnected & ) { return ECA_DISCONN; } catch ( cacChannel::unsupportedByService & ) { return ECA_UNAVAILINSERV; } catch ( cacChannel::requestTimedOut & ) { return ECA_TIMEOUT; } catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } catch ( ... ) { return ECA_INTERNAL; } } /* * ca_sg_array_get() */ extern "C" int epicsShareAPI ca_sg_array_get ( const CA_SYNC_GID gid, chtype type, arrayElementCount count, chid pChan, void *pValue ) { ca_client_context *pcac; int caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); CASG * const pcasg = pcac->lookupCASG ( guard, gid ); if ( ! pcasg ) { return ECA_BADSYNCGRP; } try { pcasg->get ( guard, pChan, type, static_cast < unsigned > ( count ), pValue ); return ECA_NORMAL; } catch ( cacChannel::badString & ) { return ECA_BADSTR; } catch ( cacChannel::badType & ) { return ECA_BADTYPE; } catch ( cacChannel::outOfBounds & ) { return ECA_BADCOUNT; } catch ( cacChannel::noReadAccess & ) { return ECA_NORDACCESS; } catch ( cacChannel::notConnected & ) { return ECA_DISCONN; } catch ( cacChannel::unsupportedByService & ) { return ECA_UNAVAILINSERV; } catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } catch ( ... ) { return ECA_INTERNAL; } } base-7.0.3.1/modules/ca/src/client/tcpRecvThread.cpp0000664000577000060420000000102713557101274020755 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ base-7.0.3.1/modules/ca/src/client/tcpRecvWatchdog.cpp0000664000577000060420000002135113557101274021310 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "iocinf.h" #include "cac.h" #include "virtualCircuit.h" // // the recv watchdog timer is active when this object is created // tcpRecvWatchdog::tcpRecvWatchdog ( epicsMutex & cbMutexIn, cacContextNotify & ctxNotifyIn, epicsMutex & mutexIn, tcpiiu & iiuIn, double periodIn, epicsTimerQueue & queueIn ) : period ( periodIn ), timer ( queueIn.createTimer () ), cbMutex ( cbMutexIn ), ctxNotify ( ctxNotifyIn ), mutex ( mutexIn ), iiu ( iiuIn ), probeResponsePending ( false ), beaconAnomaly ( true ), probeTimeoutDetected ( false ), shuttingDown ( false ) { } tcpRecvWatchdog::~tcpRecvWatchdog () { this->timer.destroy (); } epicsTimerNotify::expireStatus tcpRecvWatchdog::expire ( const epicsTime & /* currentTime */ ) { epicsGuard < epicsMutex > guard ( this->mutex ); if ( this->shuttingDown ) { return noRestart; } if ( this->probeResponsePending ) { if ( this->iiu.receiveThreadIsBusy ( guard ) ) { return expireStatus ( restart, CA_ECHO_TIMEOUT ); } { # ifdef DEBUG char hostName[128]; this->iiu.getHostName ( guard, hostName, sizeof (hostName) ); debugPrintf ( ( "CA server \"%s\" unresponsive after %g inactive sec" "- disconnecting.\n", hostName, this->period ) ); # endif // to get the callback lock safely we must reorder // the lock hierarchy epicsGuardRelease < epicsMutex > unguard ( guard ); { // callback lock is required because channel disconnect // state change is initiated from this thread, and // this can cause their disconnect notify callback // to be invoked. callbackManager mgr ( this->ctxNotify, this->cbMutex ); epicsGuard < epicsMutex > tmpGuard ( this->mutex ); this->iiu.receiveTimeoutNotify ( mgr, tmpGuard ); this->probeTimeoutDetected = true; } } return noRestart; } else { if ( this->iiu.receiveThreadIsBusy ( guard ) ) { return expireStatus ( restart, this->period ); } this->probeTimeoutDetected = false; this->probeResponsePending = this->iiu.setEchoRequestPending ( guard ); debugPrintf ( ("circuit timed out - sending echo request\n") ); return expireStatus ( restart, CA_ECHO_TIMEOUT ); } } void tcpRecvWatchdog::beaconArrivalNotify ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( ! ( this->shuttingDown || this->beaconAnomaly || this->probeResponsePending ) ) { this->timer.start ( *this, this->period ); debugPrintf ( ("saw a normal beacon - reseting circuit receive watchdog\n") ); } } // // be careful about using beacons to reset the connection // time out watchdog until we have received a ping response // from the IOC (this makes the software detect reconnects // faster when the server is rebooted twice in rapid // succession before a 1st or 2nd beacon has been received) // void tcpRecvWatchdog::beaconAnomalyNotify ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->beaconAnomaly = true; debugPrintf ( ("Saw an abnormal beacon\n") ); } void tcpRecvWatchdog::messageArrivalNotify ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( ! ( this->shuttingDown || this->probeResponsePending ) ) { this->beaconAnomaly = false; this->timer.start ( *this, this->period ); debugPrintf ( ("received a message - reseting circuit recv watchdog\n") ); } } void tcpRecvWatchdog::probeResponseNotify ( epicsGuard < epicsMutex > & cbGuard ) { bool restartNeeded = false; double restartDelay = DBL_MAX; { epicsGuard < epicsMutex > guard ( this->mutex ); if ( this->probeResponsePending && ! this->shuttingDown ) { restartNeeded = true; if ( this->probeTimeoutDetected ) { this->probeTimeoutDetected = false; this->probeResponsePending = this->iiu.setEchoRequestPending ( guard ); restartDelay = CA_ECHO_TIMEOUT; debugPrintf ( ("late probe response - sending another probe request\n") ); } else { this->probeResponsePending = false; restartDelay = this->period; this->iiu.responsiveCircuitNotify ( cbGuard, guard ); debugPrintf ( ("probe response on time - circuit was tagged reponsive if unresponsive\n") ); } } } if ( restartNeeded ) { this->timer.start ( *this, restartDelay ); debugPrintf ( ("recv wd restarted with delay %f\n", restartDelay) ); } } // // The thread for outgoing requests in the client runs // at a higher priority than the thread in the client // that receives responses. Therefore, there could // be considerable large array write send backlog that // is delaying departure of an echo request and also // interrupting delivery of an echo response. // We must be careful not to timeout the echo response as // long as we see indication of regular departures of // message buffers from the client in a situation where // we know that the TCP send queueing has been exceeded. // The send watchdog will be responsible for detecting // dead connections in this case. // void tcpRecvWatchdog::sendBacklogProgressNotify ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); // We dont set "beaconAnomaly" to be false here because, after we see a // beacon anomaly (which could be transiently detecting a reboot) we will // not trust the beacon as an indicator of a healthy server until we // receive at least one message from the server. if ( this->probeResponsePending && ! this->shuttingDown ) { this->timer.start ( *this, CA_ECHO_TIMEOUT ); debugPrintf ( ("saw heavy send backlog - reseting circuit recv watchdog\n") ); } } void tcpRecvWatchdog::connectNotify ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( this->shuttingDown ) { return; } this->timer.start ( *this, this->period ); debugPrintf ( ("connected to the server - initiating circuit recv watchdog\n") ); } void tcpRecvWatchdog::sendTimeoutNotify ( epicsGuard < epicsMutex > & /* cbGuard */, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); bool restartNeeded = false; if ( ! ( this->probeResponsePending || this->shuttingDown ) ) { this->probeResponsePending = this->iiu.setEchoRequestPending ( guard ); restartNeeded = true; } if ( restartNeeded ) { this->timer.start ( *this, CA_ECHO_TIMEOUT ); } debugPrintf ( ("TCP send timed out - sending echo request\n") ); } void tcpRecvWatchdog::cancel () { this->timer.cancel (); debugPrintf ( ("canceling TCP recv watchdog\n") ); } void tcpRecvWatchdog::shutdown () { { epicsGuard < epicsMutex > guard ( this->mutex ); this->shuttingDown = true; } this->timer.cancel (); debugPrintf ( ("canceling TCP recv watchdog\n") ); } double tcpRecvWatchdog::delay () const { return this->timer.getExpireDelay (); } void tcpRecvWatchdog::show ( unsigned level ) const { epicsGuard < epicsMutex > guard ( this->mutex ); ::printf ( "Receive virtual circuit watchdog at %p, period %f\n", static_cast ( this ), this->period ); if ( level > 0u ) { ::printf ( "\t%s %s %s\n", this->probeResponsePending ? "probe-response-pending" : "", this->beaconAnomaly ? "beacon-anomaly-detected" : "", this->probeTimeoutDetected ? "probe-response-timeout" : "" ); } } base-7.0.3.1/modules/ca/src/client/tcpRecvWatchdog.h0000664000577000060420000000513213557101274020754 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef tcpRecvWatchdogh #define tcpRecvWatchdogh #ifdef epicsExportSharedSymbols # define tcpRecvWatchdogh_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "epicsTimer.h" #ifdef tcpRecvWatchdogh_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif class tcpiiu; class tcpRecvWatchdog : private epicsTimerNotify { public: tcpRecvWatchdog ( epicsMutex & cbMutex, cacContextNotify & ctxNotify, epicsMutex & mutex, tcpiiu &, double periodIn, epicsTimerQueue & ); virtual ~tcpRecvWatchdog (); void sendBacklogProgressNotify ( epicsGuard < epicsMutex > & ); void messageArrivalNotify ( epicsGuard < epicsMutex > & guard ); void probeResponseNotify ( epicsGuard < epicsMutex > & ); void beaconArrivalNotify ( epicsGuard < epicsMutex > & ); void beaconAnomalyNotify ( epicsGuard < epicsMutex > & ); void connectNotify ( epicsGuard < epicsMutex > & ); void sendTimeoutNotify ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); void cancel (); void shutdown (); void show ( unsigned level ) const; double delay () const; private: const double period; epicsTimer & timer; epicsMutex & cbMutex; cacContextNotify & ctxNotify; epicsMutex & mutex; tcpiiu & iiu; bool probeResponsePending; bool beaconAnomaly; bool probeTimeoutDetected; bool shuttingDown; expireStatus expire ( const epicsTime & currentTime ); tcpRecvWatchdog ( const tcpRecvWatchdog & ); tcpRecvWatchdog & operator = ( const tcpRecvWatchdog & ); }; #endif // #ifndef tcpRecvWatchdogh base-7.0.3.1/modules/ca/src/client/tcpSendWatchdog.cpp0000664000577000060420000000437313557101274021307 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "iocinf.h" #include "cac.h" #include "virtualCircuit.h" tcpSendWatchdog::tcpSendWatchdog ( epicsMutex & cbMutexIn, cacContextNotify & ctxNotifyIn, epicsMutex & mutexIn, tcpiiu & iiuIn, double periodIn, epicsTimerQueue & queueIn ) : period ( periodIn ), timer ( queueIn.createTimer () ), cbMutex ( cbMutexIn ), ctxNotify ( ctxNotifyIn ), mutex ( mutexIn ), iiu ( iiuIn ) { } tcpSendWatchdog::~tcpSendWatchdog () { this->timer.destroy (); } epicsTimerNotify::expireStatus tcpSendWatchdog::expire ( const epicsTime & /* currentTime */ ) { { epicsGuard < epicsMutex > guard ( this->mutex ); if ( this->iiu.receiveThreadIsBusy ( guard ) ) { return expireStatus ( restart, this->period ); } } { callbackManager mgr ( this->ctxNotify, this->cbMutex ); epicsGuard < epicsMutex > guard ( this->mutex ); # ifdef DEBUG char hostName[128]; this->iiu.getHostName ( guard, hostName, sizeof ( hostName ) ); debugPrintf ( ( "Request not accepted by CA server %s for %g sec. Disconnecting.\n", hostName, this->period ) ); # endif this->iiu.sendTimeoutNotify ( mgr, guard ); } return noRestart; } void tcpSendWatchdog::start ( const epicsTime & /* currentTime */ ) { this->timer.start ( *this, this->period ); } void tcpSendWatchdog::cancel () { this->timer.cancel (); } base-7.0.3.1/modules/ca/src/client/tcpSendWatchdog.h0000664000577000060420000000362413557101274020752 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef tcpSendWatchdogh #define tcpSendWatchdogh #ifdef epicsExportSharedSymbols # define tcpSendWatchdogh_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "epicsTimer.h" #ifdef tcpSendWatchdogh_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif class tcpSendWatchdog : private epicsTimerNotify { public: tcpSendWatchdog ( epicsMutex & cbMutex, cacContextNotify & ctxNotify, epicsMutex & mutex, tcpiiu &, double periodIn, epicsTimerQueue & queueIn ); virtual ~tcpSendWatchdog (); void start ( const epicsTime & ); void cancel (); private: const double period; epicsTimer & timer; epicsMutex & cbMutex; cacContextNotify & ctxNotify; epicsMutex & mutex; tcpiiu & iiu; expireStatus expire ( const epicsTime & currentTime ); tcpSendWatchdog ( const tcpSendWatchdog & ); tcpSendWatchdog & operator = ( const tcpSendWatchdog & ); }; #endif // #ifndef tcpSendWatchdog base-7.0.3.1/modules/ca/src/client/tcpiiu.cpp0000664000577000060420000022271413557101274017524 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill * */ #ifdef _MSC_VER # pragma warning(disable:4355) #endif #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include #include #include #include "errlog.h" #define epicsExportSharedSymbols #include "localHostName.h" #include "iocinf.h" #include "virtualCircuit.h" #include "inetAddrID.h" #include "cac.h" #include "netiiu.h" #include "hostNameCache.h" #include "net_convert.h" #include "bhe.h" #include "epicsSignal.h" #include "caerr.h" #include "udpiiu.h" using namespace std; tcpSendThread::tcpSendThread ( class tcpiiu & iiuIn, const char * pName, unsigned stackSize, unsigned priority ) : thread ( *this, pName, stackSize, priority ), iiu ( iiuIn ) { } tcpSendThread::~tcpSendThread () { } void tcpSendThread::start () { this->thread.start (); } void tcpSendThread::show ( unsigned /* level */ ) const { } void tcpSendThread::exitWait () { this->thread.exitWait (); } void tcpSendThread::run () { try { epicsGuard < epicsMutex > guard ( this->iiu.mutex ); bool laborPending = false; while ( true ) { // dont wait if there is still labor to be done below if ( ! laborPending ) { epicsGuardRelease < epicsMutex > unguard ( guard ); this->iiu.sendThreadFlushEvent.wait (); } if ( this->iiu.state != tcpiiu::iiucs_connected ) { break; } laborPending = false; bool flowControlLaborNeeded = this->iiu.busyStateDetected != this->iiu.flowControlActive; bool echoLaborNeeded = this->iiu.echoRequestPending; this->iiu.echoRequestPending = false; if ( flowControlLaborNeeded ) { if ( this->iiu.flowControlActive ) { this->iiu.disableFlowControlRequest ( guard ); this->iiu.flowControlActive = false; debugPrintf ( ( "fc off\n" ) ); } else { this->iiu.enableFlowControlRequest ( guard ); this->iiu.flowControlActive = true; debugPrintf ( ( "fc on\n" ) ); } } if ( echoLaborNeeded ) { this->iiu.echoRequest ( guard ); } while ( nciu * pChan = this->iiu.createReqPend.get () ) { this->iiu.createChannelRequest ( *pChan, guard ); if ( CA_V42 ( this->iiu.minorProtocolVersion ) ) { this->iiu.createRespPend.add ( *pChan ); pChan->channelNode::listMember = channelNode::cs_createRespPend; } else { // This wakes up the resp thread so that it can call // the connect callback. This isnt maximally efficent // but it has the excellent side effect of not requiring // that the UDP thread take the callback lock. There are // almost no V42 servers left at this point. this->iiu.v42ConnCallbackPend.add ( *pChan ); pChan->channelNode::listMember = channelNode::cs_v42ConnCallbackPend; this->iiu.echoRequestPending = true; laborPending = true; } if ( this->iiu.sendQue.flushBlockThreshold () ) { laborPending = true; break; } } while ( nciu * pChan = this->iiu.subscripReqPend.get () ) { // this installs any subscriptions as needed pChan->resubscribe ( guard ); this->iiu.connectedList.add ( *pChan ); pChan->channelNode::listMember = channelNode::cs_connected; if ( this->iiu.sendQue.flushBlockThreshold () ) { laborPending = true; break; } } while ( nciu * pChan = this->iiu.subscripUpdateReqPend.get () ) { // this updates any subscriptions as needed pChan->sendSubscriptionUpdateRequests ( guard ); this->iiu.connectedList.add ( *pChan ); pChan->channelNode::listMember = channelNode::cs_connected; if ( this->iiu.sendQue.flushBlockThreshold () ) { laborPending = true; break; } } if ( ! this->iiu.sendThreadFlush ( guard ) ) { break; } } if ( this->iiu.state == tcpiiu::iiucs_clean_shutdown ) { this->iiu.sendThreadFlush ( guard ); // this should cause the server to disconnect from // the client int status = ::shutdown ( this->iiu.sock, SHUT_WR ); if ( status ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ("CAC TCP clean socket shutdown error was %s\n", sockErrBuf ); } } } catch ( ... ) { errlogPrintf ( "cac: tcp send thread received an unexpected exception " "- disconnecting\n"); // this should cause the server to disconnect from // the client int status = ::shutdown ( this->iiu.sock, SHUT_WR ); if ( status ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ("CAC TCP clean socket shutdown error was %s\n", sockErrBuf ); } } this->iiu.sendDog.cancel (); this->iiu.recvDog.shutdown (); while ( ! this->iiu.recvThread.exitWait ( 30.0 ) ) { // it is possible to get stuck here if the user calls // ca_context_destroy() when a circuit isnt known to // be unresponsive, but is. That situation is probably // rare, and the IP kernel might have a timeout for // such situations, nevertheless we will attempt to deal // with it here after waiting a reasonable amount of time // for a clean shutdown to finish. epicsGuard < epicsMutex > guard ( this->iiu.mutex ); this->iiu.initiateAbortShutdown ( guard ); } // user threads blocking for send backlog to be reduced // will abort their attempt to get space if // the state of the tcpiiu changes from connected to a // disconnecting state. Nevertheless, we need to wait // for them to finish prior to destroying the IIU. { epicsGuard < epicsMutex > guard ( this->iiu.mutex ); while ( this->iiu.blockingForFlush ) { epicsGuardRelease < epicsMutex > unguard ( guard ); epicsThreadSleep ( 0.1 ); } } this->iiu.cacRef.destroyIIU ( this->iiu ); } unsigned tcpiiu::sendBytes ( const void *pBuf, unsigned nBytesInBuf, const epicsTime & currentTime ) { unsigned nBytes = 0u; assert ( nBytesInBuf <= INT_MAX ); this->sendDog.start ( currentTime ); while ( true ) { int status = ::send ( this->sock, static_cast < const char * > (pBuf), (int) nBytesInBuf, 0 ); if ( status > 0 ) { nBytes = static_cast ( status ); // printf("SEND: %u\n", nBytes ); break; } else { epicsGuard < epicsMutex > guard ( this->mutex ); if ( this->state != iiucs_connected && this->state != iiucs_clean_shutdown ) { break; } // winsock indicates disconnect by returning zero here if ( status == 0 ) { this->disconnectNotify ( guard ); break; } int localError = SOCKERRNO; if ( localError == SOCK_EINTR ) { continue; } if ( localError == SOCK_ENOBUFS ) { errlogPrintf ( "CAC: system low on network buffers " "- send retry in 15 seconds\n" ); { epicsGuardRelease < epicsMutex > unguard ( guard ); epicsThreadSleep ( 15.0 ); } continue; } if ( localError != SOCK_EPIPE && localError != SOCK_ECONNRESET && localError != SOCK_ETIMEDOUT && localError != SOCK_ECONNABORTED && localError != SOCK_SHUTDOWN ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAC: unexpected TCP send error: %s\n", sockErrBuf ); } this->disconnectNotify ( guard ); break; } } this->sendDog.cancel (); return nBytes; } void tcpiiu::recvBytes ( void * pBuf, unsigned nBytesInBuf, statusWireIO & stat ) { assert ( nBytesInBuf <= INT_MAX ); while ( true ) { int status = ::recv ( this->sock, static_cast ( pBuf ), static_cast ( nBytesInBuf ), 0 ); if ( status > 0 ) { stat.bytesCopied = static_cast ( status ); assert ( stat.bytesCopied <= nBytesInBuf ); stat.circuitState = swioConnected; return; } else { epicsGuard < epicsMutex > guard ( this->mutex ); if ( status == 0 ) { this->disconnectNotify ( guard ); stat.bytesCopied = 0u; stat.circuitState = swioPeerHangup; return; } // if the circuit was locally aborted then supress // warning messages about bad file descriptor etc if ( this->state != iiucs_connected && this->state != iiucs_clean_shutdown ) { stat.bytesCopied = 0u; stat.circuitState = swioLocalAbort; return; } int localErrno = SOCKERRNO; if ( localErrno == SOCK_SHUTDOWN ) { stat.bytesCopied = 0u; stat.circuitState = swioPeerHangup; return; } if ( localErrno == SOCK_EINTR ) { continue; } if ( localErrno == SOCK_ENOBUFS ) { errlogPrintf ( "CAC: system low on network buffers " "- receive retry in 15 seconds\n" ); { epicsGuardRelease < epicsMutex > unguard ( guard ); epicsThreadSleep ( 15.0 ); } continue; } char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); // the replacable printf handler isnt called here // because it reqires a callback lock which probably // isnt appropriate here char name[64]; this->hostNameCacheInstance.getName ( name, sizeof ( name ) ); errlogPrintf ( "Unexpected problem with CA circuit to" " server \"%s\" was \"%s\" - disconnecting\n", name, sockErrBuf ); stat.bytesCopied = 0u; stat.circuitState = swioPeerAbort; return; } } } tcpRecvThread::tcpRecvThread ( class tcpiiu & iiuIn, class epicsMutex & cbMutexIn, cacContextNotify & ctxNotifyIn, const char * pName, unsigned int stackSize, unsigned int priority ) : thread ( *this, pName, stackSize, priority ), iiu ( iiuIn ), cbMutex ( cbMutexIn ), ctxNotify ( ctxNotifyIn ) {} tcpRecvThread::~tcpRecvThread () { } void tcpRecvThread::start () { this->thread.start (); } void tcpRecvThread::show ( unsigned /* level */ ) const { } bool tcpRecvThread::exitWait ( double delay ) { return this->thread.exitWait ( delay ); } void tcpRecvThread::exitWait () { this->thread.exitWait (); } bool tcpRecvThread::validFillStatus ( epicsGuard < epicsMutex > & guard, const statusWireIO & stat ) { if ( this->iiu.state != tcpiiu::iiucs_connected && this->iiu.state != tcpiiu::iiucs_clean_shutdown ) { return false; } if ( stat.circuitState == swioConnected ) { return true; } if ( stat.circuitState == swioPeerHangup || stat.circuitState == swioPeerAbort ) { this->iiu.disconnectNotify ( guard ); } else if ( stat.circuitState == swioLinkFailure ) { this->iiu.initiateAbortShutdown ( guard ); } else if ( stat.circuitState == swioLocalAbort ) { // state change already occurred } else { errlogMessage ( "cac: invalid fill status - disconnecting" ); this->iiu.disconnectNotify ( guard ); } return false; } void tcpRecvThread::run () { try { { bool connectSuccess = false; { epicsGuard < epicsMutex > guard ( this->iiu.mutex ); this->connect ( guard ); connectSuccess = this->iiu.state == tcpiiu::iiucs_connected; } if ( ! connectSuccess ) { this->iiu.recvDog.shutdown (); this->iiu.cacRef.destroyIIU ( this->iiu ); return; } if ( this->iiu.isNameService () ) { this->iiu.pSearchDest->setCircuit ( &this->iiu ); this->iiu.pSearchDest->enable (); } } this->iiu.sendThread.start (); epicsThreadPrivateSet ( caClientCallbackThreadId, &this->iiu ); this->iiu.cacRef.attachToClientCtx (); comBuf * pComBuf = 0; while ( true ) { // // We leave the bytes pending and fetch them after // callbacks are enabled when running in the old preemptive // call back disabled mode so that asynchronous wakeup via // file manager call backs works correctly. This does not // appear to impact performance. // if ( ! pComBuf ) { pComBuf = new ( this->iiu.comBufMemMgr ) comBuf; } statusWireIO stat; pComBuf->fillFromWire ( this->iiu, stat ); epicsTime currentTime = epicsTime::getMonotonic (); { epicsGuard < epicsMutex > guard ( this->iiu.mutex ); if ( ! this->validFillStatus ( guard, stat ) ) { break; } if ( stat.bytesCopied == 0u ) { continue; } this->iiu.recvQue.pushLastComBufReceived ( *pComBuf ); pComBuf = 0; this->iiu._receiveThreadIsBusy = true; } bool sendWakeupNeeded = false; { // only one recv thread at a time may call callbacks // - pendEvent() blocks until threads waiting for // this lock get a chance to run callbackManager mgr ( this->ctxNotify, this->cbMutex ); epicsGuard < epicsMutex > guard ( this->iiu.mutex ); // route legacy V42 channel connect through the recv thread - // the only thread that should be taking the callback lock while ( nciu * pChan = this->iiu.v42ConnCallbackPend.first () ) { this->iiu.connectNotify ( guard, *pChan ); pChan->connect ( mgr.cbGuard, guard ); } this->iiu.unacknowledgedSendBytes = 0u; bool protocolOK = false; { epicsGuardRelease < epicsMutex > unguard ( guard ); // execute receive labor protocolOK = this->iiu.processIncoming ( currentTime, mgr ); } if ( ! protocolOK ) { this->iiu.initiateAbortShutdown ( guard ); break; } this->iiu._receiveThreadIsBusy = false; // reschedule connection activity watchdog this->iiu.recvDog.messageArrivalNotify ( guard ); // // if this thread has connected channels with subscriptions // that need to be sent then wakeup the send thread if ( this->iiu.subscripReqPend.count() ) { sendWakeupNeeded = true; } } // // we dont feel comfortable calling this with a lock applied // (it might block for longer than we like) // // we would prefer to improve efficency by trying, first, a // recv with the new MSG_DONTWAIT flag set, but there isnt // universal support // bool bytesArePending = this->iiu.bytesArePendingInOS (); { epicsGuard < epicsMutex > guard ( this->iiu.mutex ); if ( bytesArePending ) { if ( ! this->iiu.busyStateDetected ) { this->iiu.contigRecvMsgCount++; if ( this->iiu.contigRecvMsgCount >= this->iiu.cacRef.maxContiguousFrames ( guard ) ) { this->iiu.busyStateDetected = true; sendWakeupNeeded = true; } } } else { // if no bytes are pending then we must immediately // switch off flow control w/o waiting for more // data to arrive this->iiu.contigRecvMsgCount = 0u; if ( this->iiu.busyStateDetected ) { sendWakeupNeeded = true; this->iiu.busyStateDetected = false; } } } if ( sendWakeupNeeded ) { this->iiu.sendThreadFlushEvent.signal (); } } if ( pComBuf ) { pComBuf->~comBuf (); this->iiu.comBufMemMgr.release ( pComBuf ); } } catch ( std::bad_alloc & ) { errlogPrintf ( "CA client library tcp receive thread " "terminating due to no space in pool " "C++ exception\n" ); epicsGuard < epicsMutex > guard ( this->iiu.mutex ); this->iiu.initiateCleanShutdown ( guard ); } catch ( std::exception & except ) { errlogPrintf ( "CA client library tcp receive thread " "terminating due to C++ exception \"%s\"\n", except.what () ); epicsGuard < epicsMutex > guard ( this->iiu.mutex ); this->iiu.initiateCleanShutdown ( guard ); } catch ( ... ) { errlogPrintf ( "CA client library tcp receive thread " "terminating due to a non-standard C++ exception\n" ); epicsGuard < epicsMutex > guard ( this->iiu.mutex ); this->iiu.initiateCleanShutdown ( guard ); } } /* * tcpRecvThread::connect () */ void tcpRecvThread::connect ( epicsGuard < epicsMutex > & guard ) { // attempt to connect to a CA server while ( true ) { int status; { epicsGuardRelease < epicsMutex > unguard ( guard ); osiSockAddr tmp = this->iiu.address (); status = ::connect ( this->iiu.sock, & tmp.sa, sizeof ( tmp.sa ) ); } if ( this->iiu.state != tcpiiu::iiucs_connecting ) { break; } if ( status >= 0 ) { // put the iiu into the connected state this->iiu.state = tcpiiu::iiucs_connected; this->iiu.recvDog.connectNotify ( guard ); break; } else { int errnoCpy = SOCKERRNO; if ( errnoCpy == SOCK_EINTR ) { continue; } else if ( errnoCpy == SOCK_SHUTDOWN ) { if ( ! this->iiu.isNameService () ) { break; } } else { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAC: Unable to connect because \"%s\"\n", sockErrBuf ); if ( ! this->iiu.isNameService () ) { this->iiu.disconnectNotify ( guard ); break; } } { double sleepTime = this->iiu.cacRef.connectionTimeout ( guard ); epicsGuardRelease < epicsMutex > unguard ( guard ); epicsThreadSleep ( sleepTime ); } continue; } } return; } // // tcpiiu::tcpiiu () // tcpiiu::tcpiiu ( cac & cac, epicsMutex & mutexIn, epicsMutex & cbMutexIn, cacContextNotify & ctxNotifyIn, double connectionTimeout, epicsTimerQueue & timerQueue, const osiSockAddr & addrIn, comBufMemoryManager & comBufMemMgrIn, unsigned minorVersion, ipAddrToAsciiEngine & engineIn, const cacChannel::priLev & priorityIn, SearchDestTCP * pSearchDestIn ) : caServerID ( addrIn.ia, priorityIn ), hostNameCacheInstance ( addrIn, engineIn ), recvThread ( *this, cbMutexIn, ctxNotifyIn, "CAC-TCP-recv", epicsThreadGetStackSize ( epicsThreadStackBig ), cac::highestPriorityLevelBelow ( cac.getInitializingThreadsPriority() ) ), sendThread ( *this, "CAC-TCP-send", epicsThreadGetStackSize ( epicsThreadStackMedium ), cac::lowestPriorityLevelAbove ( cac.getInitializingThreadsPriority() ) ), recvDog ( cbMutexIn, ctxNotifyIn, mutexIn, *this, connectionTimeout, timerQueue ), sendDog ( cbMutexIn, ctxNotifyIn, mutexIn, *this, connectionTimeout, timerQueue ), sendQue ( *this, comBufMemMgrIn ), recvQue ( comBufMemMgrIn ), curDataMax ( MAX_TCP ), curDataBytes ( 0ul ), comBufMemMgr ( comBufMemMgrIn ), cacRef ( cac ), pCurData ( (char*) freeListMalloc(this->cacRef.tcpSmallRecvBufFreeList) ), pSearchDest ( pSearchDestIn ), mutex ( mutexIn ), cbMutex ( cbMutexIn ), minorProtocolVersion ( minorVersion ), state ( iiucs_connecting ), sock ( INVALID_SOCKET ), contigRecvMsgCount ( 0u ), blockingForFlush ( 0u ), socketLibrarySendBufferSize ( 0x1000 ), unacknowledgedSendBytes ( 0u ), channelCountTot ( 0u ), _receiveThreadIsBusy ( false ), busyStateDetected ( false ), flowControlActive ( false ), echoRequestPending ( false ), oldMsgHeaderAvailable ( false ), msgHeaderAvailable ( false ), earlyFlush ( false ), recvProcessPostponedFlush ( false ), discardingPendingData ( false ), socketHasBeenClosed ( false ), unresponsiveCircuit ( false ) { if(!pCurData) throw std::bad_alloc(); this->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( this->sock == INVALID_SOCKET ) { freeListFree(this->cacRef.tcpSmallRecvBufFreeList, this->pCurData); char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); std :: string reason = "CAC: TCP circuit creation failure because \""; reason += sockErrBuf; reason += "\""; throw runtime_error ( reason ); } int flag = true; int status = setsockopt ( this->sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof ( flag ) ); if ( status < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAC: problems setting socket option TCP_NODELAY = \"%s\"\n", sockErrBuf ); } flag = true; status = setsockopt ( this->sock , SOL_SOCKET, SO_KEEPALIVE, ( char * ) &flag, sizeof ( flag ) ); if ( status < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAC: problems setting socket option SO_KEEPALIVE = \"%s\"\n", sockErrBuf ); } // load message queue with messages informing server // of version, user, and host name of client { epicsGuard < epicsMutex > guard ( this->mutex ); this->versionMessage ( guard, this->priority() ); this->userNameSetRequest ( guard ); this->hostNameSetRequest ( guard ); } # if 0 { int i; /* * some concern that vxWorks will run out of mBuf's * if this change is made joh 11-10-98 */ i = MAX_MSG_SIZE; status = setsockopt ( this->sock, SOL_SOCKET, SO_SNDBUF, ( char * ) &i, sizeof ( i ) ); if (status < 0) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAC: problems setting socket option SO_SNDBUF = \"%s\"\n", sockErrBuf ); } i = MAX_MSG_SIZE; status = setsockopt ( this->sock, SOL_SOCKET, SO_RCVBUF, ( char * ) &i, sizeof ( i ) ); if ( status < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAC: problems setting socket option SO_RCVBUF = \"%s\"\n", sockErrBuf ); } } # endif { int nBytes; osiSocklen_t sizeOfParameter = static_cast < int > ( sizeof ( nBytes ) ); status = getsockopt ( this->sock, SOL_SOCKET, SO_SNDBUF, ( char * ) &nBytes, &sizeOfParameter ); if ( status < 0 || nBytes < 0 || sizeOfParameter != static_cast < int > ( sizeof ( nBytes ) ) ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ("CAC: problems getting socket option SO_SNDBUF = \"%s\"\n", sockErrBuf ); } else { this->socketLibrarySendBufferSize = static_cast < unsigned > ( nBytes ); } } if ( isNameService() ) { pSearchDest->setCircuit ( this ); } memset ( (void *) &this->curMsg, '\0', sizeof ( this->curMsg ) ); } // this must always be called by the udp thread when it holds // the callback lock. void tcpiiu::start ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->recvThread.start (); } void tcpiiu::initiateCleanShutdown ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( this->state == iiucs_connected ) { if ( this->unresponsiveCircuit ) { this->initiateAbortShutdown ( guard ); } else { this->state = iiucs_clean_shutdown; this->sendThreadFlushEvent.signal (); this->flushBlockEvent.signal (); } } else if ( this->state == iiucs_clean_shutdown ) { if ( this->unresponsiveCircuit ) { this->initiateAbortShutdown ( guard ); } } else if ( this->state == iiucs_connecting ) { this->initiateAbortShutdown ( guard ); } } void tcpiiu::disconnectNotify ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->state = iiucs_disconnected; this->sendThreadFlushEvent.signal (); this->flushBlockEvent.signal (); } void tcpiiu::responsiveCircuitNotify ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ) { cbGuard.assertIdenticalMutex ( this->cbMutex ); guard.assertIdenticalMutex ( this->mutex ); if ( this->unresponsiveCircuit ) { this->unresponsiveCircuit = false; while ( nciu * pChan = this->unrespCircuit.get() ) { this->subscripUpdateReqPend.add ( *pChan ); pChan->channelNode::listMember = channelNode::cs_subscripUpdateReqPend; pChan->connect ( cbGuard, guard ); } this->sendThreadFlushEvent.signal (); } } void tcpiiu::sendTimeoutNotify ( callbackManager & mgr, epicsGuard < epicsMutex > & guard ) { mgr.cbGuard.assertIdenticalMutex ( this-> cbMutex ); guard.assertIdenticalMutex ( this->mutex ); this->unresponsiveCircuitNotify ( mgr.cbGuard, guard ); // setup circuit probe sequence this->recvDog.sendTimeoutNotify ( mgr.cbGuard, guard ); } void tcpiiu::receiveTimeoutNotify ( callbackManager & mgr, epicsGuard < epicsMutex > & guard ) { mgr.cbGuard.assertIdenticalMutex ( this->cbMutex ); guard.assertIdenticalMutex ( this->mutex ); this->unresponsiveCircuitNotify ( mgr.cbGuard, guard ); } void tcpiiu::unresponsiveCircuitNotify ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ) { cbGuard.assertIdenticalMutex ( this->cbMutex ); guard.assertIdenticalMutex ( this->mutex ); if ( ! this->unresponsiveCircuit ) { this->unresponsiveCircuit = true; this->echoRequestPending = true; this->sendThreadFlushEvent.signal (); this->flushBlockEvent.signal (); // must not hold lock when canceling timer { epicsGuardRelease < epicsMutex > unguard ( guard ); { epicsGuardRelease < epicsMutex > cbUnguard ( cbGuard ); this->recvDog.cancel (); this->sendDog.cancel (); } } if ( this->connectedList.count() ) { char hostNameTmp[128]; this->getHostName ( guard, hostNameTmp, sizeof ( hostNameTmp ) ); genLocalExcep ( cbGuard, guard, this->cacRef, ECA_UNRESPTMO, hostNameTmp ); while ( nciu * pChan = this->connectedList.get () ) { // The cac lock is released herein so there is concern that // the list could be changed while we are traversing it. // However, this occurs only if a circuit disconnects, // a user deletes a channel, or a server disconnects a // channel. The callback lock must be taken in all of // these situations so this code is protected. this->unrespCircuit.add ( *pChan ); pChan->channelNode::listMember = channelNode::cs_unrespCircuit; pChan->unresponsiveCircuitNotify ( cbGuard, guard ); } } } } void tcpiiu::initiateAbortShutdown ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( ! this->discardingPendingData ) { // force abortive shutdown sequence // (discard outstanding sends and receives) struct linger tmpLinger; tmpLinger.l_onoff = true; tmpLinger.l_linger = 0u; int status = setsockopt ( this->sock, SOL_SOCKET, SO_LINGER, reinterpret_cast ( &tmpLinger ), sizeof (tmpLinger) ); if ( status != 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAC TCP socket linger set error was %s\n", sockErrBuf ); } this->discardingPendingData = true; } iiu_conn_state oldState = this->state; if ( oldState != iiucs_abort_shutdown && oldState != iiucs_disconnected ) { this->state = iiucs_abort_shutdown; epicsSocketSystemCallInterruptMechanismQueryInfo info = epicsSocketSystemCallInterruptMechanismQuery (); switch ( info ) { case esscimqi_socketCloseRequired: // // on winsock and probably vxWorks shutdown() does not // unblock a thread in recv() so we use close() and introduce // some complexity because we must unregister the fd early // if ( ! this->socketHasBeenClosed ) { epicsSocketDestroy ( this->sock ); this->socketHasBeenClosed = true; } break; case esscimqi_socketBothShutdownRequired: { int status = ::shutdown ( this->sock, SHUT_RDWR ); if ( status ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ("CAC TCP socket shutdown error was %s\n", sockErrBuf ); } } break; case esscimqi_socketSigAlarmRequired: this->recvThread.interruptSocketRecv (); this->sendThread.interruptSocketSend (); break; default: break; }; // // wake up the send thread if it isnt blocking in send() // this->sendThreadFlushEvent.signal (); this->flushBlockEvent.signal (); } } // // tcpiiu::~tcpiiu () // tcpiiu :: ~tcpiiu () { if ( this->pSearchDest ) { this->pSearchDest->disable (); } this->sendThread.exitWait (); this->recvThread.exitWait (); this->sendDog.cancel (); this->recvDog.shutdown (); if ( ! this->socketHasBeenClosed ) { epicsSocketDestroy ( this->sock ); } // free message body cache if ( this->pCurData ) { if ( this->curDataMax <= MAX_TCP ) { freeListFree(this->cacRef.tcpSmallRecvBufFreeList, this->pCurData); } else if ( this->cacRef.tcpLargeRecvBufFreeList ) { freeListFree(this->cacRef.tcpLargeRecvBufFreeList, this->pCurData); } else { free ( this->pCurData ); } } } void tcpiiu::show ( unsigned level ) const { epicsGuard < epicsMutex > locker ( this->mutex ); char buf[256]; this->hostNameCacheInstance.getName ( buf, sizeof ( buf ) ); ::printf ( "Virtual circuit to \"%s\" at version V%u.%u state %u\n", buf, CA_MAJOR_PROTOCOL_REVISION, this->minorProtocolVersion, this->state ); if ( level > 1u ) { ::printf ( "\tcurrent data cache pointer = %p current data cache size = %lu\n", static_cast < void * > ( this->pCurData ), this->curDataMax ); ::printf ( "\tcontiguous receive message count=%u, busy detect bool=%u, flow control bool=%u\n", this->contigRecvMsgCount, this->busyStateDetected, this->flowControlActive ); ::printf ( "\receive thread is busy=%u\n", this->_receiveThreadIsBusy ); } if ( level > 2u ) { ::printf ( "\tvirtual circuit socket identifier %d\n", this->sock ); ::printf ( "\tsend thread flush signal:\n" ); this->sendThreadFlushEvent.show ( level-2u ); ::printf ( "\tsend thread:\n" ); this->sendThread.show ( level-2u ); ::printf ( "\trecv thread:\n" ); this->recvThread.show ( level-2u ); ::printf ("\techo pending bool = %u\n", this->echoRequestPending ); ::printf ( "IO identifier hash table:\n" ); if ( this->createReqPend.count () ) { ::printf ( "Create request pending channels\n" ); tsDLIterConst < nciu > pChan = this->createReqPend.firstIter (); while ( pChan.valid () ) { pChan->show ( level - 2u ); pChan++; } } if ( this->createRespPend.count () ) { ::printf ( "Create response pending channels\n" ); tsDLIterConst < nciu > pChan = this->createRespPend.firstIter (); while ( pChan.valid () ) { pChan->show ( level - 2u ); pChan++; } } if ( this->v42ConnCallbackPend.count () ) { ::printf ( "V42 Conn Callback pending channels\n" ); tsDLIterConst < nciu > pChan = this->v42ConnCallbackPend.firstIter (); while ( pChan.valid () ) { pChan->show ( level - 2u ); pChan++; } } if ( this->subscripReqPend.count () ) { ::printf ( "Subscription request pending channels\n" ); tsDLIterConst < nciu > pChan = this->subscripReqPend.firstIter (); while ( pChan.valid () ) { pChan->show ( level - 2u ); pChan++; } } if ( this->connectedList.count () ) { ::printf ( "Connected channels\n" ); tsDLIterConst < nciu > pChan = this->connectedList.firstIter (); while ( pChan.valid () ) { pChan->show ( level - 2u ); pChan++; } } if ( this->unrespCircuit.count () ) { ::printf ( "Unresponsive circuit channels\n" ); tsDLIterConst < nciu > pChan = this->unrespCircuit.firstIter (); while ( pChan.valid () ) { pChan->show ( level - 2u ); pChan++; } } } } bool tcpiiu::setEchoRequestPending ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->echoRequestPending = true; this->sendThreadFlushEvent.signal (); if ( CA_V43 ( this->minorProtocolVersion ) ) { // we send an echo return true; } else { // we send a NOOP return false; } } void tcpiiu::flushIfRecvProcessRequested ( epicsGuard < epicsMutex > & guard ) { if ( this->recvProcessPostponedFlush ) { this->flushRequest ( guard ); this->recvProcessPostponedFlush = false; } } bool tcpiiu::processIncoming ( const epicsTime & currentTime, callbackManager & mgr ) { mgr.cbGuard.assertIdenticalMutex ( this->cbMutex ); while ( true ) { // // fetch a complete message header // if ( ! this->msgHeaderAvailable ) { if ( ! this->oldMsgHeaderAvailable ) { this->oldMsgHeaderAvailable = this->recvQue.popOldMsgHeader ( this->curMsg ); if ( ! this->oldMsgHeaderAvailable ) { epicsGuard < epicsMutex > guard ( this->mutex ); this->flushIfRecvProcessRequested ( guard ); return true; } } if ( this->curMsg.m_postsize == 0xffff ) { static const unsigned annexSize = sizeof ( this->curMsg.m_postsize ) + sizeof ( this->curMsg.m_count ); if ( this->recvQue.occupiedBytes () < annexSize ) { epicsGuard < epicsMutex > guard ( this->mutex ); this->flushIfRecvProcessRequested ( guard ); return true; } this->curMsg.m_postsize = this->recvQue.popUInt32 (); this->curMsg.m_count = this->recvQue.popUInt32 (); } this->msgHeaderAvailable = true; # ifdef DEBUG epicsGuard < epicsMutex > guard ( this->mutex ); debugPrintf ( ( "%s Cmd=%3u Type=%3u Count=%8u Size=%8u", this->pHostName ( guard ), this->curMsg.m_cmmd, this->curMsg.m_dataType, this->curMsg.m_count, this->curMsg.m_postsize) ); debugPrintf ( ( " Avail=%8u Cid=%8u\n", this->curMsg.m_available, this->curMsg.m_cid) ); # endif } // check for 8 byte aligned protocol if ( this->curMsg.m_postsize & 0x7 ) { this->printFormated ( mgr.cbGuard, "CAC: server sent missaligned payload 0x%x\n", this->curMsg.m_postsize ); return false; } // // make sure we have a large enough message body cache // if ( this->curMsg.m_postsize > this->curDataMax ) { assert (this->curMsg.m_postsize > MAX_TCP); char * newbuf = NULL; arrayElementCount newsize; if ( !this->cacRef.tcpLargeRecvBufFreeList ) { // round size up to multiple of 4K newsize = ((this->curMsg.m_postsize-1)|0xfff)+1; if ( this->curDataMax <= MAX_TCP ) { // small -> large newbuf = (char*)malloc(newsize); } else { // expand large to larger newbuf = (char*)realloc(this->pCurData, newsize); } } else if ( this->curMsg.m_postsize <= this->cacRef.maxRecvBytesTCP ) { newbuf = (char*) freeListMalloc(this->cacRef.tcpLargeRecvBufFreeList); newsize = this->cacRef.maxRecvBytesTCP; } if ( newbuf) { if (this->curDataMax <= MAX_TCP) { freeListFree(this->cacRef.tcpSmallRecvBufFreeList, this->pCurData ); } else if (this->cacRef.tcpLargeRecvBufFreeList) { freeListFree(this->cacRef.tcpLargeRecvBufFreeList, this->pCurData ); } else { // called realloc() } this->pCurData = newbuf; this->curDataMax = newsize; } else { this->printFormated ( mgr.cbGuard, "CAC: not enough memory for message body cache (ignoring response message)\n"); } } if ( this->curMsg.m_postsize <= this->curDataMax ) { if ( this->curMsg.m_postsize > 0u ) { this->curDataBytes += this->recvQue.copyOutBytes ( &this->pCurData[this->curDataBytes], this->curMsg.m_postsize - this->curDataBytes ); if ( this->curDataBytes < this->curMsg.m_postsize ) { epicsGuard < epicsMutex > guard ( this->mutex ); this->flushIfRecvProcessRequested ( guard ); return true; } } bool msgOK = this->cacRef.executeResponse ( mgr, *this, currentTime, this->curMsg, this->pCurData ); if ( ! msgOK ) { return false; } } else { static bool once = false; if ( ! once ) { this->printFormated ( mgr.cbGuard, "CAC: response with payload size=%u > EPICS_CA_MAX_ARRAY_BYTES ignored\n", this->curMsg.m_postsize ); once = true; } this->curDataBytes += this->recvQue.removeBytes ( this->curMsg.m_postsize - this->curDataBytes ); if ( this->curDataBytes < this->curMsg.m_postsize ) { epicsGuard < epicsMutex > guard ( this->mutex ); this->flushIfRecvProcessRequested ( guard ); return true; } } this->oldMsgHeaderAvailable = false; this->msgHeaderAvailable = false; this->curDataBytes = 0u; } } void tcpiiu::hostNameSetRequest ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( ! CA_V41 ( this->minorProtocolVersion ) ) { return; } const char * pName = this->cacRef.pLocalHostName (); unsigned size = strlen ( pName ) + 1u; unsigned postSize = CA_MESSAGE_ALIGN ( size ); assert ( postSize < 0xffff ); if ( this->sendQue.flushEarlyThreshold ( postSize + 16u ) ) { this->flushRequest ( guard ); } comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_HOST_NAME, postSize, 0u, 0u, 0u, 0u, CA_V49 ( this->minorProtocolVersion ) ); this->sendQue.pushString ( pName, size ); this->sendQue.pushString ( cacNillBytes, postSize - size ); minder.commit (); } /* * tcpiiu::userNameSetRequest () */ void tcpiiu::userNameSetRequest ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( ! CA_V41 ( this->minorProtocolVersion ) ) { return; } const char *pName = this->cacRef.userNamePointer (); unsigned size = strlen ( pName ) + 1u; unsigned postSize = CA_MESSAGE_ALIGN ( size ); assert ( postSize < 0xffff ); if ( this->sendQue.flushEarlyThreshold ( postSize + 16u ) ) { this->flushRequest ( guard ); } comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_CLIENT_NAME, postSize, 0u, 0u, 0u, 0u, CA_V49 ( this->minorProtocolVersion ) ); this->sendQue.pushString ( pName, size ); this->sendQue.pushString ( cacNillBytes, postSize - size ); minder.commit (); } void tcpiiu::disableFlowControlRequest ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( this->sendQue.flushEarlyThreshold ( 16u ) ) { this->flushRequest ( guard ); } comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_EVENTS_ON, 0u, 0u, 0u, 0u, 0u, CA_V49 ( this->minorProtocolVersion ) ); minder.commit (); } void tcpiiu::enableFlowControlRequest ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( this->sendQue.flushEarlyThreshold ( 16u ) ) { this->flushRequest ( guard ); } comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_EVENTS_OFF, 0u, 0u, 0u, 0u, 0u, CA_V49 ( this->minorProtocolVersion ) ); minder.commit (); } void tcpiiu::versionMessage ( epicsGuard < epicsMutex > & guard, const cacChannel::priLev & priority ) { guard.assertIdenticalMutex ( this->mutex ); assert ( priority <= 0xffff ); if ( this->sendQue.flushEarlyThreshold ( 16u ) ) { this->flushRequest ( guard ); } comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_VERSION, 0u, static_cast < ca_uint16_t > ( priority ), CA_MINOR_PROTOCOL_REVISION, 0u, 0u, CA_V49 ( this->minorProtocolVersion ) ); minder.commit (); } void tcpiiu::echoRequest ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); epicsUInt16 command = CA_PROTO_ECHO; if ( ! CA_V43 ( this->minorProtocolVersion ) ) { // we fake an echo to early server using a read sync command = CA_PROTO_READ_SYNC; } if ( this->sendQue.flushEarlyThreshold ( 16u ) ) { this->flushRequest ( guard ); } comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( command, 0u, 0u, 0u, 0u, 0u, CA_V49 ( this->minorProtocolVersion ) ); minder.commit (); } void tcpiiu::writeRequest ( epicsGuard < epicsMutex > & guard, nciu &chan, unsigned type, arrayElementCount nElem, const void *pValue ) { guard.assertIdenticalMutex ( this->mutex ); if ( INVALID_DB_REQ ( type ) ) { throw cacChannel::badType (); } comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestWithPayLoad ( CA_PROTO_WRITE, type, nElem, chan.getSID(guard), chan.getCID(guard), pValue, CA_V49 ( this->minorProtocolVersion ) ); minder.commit (); } void tcpiiu::writeNotifyRequest ( epicsGuard < epicsMutex > & guard, nciu &chan, netWriteNotifyIO &io, unsigned type, arrayElementCount nElem, const void *pValue ) { guard.assertIdenticalMutex ( this->mutex ); if ( ! this->ca_v41_ok ( guard ) ) { throw cacChannel::unsupportedByService(); } if ( INVALID_DB_REQ ( type ) ) { throw cacChannel::badType (); } comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestWithPayLoad ( CA_PROTO_WRITE_NOTIFY, type, nElem, chan.getSID(guard), io.getId(), pValue, CA_V49 ( this->minorProtocolVersion ) ); minder.commit (); } void tcpiiu::readNotifyRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netReadNotifyIO & io, unsigned dataType, arrayElementCount nElem ) { guard.assertIdenticalMutex ( this->mutex ); if ( INVALID_DB_REQ ( dataType ) ) { throw cacChannel::badType (); } arrayElementCount maxBytes; if ( CA_V49 ( this->minorProtocolVersion ) ) { maxBytes = 0xfffffff0; } else { maxBytes = MAX_TCP; } arrayElementCount maxElem = ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; if ( nElem > maxElem ) { throw cacChannel::msgBodyCacheTooSmall (); } if (nElem == 0 && !CA_V413(this->minorProtocolVersion)) nElem = chan.getcount(); comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_READ_NOTIFY, 0u, static_cast < ca_uint16_t > ( dataType ), static_cast < ca_uint32_t > ( nElem ), chan.getSID(guard), io.getId(), CA_V49 ( this->minorProtocolVersion ) ); minder.commit (); } void tcpiiu::createChannelRequest ( nciu & chan, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( this->state != iiucs_connected && this->state != iiucs_connecting ) { return; } const char *pName; unsigned nameLength; ca_uint32_t identity; if ( this->ca_v44_ok ( guard ) ) { identity = chan.getCID ( guard ); pName = chan.pName ( guard ); nameLength = chan.nameLen ( guard ); } else { identity = chan.getSID ( guard ); pName = 0; nameLength = 0u; } unsigned postCnt = CA_MESSAGE_ALIGN ( nameLength ); if ( postCnt >= 0xffff ) { throw cacChannel::unsupportedByService(); } comQueSendMsgMinder minder ( this->sendQue, guard ); // // The available field is used (abused) // here to communicate the minor version number // starting with CA 4.1. // this->sendQue.insertRequestHeader ( CA_PROTO_CREATE_CHAN, postCnt, 0u, 0u, identity, CA_MINOR_PROTOCOL_REVISION, CA_V49 ( this->minorProtocolVersion ) ); if ( nameLength ) { this->sendQue.pushString ( pName, nameLength ); } if ( postCnt > nameLength ) { this->sendQue.pushString ( cacNillBytes, postCnt - nameLength ); } minder.commit (); } void tcpiiu::clearChannelRequest ( epicsGuard < epicsMutex > & guard, ca_uint32_t sid, ca_uint32_t cid ) { guard.assertIdenticalMutex ( this->mutex ); // there are situations where the circuit is disconnected, but // the channel does not know this yet if ( this->state != iiucs_connected ) { return; } comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_CLEAR_CHANNEL, 0u, 0u, 0u, sid, cid, CA_V49 ( this->minorProtocolVersion ) ); minder.commit (); } // // this routine return void because if this internally fails the best response // is to try again the next time that we reconnect // void tcpiiu::subscriptionRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netSubscription & subscr ) { guard.assertIdenticalMutex ( this->mutex ); // there are situations where the circuit is disconnected, but // the channel does not know this yet if ( this->state != iiucs_connected && this->state != iiucs_connecting ) { return; } unsigned mask = subscr.getMask(guard); if ( mask > 0xffff ) { throw cacChannel::badEventSelection (); } arrayElementCount nElem = subscr.getCount ( guard, CA_V413(this->minorProtocolVersion) ); arrayElementCount maxBytes; if ( CA_V49 ( this->minorProtocolVersion ) ) { maxBytes = 0xfffffff0; } else { maxBytes = MAX_TCP; } unsigned dataType = subscr.getType ( guard ); // data type bounds checked when sunscription created arrayElementCount maxElem = ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; if ( nElem > maxElem ) { throw cacChannel::msgBodyCacheTooSmall (); } comQueSendMsgMinder minder ( this->sendQue, guard ); // nElement bounds checked above this->sendQue.insertRequestHeader ( CA_PROTO_EVENT_ADD, 16u, static_cast < ca_uint16_t > ( dataType ), static_cast < ca_uint32_t > ( nElem ), chan.getSID(guard), subscr.getId(), CA_V49 ( this->minorProtocolVersion ) ); // extension this->sendQue.pushFloat32 ( 0.0f ); // m_lval this->sendQue.pushFloat32 ( 0.0f ); // m_hval this->sendQue.pushFloat32 ( 0.0f ); // m_toval this->sendQue.pushUInt16 ( static_cast < ca_uint16_t > ( mask ) ); // m_mask this->sendQue.pushUInt16 ( 0u ); // m_pad minder.commit (); } // // this routine return void because if this internally fails the best response // is to try again the next time that we reconnect // void tcpiiu::subscriptionUpdateRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netSubscription & subscr ) { guard.assertIdenticalMutex ( this->mutex ); // there are situations where the circuit is disconnected, but // the channel does not know this yet if ( this->state != iiucs_connected ) { return; } arrayElementCount nElem = subscr.getCount ( guard, CA_V413(this->minorProtocolVersion) ); arrayElementCount maxBytes; if ( CA_V49 ( this->minorProtocolVersion ) ) { maxBytes = 0xfffffff0; } else { maxBytes = MAX_TCP; } unsigned dataType = subscr.getType ( guard ); // data type bounds checked when subscription constructed arrayElementCount maxElem = ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; if ( nElem > maxElem ) { throw cacChannel::msgBodyCacheTooSmall (); } comQueSendMsgMinder minder ( this->sendQue, guard ); // nElem boounds checked above this->sendQue.insertRequestHeader ( CA_PROTO_READ_NOTIFY, 0u, static_cast < ca_uint16_t > ( dataType ), static_cast < ca_uint32_t > ( nElem ), chan.getSID (guard), subscr.getId (), CA_V49 ( this->minorProtocolVersion ) ); minder.commit (); } void tcpiiu::subscriptionCancelRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netSubscription & subscr ) { guard.assertIdenticalMutex ( this->mutex ); // there are situations where the circuit is disconnected, but // the channel does not know this yet if ( this->state != iiucs_connected ) { return; } comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_EVENT_CANCEL, 0u, static_cast < ca_uint16_t > ( subscr.getType ( guard ) ), static_cast < ca_uint16_t > ( subscr.getCount ( guard, CA_V413(this->minorProtocolVersion) ) ), chan.getSID(guard), subscr.getId(), CA_V49 ( this->minorProtocolVersion ) ); minder.commit (); } bool tcpiiu::sendThreadFlush ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( this->sendQue.occupiedBytes() > 0 ) { while ( comBuf * pBuf = this->sendQue.popNextComBufToSend () ) { epicsTime current = epicsTime::getMonotonic (); unsigned bytesToBeSent = pBuf->occupiedBytes (); bool success = false; { // no lock while blocking to send epicsGuardRelease < epicsMutex > unguard ( guard ); success = pBuf->flushToWire ( *this, current ); pBuf->~comBuf (); this->comBufMemMgr.release ( pBuf ); } if ( ! success ) { while ( ( pBuf = this->sendQue.popNextComBufToSend () ) ) { pBuf->~comBuf (); this->comBufMemMgr.release ( pBuf ); } return false; } // set it here with this odd order because we must have // the lock and we must have already sent the bytes this->unacknowledgedSendBytes += bytesToBeSent; if ( this->unacknowledgedSendBytes > this->socketLibrarySendBufferSize ) { this->recvDog.sendBacklogProgressNotify ( guard ); } } } this->earlyFlush = false; if ( this->blockingForFlush ) { this->flushBlockEvent.signal (); } return true; } void tcpiiu :: flush ( epicsGuard < epicsMutex > & guard ) { this->flushRequest ( guard ); // the process thread is not permitted to flush as this // can result in a push / pull deadlock on the TCP pipe. // Instead, the process thread scheduals the flush with the // send thread which runs at a higher priority than the // receive thread. The same applies to the UDP thread for // locking hierarchy reasons. if ( ! epicsThreadPrivateGet ( caClientCallbackThreadId ) ) { // enable / disable of call back preemption must occur here // because the tcpiiu might disconnect while waiting and its // pointer to this cac might become invalid assert ( this->blockingForFlush < UINT_MAX ); this->blockingForFlush++; while ( this->sendQue.flushBlockThreshold() ) { bool userRequestsCanBeAccepted = this->state == iiucs_connected || ( ! this->ca_v42_ok ( guard ) && this->state == iiucs_connecting ); // fail the users request if we have a disconnected // or unresponsive circuit if ( ! userRequestsCanBeAccepted || this->unresponsiveCircuit ) { this->decrementBlockingForFlushCount ( guard ); throw cacChannel::notConnected (); } epicsGuardRelease < epicsMutex > unguard ( guard ); this->flushBlockEvent.wait ( 30.0 ); } this->decrementBlockingForFlushCount ( guard ); } } unsigned tcpiiu::requestMessageBytesPending ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); #if 0 if ( ! this->earlyFlush && this->sendQue.flushEarlyThreshold(0u) ) { this->earlyFlush = true; this->sendThreadFlushEvent.signal (); } #endif return sendQue.occupiedBytes (); } void tcpiiu::decrementBlockingForFlushCount ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); assert ( this->blockingForFlush > 0u ); this->blockingForFlush--; if ( this->blockingForFlush > 0 ) { this->flushBlockEvent.signal (); } } osiSockAddr tcpiiu::getNetworkAddress ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); return this->address(); } // not inline because its virtual bool tcpiiu::ca_v42_ok ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); return CA_V42 ( this->minorProtocolVersion ); } void tcpiiu::requestRecvProcessPostponedFlush ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->recvProcessPostponedFlush = true; } unsigned tcpiiu::getHostName ( epicsGuard < epicsMutex > & guard, char * pBuf, unsigned bufLength ) const throw () { guard.assertIdenticalMutex ( this->mutex ); return this->hostNameCacheInstance.getName ( pBuf, bufLength ); } const char * tcpiiu::pHostName ( epicsGuard < epicsMutex > & guard ) const throw () { guard.assertIdenticalMutex ( this->mutex ); return this->hostNameCacheInstance.pointer (); } void tcpiiu::disconnectAllChannels ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard, class udpiiu & discIIU ) { cbGuard.assertIdenticalMutex ( this->cbMutex ); guard.assertIdenticalMutex ( this->mutex ); while ( nciu * pChan = this->createReqPend.get () ) { discIIU.installDisconnectedChannel ( guard, *pChan ); } while ( nciu * pChan = this->createRespPend.get () ) { // we dont yet know the server's id so we cant // send a channel delete request and will instead // trust that the server can do the proper cleanup // when the circuit disconnects discIIU.installDisconnectedChannel ( guard, *pChan ); } while ( nciu * pChan = this->v42ConnCallbackPend.get () ) { this->clearChannelRequest ( guard, pChan->getSID(guard), pChan->getCID(guard) ); discIIU.installDisconnectedChannel ( guard, *pChan ); } while ( nciu * pChan = this->subscripReqPend.get () ) { pChan->disconnectAllIO ( cbGuard, guard ); this->clearChannelRequest ( guard, pChan->getSID(guard), pChan->getCID(guard) ); discIIU.installDisconnectedChannel ( guard, *pChan ); pChan->unresponsiveCircuitNotify ( cbGuard, guard ); } while ( nciu * pChan = this->connectedList.get () ) { pChan->disconnectAllIO ( cbGuard, guard ); this->clearChannelRequest ( guard, pChan->getSID(guard), pChan->getCID(guard) ); discIIU.installDisconnectedChannel ( guard, *pChan ); pChan->unresponsiveCircuitNotify ( cbGuard, guard ); } while ( nciu * pChan = this->unrespCircuit.get () ) { // if we know that the circuit is unresponsive // then we dont send a channel delete request and // will instead trust that the server can do the // proper cleanup when the circuit disconnects pChan->disconnectAllIO ( cbGuard, guard ); discIIU.installDisconnectedChannel ( guard, *pChan ); } while ( nciu * pChan = this->subscripUpdateReqPend.get () ) { pChan->disconnectAllIO ( cbGuard, guard ); this->clearChannelRequest ( guard, pChan->getSID(guard), pChan->getCID(guard) ); discIIU.installDisconnectedChannel ( guard, *pChan ); pChan->unresponsiveCircuitNotify ( cbGuard, guard ); } this->channelCountTot = 0u; this->initiateCleanShutdown ( guard ); } void tcpiiu::unlinkAllChannels ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ) { cbGuard.assertIdenticalMutex ( this->cbMutex ); guard.assertIdenticalMutex ( this->mutex ); while ( nciu * pChan = this->createReqPend.get () ) { pChan->channelNode::listMember = channelNode::cs_none; pChan->serviceShutdownNotify ( cbGuard, guard ); } while ( nciu * pChan = this->createRespPend.get () ) { pChan->channelNode::listMember = channelNode::cs_none; // we dont yet know the server's id so we cant // send a channel delete request and will instead // trust that the server can do the proper cleanup // when the circuit disconnects pChan->serviceShutdownNotify ( cbGuard, guard ); } while ( nciu * pChan = this->v42ConnCallbackPend.get () ) { pChan->channelNode::listMember = channelNode::cs_none; this->clearChannelRequest ( guard, pChan->getSID(guard), pChan->getCID(guard) ); pChan->serviceShutdownNotify ( cbGuard, guard ); } while ( nciu * pChan = this->subscripReqPend.get () ) { pChan->channelNode::listMember = channelNode::cs_none; pChan->disconnectAllIO ( cbGuard, guard ); this->clearChannelRequest ( guard, pChan->getSID(guard), pChan->getCID(guard) ); pChan->serviceShutdownNotify ( cbGuard, guard ); } while ( nciu * pChan = this->connectedList.get () ) { pChan->channelNode::listMember = channelNode::cs_none; pChan->disconnectAllIO ( cbGuard, guard ); this->clearChannelRequest ( guard, pChan->getSID(guard), pChan->getCID(guard) ); pChan->serviceShutdownNotify ( cbGuard, guard ); } while ( nciu * pChan = this->unrespCircuit.get () ) { pChan->channelNode::listMember = channelNode::cs_none; pChan->disconnectAllIO ( cbGuard, guard ); // if we know that the circuit is unresponsive // then we dont send a channel delete request and // will instead trust that the server can do the // proper cleanup when the circuit disconnects pChan->serviceShutdownNotify ( cbGuard, guard ); } while ( nciu * pChan = this->subscripUpdateReqPend.get () ) { pChan->channelNode::listMember = channelNode::cs_none; pChan->disconnectAllIO ( cbGuard, guard ); this->clearChannelRequest ( guard, pChan->getSID(guard), pChan->getCID(guard) ); pChan->serviceShutdownNotify ( cbGuard, guard ); } this->channelCountTot = 0u; this->initiateCleanShutdown ( guard ); } void tcpiiu::installChannel ( epicsGuard < epicsMutex > & guard, nciu & chan, unsigned sidIn, ca_uint16_t typeIn, arrayElementCount countIn ) { guard.assertIdenticalMutex ( this->mutex ); this->createReqPend.add ( chan ); this->channelCountTot++; chan.channelNode::listMember = channelNode::cs_createReqPend; chan.searchReplySetUp ( *this, sidIn, typeIn, countIn, guard ); // The tcp send thread runs at apriority below the udp thread // so that this will not send small packets this->sendThreadFlushEvent.signal (); } bool tcpiiu :: connectNotify ( epicsGuard < epicsMutex > & guard, nciu & chan ) { guard.assertIdenticalMutex ( this->mutex ); bool wasExpected = false; // this improves robustness in the face of a server sending // protocol that does not match its declared protocol revision if ( chan.channelNode::listMember == channelNode::cs_createRespPend ) { this->createRespPend.remove ( chan ); this->subscripReqPend.add ( chan ); chan.channelNode::listMember = channelNode::cs_subscripReqPend; wasExpected = true; } else if ( chan.channelNode::listMember == channelNode::cs_v42ConnCallbackPend ) { this->v42ConnCallbackPend.remove ( chan ); this->subscripReqPend.add ( chan ); chan.channelNode::listMember = channelNode::cs_subscripReqPend; wasExpected = true; } // the TCP send thread is awakened by its receive thread whenever the receive thread // is about to block if this->subscripReqPend has items in it return wasExpected; } void tcpiiu::uninstallChan ( epicsGuard < epicsMutex > & guard, nciu & chan ) { guard.assertIdenticalMutex ( this->mutex ); switch ( chan.channelNode::listMember ) { case channelNode::cs_createReqPend: this->createReqPend.remove ( chan ); break; case channelNode::cs_createRespPend: this->createRespPend.remove ( chan ); break; case channelNode::cs_v42ConnCallbackPend: this->v42ConnCallbackPend.remove ( chan ); break; case channelNode::cs_subscripReqPend: this->subscripReqPend.remove ( chan ); break; case channelNode::cs_connected: this->connectedList.remove ( chan ); break; case channelNode::cs_unrespCircuit: this->unrespCircuit.remove ( chan ); break; case channelNode::cs_subscripUpdateReqPend: this->subscripUpdateReqPend.remove ( chan ); break; default: errlogPrintf ( "cac: attempt to uninstall channel from tcp iiu, but it inst installed there?" ); } chan.channelNode::listMember = channelNode::cs_none; this->channelCountTot--; if ( this->channelCountTot == 0 && ! this->isNameService() ) { this->initiateCleanShutdown ( guard ); } } int tcpiiu :: printFormated ( epicsGuard < epicsMutex > & cbGuard, const char *pformat, ... ) { cbGuard.assertIdenticalMutex ( this->cbMutex ); va_list theArgs; int status; va_start ( theArgs, pformat ); status = this->cacRef.varArgsPrintFormated ( cbGuard, pformat, theArgs ); va_end ( theArgs ); return status; } void tcpiiu::flushRequest ( epicsGuard < epicsMutex > & ) { if ( this->sendQue.occupiedBytes () > 0 ) { this->sendThreadFlushEvent.signal (); } } bool tcpiiu::bytesArePendingInOS () const { #if 0 FD_SET readBits; FD_ZERO ( & readBits ); FD_SET ( this->sock, & readBits ); struct timeval tmo; tmo.tv_sec = 0; tmo.tv_usec = 0; int status = select ( this->sock + 1, & readBits, NULL, NULL, & tmo ); if ( status > 0 ) { if ( FD_ISSET ( this->sock, & readBits ) ) { return true; } } return false; #else osiSockIoctl_t bytesPending = 0; /* shut up purifys yapping */ int status = socket_ioctl ( this->sock, FIONREAD, & bytesPending ); if ( status >= 0 ) { if ( bytesPending > 0 ) { return true; } } return false; #endif } double tcpiiu::receiveWatchdogDelay ( epicsGuard < epicsMutex > & ) const { return this->recvDog.delay (); } /* * Certain OS, such as HPUX, do not unblock a socket system call * when another thread asynchronously calls both shutdown() and * close(). To solve this problem we need to employ OS specific * mechanisms. */ void tcpRecvThread::interruptSocketRecv () { epicsThreadId threadId = this->thread.getId (); if ( threadId ) { epicsSignalRaiseSigAlarm ( threadId ); } } void tcpSendThread::interruptSocketSend () { epicsThreadId threadId = this->thread.getId (); if ( threadId ) { epicsSignalRaiseSigAlarm ( threadId ); } } void tcpiiu::operator delete ( void * /* pCadaver */ ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about " "placement delete - memory was probably leaked", __FILE__, __LINE__ ); } unsigned tcpiiu::channelCount ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); return this->channelCountTot; } void tcpiiu::uninstallChanDueToSuccessfulSearchResponse ( epicsGuard < epicsMutex > & guard, nciu & chan, const class epicsTime & currentTime ) { netiiu::uninstallChanDueToSuccessfulSearchResponse ( guard, chan, currentTime ); } bool tcpiiu::searchMsg ( epicsGuard < epicsMutex > & guard, ca_uint32_t id, const char * pName, unsigned nameLength ) { return netiiu::searchMsg ( guard, id, pName, nameLength ); } SearchDestTCP :: SearchDestTCP ( cac & cacIn, const osiSockAddr & addrIn ) : _ptcpiiu ( NULL ), _cac ( cacIn ), _addr ( addrIn ), _active ( false ) { } void SearchDestTCP :: disable () { _active = false; _ptcpiiu = NULL; } void SearchDestTCP :: enable () { _active = true; } void SearchDestTCP :: searchRequest ( epicsGuard < epicsMutex > & guard, const char * pBuf, size_t len ) { // restart circuit if it was shut down if ( ! _ptcpiiu ) { tcpiiu * piiu = NULL; bool newIIU = _cac.findOrCreateVirtCircuit ( guard, _addr, cacChannel::priorityDefault, piiu, CA_UKN_MINOR_VERSION, this ); if ( newIIU ) { piiu->start ( guard ); } _ptcpiiu = piiu; } // does this server support TCP-based name resolution? if ( CA_V412 ( _ptcpiiu->minorProtocolVersion ) ) { guard.assertIdenticalMutex ( _ptcpiiu->mutex ); assert ( CA_MESSAGE_ALIGN ( len ) == len ); comQueSendMsgMinder minder ( _ptcpiiu->sendQue, guard ); _ptcpiiu->sendQue.pushString ( pBuf, len ); minder.commit (); _ptcpiiu->flushRequest ( guard ); } } void SearchDestTCP :: show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { :: printf ( "tcpiiu :: SearchDestTCP\n" ); } void tcpiiu :: versionRespNotify ( const caHdrLargeArray & msg ) { this->minorProtocolVersion = msg.m_count; } void tcpiiu :: searchRespNotify ( const epicsTime & currentTime, const caHdrLargeArray & msg ) { /* * the type field is abused to carry the port number * so that we can have multiple servers on one host */ osiSockAddr serverAddr; if ( msg.m_cid != INADDR_BROADCAST ) { serverAddr.ia.sin_family = AF_INET; serverAddr.ia.sin_addr.s_addr = htonl ( msg.m_cid ); serverAddr.ia.sin_port = htons ( msg.m_dataType ); } else { serverAddr = this->address (); } cacRef.transferChanToVirtCircuit ( msg.m_available, msg.m_cid, 0xffff, 0, minorProtocolVersion, serverAddr, currentTime ); } base-7.0.3.1/modules/ca/src/client/test/ca_test.c0000664000577000060420000001407013557101274020262 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeff Hill * Date: 07-01-91 */ /* * ANSI */ #include #include #include #include "cadef.h" #include "epicsTime.h" int ca_test(char *pname, char *pvalue); static int cagft(char *pname); static void printit(struct event_handler_args args); static int capft(char *pname, char *pvalue); static void verify_value(chid chan_id, chtype type); static unsigned long outstanding; /* * ca_test * * find channel, write a value if supplied, and * read back the current value * */ int ca_test( char *pname, char *pvalue ) { int status; if(pvalue){ status = capft(pname,pvalue); } else{ status = cagft(pname); } ca_task_exit(); return status; } /* * cagft() * * ca get field test * * test ca get over the range of CA data types */ static int cagft(char *pname) { const unsigned maxTries = 1000ul; unsigned ntries = 0u; chid chan_id; int status; int i; /* * convert name to chan id */ status = ca_search(pname, &chan_id); SEVCHK(status,NULL); status = ca_pend_io(5.0); if(status != ECA_NORMAL){ SEVCHK(ca_clear_channel(chan_id),NULL); printf("Not Found %s\n", pname); return -1; } printf("name:\t%s\n", ca_name(chan_id)); printf("native type:\t%s\n", dbr_type_to_text(ca_field_type(chan_id))); printf("native count:\t%lu\n", ca_element_count(chan_id)); /* * fetch as each type */ for(i=0; i<=LAST_BUFFER_TYPE; i++){ if(ca_field_type(chan_id)==DBR_STRING) { if( (i!=DBR_STRING) && (i!=DBR_STS_STRING) && (i!=DBR_TIME_STRING) && (i!=DBR_GR_STRING) && (i!=DBR_CTRL_STRING)) { continue; } } /* ignore write only types */ if ( i == DBR_PUT_ACKT || i == DBR_PUT_ACKS ) { continue; } status = ca_array_get_callback( i, ca_element_count(chan_id), chan_id, printit, NULL); SEVCHK(status, NULL); outstanding++; } /* * wait for the operation to complete * before returning */ while ( ntries < maxTries ) { unsigned long oldOut; oldOut = outstanding; ca_pend_event ( 0.05 ); if ( ! outstanding ) { SEVCHK ( ca_clear_channel ( chan_id ), NULL ); printf ( "\n\n" ); return 0; } if ( outstanding == oldOut ) { ntries++; } } SEVCHK ( ca_clear_channel ( chan_id ), NULL ); return -1; } /* * PRINTIT() */ static void printit ( struct event_handler_args args ) { if ( args.status == ECA_NORMAL ) { ca_dump_dbr ( args.type, args.count, args.dbr ); } else { printf ( "%s\t%s\n", dbr_text[args.type], ca_message(args.status) ); } outstanding--; } /* * capft * * test ca_put() over a range of data types * */ static int capft( char *pname, char *pvalue ) { dbr_short_t shortvalue; dbr_long_t longvalue; dbr_float_t floatvalue; dbr_char_t charvalue; dbr_double_t doublevalue; unsigned long ntries = 10ul; int status; chid chan_id; if (((*pname < ' ') || (*pname > 'z')) || ((*pvalue < ' ') || (*pvalue > 'z'))){ printf("\nusage \"pv name\",\"value\"\n"); return -1; } /* * convert name to chan id */ status = ca_search(pname, &chan_id); SEVCHK(status,NULL); status = ca_pend_io(5.0); if(status != ECA_NORMAL){ SEVCHK(ca_clear_channel(chan_id),NULL); printf("Not Found %s\n", pname); return -1; } printf("name:\t%s\n", ca_name(chan_id)); printf("native type:\t%d\n", ca_field_type(chan_id)); printf("native count:\t%lu\n", ca_element_count(chan_id)); /* * string value ca_put */ status = ca_put( DBR_STRING, chan_id, pvalue); SEVCHK(status, NULL); verify_value(chan_id, DBR_STRING); if(ca_field_type(chan_id)==0)goto skip_rest; if(sscanf(pvalue,"%hd",&shortvalue)==1) { /* * short integer ca_put */ status = ca_put( DBR_SHORT, chan_id, &shortvalue); SEVCHK(status, NULL); verify_value(chan_id, DBR_SHORT); status = ca_put( DBR_ENUM, chan_id, &shortvalue); SEVCHK(status, NULL); verify_value(chan_id, DBR_ENUM); charvalue=(dbr_char_t)shortvalue; status = ca_put( DBR_CHAR, chan_id, &charvalue); SEVCHK(status, NULL); verify_value(chan_id, DBR_CHAR); } if(sscanf(pvalue,"%d",&longvalue)==1) { /* * long integer ca_put */ status = ca_put( DBR_LONG, chan_id, &longvalue); SEVCHK(status, NULL); verify_value(chan_id, DBR_LONG); } if(epicsScanFloat(pvalue, &floatvalue)==1) { /* * single precision float ca_put */ status = ca_put( DBR_FLOAT, chan_id, &floatvalue); SEVCHK(status, NULL); verify_value(chan_id, DBR_FLOAT); } if(epicsScanDouble(pvalue, &doublevalue)==1) { /* * double precision float ca_put */ status = ca_put( DBR_DOUBLE, chan_id, &doublevalue); SEVCHK(status, NULL); verify_value(chan_id, DBR_DOUBLE); } skip_rest: /* * wait for the operation to complete * (outstabnding decrements to zero) */ while(ntries){ ca_pend_event(1.0); if(!outstanding){ SEVCHK(ca_clear_channel(chan_id),NULL); printf("\n\n"); return 0; } ntries--; } SEVCHK(ca_clear_channel(chan_id),NULL); return -1; } /* * VERIFY_VALUE * * initiate print out the values in a database access interface structure */ static void verify_value(chid chan_id, chtype type) { int status; /* * issue a get which calls back `printit' * upon completion */ status = ca_array_get_callback( type, ca_element_count(chan_id), chan_id, printit, NULL); SEVCHK(status, NULL); outstanding++; } base-7.0.3.1/modules/ca/src/client/test/ca_test.h0000664000577000060420000000126213557101274020266 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeff Hill * Date: 21JAN2000 */ #ifdef __cplusplus extern "C" { #endif int ca_test(char *pname, char *pvalue); #ifdef __cplusplus } #endif base-7.0.3.1/modules/ca/src/client/test/ca_test_main.c0000664000577000060420000000243113557101274021264 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeff Hill * Date: 21JAN2000 */ #include #include #include "ca_test.h" #include "dbDefs.h" int main(int argc, char **argv) { /* * print error and return if arguments are invalid */ if(argc < 2 || argc > 3){ printf("usage: %s [optional value to be written]\n", argv[0]); printf("the following arguments were received\n"); while(argc>0) { printf("%s\n",argv[0]); argv++; argc--; } return -1; } /* * check for supplied value */ if(argc == 2){ return ca_test(argv[1], NULL); } else if(argc == 3){ char *pt; /* strip leading and trailing quotes*/ if(argv[2][1]=='"') argv[2]++; if( (pt=strchr(argv[2],'"')) ) *pt = 0; return ca_test(argv[1], argv[2]); } else{ return -1; } } base-7.0.3.1/modules/ca/src/client/test_event.cpp0000664000577000060420000004647313557101274020415 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * T E S T _ E V E N T . C * Author: Jeffrey O. Hill * simple stub for testing monitors */ #include "epicsStdioRedirect.h" #define epicsExportSharedSymbols #include "cadef.h" extern "C" void epicsShareAPI ca_test_event ( struct event_handler_args args ) { chtype nativeType = ca_field_type ( args.chid ); const char * pNativeTypeName = ""; if ( VALID_DB_REQ ( nativeType ) ) { pNativeTypeName = dbr_text[nativeType]; } else { if ( nativeType == TYPENOTCONN ) { pNativeTypeName = ""; } } printf ( "ca_test_event() for channel \"%s\" with native type %s\n", ca_name(args.chid), pNativeTypeName ); if ( ! ( CA_M_SUCCESS & args.status ) ) { printf ( "Invalid CA status \"%s\"\n", ca_message ( args.status ) ); return; } if ( args.dbr ) { ca_dump_dbr ( args.type, args.count, args.dbr ); } } /* * ca_dump_dbr() * dump the specified dbr type to stdout */ extern "C" void epicsShareAPI ca_dump_dbr ( chtype type, unsigned count, const void * pbuffer ) { unsigned i; char tsString[50]; if ( INVALID_DB_REQ ( type ) ) { printf ( "bad DBR type %ld\n", type ); } printf ( "%s\t", dbr_text[type] ); switch ( type ) { case DBR_STRING: { dbr_string_t *pString = (dbr_string_t *) pbuffer; for(i=0; istatus,pvalue->severity); printf("\tValue: %s",pvalue->value); break; } case DBR_STS_ENUM: { struct dbr_sts_enum *pvalue = (struct dbr_sts_enum *)pbuffer; dbr_enum_t *pEnum = &pvalue->value; printf("%2d %2d",pvalue->status,pvalue->severity); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pEnum++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%u ",*pEnum); } break; } case DBR_STS_SHORT: { struct dbr_sts_short *pvalue = (struct dbr_sts_short *)pbuffer; dbr_short_t *pshort = &pvalue->value; printf("%2d %2d",pvalue->status,pvalue->severity); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pshort++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%u ",*pshort); } break; } case DBR_STS_FLOAT: { struct dbr_sts_float *pvalue = (struct dbr_sts_float *)pbuffer; dbr_float_t *pfloat = &pvalue->value; printf("%2d %2d",pvalue->status,pvalue->severity); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pfloat++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%6.4f ",*pfloat); } break; } case DBR_STS_CHAR: { struct dbr_sts_char *pvalue = (struct dbr_sts_char *)pbuffer; dbr_char_t *pchar = &pvalue->value; printf("%2d %2d",pvalue->status,pvalue->severity); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pchar++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%u ", *pchar); } break; } case DBR_STS_LONG: { struct dbr_sts_long *pvalue = (struct dbr_sts_long *)pbuffer; dbr_long_t *plong = &pvalue->value; printf("%2d %2d",pvalue->status,pvalue->severity); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,plong++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%d ",*plong); } break; } case DBR_STS_DOUBLE: { struct dbr_sts_double *pvalue = (struct dbr_sts_double *)pbuffer; dbr_double_t *pdouble = &pvalue->value; printf("%2d %2d",pvalue->status,pvalue->severity); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pdouble++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%6.4f ",(float)(*pdouble)); } break; } case DBR_TIME_STRING: { struct dbr_time_string *pvalue = (struct dbr_time_string *) pbuffer; epicsTimeToStrftime(tsString,sizeof(tsString), "%Y/%m/%d %H:%M:%S.%06f",&pvalue->stamp); printf("%2d %2d",pvalue->status,pvalue->severity); printf("\tTimeStamp: %s",tsString); printf("\tValue: "); printf("%s",pvalue->value); break; } case DBR_TIME_ENUM: { struct dbr_time_enum *pvalue = (struct dbr_time_enum *)pbuffer; dbr_enum_t *pshort = &pvalue->value; epicsTimeToStrftime(tsString,sizeof(tsString), "%Y/%m/%d %H:%M:%S.%06f",&pvalue->stamp); printf("%2d %2d",pvalue->status,pvalue->severity); printf("\tTimeStamp: %s",tsString); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pshort++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%d ",*pshort); } break; } case DBR_TIME_SHORT: { struct dbr_time_short *pvalue = (struct dbr_time_short *)pbuffer; dbr_short_t *pshort = &pvalue->value; epicsTimeToStrftime(tsString,sizeof(tsString), "%Y/%m/%d %H:%M:%S.%06f",&pvalue->stamp); printf("%2d %2d", pvalue->status, pvalue->severity); printf("\tTimeStamp: %s",tsString); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pshort++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%d ",*pshort); } break; } case DBR_TIME_FLOAT: { struct dbr_time_float *pvalue = (struct dbr_time_float *)pbuffer; dbr_float_t *pfloat = &pvalue->value; epicsTimeToStrftime(tsString,sizeof(tsString), "%Y/%m/%d %H:%M:%S.%06f",&pvalue->stamp); printf("%2d %2d",pvalue->status,pvalue->severity); printf("\tTimeStamp: %s",tsString); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pfloat++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%6.4f ",*pfloat); } break; } case DBR_TIME_CHAR: { struct dbr_time_char *pvalue = (struct dbr_time_char *)pbuffer; dbr_char_t *pchar = &pvalue->value; epicsTimeToStrftime(tsString,sizeof(tsString), "%Y/%m/%d %H:%M:%S.%06f",&pvalue->stamp); printf("%2d %2d",pvalue->status,pvalue->severity); printf("\tTimeStamp: %s",tsString); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pchar++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%d ",(short)(*pchar)); } break; } case DBR_TIME_LONG: { struct dbr_time_long *pvalue = (struct dbr_time_long *)pbuffer; dbr_long_t *plong = &pvalue->value; epicsTimeToStrftime(tsString,sizeof(tsString), "%Y/%m/%d %H:%M:%S.%06f",&pvalue->stamp); printf("%2d %2d",pvalue->status,pvalue->severity); printf("\tTimeStamp: %s",tsString); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,plong++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%d ",*plong); } break; } case DBR_TIME_DOUBLE: { struct dbr_time_double *pvalue = (struct dbr_time_double *)pbuffer; dbr_double_t *pdouble = &pvalue->value; epicsTimeToStrftime(tsString,sizeof(tsString), "%Y/%m/%d %H:%M:%S.%06f",&pvalue->stamp); printf("%2d %2d",pvalue->status,pvalue->severity); printf("\tTimeStamp: %s",tsString); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pdouble++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%6.4f ",(float)(*pdouble)); } break; } case DBR_GR_SHORT: { struct dbr_gr_short *pvalue = (struct dbr_gr_short *)pbuffer; dbr_short_t *pshort = &pvalue->value; printf("%2d %2d %.8s",pvalue->status,pvalue->severity, pvalue->units); printf("\n\t%8d %8d %8d %8d %8d %8d", pvalue->upper_disp_limit,pvalue->lower_disp_limit, pvalue->upper_alarm_limit,pvalue->upper_warning_limit, pvalue->lower_warning_limit,pvalue->lower_alarm_limit); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pshort++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%d ",*pshort); } break; } case DBR_GR_FLOAT: { struct dbr_gr_float *pvalue = (struct dbr_gr_float *)pbuffer; dbr_float_t *pfloat = &pvalue->value; printf("%2d %2d %.8s",pvalue->status,pvalue->severity, pvalue->units); printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", pvalue->precision, pvalue->upper_disp_limit,pvalue->lower_disp_limit, pvalue->upper_alarm_limit,pvalue->upper_warning_limit, pvalue->lower_warning_limit,pvalue->lower_alarm_limit); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pfloat++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%6.4f ",*pfloat); } break; } case DBR_GR_ENUM: { struct dbr_gr_enum *pvalue = (struct dbr_gr_enum *)pbuffer; printf("%2d %2d",pvalue->status, pvalue->severity); printf("\tValue: %d",pvalue->value); if(pvalue->no_str>0) { printf("\n\t%3d",pvalue->no_str); for (i = 0; i < (unsigned) pvalue->no_str; i++) printf("\n\t%.26s",pvalue->strs[i]); } break; } case DBR_CTRL_ENUM: { struct dbr_ctrl_enum *pvalue = (struct dbr_ctrl_enum *)pbuffer; printf("%2d %2d",pvalue->status, pvalue->severity); printf("\tValue: %d",pvalue->value); if(pvalue->no_str>0) { printf("\n\t%3d",pvalue->no_str); for (i = 0; i < (unsigned) pvalue->no_str; i++) printf("\n\t%.26s",pvalue->strs[i]); } break; } case DBR_GR_CHAR: { struct dbr_gr_char *pvalue = (struct dbr_gr_char *)pbuffer; dbr_char_t *pchar = &pvalue->value; printf("%2d %2d %.8s",pvalue->status,pvalue->severity, pvalue->units); printf("\n\t%8d %8d %8d %8d %8d %8d", pvalue->upper_disp_limit,pvalue->lower_disp_limit, pvalue->upper_alarm_limit,pvalue->upper_warning_limit, pvalue->lower_warning_limit,pvalue->lower_alarm_limit); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pchar++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%u ",*pchar); } break; } case DBR_GR_LONG: { struct dbr_gr_long *pvalue = (struct dbr_gr_long *)pbuffer; dbr_long_t *plong = &pvalue->value; printf("%2d %2d %.8s",pvalue->status,pvalue->severity, pvalue->units); printf("\n\t%8d %8d %8d %8d %8d %8d", pvalue->upper_disp_limit,pvalue->lower_disp_limit, pvalue->upper_alarm_limit,pvalue->upper_warning_limit, pvalue->lower_warning_limit,pvalue->lower_alarm_limit); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,plong++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%d ",*plong); } break; } case DBR_GR_DOUBLE: { struct dbr_gr_double *pvalue = (struct dbr_gr_double *)pbuffer; dbr_double_t *pdouble = &pvalue->value; printf("%2d %2d %.8s",pvalue->status,pvalue->severity, pvalue->units); printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", pvalue->precision, (float)(pvalue->upper_disp_limit), (float)(pvalue->lower_disp_limit), (float)(pvalue->upper_alarm_limit), (float)(pvalue->upper_warning_limit), (float)(pvalue->lower_warning_limit), (float)(pvalue->lower_alarm_limit)); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pdouble++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%6.4f ",(float)(*pdouble)); } break; } case DBR_CTRL_SHORT: { struct dbr_ctrl_short *pvalue = (struct dbr_ctrl_short *)pbuffer; dbr_short_t *pshort = &pvalue->value; printf("%2d %2d %.8s",pvalue->status,pvalue->severity, pvalue->units); printf("\n\t%8d %8d %8d %8d %8d %8d", pvalue->upper_disp_limit,pvalue->lower_disp_limit, pvalue->upper_alarm_limit,pvalue->upper_warning_limit, pvalue->lower_warning_limit,pvalue->lower_alarm_limit); printf(" %8d %8d", pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pshort++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%d ",*pshort); } break; } case DBR_CTRL_FLOAT: { struct dbr_ctrl_float *pvalue = (struct dbr_ctrl_float *)pbuffer; dbr_float_t *pfloat = &pvalue->value; printf("%2d %2d %.8s",pvalue->status,pvalue->severity, pvalue->units); printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", pvalue->precision, pvalue->upper_disp_limit,pvalue->lower_disp_limit, pvalue->upper_alarm_limit,pvalue->upper_warning_limit, pvalue->lower_warning_limit,pvalue->lower_alarm_limit); printf(" %8.3f %8.3f", pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pfloat++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%6.4f ",*pfloat); } break; } case DBR_CTRL_CHAR: { struct dbr_ctrl_char *pvalue = (struct dbr_ctrl_char *)pbuffer; dbr_char_t *pchar = &pvalue->value; printf("%2d %2d %.8s",pvalue->status,pvalue->severity, pvalue->units); printf("\n\t%8d %8d %8d %8d %8d %8d", pvalue->upper_disp_limit,pvalue->lower_disp_limit, pvalue->upper_alarm_limit,pvalue->upper_warning_limit, pvalue->lower_warning_limit,pvalue->lower_alarm_limit); printf(" %8d %8d", pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pchar++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%4d ",(short)(*pchar)); } break; } case DBR_CTRL_LONG: { struct dbr_ctrl_long *pvalue = (struct dbr_ctrl_long *)pbuffer; dbr_long_t *plong = &pvalue->value; printf("%2d %2d %.8s",pvalue->status,pvalue->severity, pvalue->units); printf("\n\t%8d %8d %8d %8d %8d %8d", pvalue->upper_disp_limit,pvalue->lower_disp_limit, pvalue->upper_alarm_limit,pvalue->upper_warning_limit, pvalue->lower_warning_limit,pvalue->lower_alarm_limit); printf(" %8d %8d", pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,plong++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%d ",*plong); } break; } case DBR_CTRL_DOUBLE: { struct dbr_ctrl_double *pvalue = (struct dbr_ctrl_double *)pbuffer; dbr_double_t *pdouble = &pvalue->value; printf("%2d %2d %.8s",pvalue->status,pvalue->severity, pvalue->units); printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", pvalue->precision, (float)(pvalue->upper_disp_limit), (float)(pvalue->lower_disp_limit), (float)(pvalue->upper_alarm_limit), (float)(pvalue->upper_warning_limit), (float)(pvalue->lower_warning_limit), (float)(pvalue->lower_alarm_limit)); printf(" %8.3f %8.3f", (float)(pvalue->upper_ctrl_limit), (float)(pvalue->lower_ctrl_limit)); if(count==1) printf("\tValue: "); for (i = 0; i < count; i++,pdouble++){ if(count!=1 && (i%10 == 0)) printf("\n"); printf("%6.6f ",(float)(*pdouble)); } break; } case DBR_STSACK_STRING: { struct dbr_stsack_string *pvalue = (struct dbr_stsack_string *)pbuffer; printf("%2d %2d",pvalue->status,pvalue->severity); printf(" %2d %2d",pvalue->ackt,pvalue->acks); printf(" %s",pvalue->value); break; } case DBR_CLASS_NAME: { dbr_class_name_t * pvalue = ( dbr_class_name_t * ) pbuffer; printf ( "%s", *pvalue ); break; } default: printf ( "unsupported by ca_dbrDump()" ); break; } printf("\n"); } base-7.0.3.1/modules/ca/src/client/ucx.h0000664000577000060420000000573213557101274016472 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * * U C X . H * UNIX ioctl structures and defines used for VAX/UCX * */ #ifndef _UCX_H_ # define _UCX_H_ #ifdef UCX #define IFF_UP 0x1 /* interface is up */ #define IFF_BROADCAST 0x2 /* broadcast address valid */ #define IFF_LOOPBACK 0x8 /* is a loopback net */ #define IFF_POINTOPOINT 0x10 /* interface is point to point */ /* * Interface request structure used for socket * ioctl's. All interface ioctl's must have parameter * definitions which begin with ifr_name. The * remainder may be interface specific. */ struct ifreq { #define IFNAMSIZ 16 char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */ union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; struct sockaddr ifru_broadaddr; short ifru_flags; int ifru_metric; caddr_t ifru_data; } ifr_ifru; #define ifr_addr ifr_ifru.ifru_addr /* address */ #define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */ #define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ #define ifr_flags ifr_ifru.ifru_flags /* flags */ #define ifr_metric ifr_ifru.ifru_metric /* metric */ #define ifr_data ifr_ifru.ifru_data /* for use by interface */ }; /* Structure used in SIOCGIFCONF request. * Used to retrieve interface configuration * for machine (useful for programs which * must know all networks accessible). */ struct ifconf { int ifc_len; /* size of associated buffer */ union { caddr_t ifcu_buf; struct ifreq *ifcu_req; } ifc_ifcu; #define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ #define ifc_req ifc_ifcu.ifcu_req /* array of structures returned */ }; #ifndef NBBY # define NBBY 8 #endif #ifndef FD_SETSIZE # define FD_SETSIZE 256 #endif typedef long fd_mask ; #define NFDBITS (sizeof (fd_mask) * NBBY ) /* bits per mask */ #ifndef howmany # define howmany(x, y) (((x)+((y)-1))/(y)) #endif /* * Both DEC C and VAX C only allow 32 fd's at once */ typedef int fd_set ; #define FD_SET(n, p) (*(p) |= (1 << ((n) % NFDBITS))) #define FD_CLR(n, p) (*(p) &= ~(1 << ((n) % NFDBITS))) #define FD_ISSET(n, p) (*(p) & (1 << ((n) % NFDBITS))) #define FD_ZERO(p) memset((char *)(p), 0, sizeof (*(p))) #include #define IO$_RECEIVE (IO$_WRITEVBLK) struct timezone { int tz_minuteswest ; /* minutes west of Greenwich */ int tz_dsttime ; /* type of dst correction */ }; #define TWOPOWER32 4294967296.0 #define TWOPOWER31 2147483648.0 #define UNIX_EPOCH_AS_MJD 40587.0 #endif #endif base-7.0.3.1/modules/ca/src/client/udpiiu.cpp0000664000577000060420000013164513557101274017530 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #ifdef _MSC_VER # pragma warning(disable:4355) #endif #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "envDefs.h" #include "dbDefs.h" #include "osiProcess.h" #include "osiWireFormat.h" #include "epicsAlgorithm.h" #include "errlog.h" #include "locationException.h" #define epicsExportSharedSymbols #include "addrList.h" #include "caerr.h" // for ECA_NOSEARCHADDR #include "udpiiu.h" #include "iocinf.h" #include "inetAddrID.h" #include "cac.h" #include "disconnectGovernorTimer.h" // UDP protocol dispatch table const udpiiu::pProtoStubUDP udpiiu::udpJumpTableCAC [] = { &udpiiu::versionAction, &udpiiu::badUDPRespAction, &udpiiu::badUDPRespAction, &udpiiu::badUDPRespAction, &udpiiu::badUDPRespAction, &udpiiu::badUDPRespAction, &udpiiu::searchRespAction, &udpiiu::badUDPRespAction, &udpiiu::badUDPRespAction, &udpiiu::badUDPRespAction, &udpiiu::badUDPRespAction, &udpiiu::exceptionRespAction, &udpiiu::badUDPRespAction, &udpiiu::beaconAction, &udpiiu::notHereRespAction, &udpiiu::badUDPRespAction, &udpiiu::badUDPRespAction, &udpiiu::repeaterAckAction, }; static double getMaxPeriod() { double maxPeriod = maxSearchPeriodDefault; if ( envGetConfigParamPtr ( & EPICS_CA_MAX_SEARCH_PERIOD ) ) { long longStatus = envGetDoubleConfigParam ( & EPICS_CA_MAX_SEARCH_PERIOD, & maxPeriod ); if ( ! longStatus ) { if ( maxPeriod < maxSearchPeriodLowerLimit ) { epicsPrintf ( "\"%s\" out of range (low)\n", EPICS_CA_MAX_SEARCH_PERIOD.name ); maxPeriod = maxSearchPeriodLowerLimit; epicsPrintf ( "Setting \"%s\" = %f seconds\n", EPICS_CA_MAX_SEARCH_PERIOD.name, maxPeriod ); } } else { epicsPrintf ( "EPICS \"%s\" wasnt a real number\n", EPICS_CA_MAX_SEARCH_PERIOD.name ); epicsPrintf ( "Setting \"%s\" = %f seconds\n", EPICS_CA_MAX_SEARCH_PERIOD.name, maxPeriod ); } } return maxPeriod; } static unsigned getNTimers(double maxPeriod) { unsigned nTimers = static_cast < unsigned > ( 1.0 + log ( maxPeriod / minRoundTripEstimate ) / log ( 2.0 ) ); if ( nTimers > channelNode::getMaxSearchTimerCount () ) { nTimers = channelNode::getMaxSearchTimerCount (); epicsPrintf ( "\"%s\" out of range (high)\n", EPICS_CA_MAX_SEARCH_PERIOD.name ); epicsPrintf ( "Setting \"%s\" = %f seconds\n", EPICS_CA_MAX_SEARCH_PERIOD.name, (1<<(nTimers-1)) * minRoundTripEstimate ); } return nTimers; } // // udpiiu::udpiiu () // udpiiu::udpiiu ( epicsGuard < epicsMutex > & cacGuard, epicsTimerQueueActive & timerQueue, epicsMutex & cbMutexIn, epicsMutex & cacMutexIn, cacContextNotify & ctxNotifyIn, cac & cac, unsigned port, tsDLList < SearchDest > & searchDestListIn ) : recvThread ( *this, ctxNotifyIn, cbMutexIn, "CAC-UDP", epicsThreadGetStackSize ( epicsThreadStackMedium ), cac::lowestPriorityLevelAbove ( cac::lowestPriorityLevelAbove ( cac.getInitializingThreadsPriority () ) ) ), m_repeaterTimerNotify ( *this ), repeaterSubscribeTmr ( m_repeaterTimerNotify, timerQueue, cbMutexIn, ctxNotifyIn ), govTmr ( *this, timerQueue, cacMutexIn ), maxPeriod ( getMaxPeriod() ), rtteMean ( minRoundTripEstimate ), rtteMeanDev ( 0 ), cacRef ( cac ), cbMutex ( cbMutexIn ), cacMutex ( cacMutexIn ), nTimers ( getNTimers(maxPeriod) ), ppSearchTmr ( nTimers ), nBytesInXmitBuf ( 0 ), beaconAnomalyTimerIndex ( 0 ), sequenceNumber ( 0 ), lastReceivedSeqNo ( 0 ), sock ( 0 ), repeaterPort ( 0 ), serverPort ( port ), localPort ( 0 ), shutdownCmd ( false ), lastReceivedSeqNoIsValid ( false ) { cacGuard.assertIdenticalMutex ( cacMutex ); double powerOfTwo = log ( beaconAnomalySearchPeriod / minRoundTripEstimate ) / log ( 2.0 ); this->beaconAnomalyTimerIndex = static_cast < unsigned > ( powerOfTwo + 1.0 ); if ( this->beaconAnomalyTimerIndex >= this->nTimers ) { this->beaconAnomalyTimerIndex = this->nTimers - 1; } for ( unsigned i = 0; i < this->nTimers; i++ ) { this->ppSearchTmr[i].reset ( new searchTimer ( *this, timerQueue, i, cacMutexIn, i > this->beaconAnomalyTimerIndex ) ); } this->repeaterPort = envGetInetPortConfigParam ( &EPICS_CA_REPEATER_PORT, static_cast (CA_REPEATER_PORT) ); this->sock = epicsSocketCreate ( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if ( this->sock == INVALID_SOCKET ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ("CAC: unable to create datagram socket because = \"%s\"\n", sockErrBuf ); throwWithLocation ( noSocket () ); } #ifdef IP_ADD_MEMBERSHIP { osiSockOptMcastLoop_t flag = 1; if ( setsockopt ( this->sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &flag, sizeof ( flag ) ) == -1 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf("CAC: failed to set mcast loopback\n"); } } #endif #ifdef IP_MULTICAST_TTL { osiSockOptMcastTTL_t ttl; long val; if(envGetLongConfigParam(&EPICS_CA_MCAST_TTL, &val)) val =1; ttl = val; if ( setsockopt(this->sock, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(ttl))) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf("CAC: failed to set mcast ttl %d\n", ttl); } } #endif int boolValue = true; int status = setsockopt ( this->sock, SOL_SOCKET, SO_BROADCAST, (char *) &boolValue, sizeof ( boolValue ) ); if ( status < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ("CAC: IP broadcasting enable failed because = \"%s\"\n", sockErrBuf ); } #if 0 { /* * some concern that vxWorks will run out of mBuf's * if this change is made joh 11-10-98 * * bump up the UDP recv buffer */ int size = 1u<<15u; status = setsockopt ( this->sock, SOL_SOCKET, SO_RCVBUF, (char *)&size, sizeof (size) ); if (status<0) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAC: unable to set socket option SO_RCVBUF because \"%s\"\n", sockErrBuf ); } } #endif // force a bind to an unconstrained address so we can obtain // the local port number below static const unsigned short PORT_ANY = 0u; osiSockAddr addr; memset ( (char *)&addr, 0 , sizeof (addr) ); addr.ia.sin_family = AF_INET; addr.ia.sin_addr.s_addr = htonl ( INADDR_ANY ); addr.ia.sin_port = htons ( PORT_ANY ); status = bind (this->sock, &addr.sa, sizeof (addr) ); if ( status < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); epicsSocketDestroy (this->sock); errlogPrintf ( "CAC: unable to bind to an unconstrained address because = \"%s\"\n", sockErrBuf ); throwWithLocation ( noSocket () ); } { osiSockAddr tmpAddr; osiSocklen_t saddr_length = sizeof ( tmpAddr ); status = getsockname ( this->sock, &tmpAddr.sa, &saddr_length ); if ( status < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); epicsSocketDestroy ( this->sock ); errlogPrintf ( "CAC: getsockname () error was \"%s\"\n", sockErrBuf ); throwWithLocation ( noSocket () ); } if ( tmpAddr.sa.sa_family != AF_INET) { epicsSocketDestroy ( this->sock ); errlogPrintf ( "CAC: UDP socket was not inet addr family\n" ); throwWithLocation ( noSocket () ); } this->localPort = ntohs ( tmpAddr.ia.sin_port ); } /* * load user and auto configured * broadcast address list */ ELLLIST dest; ellInit ( & dest ); configureChannelAccessAddressList ( & dest, this->sock, this->serverPort ); while ( osiSockAddrNode * pNode = reinterpret_cast < osiSockAddrNode * > ( ellGet ( & dest ) ) ) { SearchDestUDP & searchDest = * new SearchDestUDP ( pNode->addr, *this ); _searchDestList.add ( searchDest ); free ( pNode ); } /* add list of tcp name service addresses */ _searchDestList.add ( searchDestListIn ); caStartRepeaterIfNotInstalled ( this->repeaterPort ); this->pushVersionMsg (); // start timers and receive thread for ( unsigned j =0; j < this->nTimers; j++ ) { this->ppSearchTmr[j]->start ( cacGuard ); } this->govTmr.start (); this->repeaterSubscribeTmr.start (); this->recvThread.start (); } /* * udpiiu::~udpiiu () */ udpiiu::~udpiiu () { { epicsGuard < epicsMutex > cbGuard ( this->cbMutex ); epicsGuard < epicsMutex > guard ( this->cacMutex ); this->shutdown ( cbGuard, guard ); } tsDLIter < SearchDest > iter ( _searchDestList.firstIter () ); while ( iter.valid () ) { SearchDest & curr ( *iter ); iter++; delete & curr; } epicsSocketDestroy ( this->sock ); } void udpiiu::shutdown ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ) { // stop all of the timers this->repeaterSubscribeTmr.shutdown ( cbGuard, guard ); this->govTmr.shutdown ( cbGuard, guard ); for ( unsigned i =0; i < this->nTimers; i++ ) { this->ppSearchTmr[i]->shutdown ( cbGuard, guard ); } { this->shutdownCmd = true; epicsGuardRelease < epicsMutex > unguard ( guard ); { epicsGuardRelease < epicsMutex > cbUnguard ( cbGuard ); if ( ! this->recvThread.exitWait ( 0.0 ) ) { unsigned tries = 0u; this->wakeupMsg (); // wait for recv threads to exit double shutdownDelay = 1.0; while ( ! this->recvThread.exitWait ( shutdownDelay ) ) { this->wakeupMsg (); if ( shutdownDelay < 16.0 ) { shutdownDelay += shutdownDelay; } if ( ++tries > 3 ) { fprintf ( stderr, "cac: timing out waiting for UDP thread shutdown\n" ); } } } } } } udpRecvThread::udpRecvThread ( udpiiu & iiuIn, cacContextNotify & ctxNotifyIn, epicsMutex & cbMutexIn, const char * pName, unsigned stackSize, unsigned priority ) : iiu ( iiuIn ), cbMutex ( cbMutexIn ), ctxNotify ( ctxNotifyIn ), thread ( *this, pName, stackSize, priority ) {} udpRecvThread::~udpRecvThread () { } void udpRecvThread::start () { this->thread.start (); } bool udpRecvThread::exitWait ( double delay ) { return this->thread.exitWait ( delay ); } void udpRecvThread::show ( unsigned /* level */ ) const { } void udpRecvThread::run () { epicsThreadPrivateSet ( caClientCallbackThreadId, &this->iiu ); if ( this->iiu._searchDestList.count () == 0 ) { callbackManager mgr ( this->ctxNotify, this->cbMutex ); epicsGuard < epicsMutex > guard ( this->iiu.cacMutex ); genLocalExcep ( mgr.cbGuard, guard, this->iiu.cacRef, ECA_NOSEARCHADDR, NULL ); } do { osiSockAddr src; osiSocklen_t src_size = sizeof ( src ); int status = recvfrom ( this->iiu.sock, this->iiu.recvBuf, sizeof ( this->iiu.recvBuf ), 0, & src.sa, & src_size ); if ( status <= 0 ) { if ( status < 0 ) { int errnoCpy = SOCKERRNO; if ( errnoCpy != SOCK_EINTR && errnoCpy != SOCK_SHUTDOWN && errnoCpy != SOCK_ENOTSOCK && errnoCpy != SOCK_EBADF && // Avoid spurious ECONNREFUSED bug in linux errnoCpy != SOCK_ECONNREFUSED && // Avoid ECONNRESET from disconnected socket bug // in windows errnoCpy != SOCK_ECONNRESET ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAC: UDP recv error was \"%s\"\n", sockErrBuf ); } } } else if ( status > 0 ) { this->iiu.postMsg ( src, this->iiu.recvBuf, (arrayElementCount) status, epicsTime::getMonotonic() ); } } while ( ! this->iiu.shutdownCmd ); } /* for sunpro compiler */ udpiiu::M_repeaterTimerNotify::~M_repeaterTimerNotify () { } /* * udpiiu::M_repeaterTimerNotify::repeaterRegistrationMessage () * * register with the repeater */ void udpiiu :: M_repeaterTimerNotify :: repeaterRegistrationMessage ( unsigned attemptNumber ) { epicsGuard < epicsMutex > cbGuard ( m_udpiiu.cacMutex ); caRepeaterRegistrationMessage ( m_udpiiu.sock, m_udpiiu.repeaterPort, attemptNumber ); } /* * caRepeaterRegistrationMessage () * * register with the repeater */ void epicsShareAPI caRepeaterRegistrationMessage ( SOCKET sock, unsigned repeaterPort, unsigned attemptNumber ) { osiSockAddr saddr; caHdr msg; int status; int len; assert ( repeaterPort <= USHRT_MAX ); unsigned short port = static_cast ( repeaterPort ); /* * In 3.13 beta 11 and before the CA repeater calls local_addr() * to determine a local address and does not allow registration * messages originating from other addresses. In these * releases local_addr() returned the address of the first enabled * interface found, and this address may or may not have been the loop * back address. Starting with 3.13 beta 12 local_addr() was * changed to always return the address of the first enabled * non-loopback interface because a valid non-loopback local * address is required in the beacon messages. Therefore, to * guarantee compatibility with past versions of the repeater * we alternate between the address returned by local_addr() * and the loopback address here. * * CA repeaters in R3.13 beta 12 and higher allow * either the loopback address or the address returned * by local address (the first non-loopback address found) */ if ( attemptNumber & 1 ) { saddr = osiLocalAddr ( sock ); if ( saddr.sa.sa_family != AF_INET ) { /* * use the loop back address to communicate with the CA repeater * if this os does not have interface query capabilities * * this will only work with 3.13 beta 12 CA repeaters or later */ saddr.ia.sin_family = AF_INET; saddr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK ); saddr.ia.sin_port = htons ( port ); } else { saddr.ia.sin_port = htons ( port ); } } else { saddr.ia.sin_family = AF_INET; saddr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK ); saddr.ia.sin_port = htons ( port ); } memset ( (char *) &msg, 0, sizeof (msg) ); AlignedWireRef < epicsUInt16 > ( msg.m_cmmd ) = REPEATER_REGISTER; msg.m_available = saddr.ia.sin_addr.s_addr; /* * Intentionally sending a zero length message here * until most CA repeater daemons have been restarted * (and only then will they accept the above protocol) * (repeaters began accepting this protocol * starting with EPICS 3.12) */ # if defined ( DOES_NOT_ACCEPT_ZERO_LENGTH_UDP ) len = sizeof (msg); # else len = 0; # endif status = sendto ( sock, (char *) &msg, len, 0, &saddr.sa, sizeof ( saddr ) ); if ( status < 0 ) { int errnoCpy = SOCKERRNO; /* * Different OS return different codes when the repeater isnt running. * Its ok to supress these messages because I print another warning message * if we time out registerring with the repeater. * * Linux returns SOCK_ECONNREFUSED * Windows 2000 returns SOCK_ECONNRESET */ if ( errnoCpy != SOCK_EINTR && errnoCpy != SOCK_ECONNREFUSED && errnoCpy != SOCK_ECONNRESET ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf ( stderr, "error sending registration message to CA repeater daemon was \"%s\"\n", sockErrBuf ); } } } /* * caStartRepeaterIfNotInstalled () * * Test for the repeater already installed * * NOTE: potential race condition here can result * in two copies of the repeater being spawned * however the repeater detects this, prints a message, * and lets the other task start the repeater. * * QUESTION: is there a better way to test for a port in use? * ANSWER: none that I can find. * * Problems with checking for the repeater installed * by attempting to bind a socket to its address * and port. * * 1) Closed socket may not release the bound port * before the repeater wakes up and tries to grab it. * Attempting to bind the open socket to another port * also does not work. * * 072392 - problem solved by using SO_REUSEADDR */ void epicsShareAPI caStartRepeaterIfNotInstalled ( unsigned repeaterPort ) { bool installed = false; int status; SOCKET tmpSock; union { struct sockaddr_in ia; struct sockaddr sa; } bd; if ( repeaterPort > 0xffff ) { fprintf ( stderr, "caStartRepeaterIfNotInstalled () : strange repeater port specified\n" ); return; } tmpSock = epicsSocketCreate ( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if ( tmpSock != INVALID_SOCKET ) { ca_uint16_t port = static_cast < ca_uint16_t > ( repeaterPort ); memset ( (char *) &bd, 0, sizeof ( bd ) ); bd.ia.sin_family = AF_INET; bd.ia.sin_addr.s_addr = htonl ( INADDR_ANY ); bd.ia.sin_port = htons ( port ); status = bind ( tmpSock, &bd.sa, sizeof ( bd ) ); if ( status < 0 ) { if ( SOCKERRNO == SOCK_EADDRINUSE ) { installed = true; } else { fprintf ( stderr, "caStartRepeaterIfNotInstalled () : bind failed\n" ); } } } /* * turn on reuse only after the test so that * this works on kernels that support multicast */ epicsSocketEnableAddressReuseDuringTimeWaitState ( tmpSock ); epicsSocketDestroy ( tmpSock ); if ( ! installed ) { /* * This is not called if the repeater is known to be * already running. (in the event of a race condition * the 2nd repeater exits when unable to attach to the * repeater's port) */ osiSpawnDetachedProcessReturn osptr = osiSpawnDetachedProcess ( "CA Repeater", "caRepeater" ); if ( osptr == osiSpawnDetachedProcessNoSupport ) { epicsThreadId tid; tid = epicsThreadCreate ( "CAC-repeater", epicsThreadPriorityLow, epicsThreadGetStackSize ( epicsThreadStackMedium ), caRepeaterThread, 0); if ( tid == 0 ) { fprintf ( stderr, "caStartRepeaterIfNotInstalled : unable to create CA repeater daemon thread\n" ); } } else if ( osptr == osiSpawnDetachedProcessFail ) { fprintf ( stderr, "caStartRepeaterIfNotInstalled (): unable to start CA repeater daemon detached process\n" ); } } } bool udpiiu::badUDPRespAction ( const caHdr &msg, const osiSockAddr &netAddr, const epicsTime ¤tTime ) { char buf[64]; sockAddrToDottedIP ( &netAddr.sa, buf, sizeof ( buf ) ); char date[64]; currentTime.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S"); errlogPrintf ( "CAC: Undecipherable ( bad msg code %u ) UDP message from %s at %s\n", msg.m_cmmd, buf, date ); return false; } bool udpiiu::versionAction ( const caHdr & hdr, const osiSockAddr &, const epicsTime & /* currentTime */ ) { epicsGuard < epicsMutex > guard ( this->cacMutex ); // update the round trip time estimate if ( hdr.m_dataType & sequenceNoIsValid ) { this->lastReceivedSeqNo = hdr.m_cid; this->lastReceivedSeqNoIsValid = true; } return true; } bool udpiiu :: searchRespAction ( const caHdr & msg, const osiSockAddr & addr, const epicsTime & currentTime ) { /* * we dont currently know what to do with channel's * found to be at non-IP type addresses */ if ( addr.sa.sa_family != AF_INET ) { return true; } /* * Starting with CA V4.1 the minor version number * is appended to the end of each UDP search reply. * This value is ignored by earlier clients. */ ca_uint32_t minorVersion; if ( msg.m_postsize >= sizeof ( minorVersion ) ){ /* * care is taken here not to break gcc 3.2 aggressive alias * analysis rules */ const ca_uint8_t * pPayLoad = reinterpret_cast < const ca_uint8_t *> ( & msg + 1 ); unsigned byte0 = pPayLoad[0]; unsigned byte1 = pPayLoad[1]; minorVersion = ( byte0 << 8u ) | byte1; } else { minorVersion = CA_UKN_MINOR_VERSION; } /* * the type field is abused to carry the port number * so that we can have multiple servers on one host */ osiSockAddr serverAddr; serverAddr.ia.sin_family = AF_INET; if ( CA_V48 ( minorVersion ) ) { if ( msg.m_cid != INADDR_BROADCAST ) { serverAddr.ia.sin_addr.s_addr = htonl ( msg.m_cid ); } else { serverAddr.ia.sin_addr = addr.ia.sin_addr; } serverAddr.ia.sin_port = htons ( msg.m_dataType ); } else if ( CA_V45 (minorVersion) ) { serverAddr.ia.sin_port = htons ( msg.m_dataType ); serverAddr.ia.sin_addr = addr.ia.sin_addr; } else { serverAddr.ia.sin_port = htons ( this->serverPort ); serverAddr.ia.sin_addr = addr.ia.sin_addr; } if ( CA_V42 ( minorVersion ) ) { cacRef.transferChanToVirtCircuit ( msg.m_available, msg.m_cid, 0xffff, 0, minorVersion, serverAddr, currentTime ); } else { cacRef.transferChanToVirtCircuit ( msg.m_available, msg.m_cid, msg.m_dataType, msg.m_count, minorVersion, serverAddr, currentTime ); } return true; } bool udpiiu::beaconAction ( const caHdr & msg, const osiSockAddr & net_addr, const epicsTime & currentTime ) { struct sockaddr_in ina; memset(&ina, 0, sizeof(struct sockaddr_in)); if ( net_addr.sa.sa_family != AF_INET ) { return false; } /* * this allows a fan-out server to potentially * insert the true address of the CA server * * old servers: * 1) set this field to one of the ip addresses of the host _or_ * 2) set this field to INADDR_ANY * new servers: * always set this field to INADDR_ANY * * clients always assume that if this * field is set to something that isnt INADDR_ANY * then it is the overriding IP address of the server. */ ina.sin_family = AF_INET; ina.sin_addr.s_addr = htonl ( msg.m_available ); if ( msg.m_count != 0 ) { ina.sin_port = htons ( msg.m_count ); } else { /* * old servers dont supply this and the * default port must be assumed */ ina.sin_port = htons ( this->serverPort ); } unsigned protocolRevision = msg.m_dataType; ca_uint32_t beaconNumber = msg.m_cid; this->cacRef.beaconNotify ( ina, currentTime, beaconNumber, protocolRevision ); return true; } bool udpiiu::repeaterAckAction ( const caHdr &, const osiSockAddr &, const epicsTime &) { this->repeaterSubscribeTmr.confirmNotify (); return true; } bool udpiiu::notHereRespAction ( const caHdr &, const osiSockAddr &, const epicsTime & ) { return true; } bool udpiiu::exceptionRespAction ( const caHdr &msg, const osiSockAddr & net_addr, const epicsTime & currentTime ) { const caHdr &reqMsg = * ( &msg + 1 ); char name[64]; sockAddrToDottedIP ( &net_addr.sa, name, sizeof ( name ) ); char date[64]; currentTime.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S"); if ( msg.m_postsize > sizeof ( caHdr ) ){ errlogPrintf ( "error condition \"%s\" detected by %s with context \"%s\" at %s\n", ca_message ( msg.m_available ), name, reinterpret_cast ( &reqMsg + 1 ), date ); } else{ errlogPrintf ( "error condition \"%s\" detected by %s at %s\n", ca_message ( msg.m_available ), name, date ); } return true; } void udpiiu::postMsg ( const osiSockAddr & net_addr, char * pInBuf, arrayElementCount blockSize, const epicsTime & currentTime ) { caHdr *pCurMsg; this->lastReceivedSeqNoIsValid = false; this->lastReceivedSeqNo = 0u; while ( blockSize ) { arrayElementCount size; if ( blockSize < sizeof ( *pCurMsg ) ) { char buf[64]; sockAddrToDottedIP ( &net_addr.sa, buf, sizeof ( buf ) ); errlogPrintf ( "%s: Undecipherable (too small) UDP msg from %s ignored\n", __FILE__, buf ); return; } pCurMsg = reinterpret_cast < caHdr * > ( pInBuf ); /* * fix endian of bytes */ pCurMsg->m_postsize = AlignedWireRef < epicsUInt16 > ( pCurMsg->m_postsize ); pCurMsg->m_cmmd = AlignedWireRef < epicsUInt16 > ( pCurMsg->m_cmmd ); pCurMsg->m_dataType = AlignedWireRef < epicsUInt16 > ( pCurMsg->m_dataType ); pCurMsg->m_count = AlignedWireRef < epicsUInt16 > ( pCurMsg->m_count ); pCurMsg->m_available = AlignedWireRef < epicsUInt32 > ( pCurMsg->m_available ); pCurMsg->m_cid = AlignedWireRef < epicsUInt32 > ( pCurMsg->m_cid ); #if 0 printf ( "UDP Cmd=%3d Type=%3d Count=%4d Size=%4d", pCurMsg->m_cmmd, pCurMsg->m_dataType, pCurMsg->m_count, pCurMsg->m_postsize ); printf (" Avail=%8x Cid=%6d\n", pCurMsg->m_available, pCurMsg->m_cid ); #endif size = pCurMsg->m_postsize + sizeof ( *pCurMsg ); /* * dont allow msg body extending beyond frame boundary */ if ( size > blockSize ) { char buf[64]; sockAddrToDottedIP ( &net_addr.sa, buf, sizeof ( buf ) ); errlogPrintf ( "%s: Undecipherable (payload too small) UDP msg from %s ignored\n", __FILE__, buf ); return; } /* * execute the response message */ pProtoStubUDP pStub; if ( pCurMsg->m_cmmd < NELEMENTS ( udpJumpTableCAC ) ) { pStub = udpJumpTableCAC [pCurMsg->m_cmmd]; } else { pStub = &udpiiu::badUDPRespAction; } bool success = ( this->*pStub ) ( *pCurMsg, net_addr, currentTime ); if ( ! success ) { char buf[256]; sockAddrToDottedIP ( &net_addr.sa, buf, sizeof ( buf ) ); errlogPrintf ( "CAC: Undecipherable UDP message from %s\n", buf ); return; } blockSize -= size; pInBuf += size;; } } bool udpiiu::pushVersionMsg () { epicsGuard < epicsMutex > guard ( this->cacMutex ); this->sequenceNumber++; caHdr msg; AlignedWireRef < epicsUInt16 > ( msg.m_cmmd ) = CA_PROTO_VERSION; AlignedWireRef < epicsUInt32 > ( msg.m_available ) = 0; AlignedWireRef < epicsUInt16 > ( msg.m_dataType ) = sequenceNoIsValid; AlignedWireRef < epicsUInt16 > ( msg.m_count ) = CA_MINOR_PROTOCOL_REVISION; AlignedWireRef < epicsUInt32 > ( msg.m_cid ) = this->sequenceNumber; // sequence number return this->pushDatagramMsg ( guard, msg, 0, 0 ); } bool udpiiu::pushDatagramMsg ( epicsGuard < epicsMutex > & guard, const caHdr & msg, const void * pExt, ca_uint16_t extsize ) { guard.assertIdenticalMutex ( this->cacMutex ); ca_uint16_t alignedExtSize = static_cast (CA_MESSAGE_ALIGN ( extsize )); arrayElementCount msgsize = sizeof ( caHdr ) + alignedExtSize; /* fail out if max message size exceeded */ if ( msgsize >= sizeof ( this->xmitBuf ) - 7 ) { return false; } if ( msgsize + this->nBytesInXmitBuf > sizeof ( this->xmitBuf ) ) { return false; } caHdr * pbufmsg = ( caHdr * ) &this->xmitBuf[this->nBytesInXmitBuf]; *pbufmsg = msg; if ( extsize ) { memcpy ( pbufmsg + 1, pExt, extsize ); if ( extsize != alignedExtSize ) { char *pDest = (char *) ( pbufmsg + 1 ); memset ( pDest + extsize, '\0', alignedExtSize - extsize ); } } AlignedWireRef < epicsUInt16 > ( pbufmsg->m_postsize ) = alignedExtSize; this->nBytesInXmitBuf += msgsize; return true; } udpiiu :: SearchDestUDP :: SearchDestUDP ( const osiSockAddr & destAddr, udpiiu & udpiiuIn ) : _lastError (0u), _destAddr ( destAddr ), _udpiiu ( udpiiuIn ) { } void udpiiu :: SearchDestUDP :: searchRequest ( epicsGuard < epicsMutex > & guard, const char * pBuf, size_t bufSize ) { guard.assertIdenticalMutex ( _udpiiu.cacMutex ); assert ( bufSize <= INT_MAX ); int bufSizeAsInt = static_cast < int > ( bufSize ); while ( true ) { // This const_cast is needed for vxWorks: int status = sendto ( _udpiiu.sock, const_cast(pBuf), bufSizeAsInt, 0, & _destAddr.sa, sizeof ( _destAddr.sa ) ); if ( status == bufSizeAsInt ) { if ( _lastError ) { char buf[64]; sockAddrToDottedIP ( &_destAddr.sa, buf, sizeof ( buf ) ); errlogPrintf ( "CAC: ok sending UDP msg to %s\n", buf); } _lastError = 0; break; } if ( status >= 0 ) { errlogPrintf ( "CAC: UDP sendto () call returned strange xmit count?\n" ); break; } else { int localErrno = SOCKERRNO; if ( localErrno == SOCK_EINTR ) { if ( _udpiiu.shutdownCmd ) { break; } else { continue; } } else if ( localErrno == SOCK_SHUTDOWN ) { break; } else if ( localErrno == SOCK_ENOTSOCK ) { break; } else if ( localErrno == SOCK_EBADF ) { break; } else if ( localErrno == _lastError) { break; } else { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); char buf[64]; sockAddrToDottedIP ( &_destAddr.sa, buf, sizeof ( buf ) ); errlogPrintf ( "CAC: error = \"%s\" sending UDP msg to %s\n", sockErrBuf, buf); _lastError = localErrno; break; } } } } void udpiiu :: SearchDestUDP :: show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( _udpiiu.cacMutex ); char buf[64]; sockAddrToDottedIP ( &_destAddr.sa, buf, sizeof ( buf ) ); :: printf ( "UDP Search destination \"%s\"\n", buf ); } udpiiu :: SearchRespCallback :: SearchRespCallback ( udpiiu & udpiiuIn ) : _udpiiu ( udpiiuIn ) { } void udpiiu :: SearchRespCallback :: notify ( const caHdr & msg, const void * pPayloadUntyped, const osiSockAddr & addr, const epicsTime & currentTime ) { /* * we dont currently know what to do with channel's * found to be at non-IP type addresses */ if ( addr.sa.sa_family != AF_INET ) { return; } /* * Starting with CA V4.1 the minor version number * is appended to the end of each search reply. * This value is ignored by earlier clients. */ ca_uint32_t minorVersion; if ( msg.m_postsize >= sizeof ( minorVersion ) ){ /* * care is taken here not to break gcc 3.2 aggressive alias * analysis rules */ const ca_uint8_t * pPayLoad = reinterpret_cast < const ca_uint8_t *> ( pPayloadUntyped ); unsigned byte0 = pPayLoad[0]; unsigned byte1 = pPayLoad[1]; minorVersion = ( byte0 << 8u ) | byte1; } else { minorVersion = CA_UKN_MINOR_VERSION; } /* * the type field is abused to carry the port number * so that we can have multiple servers on one host */ osiSockAddr serverAddr; serverAddr.ia.sin_family = AF_INET; if ( CA_V48 ( minorVersion ) ) { if ( msg.m_cid != INADDR_BROADCAST ) { serverAddr.ia.sin_addr.s_addr = htonl ( msg.m_cid ); } else { serverAddr.ia.sin_addr = addr.ia.sin_addr; } serverAddr.ia.sin_port = htons ( msg.m_dataType ); } else if ( CA_V45 (minorVersion) ) { serverAddr.ia.sin_port = htons ( msg.m_dataType ); serverAddr.ia.sin_addr = addr.ia.sin_addr; } else { serverAddr.ia.sin_port = htons ( _udpiiu.serverPort ); serverAddr.ia.sin_addr = addr.ia.sin_addr; } if ( CA_V42 ( minorVersion ) ) { _udpiiu.cacRef.transferChanToVirtCircuit ( msg.m_available, msg.m_cid, 0xffff, 0, minorVersion, serverAddr, currentTime ); } else { _udpiiu.cacRef.transferChanToVirtCircuit ( msg.m_available, msg.m_cid, msg.m_dataType, msg.m_count, minorVersion, serverAddr, currentTime ); } } void udpiiu :: SearchRespCallback :: show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( _udpiiu.cacMutex ); ::printf ( "udpiiu :: SearchRespCallback\n" ); } bool udpiiu :: datagramFlush ( epicsGuard < epicsMutex > & guard, const epicsTime & currentTime ) { guard.assertIdenticalMutex ( cacMutex ); // dont send the version header by itself if ( this->nBytesInXmitBuf <= sizeof ( caHdr ) ) { return false; } tsDLIter < SearchDest > iter ( _searchDestList.firstIter () ); while ( iter.valid () ) { iter->searchRequest ( guard, this->xmitBuf, this->nBytesInXmitBuf ); iter++; } this->nBytesInXmitBuf = 0u; this->pushVersionMsg (); return true; } void udpiiu :: show ( unsigned level ) const { epicsGuard < epicsMutex > guard ( this->cacMutex ); ::printf ( "Datagram IO circuit (and disconnected channel repository)\n"); if ( level > 1u ) { ::printf ("\trepeater port %u\n", this->repeaterPort ); ::printf ("\tdefault server port %u\n", this->serverPort ); ::printf ( "Search Destination List with %u items\n", _searchDestList.count () ); if ( level > 2u ) { tsDLIterConst < SearchDest > iter ( _searchDestList.firstIter () ); while ( iter.valid () ) { iter->show ( guard, level - 2 ); iter++; } } } if ( level > 2u ) { ::printf ("\tsocket identifier %d\n", this->sock ); ::printf ("\tbytes in xmit buffer %u\n", this->nBytesInXmitBuf ); ::printf ("\tshut down command bool %u\n", this->shutdownCmd ); ::printf ( "\trecv thread exit signal:\n" ); this->recvThread.show ( level - 2u ); this->repeaterSubscribeTmr.show ( level - 2u ); this->govTmr.show ( level - 2u ); } if ( level > 3u ) { for ( unsigned i =0; i < this->nTimers; i++ ) { this->ppSearchTmr[i]->show ( level - 3u ); } } } bool udpiiu::wakeupMsg () { caHdr msg; AlignedWireRef < epicsUInt16 > ( msg.m_cmmd ) = CA_PROTO_VERSION; AlignedWireRef < epicsUInt32 > ( msg.m_available ) = 0u; AlignedWireRef < epicsUInt16 > ( msg.m_dataType ) = 0u; AlignedWireRef < epicsUInt16 > ( msg.m_count ) = 0u; AlignedWireRef < epicsUInt32 > ( msg.m_cid ) = 0u; AlignedWireRef < epicsUInt16 > ( msg.m_postsize ) = 0u; osiSockAddr addr; addr.ia.sin_family = AF_INET; addr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK ); addr.ia.sin_port = htons ( this->localPort ); // send a wakeup msg so the UDP recv thread will exit int status = sendto ( this->sock, reinterpret_cast < char * > ( &msg ), sizeof (msg), 0, &addr.sa, sizeof ( addr.sa ) ); if ( status == sizeof (msg) ) { return true; } return false; } void udpiiu::beaconAnomalyNotify ( epicsGuard < epicsMutex > & cacGuard ) { for ( unsigned i = this->beaconAnomalyTimerIndex+1u; i < this->nTimers; i++ ) { this->ppSearchTmr[i]->moveChannels ( cacGuard, *this->ppSearchTmr[this->beaconAnomalyTimerIndex] ); } } void udpiiu::uninstallChanDueToSuccessfulSearchResponse ( epicsGuard < epicsMutex > & guard, nciu & chan, const epicsTime & currentTime ) { channelNode::channelState chanState = chan.channelNode::listMember; if ( chanState == channelNode::cs_disconnGov ) { this->govTmr.uninstallChan ( guard, chan ); } else { this->ppSearchTmr[ chan.getSearchTimerIndex ( guard ) ]-> uninstallChanDueToSuccessfulSearchResponse ( guard, chan, this->lastReceivedSeqNo, this->lastReceivedSeqNoIsValid, currentTime ); } } void udpiiu::uninstallChan ( epicsGuard < epicsMutex > & guard, nciu & chan ) { channelNode::channelState chanState = chan.channelNode::listMember; if ( chanState == channelNode::cs_disconnGov ) { this->govTmr.uninstallChan ( guard, chan ); } else { this->ppSearchTmr[ chan.getSearchTimerIndex ( guard ) ]-> uninstallChan ( guard, chan ); } } bool udpiiu::searchMsg ( epicsGuard < epicsMutex > & guard, ca_uint32_t id, const char * pName, unsigned nameLength ) { caHdr msg; AlignedWireRef < epicsUInt16 > ( msg.m_cmmd ) = CA_PROTO_SEARCH; AlignedWireRef < epicsUInt32 > ( msg.m_available ) = id; AlignedWireRef < epicsUInt16 > ( msg.m_dataType ) = DONTREPLY; AlignedWireRef < epicsUInt16 > ( msg.m_count ) = CA_MINOR_PROTOCOL_REVISION; AlignedWireRef < epicsUInt32 > ( msg.m_cid ) = id; return this->pushDatagramMsg ( guard, msg, pName, (ca_uint16_t) nameLength ); } void udpiiu::installNewChannel ( epicsGuard < epicsMutex > & guard, nciu & chan, netiiu * & piiu ) { piiu = this; this->ppSearchTmr[0]->installChannel ( guard, chan ); } void udpiiu::installDisconnectedChannel ( epicsGuard < epicsMutex > & guard, nciu & chan ) { chan.setServerAddressUnknown ( *this, guard ); this->govTmr.installChan ( guard, chan ); } void udpiiu::noSearchRespNotify ( epicsGuard < epicsMutex > & guard, nciu & chan, unsigned index ) { const unsigned nTimersMinusOne = this->nTimers - 1; if ( index < nTimersMinusOne ) { index++; } else { index = nTimersMinusOne; } this->ppSearchTmr[index]->installChannel ( guard, chan ); } void udpiiu::boostChannel ( epicsGuard < epicsMutex > & guard, nciu & chan ) { this->ppSearchTmr[this->beaconAnomalyTimerIndex]-> installChannel ( guard, chan ); } void udpiiu::govExpireNotify ( epicsGuard < epicsMutex > & guard, nciu & chan ) { this->ppSearchTmr[0]->installChannel ( guard, chan ); } int udpiiu :: M_repeaterTimerNotify :: printFormated ( epicsGuard < epicsMutex > & cbGuard, const char * pformat, ... ) { va_list theArgs; int status; va_start ( theArgs, pformat ); status = m_udpiiu.cacRef.varArgsPrintFormated ( cbGuard, pformat, theArgs ); va_end ( theArgs ); return status; } void udpiiu::updateRTTE ( epicsGuard < epicsMutex > & guard, double measured ) { guard.assertIdenticalMutex ( this->cacMutex ); if ( measured > maxRoundTripEstimate ) { measured = maxRoundTripEstimate; } if ( measured < minRoundTripEstimate ) { measured = minRoundTripEstimate; } double error = measured - this->rtteMean; this->rtteMean += 0.125 * error; if ( error < 0.0 ) { error = - error; } this->rtteMeanDev = this->rtteMeanDev + .25 * ( error - this->rtteMeanDev ); } double udpiiu::getRTTE ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->cacMutex ); return this->rtteMean + 4 * this->rtteMeanDev; } unsigned udpiiu::getHostName ( epicsGuard < epicsMutex > & cacGuard, char *pBuf, unsigned bufLength ) const throw () { return netiiu::getHostName ( cacGuard, pBuf, bufLength ); } const char * udpiiu::pHostName ( epicsGuard < epicsMutex > & cacGuard ) const throw () { return netiiu::pHostName ( cacGuard ); } bool udpiiu::ca_v42_ok ( epicsGuard < epicsMutex > & cacGuard ) const { return netiiu::ca_v42_ok ( cacGuard ); } bool udpiiu::ca_v41_ok ( epicsGuard < epicsMutex > & cacGuard ) const { return netiiu::ca_v41_ok ( cacGuard ); } void udpiiu::writeRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, unsigned type, arrayElementCount nElem, const void * pValue ) { netiiu::writeRequest ( guard, chan, type, nElem, pValue ); } void udpiiu::writeNotifyRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netWriteNotifyIO & io, unsigned type, arrayElementCount nElem, const void *pValue ) { netiiu::writeNotifyRequest ( guard, chan, io, type, nElem, pValue ); } void udpiiu::readNotifyRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netReadNotifyIO & io, unsigned type, arrayElementCount nElem ) { netiiu::readNotifyRequest ( guard, chan, io, type, nElem ); } void udpiiu::clearChannelRequest ( epicsGuard < epicsMutex > & guard, ca_uint32_t sid, ca_uint32_t cid ) { netiiu::clearChannelRequest ( guard, sid, cid ); } void udpiiu::subscriptionRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netSubscription & subscr ) { netiiu::subscriptionRequest ( guard, chan, subscr ); } void udpiiu::subscriptionUpdateRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netSubscription & subscr ) { netiiu::subscriptionUpdateRequest ( guard, chan, subscr ); } void udpiiu::subscriptionCancelRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netSubscription & subscr ) { netiiu::subscriptionCancelRequest ( guard, chan, subscr ); } void udpiiu::flushRequest ( epicsGuard < epicsMutex > & guard ) { netiiu::flushRequest ( guard ); } unsigned udpiiu::requestMessageBytesPending ( epicsGuard < epicsMutex > & guard ) { return netiiu::requestMessageBytesPending ( guard ); } void udpiiu::flush ( epicsGuard < epicsMutex > & guard ) { netiiu::flush ( guard ); } void udpiiu::requestRecvProcessPostponedFlush ( epicsGuard < epicsMutex > & guard ) { netiiu::requestRecvProcessPostponedFlush ( guard ); } osiSockAddr udpiiu::getNetworkAddress ( epicsGuard < epicsMutex > & guard ) const { return netiiu::getNetworkAddress ( guard ); } double udpiiu::receiveWatchdogDelay ( epicsGuard < epicsMutex > & guard ) const { return netiiu::receiveWatchdogDelay ( guard ); } ca_uint32_t udpiiu::datagramSeqNumber ( epicsGuard < epicsMutex > & ) const { return this->sequenceNumber; } base-7.0.3.1/modules/ca/src/client/udpiiu.h0000664000577000060420000002433313557101274017170 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef udpiiuh #define udpiiuh #include #ifdef epicsExportSharedSymbols # define udpiiuh_accessh_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "osiSock.h" #include "epicsThread.h" #include "epicsTime.h" #include "tsDLList.h" #ifdef udpiiuh_accessh_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif #include "netiiu.h" #include "searchTimer.h" #include "disconnectGovernorTimer.h" #include "repeaterSubscribeTimer.h" #include "SearchDest.h" extern "C" void cacRecvThreadUDP ( void *pParam ); epicsShareFunc void epicsShareAPI caStartRepeaterIfNotInstalled ( unsigned repeaterPort ); epicsShareFunc void epicsShareAPI caRepeaterRegistrationMessage ( SOCKET sock, unsigned repeaterPort, unsigned attemptNumber ); extern "C" epicsShareFunc void caRepeaterThread ( void * pDummy ); epicsShareFunc void ca_repeater ( void ); class cac; class cacContextNotify; class udpRecvThread : private epicsThreadRunable { public: udpRecvThread ( class udpiiu & iiuIn, cacContextNotify &, epicsMutex &, const char * pName, unsigned stackSize, unsigned priority ); virtual ~udpRecvThread (); void start (); bool exitWait ( double delay ); void show ( unsigned level ) const; private: class udpiiu & iiu; epicsMutex & cbMutex; cacContextNotify & ctxNotify; epicsThread thread; void run(); }; static const double minRoundTripEstimate = 32e-3; // seconds static const double maxRoundTripEstimate = 30; // seconds static const double maxSearchPeriodDefault = 5.0 * 60.0; // seconds static const double maxSearchPeriodLowerLimit = 60.0; // seconds static const double beaconAnomalySearchPeriod = 5.0; // seconds class udpiiu : private netiiu, private searchTimerNotify, private disconnectGovernorNotify { public: udpiiu ( epicsGuard < epicsMutex > & cacGuard, class epicsTimerQueueActive &, epicsMutex & callbackControl, epicsMutex & mutualExclusion, cacContextNotify &, class cac &, unsigned port, tsDLList < SearchDest > & ); virtual ~udpiiu (); void installNewChannel ( epicsGuard < epicsMutex > &, nciu &, netiiu * & ); void installDisconnectedChannel ( epicsGuard < epicsMutex > &, nciu & ); void beaconAnomalyNotify ( epicsGuard < epicsMutex > & guard ); void shutdown ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); void show ( unsigned level ) const; // exceptions class noSocket {}; private: class SearchDestUDP : public SearchDest { public: SearchDestUDP ( const osiSockAddr &, udpiiu & ); void searchRequest ( epicsGuard < epicsMutex > &, const char * pBuf, size_t bufLen ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; private: int _lastError; osiSockAddr _destAddr; udpiiu & _udpiiu; }; class SearchRespCallback : public SearchDest :: Callback { public: SearchRespCallback ( udpiiu & ); void notify ( const caHdr &, const void * pPayload, const osiSockAddr &, const epicsTime & ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; private: udpiiu & _udpiiu; }; class M_repeaterTimerNotify : public repeaterTimerNotify { public: M_repeaterTimerNotify ( udpiiu & iiu ) : m_udpiiu ( iiu ) {} ~M_repeaterTimerNotify (); /* for sunpro compiler */ // repeaterTimerNotify void repeaterRegistrationMessage ( unsigned attemptNumber ); int printFormated ( epicsGuard < epicsMutex > & callbackControl, const char * pformat, ... ); private: udpiiu & m_udpiiu; }; char xmitBuf [MAX_UDP_SEND]; char recvBuf [MAX_UDP_RECV]; udpRecvThread recvThread; M_repeaterTimerNotify m_repeaterTimerNotify; repeaterSubscribeTimer repeaterSubscribeTmr; disconnectGovernorTimer govTmr; tsDLList < SearchDest > _searchDestList; const double maxPeriod; double rtteMean; double rtteMeanDev; cac & cacRef; epicsMutex & cbMutex; epicsMutex & cacMutex; const unsigned nTimers; struct SearchArray { typedef std::auto_ptr value_type; value_type *arr; SearchArray(size_t n) : arr(new value_type[n]) {} ~SearchArray() { delete[] arr; } value_type& operator[](size_t i) const { return arr[i]; } private: SearchArray(const SearchArray&); SearchArray& operator=(const SearchArray&); } ppSearchTmr; unsigned nBytesInXmitBuf; unsigned beaconAnomalyTimerIndex; ca_uint32_t sequenceNumber; ca_uint32_t lastReceivedSeqNo; SOCKET sock; ca_uint16_t repeaterPort; ca_uint16_t serverPort; ca_uint16_t localPort; bool shutdownCmd; bool lastReceivedSeqNoIsValid; bool wakeupMsg (); void postMsg ( const osiSockAddr & net_addr, char *pInBuf, arrayElementCount blockSize, const epicsTime ¤Time ); bool pushDatagramMsg ( epicsGuard < epicsMutex > &, const caHdr & hdr, const void * pExt, ca_uint16_t extsize); typedef bool ( udpiiu::*pProtoStubUDP ) ( const caHdr &, const osiSockAddr &, const epicsTime & ); // UDP protocol dispatch table static const pProtoStubUDP udpJumpTableCAC[]; // UDP protocol stubs bool versionAction ( const caHdr &, const osiSockAddr &, const epicsTime & ); bool badUDPRespAction ( const caHdr &msg, const osiSockAddr &netAddr, const epicsTime & ); bool searchRespAction ( const caHdr &msg, const osiSockAddr &net_addr, const epicsTime & ); bool exceptionRespAction ( const caHdr &msg, const osiSockAddr &net_addr, const epicsTime & ); bool beaconAction ( const caHdr &msg, const osiSockAddr &net_addr, const epicsTime & ); bool notHereRespAction ( const caHdr &msg, const osiSockAddr &net_addr, const epicsTime & ); bool repeaterAckAction ( const caHdr &msg, const osiSockAddr &net_addr, const epicsTime & ); // netiiu stubs unsigned getHostName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLength ) const throw (); const char * pHostName ( epicsGuard < epicsMutex > & ) const throw (); bool ca_v41_ok ( epicsGuard < epicsMutex > & ) const; bool ca_v42_ok ( epicsGuard < epicsMutex > & ) const; unsigned requestMessageBytesPending ( epicsGuard < epicsMutex > & mutualExclusionGuard ); void flush ( epicsGuard < epicsMutex > & mutualExclusionGuard ); void writeRequest ( epicsGuard < epicsMutex > &, nciu &, unsigned type, arrayElementCount nElem, const void *pValue ); void writeNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, netWriteNotifyIO &, unsigned type, arrayElementCount nElem, const void *pValue ); void readNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, netReadNotifyIO &, unsigned type, arrayElementCount nElem ); void clearChannelRequest ( epicsGuard < epicsMutex > &, ca_uint32_t sid, ca_uint32_t cid ); void subscriptionRequest ( epicsGuard < epicsMutex > &, nciu &, netSubscription & ); void subscriptionUpdateRequest ( epicsGuard < epicsMutex > &, nciu &, netSubscription & ); void subscriptionCancelRequest ( epicsGuard < epicsMutex > &, nciu & chan, netSubscription & subscr ); void flushRequest ( epicsGuard < epicsMutex > & ); void requestRecvProcessPostponedFlush ( epicsGuard < epicsMutex > & ); osiSockAddr getNetworkAddress ( epicsGuard < epicsMutex > & ) const; void uninstallChan ( epicsGuard < epicsMutex > &, nciu & ); void uninstallChanDueToSuccessfulSearchResponse ( epicsGuard < epicsMutex > &, nciu &, const class epicsTime & currentTime ); double receiveWatchdogDelay ( epicsGuard < epicsMutex > & ) const; bool searchMsg ( epicsGuard < epicsMutex > &, ca_uint32_t id, const char * pName, unsigned nameLength ); // searchTimerNotify stubs double getRTTE ( epicsGuard < epicsMutex > & ) const; void updateRTTE ( epicsGuard < epicsMutex > &, double rtte ); bool pushVersionMsg (); void boostChannel ( epicsGuard < epicsMutex > & guard, nciu & chan ); void noSearchRespNotify ( epicsGuard < epicsMutex > &, nciu & chan, unsigned index ); bool datagramFlush ( epicsGuard < epicsMutex > &, const epicsTime & currentTime ); ca_uint32_t datagramSeqNumber ( epicsGuard < epicsMutex > & ) const; // disconnectGovernorNotify void govExpireNotify ( epicsGuard < epicsMutex > &, nciu & ); udpiiu ( const udpiiu & ); udpiiu & operator = ( const udpiiu & ); friend class udpRecvThread; // These are needed for the vxWorks 5.5 compiler: friend class udpiiu::SearchDestUDP; friend class udpiiu::SearchRespCallback; friend class udpiiu::M_repeaterTimerNotify; }; #endif // udpiiuh base-7.0.3.1/modules/ca/src/client/virtualCircuit.h0000664000577000060420000003264213557101274020704 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef virtualCircuith #define virtualCircuith #include "tsDLList.h" #include "comBuf.h" #include "caServerID.h" #include "netiiu.h" #include "comQueSend.h" #include "comQueRecv.h" #include "tcpRecvWatchdog.h" #include "tcpSendWatchdog.h" #include "hostNameCache.h" #include "SearchDest.h" #include "compilerDependencies.h" class callbackManager; // a modified ca header with capacity for large arrays struct caHdrLargeArray { ca_uint32_t m_postsize; // size of message extension ca_uint32_t m_count; // operation data count ca_uint32_t m_cid; // channel identifier ca_uint32_t m_available; // protocol stub dependent ca_uint16_t m_dataType; // operation data type ca_uint16_t m_cmmd; // operation to be performed }; class ipAddrToAsciiEngine; class tcpRecvThread : private epicsThreadRunable { public: tcpRecvThread ( class tcpiiu & iiuIn, epicsMutex & cbMutexIn, cacContextNotify &, const char * pName, unsigned int stackSize, unsigned int priority ); virtual ~tcpRecvThread (); void start (); void exitWait (); bool exitWait ( double delay ); void interruptSocketRecv (); void show ( unsigned level ) const; private: epicsThread thread; class tcpiiu & iiu; epicsMutex & cbMutex; cacContextNotify & ctxNotify; void run (); void connect ( epicsGuard < epicsMutex > & guard ); bool validFillStatus ( epicsGuard < epicsMutex > & guard, const statusWireIO & stat ); }; class tcpSendThread : private epicsThreadRunable { public: tcpSendThread ( class tcpiiu & iiuIn, const char * pName, unsigned int stackSize, unsigned int priority ); virtual ~tcpSendThread (); void start (); void exitWait (); void interruptSocketSend (); void show ( unsigned level ) const; private: epicsThread thread; class tcpiiu & iiu; void run (); }; class SearchDestTCP : public SearchDest { public: SearchDestTCP ( cac &, const osiSockAddr & ); void searchRequest ( epicsGuard < epicsMutex > & guard, const char * pbuf, size_t len ); void show ( epicsGuard < epicsMutex > & guard, unsigned level ) const; void setCircuit ( tcpiiu * ); void disable (); void enable (); private: tcpiiu * _ptcpiiu; cac & _cac; const osiSockAddr _addr; bool _active; }; class tcpiiu : public netiiu, public tsDLNode < tcpiiu >, public tsSLNode < tcpiiu >, public caServerID, private wireSendAdapter, private wireRecvAdapter { friend void SearchDestTCP::searchRequest ( epicsGuard < epicsMutex > & guard, const char * pbuf, size_t len ); public: tcpiiu ( cac & cac, epicsMutex & mutualExclusion, epicsMutex & callbackControl, cacContextNotify &, double connectionTimeout, epicsTimerQueue & timerQueue, const osiSockAddr & addrIn, comBufMemoryManager &, unsigned minorVersion, ipAddrToAsciiEngine & engineIn, const cacChannel::priLev & priorityIn, SearchDestTCP * pSearchDestIn = NULL); ~tcpiiu (); void start ( epicsGuard < epicsMutex > & ); void responsiveCircuitNotify ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); void sendTimeoutNotify ( callbackManager & cbMgr, epicsGuard < epicsMutex > & guard ); void receiveTimeoutNotify( callbackManager &, epicsGuard < epicsMutex > & ); void beaconAnomalyNotify ( epicsGuard < epicsMutex > & ); void beaconArrivalNotify ( epicsGuard < epicsMutex > & ); void probeResponseNotify ( epicsGuard < epicsMutex > & ); void flushRequest ( epicsGuard < epicsMutex > & ); unsigned requestMessageBytesPending ( epicsGuard < epicsMutex > & mutualExclusionGuard ); void flush ( epicsGuard < epicsMutex > & mutualExclusionGuard ); void show ( unsigned level ) const; bool setEchoRequestPending ( epicsGuard < epicsMutex > & ); void requestRecvProcessPostponedFlush ( epicsGuard < epicsMutex > & ); void clearChannelRequest ( epicsGuard < epicsMutex > &, ca_uint32_t sid, ca_uint32_t cid ); bool ca_v41_ok ( epicsGuard < epicsMutex > & ) const; bool ca_v42_ok ( epicsGuard < epicsMutex > & ) const; bool ca_v44_ok ( epicsGuard < epicsMutex > & ) const; bool ca_v49_ok ( epicsGuard < epicsMutex > & ) const; unsigned getHostName ( epicsGuard < epicsMutex > &, char *pBuf, unsigned bufLength ) const throw (); bool alive ( epicsGuard < epicsMutex > & ) const; bool connecting ( epicsGuard < epicsMutex > & ) const; bool receiveThreadIsBusy ( epicsGuard < epicsMutex > & ); osiSockAddr getNetworkAddress ( epicsGuard < epicsMutex > & ) const; int printFormated ( epicsGuard < epicsMutex > & cbGuard, const char *pformat, ... ); unsigned channelCount ( epicsGuard < epicsMutex > & ); void disconnectAllChannels ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard, class udpiiu & ); void unlinkAllChannels ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); void installChannel ( epicsGuard < epicsMutex > &, nciu & chan, unsigned sidIn, ca_uint16_t typeIn, arrayElementCount countIn ); void uninstallChan ( epicsGuard < epicsMutex > & guard, nciu & chan ); bool connectNotify ( epicsGuard < epicsMutex > &, nciu & chan ); void searchRespNotify ( const epicsTime &, const caHdrLargeArray & ); void versionRespNotify ( const caHdrLargeArray & ); void * operator new ( size_t size, tsFreeList < class tcpiiu, 32, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < class tcpiiu, 32, epicsMutexNOOP > & )) private: hostNameCache hostNameCacheInstance; tcpRecvThread recvThread; tcpSendThread sendThread; tcpRecvWatchdog recvDog; tcpSendWatchdog sendDog; comQueSend sendQue; comQueRecv recvQue; // nciu state field tells us which list // protected by the callback mutex tsDLList < nciu > createReqPend; tsDLList < nciu > createRespPend; tsDLList < nciu > v42ConnCallbackPend; tsDLList < nciu > subscripReqPend; tsDLList < nciu > connectedList; tsDLList < nciu > unrespCircuit; tsDLList < nciu > subscripUpdateReqPend; caHdrLargeArray curMsg; arrayElementCount curDataMax; arrayElementCount curDataBytes; comBufMemoryManager & comBufMemMgr; cac & cacRef; char * pCurData; SearchDestTCP * pSearchDest; epicsMutex & mutex; epicsMutex & cbMutex; unsigned minorProtocolVersion; enum iiu_conn_state { iiucs_connecting, // pending circuit connect iiucs_connected, // live circuit iiucs_clean_shutdown, // live circuit will shutdown when flush completes iiucs_disconnected, // socket informed us of disconnect iiucs_abort_shutdown // socket has been closed } state; epicsEvent sendThreadFlushEvent; epicsEvent flushBlockEvent; SOCKET sock; unsigned contigRecvMsgCount; unsigned blockingForFlush; unsigned socketLibrarySendBufferSize; unsigned unacknowledgedSendBytes; unsigned channelCountTot; bool _receiveThreadIsBusy; bool busyStateDetected; // only modified by the recv thread bool flowControlActive; // only modified by the send process thread bool echoRequestPending; bool oldMsgHeaderAvailable; bool msgHeaderAvailable; bool earlyFlush; bool recvProcessPostponedFlush; bool discardingPendingData; bool socketHasBeenClosed; bool unresponsiveCircuit; bool processIncoming ( const epicsTime & currentTime, callbackManager & ); unsigned sendBytes ( const void *pBuf, unsigned nBytesInBuf, const epicsTime & currentTime ); void recvBytes ( void * pBuf, unsigned nBytesInBuf, statusWireIO & ); const char * pHostName ( epicsGuard < epicsMutex > & ) const throw (); double receiveWatchdogDelay ( epicsGuard < epicsMutex > & ) const; void unresponsiveCircuitNotify ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard ); void initiateCleanShutdown ( epicsGuard < epicsMutex > & ); void initiateAbortShutdown ( epicsGuard < epicsMutex > & ); void disconnectNotify ( epicsGuard < epicsMutex > & ); bool bytesArePendingInOS () const; void decrementBlockingForFlushCount ( epicsGuard < epicsMutex > & guard ); bool isNameService () const; // send protocol stubs void echoRequest ( epicsGuard < epicsMutex > & ); void versionMessage ( epicsGuard < epicsMutex > &, const cacChannel::priLev & priority ); void disableFlowControlRequest ( epicsGuard < epicsMutex > & ); void enableFlowControlRequest ( epicsGuard < epicsMutex > & ); void hostNameSetRequest ( epicsGuard < epicsMutex > & ); void userNameSetRequest ( epicsGuard < epicsMutex > & ); void createChannelRequest ( nciu &, epicsGuard < epicsMutex > & ); void writeRequest ( epicsGuard < epicsMutex > &, nciu &, unsigned type, arrayElementCount nElem, const void *pValue ); void writeNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, netWriteNotifyIO &, unsigned type, arrayElementCount nElem, const void *pValue ); void readNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, netReadNotifyIO &, unsigned type, arrayElementCount nElem ); void subscriptionRequest ( epicsGuard < epicsMutex > &, nciu &, netSubscription & subscr ); void subscriptionUpdateRequest ( epicsGuard < epicsMutex > &, nciu & chan, netSubscription & subscr ); void subscriptionCancelRequest ( epicsGuard < epicsMutex > &, nciu & chan, netSubscription & subscr ); void flushIfRecvProcessRequested ( epicsGuard < epicsMutex > & ); bool sendThreadFlush ( epicsGuard < epicsMutex > & ); // netiiu stubs void uninstallChanDueToSuccessfulSearchResponse ( epicsGuard < epicsMutex > &, nciu &, const class epicsTime & ); bool searchMsg ( epicsGuard < epicsMutex > &, ca_uint32_t id, const char * pName, unsigned nameLength ); friend class tcpRecvThread; friend class tcpSendThread; tcpiiu ( const tcpiiu & ); tcpiiu & operator = ( const tcpiiu & ); void operator delete ( void * ); }; inline void * tcpiiu::operator new ( size_t size, tsFreeList < class tcpiiu, 32, epicsMutexNOOP > & mgr ) { return mgr.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void tcpiiu::operator delete ( void * pCadaver, tsFreeList < class tcpiiu, 32, epicsMutexNOOP > & mgr ) { mgr.release ( pCadaver ); } #endif inline bool tcpiiu::ca_v41_ok ( epicsGuard < epicsMutex > & ) const { return CA_V41 ( this->minorProtocolVersion ); } inline bool tcpiiu::ca_v44_ok ( epicsGuard < epicsMutex > & ) const { return CA_V44 ( this->minorProtocolVersion ); } inline bool tcpiiu::ca_v49_ok ( epicsGuard < epicsMutex > & ) const { return CA_V49 ( this->minorProtocolVersion ); } inline bool tcpiiu::alive ( epicsGuard < epicsMutex > & ) const { return ( this->state == iiucs_connecting || this->state == iiucs_connected ); } inline bool tcpiiu::connecting ( epicsGuard < epicsMutex > & ) const { return ( this->state == iiucs_connecting ); } inline bool tcpiiu::receiveThreadIsBusy ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); return this->_receiveThreadIsBusy; } inline void tcpiiu::beaconAnomalyNotify ( epicsGuard < epicsMutex > & guard ) { //guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); this->recvDog.beaconAnomalyNotify ( guard ); } inline void tcpiiu::beaconArrivalNotify ( epicsGuard < epicsMutex > & guard ) { //guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); this->recvDog.beaconArrivalNotify ( guard ); } inline void tcpiiu::probeResponseNotify ( epicsGuard < epicsMutex > & cbGuard ) { this->recvDog.probeResponseNotify ( cbGuard ); } inline bool tcpiiu::isNameService () const { return ( this->pSearchDest != NULL ); } inline void SearchDestTCP::setCircuit ( tcpiiu * piiu ) { _ptcpiiu = piiu; } #endif // ifdef virtualCircuith base-7.0.3.1/modules/ca/src/perl/CA.pm0000664000577000060420000005200013557101274016015 0ustar anjaesctl# Bootstrap wrapper for the Perl 5 Channel Access client module. # This wrapper also contains the POD documentation for the module. use strict; use warnings; my $version = '0.6'; package CA; our $VERSION = $version; package Cap5; # This package is required because the loadable library containing the # Perl interface code shouldn't be called CA but DynaLoader needs the # package name to match the library name. The loadable library actually # declares the packages for both Cap5 and CA which is why this works, # although the only symbols in the Cap5 package are associated with the # requirements of the DynaLoader module. our $VERSION = $version; our @ISA = qw(DynaLoader); # Library is specific to the Perl version and archname use Config; my $perl_version = $Config::Config{version}; my $perl_archname = $Config::Config{archname}; require DynaLoader; # Add our lib/ directory to the shared library search path use File::Basename; my $Lib = dirname(__FILE__); push @DynaLoader::dl_library_path, "$Lib/$perl_version/$perl_archname"; bootstrap Cap5 $VERSION; package CA; # This END block runs the ca_context_destroy function, but we don't # document it since the user never needs to call it explicitly. END { CA->context_destroy; } package CA::Subscription; # A subscription reference is a distinct object type. This package # provides a convenience method allowing a subscription to clear itself. our $VERSION = $version; sub clear { CA->clear_subscription(shift); } 1; __END__ =head1 NAME CA - Perl 5 interface to EPICS Channel Access =head1 SYNOPSIS use lib '/path/to/cap5/lib/perl'; use CA; my $chan = CA->new('pvname'); CA->pend_io(1); my @access = ('no ', ''); printf " PV name: %s\n", $chan->name; printf " Data type: %s\n", $chan->field_type; printf " Element count: %d\n", $chan->element_count; printf " Host: %s\n", $chan->host_name; printf " State: %s\n", $chan->state; printf " Access: %sread, %swrite\n", $access[$chan->read_access], $access[$chan->write_access]; die "PV not found!" unless $chan->is_connected; $chan->get; CA->pend_io(1); printf " Value: %s\n", $chan->value; $chan->create_subscription('v', \&callback, 'DBR_TIME_DOUBLE'); CA->pend_event(10); sub callback { my ($chan, $status, $data) = @_; if ($status) { printf "%-30s %s\n", $chan->name, $status; } else { printf " Value: %g\n", $data->{value}; printf " Severity: %s\n", $data->{severity}; printf " Timestamp: %.6f\n", $data->{stamp} + $data->{stamp_fraction}; } } =head1 DESCRIPTION C is an efficient interface to the EPICS Channel Access client library for use by Perl 5 programs. It provides most of the functionality of the C library (omitting Synchronous Groups) but only handles the three standard Perl data types integer (long), floating point (double) and string (now including long strings). Programmers who understand the C API will very quickly pick up how to use this library since the calls and concepts are virtually identical. =head1 FUNCTIONS =head2 Constructor =over 4 =item new( I ) =item new( I, I ) Create a channel for the named PV. If given, I will be called whenever the connection state of the channel changes. The arguments passed to I are the channel object and a scalar value that is true if the channel is now up. The underlying CA channel will be cleaned up properly when the channel object is garbage-collected by Perl. =back =head2 Object Methods The following methods are provided for channel objects returned by C<< CA->new() >>. =over 4 =item name The PV name provided when this channel was created. =item field_type Returns the native DBF type of the process variable as a string, or the string C if unconnected. =item element_count The maximum array element count from the server. Zero if the channel is not connected. =item host_name A string containing the server's hostname and port number. If the channel is disconnected it will report C<< >>. =item read_access =item write_access A true/false value that indicates whether the client has read or write access to the specified channel. =item state A string giving the current connection state of the channel, one of C, C, C or C. =item is_connected Returns C if the channel is currently connected, else C. Use this in preference to the equivalent code Sstate eq 'connected' >>>. =item get =item value The C method makes a C request for a single element of the Perl type closest to the channel's native data type; a C field will be fetched as a DBF_STRING, and a C array with multiple elements will converted into a Perl string. Once the server has returned the value (for which see the C function below) it can be retrieved using the channel's C method. Note that the C method deliberately only provides limited capabilities; the C method must be used for more complex requirements. =item get_callback( I ) =item get_callback( I, I ) =item get_callback( I, I ) =item get_callback( I, I, I ) The C method takes a subroutine reference or name and calls that routine when the server returns the data requested. With no other arguments the data type requested will be the widened form of the channel's native type (widening is discussed below), and if the channel is an array the request will fetch all available elements. The element count can be overridden by providing an integer argument in the range 0 .. C, where zero means use the current length from the server. Note that the count argument must be an integer; add 0 to it if it is necessary to convert it from a string. The optional data type I should be a string naming the desired C type; the actual type used will have the C part widened to one of C, C, C or C. The valid type names are listed in the L under the section titled Channel Access Data Types; look in the CA Type Code column of the two tables. The callback subroutine will be given three arguments: the channel object, a status value from the server, and the returned data. If there were no errors the status value will be C and the data will be valid; if an error occurred the data will be C and the status a printable string giving more information. The format of the data is described under L below. Callback subroutines should only call Perl's C, C or similar functions if they are expecting the program to exit at that time; attempts to C with an exception object in the callback and catch that using C in the main thread are not likely to succeed and will probably result in a crash. Callbacks should not perform any operations that would block for more than a fraction of a second as this will hold up network communications with the relevant server and could cause the Perl program and/or the Channel Access server to crash. Calling C<< CA->pend_event >> from within a callback is not permitted by the underlying Channel Access library. =item create_subscription( I, I ) =item create_subscription( I, I, I ) =item create_subscription( I, I, I ) =item create_subscription( I, I, I, I ) Register a state change subscription and specify a subroutine to be called whenever the process variable undergoes a significant state change. I must be a string containing one or more of the letters C, C, C and C

which indicate that this subscription is for Value, Log (Archive), Alarm and Property changes. The subroutine I is called as described for the C method above, and the same optional I and I arguments may be supplied to modify the data type and element count requested from the server. The C method returns a C object which is required to cancel that particular subscription. Either call the C method on that object directly, or pass it to the C<< CA->clear_subscription >> class method. =item put( I ) =item put( I, I, ... ) The C method makes a C or C call depending on the number of elements given in its argument list. The data type used will be the native type of the channel, widened to one of C, array of C, C or C. =item put_callback( I, I ) =item put_callback( I, I, I, ... ) C is similar to the C method with the addition of the subroutine reference or name I which is called when the server reports that all actions resulting from the put have completed. For some applications this callback can be delayed by minutes, hours or possibly even longer. The data type is chosen the same way as for C. The arguments to the subroutine will be the channel object and the status value from the server, which is either C or a printable string if an error occurred. The same restrictions apply to the callback subroutine as described in C above. =item put_acks( I ) =item put_acks( I, I ) Applications that need to acknowledge alarms by doing a C with type C can do so using the C method. The severity argument may be provided as an integer from zero through three or as a string containing one of the corresponding EPICS severity names C, C, C or C. If a subroutine reference is provided it will be called after the operation has completed on the server as described in C above. =item put_ackt( I ) =item put_ackt( I, I ) This method is for applications that need to enable/disable transient alarms by doing a C with type C. The C argument is a true/false value, and an optional subroutine reference can be provided as described above. =item change_connection_event( I ) This method replaces, adds or cancels the connection handler subroutine for the channel; see the C constructor for details. If I is C any existing handler is removed, otherwise the new subroutine will be used for all future connection events on this channel. =item change_access_rights_event( I ) This method replaces, adds or cancels an access rights handler subroutine for the channel, which will be called if the client's right to read from or write to the channel changes. If I is C any existing handler is removed, otherwise the new subroutine will be used for all future rights change events on this channel. The arguments passed to I are the channel object and a pair of scalar values for read and write permissions respectively, that are true when the access is permitted, false when it is not. =back =head2 Channel Data The data provided to a callback function registered with either C or C can be a scalar value or a reference to an array or a hash, depending on the data type that was used for the data transfer. If the request was for a single item of one of the basic data types, the data argument will be a Perl scalar that holds the value directly. If the request was for multiple items of one of the basic types, the data argument will be a reference to an array holding the data. There is one exception though; if the data type requested was for an array of C values that array will be represented as a single Perl string containing all the characters before the first zero byte. If the request was for one of the compound data types, the data argument will be a reference to a hash with keys as described below. Keys that are not classed as metadata are named directly after the fields in the C C, and are only included when the C structure contains that particular field. =head3 Metadata These metadata will always be present in the hash: =over 4 =item TYPE The C name of the data type from the server. This might have been widened from the original type used to request or subscribe for the data. =item COUNT The number of elements in the data returned by the server. If the data type is C the value given for C is the number of bytes (including any trailing zeros) returned by the server, although the value field is given as a Perl string containing all the characters before the first zero byte. =back =head3 Fixed Fields These fields are always present in the hash: =over 4 =item value The actual process variable data, expressed as a Perl scalar or a reference to an array of scalars, depending on the request. An array of C elements will be represented as a string; to access the array elements as numeric values the request must be for the C equivalent data type. If I is C or C, C can be accessed both as the integer choice value and (if within range) as the string associated with that particular choice. =item status The alarm status of the PV as a printable string, or C if not in alarm. =item severity The alarm severity of the PV, or C if not in alarm. A defined severity can be used as a human readable string or as a number giving the numeric value of the alarm severity (1 = C, 2 = C, 3 = C). =back =head3 Ephemeral Fields These fields are only present for some values of I: =over 4 =item strs A reference to an array containing all the possible choice strings for an ENUM. Present only when I is C or C. =item no_str The number of choices defined for an ENUM. Present only when I is C or C. =item stamp The process variable timestamp, converted to a local C. This value is suitable for passing to the Perl C or C functions. Present only when I is C. =item stamp_fraction The fractional part of the process variable timestamp as a positive floating point number less than 1.0. Present only when I is C. =item ackt The value of the process variable's transient acknowledgment flag, an integer. Present only when I is C. =item acks The alarm severity of the highest unacknowledged alarm for this process variable. As with the C value, this scalar is both a string and numeric severity. Present only when I is C. =item precision The process variable's display precision, an integer giving the number of decimal places to display. Present only when I is C or C. =item units The engineering units string for the process variable. Present only when I is C or C where C is not C. =item upper_disp_limit =item lower_disp_limit The display range for the process variable; graphical tools often provide a way to override these limits. Present only when I is C or C where C is not C. =item upper_alarm_limit =item upper_warning_limit =item lower_warning_limit =item lower_alarm_limit These items give the values at which the process variable should go into an alarm state, although in practice the alarm severity associated with each level is not provided. Present only when I is C or C where C is not C. =item upper_ctrl_limit =item lower_ctrl_limit The range over which a client can control the value of the process variable. Present only when I is C where C is not C. =back =head2 Class Methods The following functions are not channel methods, and should be called using the class method syntax, e.g. C<< CA->pend_io(10) >>. =over 4 =item version Returns the EPICS_VERSION_STRING from the version of EPICS Base this software was built using. =item flush_io Flush outstanding IO requests to the server. This routine is useful for users who need to flush requests prior to performing client side labor in parallel with labor performed in the server. Outstanding requests are also sent whenever the buffer which holds them becomes full. Note that the routine can return before all flush operations have completed. =item test_io This function tests to see if all C requests are complete and channels created without a connection callback subroutine are connected. It will return a true value if all such operations are complete, otherwise false. =item pend_io( I ) This function flushes the send buffer and then blocks until all outstanding C requests complete and all channels created without a connection callback subroutine have connected for the first time. Unlike C, this routine does not process CA's background activities if no IO requests are pending. If any I/O or connection operations remain incomplete after I seconds, the function will die with the error C; see L below. A I interval of zero is taken to mean wait forever if necessary. The I value should take into account worst case network delays such as Ethernet collision exponential back off until retransmission delays which can be quite long on overloaded networks. =item pend_event( I ) Flush the send buffer and process CA's background activities for I seconds. This function always blocks for the full I period, and if a value of zero is used it will never return. It is generally advisable to replace any uses of Perl's built-in function C with calls to this routine, allowing Channel Access to make use of the delay time to perform any necessary housekeeping operations. =item poll Flush the send buffer and process any outstanding CA background activity. =item clear_subscription( I ) Cancel a subscription. Note that for this to take effect immediately it is necessary to call C<< CA->flush_io >> or one of the other class methods that flushes the send buffer. =item add_exception_event( I ) Trap exception events and execute I whenever they occur. The subroutine is provided with four arguments: The channel object (if applicable), the status value from the server, a printable context string giving more information about the error, and a hash reference containing some additional data. If the exception is not specific to a particular channel the channel object will be C. The status value is a printable string. The hash may contain any of the following members: =over 8 =item * OP The operation in progress when the exception occurred. This scalar when used as a string is one of C, C, C, C, C or C but can also be accessed as an integer (0-5). =item * TYPE The C name of the data type involved. =item * COUNT The number of elements in the request. =item * FILE =item * LINE These refer to the source file and line number inside the CA client library where the exception was noticed. =back =item replace_printf_handler( I ) This function provides a method to trap error messages from the CA client library and redirect them to somewhere other than the C stream. The subroutine provided will be called with a single string argument every time the client library wishes to output an error or warning message. Note that a single error or warning message may result in several calls to this subroutine. To revert back to the original handler, call C<< CA->replace_printf_handler() >> passing C as the subroutine reference. =back =head1 ERROR HANDLING Errors in using the library will be indicated by the module throwing an exception, i.e. calling C with an appropriate error message. These exceptions can be caught using the standard Perl C statement and testing the C<$@> variable afterwards; if not caught, they will cause the running program to C with an appropriate error message pointing to the program line that called the C library. Error messages reported by the underlying CA client library all start with the string C and the remainder of the symbol for the associated CA error number, and are followed after a space-hyphen-space by a human-readable message describing the error. Errors that are detected by the Perl interface layer do not follow this pattern, but are still printable strings. =head1 SEE ALSO =over =item [1] R3.15 Channel Access Reference Manual by Jeffrey O. Hill L =back =head1 AUTHOR Andrew Johnson, Eanj@aps.anl.govE =head1 COPYRIGHT AND LICENSE Copyright (C) 2008-2014 UChicago Argonne LLC, as Operator of Argonne National Laboratory. This software is distributed under the terms of the EPICS Open License. =cut base-7.0.3.1/modules/ca/src/perl/Cap5.xs0000664000577000060420000011670013557101274016350 0ustar anjaesctl/* Provides an EPICS Channel Access client interface for Perl5. */ /* This macro disables perl's reentr.inc file, which we don't need * here and just generates unnecessary compiler warnings. */ #define REENTRINC #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "cadef.h" #include "db_access.h" #include "epicsVersion.h" #include "alarm.h" typedef union { dbr_long_t iv; dbr_double_t nv; dbr_string_t pv; } CA_data; typedef struct CA_channel { chid chan; CA_data data; /* Value storage for CA::get */ char *sdata; /* String storage for CA::get */ size_t ssize; /* Length allocated for sdata, excluding nil */ SV *chan_ref; SV *conn_sub; SV *rights_sub; } CA_channel; static void *p5_ctx; static const char * get_error_msg(int status) { static const char * const messages[] = { "ECA_NORMAL - Normal successful completion", "ECA_MAXIOC - Maximum simultaneous IOC connections exceeded", "ECA_UKNHOST - Unknown internet host", "ECA_UKNSERV - Unknown internet service", "ECA_SOCK - Unable to allocate a new socket", "ECA_CONN - Unable to connect to internet host or service", "ECA_ALLOCMEM - Unable to allocate additional dynamic memory", "ECA_UKNCHAN - Unknown IO channel", "ECA_UKNFIELD - Record field specified inappropriate for channel specified", "ECA_TOLARGE - The requested data transfer is greater than available memory or EPICS_CA_MAX_ARRAY_BYTES", "ECA_TIMEOUT - User specified timeout on IO operation expired", "ECA_NOSUPPORT - Sorry, that feature is planned but not supported at this time", "ECA_STRTOBIG - The supplied string is unusually large", "ECA_DISCONNCHID - The request was ignored because the specified channel is disconnected", "ECA_BADTYPE - The data type specifed is invalid", "ECA_CHIDNOTFND - Remote Channel not found", "ECA_CHIDRETRY - Unable to locate all user specified channels", "ECA_INTERNAL - Channel Access Internal Failure", "ECA_DBLCLFAIL - The requested local DB operation failed", "ECA_GETFAIL - Channel read request failed", "ECA_PUTFAIL - Channel write request failed", "ECA_ADDFAIL - Channel subscription request failed", "ECA_BADCOUNT - Invalid element count requested", "ECA_BADSTR - Invalid string", "ECA_DISCONN - Virtual circuit disconnect", "ECA_DBLCHNL - Identical process variable names on multiple servers", "ECA_EVDISALLOW - Request inappropriate within subscription (monitor) update callback", "ECA_BUILDGET - Database value get for that channel failed during channel search", "ECA_NEEDSFP - Unable to initialize without the vxWorks VX_FP_TASK task option set", "ECA_OVEVFAIL - Event queue overflow has prevented first pass event after event add", "ECA_BADMONID - Bad event subscription (monitor) identifier", "ECA_NEWADDR - Remote channel has new network address", "ECA_NEWCONN - New or resumed network connection", "ECA_NOCACTX - Specified task isnt a member of a CA context", "ECA_DEFUNCT - Attempt to use defunct CA feature failed", "ECA_EMPTYSTR - The supplied string is empty", "ECA_NOREPEATER - Unable to spawn the CA repeater thread- auto reconnect will fail", "ECA_NOCHANMSG - No channel id match for search reply- search reply ignored", "ECA_DLCKREST - Reseting dead connection- will try to reconnect", "ECA_SERVBEHIND - Server (IOC) has fallen behind or is not responding- still waiting", "ECA_NOCAST - No internet interface with broadcast available", "ECA_BADMASK - Invalid event selection mask", "ECA_IODONE - IO operations have completed", "ECA_IOINPROGRESS - IO operations are in progress", "ECA_BADSYNCGRP - Invalid synchronous group identifier", "ECA_PUTCBINPROG - Put callback timed out", "ECA_NORDACCESS - Read access denied", "ECA_NOWTACCESS - Write access denied", "ECA_ANACHRONISM - Requested feature is no longer supported", "ECA_NOSEARCHADDR - Empty PV search address list", "ECA_NOCONVERT - No reasonable data conversion between client and server types", "ECA_BADCHID - Invalid channel identifier", "ECA_BADFUNCPTR - Invalid function pointer", "ECA_ISATTACHED - Thread is already attached to a client context", "ECA_UNAVAILINSERV - Not supported by attached service", "ECA_CHANDESTROY - User destroyed channel", "ECA_BADPRIORITY - Invalid channel priority", "ECA_NOTTHREADED - Preemptive callback not enabled - additional threads may not join context", "ECA_16KARRAYCLIENT - Client's protocol revision does not support transfers exceeding 16k bytes", "ECA_CONNSEQTMO - Virtual circuit connection sequence aborted", "ECA_UNRESPTMO - Virtual circuit unresponsive" }; return messages[CA_EXTRACT_MSG_NO(status)]; } static chtype best_type(CA_channel *pch) { switch (ca_field_type(pch->chan)) { case DBF_STRING: case DBF_ENUM: return DBF_STRING; case DBF_CHAR: if (ca_element_count(pch->chan) > 1) return DBF_CHAR; /* Fall through */ case DBF_INT: case DBF_LONG: return DBF_LONG; case DBF_FLOAT: case DBF_DOUBLE: return DBF_DOUBLE; } croak("Unexpected field type %s", dbf_type_to_text(ca_field_type(pch->chan))); } static SV * newSVdbf(chtype type, const void *dbr, int index) { switch (type) { char *pc; size_t len; case DBR_STRING: pc = (char *)dbr + index * MAX_STRING_SIZE; len = strlen(pc); return newSVpv(pc, len < MAX_STRING_SIZE ? len : MAX_STRING_SIZE); case DBR_LONG: return newSViv(((dbr_long_t *)dbr)[index]); case DBR_DOUBLE: return newSVnv(((dbr_double_t *)dbr)[index]); default: croak("Unexpected data type %s", dbf_type_to_text(type)); } } static SV * newSValarm(int sevr) { SV *alarm = &PL_sv_undef; if (sevr) { alarm = newSViv(sevr); sv_setpv(alarm, epicsAlarmSeverityStrings[sevr]); SvIOK_on(alarm); } return alarm; } static void hashAdd(HV *hash, const char *key, I32 klen, SV *val) { SV **result = hv_store(hash, key, klen, val, 0); if (result == NULL) SvREFCNT_dec(val); } static SV * newSVdbr(struct event_handler_args *peha) { const int is_primitive = dbr_type_is_plain(peha->type) || (peha->type == DBR_CLASS_NAME); HV *hash; SV *val; chtype value_type; union db_access_val *u; if (dbr_type_is_STRING(peha->type) || peha->type == DBR_STSACK_STRING || peha->type == DBR_CLASS_NAME) value_type = DBR_STRING; else if (dbr_type_is_CHAR(peha->type)) value_type = DBR_CHAR; else if (dbr_type_is_LONG(peha->type)) value_type = DBR_LONG; else if (dbr_type_is_DOUBLE(peha->type)) value_type = DBR_DOUBLE; else if (dbr_type_is_ENUM(peha->type)) /* Only seen as DBR_GR_ENUM and DBR_CTRL_ENUM */ value_type = DBR_ENUM; else { croak("Unexpected data type %s", dbf_type_to_text(peha->type)); } if (is_primitive) { if (value_type == DBR_CHAR) { /* Long string => Perl scalar */ ((char *)peha->dbr) [peha->count - 1] = 0; return newSVpv(peha->dbr, 0); } if (peha->count != 1) { /* Array of values => Perl array reference */ AV *array; int i; array = newAV(); for (i = 0; i < peha->count; i++) { av_push(array, newSVdbf(value_type, peha->dbr, i)); } return newRV_noinc((SV *)array); } /* Single value => Perl scalar */ return newSVdbf(value_type, peha->dbr, 0); } /* Compound => Perl hash reference */ u = (union db_access_val *)peha->dbr; hash = newHV(); /* Add basic meta-data */ hashAdd(hash, "TYPE", 4, newSVpv(dbr_type_to_text(peha->type), 0)); hashAdd(hash, "COUNT", 5, newSViv(peha->count)); /* Alarm status and severity are always in the same place */ if (u->slngval.status) val = newSVpv(epicsAlarmConditionStrings[u->slngval.status], 0); else val = &PL_sv_undef; hashAdd(hash, "status", 6, val); hashAdd(hash, "severity", 8, newSValarm(u->slngval.severity)); if (peha->type == DBR_GR_ENUM || peha->type == DBR_CTRL_ENUM) { AV *strings = newAV(); int n = u->genmval.no_str; int i; val = newSViv(u->genmval.value); for (i = 0; i < n; i++) { size_t slen = strlen(u->genmval.strs[i]); if (slen > MAX_ENUM_STRING_SIZE) slen = MAX_ENUM_STRING_SIZE; av_push(strings, newSVpv(u->genmval.strs[i], slen)); if (i == u->genmval.value) { sv_setpvn(val, u->genmval.strs[i], slen); SvIOK_on(val); } } hashAdd(hash, "strs", 4, newRV_noinc((SV *)strings)); hashAdd(hash, "no_str", 6, newSViv(u->genmval.no_str)); hashAdd(hash, "value", 5, val); return newRV_noinc((SV *)hash); } /* Value */ if (value_type == DBR_CHAR) { char *str = dbr_value_ptr(peha->dbr, peha->type); /* Long string => Perl scalar */ str[peha->count - 1] = 0; val = newSVpv(str, 0); } else if (peha->count == 1) { /* Single value => Perl scalar */ val = newSVdbf(value_type, dbr_value_ptr(peha->dbr, peha->type), 0); } else { /* Array of values => Perl array reference */ AV *array = newAV(); int i; for (i = 0; i < peha->count; i++) { av_push(array, newSVdbf(value_type, dbr_value_ptr(peha->dbr, peha->type), i)); } val = newRV_noinc((SV *)array); } hashAdd(hash, "value", 5, val); /* Timestamp follows status and severity in DBR_TIME */ if (dbr_type_is_TIME(peha->type)) { struct timespec t; epicsTimeToTimespec(&t, &u->tlngval.stamp); hashAdd(hash, "stamp", 5, newSViv(t.tv_sec)); hashAdd(hash, "stamp_fraction", 14, newSVnv((double)t.tv_nsec / 1e9)); } else if (peha->type == DBR_STSACK_STRING) { struct dbr_stsack_string *s = (struct dbr_stsack_string *)peha->dbr; hashAdd(hash, "ackt", 4, newSViv(s->ackt)); hashAdd(hash, "acks", 4, newSValarm(s->acks)); } else if (value_type != DBR_STRING && (dbr_type_is_GR(peha->type) || dbr_type_is_CTRL(peha->type))) { char *units; size_t ulen; void *limit; int i = dbr_type_is_CTRL(peha->type) ? 7 : 5; if (value_type == DBR_DOUBLE) { units = u->gdblval.units; limit = &u->gdblval.upper_disp_limit; hashAdd(hash, "precision", 9, newSViv(u->gdblval.precision)); } else { /* value_type == DBR_LONG */ units = u->glngval.units; limit = &u->glngval.upper_disp_limit; } ulen = strlen(units); hashAdd(hash, "units", 5, newSVpv(units, ulen < MAX_UNITS_SIZE ? ulen : MAX_UNITS_SIZE)); while (i >= 0) { static const char * const limit_name[] = { "upper_disp_limit", "lower_disp_limit", "upper_alarm_limit", "upper_warning_limit", "lower_warning_limit", "lower_alarm_limit", "upper_ctrl_limit", "lower_ctrl_limit", }; hashAdd(hash, limit_name[i], strlen(limit_name[i]), newSVdbf(value_type, limit, i)); i--; } } return newRV_noinc((SV *)hash); } enum io_type { IO_GET, IO_PUT, IO_MONITOR, }; static void io_handler(struct event_handler_args *peha, enum io_type io) { PERL_SET_CONTEXT(p5_ctx); { CA_channel *pch = ca_puser(peha->chid); SV *code = (SV *)peha->usr; SV *status = &PL_sv_undef; SV *data = &PL_sv_undef; dSP; ENTER; SAVETMPS; if (peha->status != ECA_NORMAL) { status = sv_2mortal(newSVpv(get_error_msg(peha->status), 0)); } else if (io != IO_PUT) { data = sv_2mortal(newSVdbr(peha)); } sv_setsv(ERRSV, &PL_sv_undef); PUSHMARK(SP); XPUSHs(pch->chan_ref); XPUSHs(status); XPUSHs(data); PUTBACK; call_sv(code, G_VOID | G_DISCARD | G_EVAL | G_KEEPERR); if (io != IO_MONITOR) SvREFCNT_dec(code); if (SvTRUE(ERRSV)) croak(NULL); FREETMPS; LEAVE; } } static int replace_handler(SV * sub, SV ** ph_sub, long *phandler) { if (SvOK(sub) && SvTRUE(sub)) { if (*ph_sub != NULL) { SvSetSV(*ph_sub, sub); return FALSE; } *ph_sub = newSVsv(sub); } else { if (*ph_sub == NULL) return FALSE; SvREFCNT_dec(*ph_sub); *ph_sub = NULL; *phandler = 0; } return TRUE; } /******************************************************************************/ /* CA::new($class, $name, [\&sub]) */ static void connect_handler(struct connection_handler_args cha) { CA_channel *pch = ca_puser(cha.chid); PERL_SET_CONTEXT(p5_ctx); { dSP; SvSetSV(ERRSV, &PL_sv_undef); PUSHMARK(SP); XPUSHs(pch->chan_ref); XPUSHs(cha.op == CA_OP_CONN_UP ? &PL_sv_yes : &PL_sv_no); PUTBACK; call_sv(pch->conn_sub, G_EVAL | G_VOID | G_DISCARD | G_KEEPERR); if (SvTRUE(ERRSV)) croak(NULL); } } SV * CA_new(const char *class, const char *name, ...) { dXSARGS; SV *ca_ref = newSViv(0); SV *ca_obj = newSVrv(ca_ref, class); CA_channel *pch; caCh *handler; int status; Newz(0, pch, 1, CA_channel); sv_setiv(ca_obj, (IV)pch); SvREADONLY_on(ca_obj); pch->chan_ref = ca_ref; (void) SvREFCNT_inc(ca_ref); if (items > 2 && SvOK(ST(2))) { /* Connection handler provided */ pch->conn_sub = newSVsv(ST(2)); handler = &connect_handler; } else handler = NULL; status = ca_create_channel(name, handler, pch, 0, &pch->chan); if (status != ECA_NORMAL) { SvREFCNT_dec(ca_ref); if (pch->conn_sub) SvREFCNT_dec(pch->conn_sub); croak("%s", get_error_msg(status)); } return ca_ref; } static int destroyed = 0; /* CA::DESTROY($ca_ref) */ void CA_DESTROY(SV *ca_ref) { CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); int status; status = destroyed ? ECA_NORMAL : ca_clear_channel(pch->chan); if (pch->conn_sub) SvREFCNT_dec(pch->conn_sub); if (pch->rights_sub) SvREFCNT_dec(pch->rights_sub); if (pch->sdata) Safefree(pch->sdata); SvREFCNT_dec(pch->chan_ref); Safefree(pch); if (status != ECA_NORMAL) croak("%s", get_error_msg(status)); } /* CA::context_destroy($class) */ void CA_context_destroy(const char *class) { ca_context_destroy(); destroyed = 1; } /* CA::change_connection_event($ca_ref, \$sub) */ void CA_change_connection_event(SV *ca_ref, SV *sub) { CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); caCh *handler = &connect_handler; int status; if (! replace_handler(sub, &pch->conn_sub, (long *)&handler)) return; status = ca_change_connection_event(pch->chan, handler); if (status != ECA_NORMAL) { croak("%s", get_error_msg(status)); } } /* CA::replace_access_rights_event($ca_ref, \$sub) */ static void rights_handler(struct access_rights_handler_args arha) { CA_channel *pch = ca_puser(arha.chid); PERL_SET_CONTEXT(p5_ctx); { dSP; SvSetSV(ERRSV, &PL_sv_undef); PUSHMARK(SP); XPUSHs(pch->chan_ref); XPUSHs(arha.ar.read_access ? &PL_sv_yes : &PL_sv_no); XPUSHs(arha.ar.write_access ? &PL_sv_yes : &PL_sv_no); PUTBACK; call_sv(pch->rights_sub, G_EVAL | G_VOID | G_DISCARD | G_KEEPERR); if (SvTRUE(ERRSV)) croak(NULL); } } void CA_replace_access_rights_event(SV *ca_ref, SV *sub) { CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); caArh *handler = &rights_handler; int status; if (! replace_handler(sub, &pch->rights_sub, (long *)&handler)) return; status = ca_replace_access_rights_event(pch->chan, handler); if (status != ECA_NORMAL) { croak("%s", get_error_msg(status)); } } /* CA::put($ca_ref, @values) */ void CA_put(SV *ca_ref, SV *val, ...) { dXSARGS; CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); int num_values = items - 1; int status; if (num_values == 1) { if (ca_field_type(pch->chan) == DBF_CHAR && ca_element_count(pch->chan) > 1) { size_t len; char *long_string = SvPV(val, len); status = ca_array_put(DBF_CHAR, len+1, pch->chan, long_string); } else { union { dbr_long_t dbr_long; dbr_double_t dbr_double; dbr_string_t dbr_string; } data; chtype type = best_type(pch); switch (type) { case DBF_LONG: data.dbr_long = SvIV(val); break; case DBF_DOUBLE: data.dbr_double = SvNV(val); break; case DBF_STRING: strncpy(data.dbr_string, SvPV_nolen(val), MAX_STRING_SIZE); break; } status = ca_put(type, pch->chan, &data); } } else { union { dbr_char_t *dbr_char; dbr_long_t *dbr_long; dbr_double_t *dbr_double; char *dbr_string; void *dbr; } p; int i; chtype type = best_type(pch); switch (type) { case DBF_CHAR: New(0, p.dbr_char, num_values, dbr_char_t); for (i = 0; i < num_values; i++) { p.dbr_char[i] = SvIV(ST(i + 1)); } break; case DBF_LONG: New(0, p.dbr_long, num_values, dbr_long_t); for (i = 0; i < num_values; i++) { p.dbr_long[i] = SvIV(ST(i + 1)); } break; case DBF_DOUBLE: New(0, p.dbr_double, num_values, dbr_double_t); for (i = 0; i < num_values; i++) { p.dbr_double[i] = SvNV(ST(i + 1)); } break; case DBF_STRING: New(0, p.dbr_string, num_values * MAX_STRING_SIZE, char); for (i = 0; i < num_values; i++) { char * src = SvPV_nolen(ST(i + 1)); strncpy(p.dbr_string + i, src, MAX_STRING_SIZE); } break; } status = ca_array_put(type, num_values, pch->chan, p.dbr); Safefree(p.dbr); } if (status != ECA_NORMAL) { croak("%s", get_error_msg(status)); } XSRETURN(0); } /* CA::put_callback($ca_ref, \&sub, @values) */ static void put_handler(struct event_handler_args eha) { io_handler(&eha, IO_PUT); } void CA_put_callback(SV *ca_ref, SV *sub, SV *val, ...) { dXSARGS; CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); SV *put_sub = newSVsv(sub); int num_values = items - 2; int status; if (num_values == 1) { if (ca_field_type(pch->chan) == DBF_CHAR && ca_element_count(pch->chan) > 1) { size_t len; char *long_string = SvPV(val, len); status = ca_array_put_callback(DBF_CHAR, len+1, pch->chan, long_string, put_handler, put_sub); } else { union { dbr_long_t dbr_long; dbr_double_t dbr_double; dbr_string_t dbr_string; } data; chtype type = best_type(pch); switch (type) { case DBF_LONG: data.dbr_long = SvIV(val); break; case DBF_DOUBLE: data.dbr_double = SvNV(val); break; case DBF_STRING: strncpy(data.dbr_string, SvPV_nolen(val), MAX_STRING_SIZE); break; } status = ca_put_callback(type, pch->chan, &data, put_handler, put_sub); } } else { union { dbr_char_t *dbr_char; dbr_long_t *dbr_long; dbr_double_t *dbr_double; char *dbr_string; void *dbr; } p; int i; chtype type = best_type(pch); switch (type) { case DBF_CHAR: New(0, p.dbr_char, num_values, dbr_char_t); for (i = 0; i < num_values; i++) { p.dbr_char[i] = SvIV(ST(i + 1)); } break; case DBF_LONG: New(0, p.dbr_long, num_values, dbr_long_t); for (i = 0; i < num_values; i++) { p.dbr_long[i] = SvIV(ST(i + 2)); } break; case DBF_DOUBLE: New(0, p.dbr_double, num_values, dbr_double_t); for (i = 0; i < num_values; i++) { p.dbr_double[i] = SvNV(ST(i + 2)); } break; case DBF_STRING: New(0, p.dbr_string, num_values * MAX_STRING_SIZE, char); for (i = 0; i < num_values; i++) { char * src = SvPV_nolen(ST(i + 2)); strncpy(p.dbr_string + i, src, MAX_STRING_SIZE); } break; } status = ca_array_put_callback(type, num_values, pch->chan, p.dbr, put_handler, put_sub); Safefree(p.dbr); } if (status != ECA_NORMAL) { SvREFCNT_dec(put_sub); croak("%s", get_error_msg(status)); } XSRETURN(0); } /* CA::put_acks($ca_ref, $sevr, [\&sub]) */ void CA_put_acks(SV *ca_ref, SV *sevr, ...) { dXSARGS; CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); dbr_put_acks_t acks; int status; if (! SvOK(sevr)) { acks = NO_ALARM; } else if (SvIOK(sevr)) { acks = SvIV(sevr); if (acks > INVALID_ALARM) croak("Bad acknowledgement severity %d", acks); } else { size_t slen; char *sname = SvPV(sevr, slen); for (acks = NO_ALARM; acks <= INVALID_ALARM; acks++) { if (strcmp(sname, epicsAlarmSeverityStrings[acks]) == 0) break; } if (acks > INVALID_ALARM) croak("Bad acknowledgment severity '%s'", sname); } if (items > 2) { SV *put_sub = newSVsv(ST(2)); status = ca_put_callback(DBR_PUT_ACKS, pch->chan, &acks, put_handler, put_sub); if (status != ECA_NORMAL) SvREFCNT_dec(put_sub); } else status = ca_put(DBR_PUT_ACKS, pch->chan, &acks); if (status != ECA_NORMAL) croak("%s", get_error_msg(status)); XSRETURN(0); } /* CA::put_ackt($ca_ref, $trans, [\&sub]) */ void CA_put_ackt(SV *ca_ref, int ack, ...) { dXSARGS; CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); dbr_put_ackt_t ackt = !! ack; /* 0 or 1 only */ int status; if (items > 2) { SV *put_sub = newSVsv(ST(2)); status = ca_put_callback(DBR_PUT_ACKT, pch->chan, &ackt, put_handler, put_sub); if (status != ECA_NORMAL) SvREFCNT_dec(put_sub); } else status = ca_put(DBR_PUT_ACKS, pch->chan, &ackt); if (status != ECA_NORMAL) croak("%s", get_error_msg(status)); XSRETURN(0); } /* CA::get($ca_ref) */ void CA_get(SV *ca_ref) { CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); size_t count = ca_element_count(pch->chan); int status; if (ca_field_type(pch->chan) == DBF_CHAR && count > 1) { if (!pch->sdata) { New(0, pch->sdata, count + 1, char); pch->ssize = count; } else if (pch->ssize < count) { /* Reconnected to larger array? */ Safefree(pch->sdata); New(0, pch->sdata, count + 1, char); pch->ssize = count; } status = ca_array_get(DBF_CHAR, 0, pch->chan, pch->sdata); } else { status = ca_get(best_type(pch), pch->chan, &pch->data); } if (status != ECA_NORMAL) { croak("%s", get_error_msg(status)); } } /* CA::value($ca_ref) */ SV * CA_value(SV *ca_ref) { CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); if (ca_field_type(pch->chan) == DBF_CHAR && ca_element_count(pch->chan) > 1 && pch->sdata) { return newSVpv(pch->sdata, 0); } return newSVdbf(best_type(pch), &pch->data, 0); } /* CA::get_callback($ca_ref, \&sub, [$type | $count]) */ static void get_handler(struct event_handler_args eha) { io_handler(&eha, IO_GET); } void CA_get_callback(SV *ca_ref, SV *sub, ...) { dXSARGS; CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); SV *get_sub = newSVsv(sub); int status; chtype type = best_type(pch); int count = 0; int i = 2; const char *croak_msg; while (items > i && SvOK(ST(i))) { if (SvIOK(ST(i))) { /* Interger => Count arg, zero means current size */ count = SvIV(ST(i)); if (count < 0 || count > ca_element_count(pch->chan)) { croak_msg = "Requested array size is out of range"; goto exit_croak; } } else if (SvPOKp(ST(i))) { /* String => Type arg */ char *treq = SvPV_nolen(ST(i)); dbr_text_to_type(treq, type); if (type < 0 || type == DBR_PUT_ACKT || type == DBR_PUT_ACKS) { croak_msg = "Requested DBR type is invalid"; goto exit_croak; } else if (type == DBR_GR_ENUM || type == DBR_CTRL_ENUM || type == DBR_CLASS_NAME || type == DBR_STSACK_STRING) /* The above types are supported */ ; else if (dbr_type_is_SHORT(type)) type += (DBR_LONG - DBR_SHORT); else if (dbr_type_is_FLOAT(type)) type += (DBR_DOUBLE - DBR_FLOAT); else if (dbr_type_is_ENUM(type)) type += (DBR_STRING - DBR_ENUM); } i++; } status = ca_array_get_callback(type, count, pch->chan, get_handler, get_sub); if (status != ECA_NORMAL) { croak_msg = get_error_msg(status); goto exit_croak; } XSRETURN(0); return; exit_croak: SvREFCNT_dec(get_sub); croak("%s", croak_msg); } /* CA::create_subscription($ca_ref, $mask, \&sub, [$type | $count]) */ static void subscription_handler(struct event_handler_args eha) { io_handler(&eha, IO_MONITOR); } SV * CA_create_subscription(SV *ca_ref, const char *mask_str, SV *sub, ...) { dXSARGS; CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); SV *mon_sub = newSVsv(sub); SV *mon_ref = newSViv(0); SV *mon_obj = newSVrv(mon_ref, "CA::Subscription"); chtype type = best_type(pch); int count = ca_element_count(pch->chan); int i = 3; int mask = 0; evid event; int status; const char *croak_msg; if (strchr(mask_str, 'v') || strchr(mask_str, 'V')) mask |= DBE_VALUE; if (strchr(mask_str, 'l') || strchr(mask_str, 'L')) mask |= DBE_LOG; if (strchr(mask_str, 'a') || strchr(mask_str, 'A')) mask |= DBE_ALARM; if (strchr(mask_str, 'p') || strchr(mask_str, 'P')) mask |= DBE_PROPERTY; while (items > i && SvOK(ST(i))) { if (SvIOK(ST(i))) { /* Interger => Count arg, zero means current size */ count = SvIV(ST(i)); if (count < 0 || count > ca_element_count(pch->chan)) { croak_msg = "Requested array size is out of range"; goto exit_croak; } } else if (SvPOKp(ST(i))) { /* String => Type arg */ size_t tlen; char *treq = SvPV(ST(i), tlen); dbr_text_to_type(treq, type); if (type < 0) { croak_msg = "Unknown CA data type"; goto exit_croak; } if (type == DBR_PUT_ACKT || type == DBR_PUT_ACKS) { croak_msg = "DBR_PUT_ACK types are write-only"; goto exit_croak; } else if (type == DBR_GR_ENUM || type == DBR_CTRL_ENUM || type == DBR_CLASS_NAME || type == DBR_STSACK_STRING) /* These above types are supported */ ; else if (dbr_type_is_SHORT(type)) type += (DBR_LONG - DBR_SHORT); else if (dbr_type_is_FLOAT(type)) type += (DBR_DOUBLE - DBR_FLOAT); else if (dbr_type_is_ENUM(type)) type += (DBR_STRING - DBR_ENUM); } i++; } status = ca_create_subscription(type, count, pch->chan, mask, subscription_handler, mon_sub, &event); if (status != ECA_NORMAL) { croak_msg = get_error_msg(status); goto exit_croak; } sv_setiv(mon_obj, (IV)event); SvREADONLY_on(mon_obj); (void) SvREFCNT_inc(mon_ref); return mon_ref; exit_croak: SvREFCNT_dec(mon_ref); SvREFCNT_dec(mon_sub); croak("%s", croak_msg); } /* CA::clear_subscription($class, $subscription) */ void CA_clear_subscription(const char *class, SV *mon_ref) { evid event = (evid)SvIV(SvRV(mon_ref)); int status; if (! sv_isa(mon_ref, "CA::Subscription")) { croak("Not a CA::Subscription"); } status = ca_clear_subscription(event); if (status != ECA_NORMAL) { croak("%s", get_error_msg(status)); } } /* CA::pend_io($class, $timeout) */ void CA_pend_io(const char *class, double timeout) { int status = ca_pend_io(timeout); if (status != ECA_NORMAL) { croak("%s", get_error_msg(status)); } } /* CA::test_io($class) */ int CA_test_io(const char *class) { return (ca_test_io() == ECA_IODONE); } /* CA::pend_event($class, $timeout) */ void CA_pend_event(const char *class, double timeout) { int status = ca_pend_event(timeout); if (status != ECA_TIMEOUT) { croak("%s", get_error_msg(status)); } } /* CA::poll($class) */ void CA_poll(const char *class) { ca_poll(); } /* CA::flush_io($class) */ void CA_flush_io(const char *class) { ca_flush_io(); } /* CA::version($class) */ const char * CA_version(const char *class) { return EPICS_VERSION_STRING; } /* CA::add_exception_event($class, \&sub) */ static SV * exception_sub = NULL; static void exception_handler(struct exception_handler_args eha) { if (! exception_sub) return; PERL_SET_CONTEXT(p5_ctx); { SV *channel = &PL_sv_undef; SV *status = &PL_sv_undef; HV *hash = newHV(); SV *op; const char *opString[] = { "GET", "PUT", "CREATE_CHANNEL", "ADD_EVENT", "CLEAR_EVENT", "OTHER" }; dSP; ENTER; SAVETMPS; if (eha.chid) { CA_channel *pch = ca_puser(eha.chid); channel = pch->chan_ref; } if (eha.stat != ECA_NORMAL) { status = sv_2mortal(newSVpv(get_error_msg(eha.stat), 0)); } op = newSViv(eha.op); sv_setpv(op, opString[eha.op]); SvIOK_on(op); hashAdd(hash, "OP", 2, op); hashAdd(hash, "TYPE", 4, newSVpv(dbr_type_to_text(eha.type), 0)); hashAdd(hash, "COUNT", 5, newSViv(eha.count)); if (eha.pFile) hashAdd(hash, "FILE", 4, newSVpv(eha.pFile, 0)); if (eha.lineNo) hashAdd(hash, "LINE", 4, newSVuv(eha.lineNo)); PUSHMARK(SP); XPUSHs(channel); XPUSHs(status); XPUSHs(sv_2mortal(newSVpv(eha.ctx, 0))); XPUSHs(sv_2mortal(newRV_noinc((SV *)hash))); PUTBACK; call_sv(exception_sub, G_EVAL | G_VOID | G_DISCARD); FREETMPS; LEAVE; } } void CA_add_exception_event(const char *class, SV *sub) { caExceptionHandler *handler = exception_handler; int status; if (! replace_handler(sub, &exception_sub, (long *)&handler)) return; status = ca_add_exception_event(handler, NULL); if (status != ECA_NORMAL) { SvREFCNT_dec(exception_sub); exception_sub = NULL; croak("%s", get_error_msg(status)); } } /* CA::replace_printf_handler($class, \&sub) */ static SV * printf_sub = NULL; #ifndef va_copy # ifdef __GNUC__ # define va_copy(d, s) __va_copy(d, s) # else # define va_copy(d, s) ((d) = (s)) /* The above macro is NOT PORTABLE but works on Windows when * stdarg.h doesn't provide va_copy(). Some architectures need * define va_copy(d, s) ((*d) = (*s)) * while others may be even more complicated, but hopefully the * system stdarg.h header defines va_copy() in those cases. */ # endif #endif static int printf_handler(const char *format, va_list args) { if (! printf_sub) return 0; PERL_SET_CONTEXT(p5_ctx); { SV *printf_str; dSP; va_list argcopy; ENTER; SAVETMPS; va_copy(argcopy, args); printf_str = NEWSV(0, strlen(format) + 32); sv_vsetpvf(printf_str, format, &argcopy); va_end(argcopy); PUSHMARK(SP); XPUSHs(sv_2mortal(printf_str)); PUTBACK; call_sv(printf_sub, G_EVAL | G_VOID | G_DISCARD); FREETMPS; LEAVE; } return 0; } void CA_replace_printf_handler(const char *class, SV *sub) { caPrintfFunc *handler = printf_handler; int status; if (! replace_handler(sub, &printf_sub, (long *)&handler)) return; status = ca_replace_printf_handler(handler); if (status != ECA_NORMAL) { SvREFCNT_dec(printf_sub); printf_sub = NULL; croak("%s", get_error_msg(status)); } } /* CA::field_type($ca_ref) */ const char * CA_field_type(SV *ca_ref) { CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); chtype t = ca_field_type(pch->chan); if (t == TYPENOTCONN) return "TYPENOTCONN"; return dbr_type_to_text(t); } /* CA::element_count($ca_ref) */ int CA_element_count(SV *ca_ref) { CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); return ca_element_count(pch->chan); } /* CA::name($ca_ref) */ const char * CA_name(SV *ca_ref) { CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); return ca_name(pch->chan); } /* CA::state($ca_ref) */ const char * CA_state(SV *ca_ref) { CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); static const char * const state_name[] = { "never connected", "previously connected", "connected", "closed" }; return state_name[ca_state(pch->chan)]; } /* CA::is_connected($ca_ref) */ int CA_is_connected(SV *ca_ref) { CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); return ca_state(pch->chan) == cs_conn; } /* CA::host_name($ca_ref) */ const char * CA_host_name(SV *ca_ref) { CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); return ca_host_name(pch->chan); } /* CA::read_access($ca_ref) */ int CA_read_access(SV *ca_ref) { CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); return ca_read_access(pch->chan); } /* CA::write_access($ca_ref) */ int CA_write_access(SV *ca_ref) { CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); return ca_write_access(pch->chan); } /******************************************************************************/ /* Ensure that the generated boot_Cap5 function is visible * outside of the libCap5.so shared library when compiling * with GCC4+ and -fvisibility=hidden is used. */ #if __GNUC__ >= 4 XS(boot_Cap5) __attribute__ ((visibility ("default"))); #endif MODULE = Cap5 PACKAGE = Cap5 MODULE = Cap5 PACKAGE = CA PREFIX = CA_ PROTOTYPES: DISABLE BOOT: p5_ctx = Perl_get_context(); SV * CA_new (class, name, ...) const char * class const char * name PREINIT: I32* temp; CODE: temp = PL_markstack_ptr++; RETVAL = CA_new(class, name); PL_markstack_ptr = temp; OUTPUT: RETVAL void CA_DESTROY (ca_ref) SV * ca_ref void CA_context_destroy (class) const char * class void CA_change_connection_event (ca_ref, sub) SV * ca_ref SV * sub void CA_replace_access_rights_event (ca_ref, sub) SV * ca_ref SV * sub void CA_put (ca_ref, val, ...) SV * ca_ref SV * val PREINIT: I32* temp; PPCODE: temp = PL_markstack_ptr++; CA_put(ca_ref, val); if (PL_markstack_ptr != temp) { /* truly void, because dXSARGS not invoked */ PL_markstack_ptr = temp; XSRETURN_EMPTY; /* return empty stack */ } /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */ void CA_put_callback (ca_ref, sub, val, ...) SV * ca_ref SV * sub SV * val PREINIT: I32* temp; PPCODE: temp = PL_markstack_ptr++; CA_put_callback(ca_ref, sub, val); if (PL_markstack_ptr != temp) { /* truly void, because dXSARGS not invoked */ PL_markstack_ptr = temp; XSRETURN_EMPTY; /* return empty stack */ } /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */ void CA_put_acks (ca_ref, sevr, ...) SV * ca_ref SV * sevr PREINIT: I32* temp; PPCODE: temp = PL_markstack_ptr++; CA_put_acks(ca_ref, sevr); if (PL_markstack_ptr != temp) { /* truly void, because dXSARGS not invoked */ PL_markstack_ptr = temp; XSRETURN_EMPTY; /* return empty stack */ } /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */ void CA_put_ackt (ca_ref, ack, ...) SV * ca_ref int ack PREINIT: I32* temp; PPCODE: temp = PL_markstack_ptr++; CA_put_ackt(ca_ref, ack); if (PL_markstack_ptr != temp) { /* truly void, because dXSARGS not invoked */ PL_markstack_ptr = temp; XSRETURN_EMPTY; /* return empty stack */ } /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */ void CA_get (ca_ref) SV * ca_ref SV * CA_value (ca_ref) SV * ca_ref void CA_get_callback (ca_ref, sub, ...) SV * ca_ref SV * sub PREINIT: I32* temp; PPCODE: temp = PL_markstack_ptr++; CA_get_callback(ca_ref, sub); if (PL_markstack_ptr != temp) { /* truly void, because dXSARGS not invoked */ PL_markstack_ptr = temp; XSRETURN_EMPTY; /* return empty stack */ } /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */ SV * CA_create_subscription (ca_ref, mask_str, sub, ...) SV * ca_ref const char * mask_str SV * sub PREINIT: I32* temp; CODE: temp = PL_markstack_ptr++; RETVAL = CA_create_subscription(ca_ref, mask_str, sub); PL_markstack_ptr = temp; OUTPUT: RETVAL void CA_clear_subscription (class, mon_ref) const char * class SV * mon_ref void CA_pend_io (class, timeout) const char * class double timeout int CA_test_io (class) const char * class void CA_pend_event (class, timeout) const char * class double timeout void CA_poll (class) const char * class void CA_flush_io (class) const char * class const char * CA_version (class) const char * class void CA_add_exception_event (class, sub) const char * class SV * sub void CA_replace_printf_handler (class, sub) const char * class SV * sub const char * CA_field_type (ca_ref) SV * ca_ref int CA_element_count (ca_ref) SV * ca_ref const char * CA_name (ca_ref) SV * ca_ref const char * CA_state (ca_ref) SV * ca_ref int CA_is_connected (ca_ref) SV * ca_ref const char * CA_host_name (ca_ref) SV * ca_ref int CA_read_access (ca_ref) SV * ca_ref int CA_write_access (ca_ref) SV * ca_ref base-7.0.3.1/modules/ca/src/perl/Makefile0000664000577000060420000000511213557101274016636 0ustar anjaesctl#************************************************************************* # Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* TOP = ../../../.. include $(TOP)/configure/CONFIG ifdef T_A PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version) PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname) PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME) PERL_ARCHLIB := $(shell $(PERL) ../perlConfig.pl archlib) PERL_h = $(PERL_ARCHLIB)/CORE/perl.h EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils PERLBIN := $(shell $(PERL) ../perlConfig.pl bin) XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp)) # Special settings for Darwin: ifeq ($(OS_CLASS),Darwin) # Use hdepends command (not GNU compiler flags) # to generate header file dependancies for Darwin. # Darwin has multiple -arch compiler flags. HDEPENDS_METHOD = MKMF # Perl loadable libraries on Darwin have funny names LOADABLE_SHRLIB_PREFIX = LOADABLE_SHRLIB_SUFFIX = .$(shell $(PERL) ../perlConfig.pl dlext) ifeq ($(wildcard $(PERL_h)),) # Perl's headers moved in Mojave SDK_PATH := $(shell xcodebuild -version -sdk macosx Path) PERL_ARCHLIB := $(SDK_PATH)/$(PERL_ARCHLIB) endif endif ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!) ifeq ($(strip $(XSUBPP)),) $(warning Perl's xsubpp program was not found.) $(warning The Perl CA module will not be built.) else ifeq ($(wildcard $(PERL_h)),) $(warning Perl's C header files were not found.) $(warning The Perl CA module will not be built.) else ifeq ($(findstring $(OS_CLASS),WIN32 cygwin32),) # Doesn't build on WIN32 LOADABLE_LIBRARY_HOST = Cap5 PERL_SCRIPTS += capr.pl PERL_MODULES += CA.pm PERL_MODULES += $(PERL_ARCHPATH)/$(LOADABLE_SHRLIB_PREFIX)Cap5$(LOADABLE_SHRLIB_SUFFIX) HTMLS_DIR = . HTMLS = CA.html endif endif endif endif endif Cap5_SRCS = Cap5.xs Cap5_LIBS = ca Com Cap5_INCLUDES = -I$(PERL_ARCHLIB)/CORE Cap5_CFLAGS = $(shell $(PERL) ../perlConfig.pl ccflags) CLEANS += Cap5.c include $(TOP)/configure/RULES ifdef T_A %.c: ../%.xs $(RM) $@ $@_new $(PERL) $(XSUBPP) -typemap $(EXTUTILS)/typemap $< > $@_new && $(MV) $@_new $@ $(INSTALL_PERL_MODULES)/$(PERL_ARCHPATH)/%: % $(ECHO) "Installing loadable shared library $@" @$(INSTALL_LIBRARY) -d -m $(LIB_PERMISSIONS) $< $(INSTALL_PERL_MODULES)/$(PERL_ARCHPATH) endif base-7.0.3.1/modules/ca/src/perl/capr.pl0000664000577000060420000003130613557101274016464 0ustar anjaesctl#!/usr/bin/env perl ####################################################################### # # capr: A program that attempts to do a "dbpr" command via channel # access. # ####################################################################### use strict; use FindBin qw($Bin); use lib ("$Bin/../../lib/perl"); use Getopt::Std; use EPICS::Path; use CA; ######### Globals ########## our ($opt_h, $opt_f, $opt_r); our $opt_d = $ENV{EPICS_CAPR_DBD_FILE} || "$Bin/../../dbd/softIoc.dbd"; our $opt_w = 1; my %record = (); # Empty hash to put dbd data in my $iIdx = 0; # Array indexes for interest, data type and base my $tIdx = 1; my $bIdx = 2; my %device = (); # Empty hash to record which rec types have device support # EPICS field types my %fieldType = ( DBF_CHAR => 'DBF_CHAR', DBF_UCHAR => 'DBF_CHAR', DBF_DOUBLE => 'DBF_FLOAT', DBF_FLOAT => 'DBF_FLOAT', DBF_LONG => 'DBF_LONG', DBF_SHORT => 'DBF_LONG', DBF_ULONG => 'DBF_LONG', DBF_USHORT => 'DBF_LONG', DBF_DEVICE => 'DBF_STRING', DBF_ENUM => 'DBF_STRING', DBF_FWDLINK => 'DBF_STRING', DBF_INLINK => 'DBF_STRING', DBF_MENU => 'DBF_STRING', DBF_OUTLINK => 'DBF_STRING', DBF_STRING => 'DBF_STRING', DBF_NOACCESS => 'DBF_NOACCESS', ); # globals for sub caget my %callback_data; my %timed_out; my $callback_incomplete; ######### Main program ############ HELP_MESSAGE() unless getopts('hd:f:rw:'); HELP_MESSAGE() if $opt_h; die "File $opt_d not found. (\"capr.pl -h\" gives help)\n" unless -f $opt_d; parseDbd($opt_d); print "Using $opt_d\n\n"; # Print a list of record types if ($opt_r) { print ("Record types found:\n"); printList(0); exit; } # Print the fields defined for given record if ($opt_f) { printRecordList($opt_f); exit; } HELP_MESSAGE() unless @ARGV; $_ = shift; if (@ARGV) { # Drop any ".FIELD" part s/\. \w+ $//x; printRecord($_, @ARGV); } else { if (m/^ \s* ([]+:;<>0-9A-Za-z[-]+) (?:\. \w+)? \s* , \s* (\d+) \s* $/x) { # Recognizes ",n" as an interest leve, drops any ".FIELD" part printRecord($1, $2); } else { # Drop any ".FIELD" part s/\. \w+ $//x; printRecord($_, 0); } } ########## End of main ########### # parseDbd # Takes given dbd file and parses it to produce a hash table of record types # giving their fields, and for each field its interest level and data type. # usage: parseDbd("fileName"); # Output goes to the global %record, a hash of references to other hashes # containing the fields of each record type. Those hash values (keyed by # field name) are references to arrays containing the interest level, data # type and number base of the field. sub parseDbd { my $dbdFile = shift; open(DBD, "< $dbdFile") or die "Can't open file $dbdFile: $!\n"; my @dbd = ; close(DBD) or die "Can't close $dbdFile: $!\n"; my $i = 1; my $level = 0; my $isArecord = 0; my $isAfield = 0; my $thisRecord; my $thisField; my $thisType; my $field = {}; my $interest = 0; my $thisBase = 'DECIMAL'; while (@dbd) { $_ = shift @dbd; chomp; if ( m/recordtype \s* \( \s* (\w+) \)/x ) { die "File format error at line $i of file\n $opt_d\n" unless $level == 0; $isArecord = 1; $thisRecord = $1; } elsif ( m/field \s* \( \s* (\w+) \s* , \s* (\w+) \s* \)/x ) { die "File format error at line $i of file\n $opt_d\n" unless $level == 1 && $isArecord; $thisField = $1; $thisType = $2; $isAfield = 1; } elsif ( m/interest \s* \( \s* (\w+) \s* \)/x ) { die "File format error at line $i of file\n $opt_d\n" unless $level == 2 && $isAfield; $interest = $1; } elsif ( m/base \s* \( \s* (\w+) \s* \)/x ) { die "File format error at line $i of file\n $opt_d\n" unless $level == 2 && $isAfield; $thisBase = $1; } elsif ( m/device \s* \( (\w+) \s* ,/x ) { die "File format error at line $i of file\n $opt_d\n" unless $level == 0; $device{$1}++; } if ( m/\{/ ) { $level++; } if ( m/\}/ ) { if ($level == 2 && $isAfield) { my $params = []; $params->[$iIdx] = $interest; $params->[$tIdx] = $thisType; $params->[$bIdx] = $thisBase; $field->{$thisField} = $params; $isAfield = 0; $interest = 0; # reset default $thisBase = 'DECIMAL'; # reset default } elsif ($level == 1 && $isArecord) { $isArecord = 0; $record{$thisRecord} = $field; $field = {}; # start another hash } $level--; } $i++; } } # Given a record name, attempts to find the record and its type. # Usage: $recordType = getRecType("recordName"); sub getRecType { my $arg = shift; my $name = "$arg.RTYP"; my $fields_read = caget($name); die "Could not determine record type of $arg\n" unless $fields_read == 1; return $callback_data{$name}; } # Given the record type and field, returns the interest level, data type # and number base for the field # Usage: ($dataType, $interest, $base) = getFieldParams($recType, $field); sub getFieldParams { my ($recType, $field) = @_; my $params = $record{$recType}{$field} or die "Can't find params for $recType.$field"; exists($fieldType{$params->[$tIdx]}) || die "Field data type $field for $recType not found in dbd file --"; exists($params->[$iIdx]) || die "Interest level for $field in $recType not found in dbd file --"; my $fType = $fieldType{$params->[$tIdx]}; my $fInterest = $params->[$iIdx]; my $fBase = $params->[$bIdx]; return ($fType, $fInterest, $fBase); } # Prints field name and data for given field. Formats output so # that fields align in to 4 columns. Tries to imitate dbpf format # Usage: printField( $fieldName, $data, $dataType, $base, $firstColumnPosn) sub printField { my ($fieldName, $fieldData, $dataType, $base, $col) = @_; my $screenWidth = 80; my ($outStr, $wide); my $field = "$fieldName:"; if ( $dataType eq 'DBF_STRING' ) { $outStr = sprintf('%-5s %s', $field, $fieldData); } elsif ( $base eq 'HEX' ) { my $val = ( $dataType eq 'DBF_CHAR' ) ? ord($fieldData) : $fieldData; $outStr = sprintf('%-5s 0x%x', $field, $val); } elsif ( $dataType eq 'DBF_DOUBLE' || $dataType eq 'DBF_FLOAT' ) { $outStr = sprintf('%-5s %.8f', $field, $fieldData); } elsif ( $dataType eq 'DBF_CHAR' ) { $outStr = sprintf('%-5s %d', $field, ord($fieldData)); }else { # DBF_LONG, DBF_SHORT, DBF_UCHAR, DBF_ULONG, DBF_USHORT $outStr = sprintf('%-5s %d', $field, $fieldData); } my $len = length($outStr); if ($len <= 20) { $wide = 20; } elsif ( $len <= 40 ) { $wide = 40; } elsif ( $len <= 60 ) { $wide = 60; } else { $wide = 80;} my $pad = $wide - $len; $col += $wide; if ($col > $screenWidth ) { print("\n"); $col = $wide; } print $outStr, ' ' x $pad; return $col; } # Query for a list of fields simultaneously. # The results are filled in the the %callback_data global hash # and the result of the operation is the number of read pvs # # NOTE: Not re-entrant because results are written to global hash # %callback_data # # Usage: $fields_read = caget( @pvlist ) sub caget { my @chans = map { CA->new($_); } @_; #clear results; %callback_data = (); %timed_out = (); eval { CA->pend_io($opt_w); }; if ($@) { if ($@ =~ m/^ECA_TIMEOUT/) { my $err = (@chans > 1) ? 'some PV(s)' : '"' . $chans[0]->name . '"'; print "Channel connect timed out: $err not found.\n"; foreach my $chan (@chans) { $timed_out{$chan->name} = $chan->is_connected; } @chans = grep { $_->is_connected } @chans; } else { die $@; } } map { my $type; $type = $_->field_type; $_->get_callback(\&caget_callback, $type); } @chans; my $fields_read = @chans; $callback_incomplete = @chans; CA->pend_event(0.1) while $callback_incomplete; return $fields_read; } sub caget_callback { my ($chan, $status, $data) = @_; die $status if $status; $callback_data{$chan->name} = $data; $callback_incomplete--; } # Given record name and interest level prints data from record fields # that are at or below the interest level specified. # Usage: printRecord($recordName, $interestLevel) sub printRecord { my ($name, $interest) = @_; my $recType = getRecType($name); print("Record $name type $recType\n"); die "Record type $recType not found\n" unless exists $record{$recType}; #capture list of fields my @readlist = (); #fields to read via CA my @fields_pr = (); #fields for print-out my @ftypes = (); #types, from parser my @bases = (); #bases, from parser foreach my $field (sort keys %{$record{$recType}}) { # Skip DTYP field if this rec type doesn't have device support defined if ($field eq 'DTYP' && !(exists($device{$recType}))) { next; } my ($fType, $fInterest, $base) = getFieldParams($recType, $field); unless( $fType eq 'DBF_NOACCESS' ) { if ($interest >= $fInterest ) { my $fToGet = "$name.$field"; push @fields_pr, $field; push @readlist, $fToGet; push @ftypes, $fType; push @bases, $base; } } } my $fields_read = caget( @readlist ); # print while iterating over lists gathered my $col = 0; for (my $i=0; $i < scalar @readlist; $i++) { my $field = $fields_pr[$i]; my $fToGet = $readlist[$i]; my ($fType, $data, $base); if ($timed_out{$fToGet}) { $fType = $fieldType{DBF_STRING}; $data = ''; } else { $fType = $ftypes[$i]; $base = $bases[$i]; $data = $callback_data{$fToGet}; } $col = printField($field, $data, $fType, $base, $col); } print("\n"); # Final newline } # Prints list of record types found in dbd file. If level > 0 # then the fields of that record type, their interest levels and types are # also printed. # Diagnostic routine, usage: void printList(level); sub printList { my $level = shift; foreach my $rkey (sort keys(%record)) { print(" $rkey\n"); if ($level > 0) { foreach my $fkey (keys %{$record{$rkey}}) { print("\tField $fkey - interest $record{$rkey}{$fkey}[$iIdx] "); print("- type $record{$rkey}{$fkey}[$tIdx] "); print("- base $record{$rkey}{$fkey}[$bIdx]\n"); } } } } # Prints list of fields with interest levels for given record type # Diagnostic routine, usage: void printRecordList("recordType"); sub printRecordList { my $type = shift; if (exists($record{$type}) ) { print("Record type - $type\n"); foreach my $fkey (sort keys %{$record{$type}}) { printf('%-4s', $fkey); printf(" interest = $record{$type}{$fkey}[$iIdx]"); printf(" type = %-12s ",$record{$type}{$fkey}[$tIdx]); print (" base = $record{$type}{$fkey}[$bIdx]\n"); } } else { print("Record type $type not defined in dbd file $opt_d\n"); } } sub HELP_MESSAGE { print STDERR "\n", "Usage: capr.pl -h\n", " capr.pl [options] -r\n", " capr.pl [options] -f \n", " capr.pl [options] []\n", "\n", " -h Print this help message.\n", "Channel Access options:\n", " -w : Wait time, specifies CA timeout, default is $opt_w second\n", "Database Definitions:\n", " -d : The file containing record type definitions.\n", " This can be set using the EPICS_CAPR_DBD_FILE environment variable.\n", " Default: ", AbsPath($opt_d), "\n", "Output Options:\n", " -r Lists all record types in the selected dbd file.\n", " -f : Lists all fields with their interest level, data type\n", " and number base for the given record_type.\n", "\n", "Base version: ", CA->version, "\n"; exit 1; } base-7.0.3.1/modules/ca/src/perl/perlConfig.pl0000664000577000060420000000043013557101274017621 0ustar anjaesctl#!/usr/bin/env perl # This script is used to extract information about the Perl build # configuration, so the EPICS build system uses the same settings. use strict; use Config; my $arg = shift; my $val = $Config{$arg}; $val =~ s{\\}{/}go if $^O eq 'MSWin32'; print $val; base-7.0.3.1/modules/ca/src/template/Makefile0000664000577000060420000000064113557101274017511 0ustar anjaesctlTOP=../../../.. include $(TOP)/configure/CONFIG TEMPLATES_DIR = makeBaseApp TEMPLATES += top/caClientApp/Makefile TEMPLATES += top/caClientApp/caExample.c TEMPLATES += top/caClientApp/caMonitor.c TEMPLATES += top/caPerlApp/Makefile TEMPLATES += top/caPerlApp/cainfo.pl TEMPLATES += top/caPerlApp/caget.pl TEMPLATES += top/caPerlApp/caput.pl TEMPLATES += top/caPerlApp/camonitor.pl include $(TOP)/configure/RULES base-7.0.3.1/modules/ca/src/template/top/caClientApp/Makefile0000664000577000060420000000070213557101274022474 0ustar anjaesctlTOP=.. include $(TOP)/configure/CONFIG #---------------------------------------- # ADD MACRO DEFINITIONS AFTER THIS LINE #============================= PROD_HOST += caExample caExample_SRCS += caExample.c caExample_LIBS += $(EPICS_BASE_HOST_LIBS) PROD_HOST += caMonitor caMonitor_SRCS += caMonitor.c caMonitor_LIBS += $(EPICS_BASE_HOST_LIBS) include $(TOP)/configure/RULES #---------------------------------------- # ADD RULES AFTER THIS LINE base-7.0.3.1/modules/ca/src/template/top/caClientApp/caExample.c0000664000577000060420000000122513557101274023100 0ustar anjaesctl/*caExample.c*/ #include #include #include #include #include "cadef.h" int main(int argc,char **argv) { double data; chid mychid; if(argc != 2) { fprintf(stderr,"usage: caExample pvname\n"); exit(1); } SEVCHK(ca_context_create(ca_disable_preemptive_callback),"ca_context_create"); SEVCHK(ca_create_channel(argv[1],NULL,NULL,10,&mychid),"ca_create_channel failure"); SEVCHK(ca_pend_io(5.0),"ca_pend_io failure"); SEVCHK(ca_get(DBR_DOUBLE,mychid,(void *)&data),"ca_get failure"); SEVCHK(ca_pend_io(5.0),"ca_pend_io failure"); printf("%s %f\n",argv[1],data); return(0); } base-7.0.3.1/modules/ca/src/template/top/caClientApp/caMonitor.c0000664000577000060420000000637513557101274023147 0ustar anjaesctl/*caMonitor.c*/ /* This example accepts the name of a file containing a list of pvs to monitor. * It prints a message for all ca events: connection, access rights and monitor. */ #include #include #include #include #include "cadef.h" #include "dbDefs.h" #include "epicsString.h" #include "cantProceed.h" #define MAX_PV 1000 #define MAX_PV_NAME_LEN 40 typedef struct{ char value[20]; chid mychid; evid myevid; } MYNODE; static void printChidInfo(chid chid, char *message) { printf("\n%s\n",message); printf("pv: %s type(%d) nelements(%ld) host(%s)", ca_name(chid),ca_field_type(chid),ca_element_count(chid), ca_host_name(chid)); printf(" read(%d) write(%d) state(%d)\n", ca_read_access(chid),ca_write_access(chid),ca_state(chid)); } static void exceptionCallback(struct exception_handler_args args) { chid chid = args.chid; long stat = args.stat; /* Channel access status code*/ const char *channel; static char *noname = "unknown"; channel = (chid ? ca_name(chid) : noname); if(chid) printChidInfo(chid,"exceptionCallback"); printf("exceptionCallback stat %s channel %s\n", ca_message(stat),channel); } static void connectionCallback(struct connection_handler_args args) { chid chid = args.chid; printChidInfo(chid,"connectionCallback"); } static void accessRightsCallback(struct access_rights_handler_args args) { chid chid = args.chid; printChidInfo(chid,"accessRightsCallback"); } static void eventCallback(struct event_handler_args eha) { chid chid = eha.chid; if(eha.status!=ECA_NORMAL) { printChidInfo(chid,"eventCallback"); } else { char *pdata = (char *)eha.dbr; printf("Event Callback: %s = %s\n",ca_name(eha.chid),pdata); } } int main(int argc,char **argv) { char *filename; int npv = 0; MYNODE *pmynode[MAX_PV]; char *pname[MAX_PV]; int i; char tempStr[MAX_PV_NAME_LEN]; char *pstr; FILE *fp; if (argc != 2) { fprintf(stderr,"usage: caMonitor filename\n"); exit(1); } filename = argv[1]; fp = fopen(filename,"r"); if (!fp) { perror("fopen failed"); return(1); } while (npv < MAX_PV) { size_t len; pstr = fgets(tempStr, MAX_PV_NAME_LEN, fp); if (!pstr) break; len = strlen(pstr); if (len <= 1) continue; pstr[len - 1] = '\0'; /* Strip newline */ pname[npv] = epicsStrDup(pstr); pmynode[npv] = callocMustSucceed(1, sizeof(MYNODE), "caMonitor"); npv++; } fclose(fp); SEVCHK(ca_context_create(ca_disable_preemptive_callback),"ca_context_create"); SEVCHK(ca_add_exception_event(exceptionCallback,NULL), "ca_add_exception_event"); for (i=0; imychid), "ca_create_channel"); SEVCHK(ca_replace_access_rights_event(pmynode[i]->mychid, accessRightsCallback), "ca_replace_access_rights_event"); SEVCHK(ca_create_subscription(DBR_STRING,1,pmynode[i]->mychid, DBE_VALUE,eventCallback,pmynode[i],&pmynode[i]->myevid), "ca_create_subscription"); } /*Should never return from following call*/ SEVCHK(ca_pend_event(0.0),"ca_pend_event"); return 0; } base-7.0.3.1/modules/ca/src/template/top/caPerlApp/Makefile0000664000577000060420000000056513557101274022167 0ustar anjaesctlTOP=.. include $(TOP)/configure/CONFIG #---------------------------------------- # ADD MACRO DEFINITIONS AFTER THIS LINE PERL_SCRIPTS += _APPNAME_ModuleDirs.pm PERL_SCRIPTS += cainfo.pl PERL_SCRIPTS += caput.pl PERL_SCRIPTS += caget.pl PERL_SCRIPTS += camonitor.pl include $(TOP)/configure/RULES #---------------------------------------- # ADD RULES AFTER THIS LINE base-7.0.3.1/modules/ca/src/template/top/caPerlApp/caget.pl0000664000577000060420000001576413557101274022156 0ustar anjaesctl#!/usr/bin/env perl use strict; # This construct sets @INC to search lib/perl of all RELEASE entries use FindBin qw($Bin); use lib ($Bin, "$Bin/../../lib/perl"); use _APPNAME_ModuleDirs; no lib $Bin; use Getopt::Std; use Scalar::Util qw(looks_like_number); use CA; our ($opt_0, $opt_a, $opt_d, $opt_e, $opt_f, $opt_g, $opt_h, $opt_n); our ($opt_s, $opt_S, $opt_t); our $opt_c = 0; our $opt_F = ' '; our $opt_w = 1; $Getopt::Std::OUTPUT_HELP_VERSION = 1; HELP_MESSAGE() unless getopts('0:ac:d:e:f:F:g:hnsStw:'); HELP_MESSAGE() if $opt_h; die "caget.pl: -c option takes a positive number\n" unless looks_like_number($opt_c) && $opt_c >= 0; die "No pv name specified. ('caget.pl -h' gives help.)\n" unless @ARGV; my @chans = map { CA->new($_); } grep { $_ ne '' } @ARGV; die "caget.pl: Please provide at least one non-empty pv name\n" unless @chans; eval { CA->pend_io($opt_w); }; if ($@) { if ($@ =~ m/^ECA_TIMEOUT/) { my $err = (@chans > 1) ? 'some PV(s)' : "'" . $chans[0]->name . "'"; print "Channel connect timed out: $err not found.\n"; @chans = grep { $_->is_connected } @chans; } else { die $@; } } my %rtype; map { my $type; if ($opt_d) { $type = $opt_d; } else { $type = $_->field_type; $type = 'DBR_STRING' if $opt_s && $type =~ m/ ^ DBR_ ( DOUBLE | FLOAT ) $ /x; $type = 'DBR_LONG' if ($opt_n && $type eq 'DBR_ENUM') || (!$opt_S && $type eq 'DBR_CHAR'); $type =~ s/^DBR_/DBR_TIME_/ if $opt_a; } $rtype{$_} = $type; $_->get_callback(\&get_callback, $type, 0+$opt_c); } @chans; my $incomplete = @chans; CA->pend_event(0.1) while $incomplete; sub get_callback { my ($chan, $status, $data) = @_; die $status if $status; display($chan, $rtype{$chan}, $data); $incomplete--; } sub format_number { my ($data, $type) = @_; if ($type =~ m/_DOUBLE$/) { return sprintf "%.${opt_e}e", $data if $opt_e; return sprintf "%.${opt_f}f", $data if $opt_f; return sprintf "%.${opt_g}g", $data if $opt_g; } if ($type =~ m/_LONG$/) { return sprintf "%lx", $data if $opt_0 eq 'x'; return sprintf "%lo", $data if $opt_0 eq 'o'; if ($opt_0 eq 'b') { my $bin = unpack "B*", pack "l", $data; $bin =~ s/^0*//; return $bin; } } return $data; } sub display { my ($chan, $type, $data) = @_; if (ref $data eq 'ARRAY') { display($chan, $type, join($opt_F, scalar @{$data}, @{$data})); } elsif (ref $data eq 'HASH') { printf "%s\n", $chan->name; my $dtype = $data->{TYPE}; # Can differ from request printf " Native data type: %s\n", $chan->field_type; printf " Request type: %s\n", $dtype; printf " Element count: %d\n", $data->{COUNT} if exists $data->{COUNT}; my $val = $data->{value}; if (ref $val eq 'ARRAY') { printf " Value: %s\n", join($opt_F, map { format_number($_, $dtype); } @{$val}); } else { printf " Value: %s\n", format_number($val, $dtype); } if (exists $data->{stamp}) { my @t = localtime $data->{stamp}; splice @t, 6; $t[5] += 1900; $t[0] += $data->{stamp_fraction}; printf " Timestamp: %4d-%02d-%02d %02d:%02d:%09.6f\n", reverse @t; } printf " Status: %s\n", $data->{status}; printf " Severity: %s\n", $data->{severity}; if (exists $data->{units}) { printf " Units: %s\n", $data->{units}; } if (exists $data->{precision}) { printf " Precision: %d\n", $data->{precision}; } if (exists $data->{upper_disp_limit}) { printf " Lo disp limit: %g\n", $data->{lower_disp_limit}; printf " Hi disp limit: %g\n", $data->{upper_disp_limit}; } if (exists $data->{upper_alarm_limit}) { printf " Lo alarm limit: %g\n", $data->{lower_alarm_limit}; printf " Lo warn limit: %g\n", $data->{lower_warning_limit}; printf " Hi warn limit: %g\n", $data->{upper_warning_limit}; printf " Hi alarm limit: %g\n", $data->{upper_alarm_limit}; } if (exists $data->{upper_ctrl_limit}) { printf " Lo ctrl limit: %g\n", $data->{lower_ctrl_limit}; printf " Hi ctrl limit: %g\n", $data->{upper_ctrl_limit}; } if (exists $data->{ackt}) { printf " Ack transients: %s\n", $data->{ackt} ? 'YES' : 'NO'; printf " Ack severity: %s\n", $data->{acks}; } } else { my $value = format_number($data, $type); if ($opt_t) { print "$value\n"; } else { printf "%s%s%s\n", $chan->name, $opt_F, $value; } } } sub HELP_MESSAGE { print STDERR "\nUsage: caget.pl [options] ...\n", "\n", " -h: Help: Print this message\n", "Channel Access options:\n", " -w : Wait time, specifies CA timeout, default is $opt_w second\n", "Format options:\n", " -t: Terse mode - print only value, without name\n", " -a: Wide mode \"name timestamp value stat sevr\" (read PVs as DBR_TIME_xxx)\n", " -d : Request specific dbr type from one of the following:\n", " DBR_STRING DBR_LONG DBR_DOUBLE\n", " DBR_STS_STRING DBR_STS_LONG DBR_STS_DOUBLE\n", " DBR_TIME_STRING DBR_TIME_LONG DBR_TIME_DOUBLE\n", " DBR_GR_STRING DBR_GR_LONG DBR_GR_DOUBLE DBR_GR_ENUM\n", " DBR_CTRL_STRING DBR_CTRL_LONG DBR_CTRL_DOUBLE DBR_CTRL_ENUM\n", " DBR_CLASS_NAME DBR_STSACK_STRING\n", "Arrays: Value format: print number of values, then list of values\n", " Default: Print all values\n", " -c : Print first elements of an array\n", " -S: Print array of char as a string (long string)\n", "Enum format:\n", " -n: Print DBF_ENUM value as number (default is enum string)\n", "Floating point type format:\n", " Default: Use %g format\n", " -e : Use %e format, with a precision of digits\n", " -f : Use %f format, with a precision of digits\n", " -g : Use %g format, with a precision of digits\n", " -s: Get value as string (may honour server-side precision)\n", "Integer number format:\n", " Default: Print as decimal number\n", " -0x: Print as hex number\n", " -0o: Print as octal number\n", " -0b: Print as binary number\n", "Set output field separator:\n", " -F : Use to separate fields on output\n", "\n", "Base version: ", CA->version, "\n"; exit 1; } base-7.0.3.1/modules/ca/src/template/top/caPerlApp/cainfo.pl0000664000577000060420000000327513557101274022324 0ustar anjaesctl#!/usr/bin/env perl use strict; # This construct sets @INC to search lib/perl of all RELEASE entries use FindBin qw($Bin); use lib ($Bin, "$Bin/../../lib/perl"); use _APPNAME_ModuleDirs; no lib $Bin; use Getopt::Std; use CA; our $opt_w = 1; our $opt_h; $Getopt::Std::OUTPUT_HELP_VERSION = 1; HELP_MESSAGE() unless getopts('hw:'); HELP_MESSAGE() if $opt_h; die "No pv name specified. ('cainfo.pl -h' gives help.)\n" unless @ARGV; my @chans = map { CA->new($_); } grep { $_ ne '' } @ARGV; die "cainfo.pl: Please provide at least one non-empty pv name\n" unless @chans; eval { CA->pend_io($opt_w); }; if ($@) { if ($@ =~ m/^ECA_TIMEOUT/) { my $err = (@chans > 1) ? 'some PV(s)' : "'" . $chans[0]->name . "'"; print "Channel connect timed out: $err not found.\n"; } else { die $@; } } map { display($_); } @chans; sub display { my $chan = shift; printf "%s\n", $chan->name; printf " State: %s\n", $chan->state; printf " Host: %s\n", $chan->host_name; my @access = ('no ', ''); printf " Access: %sread, %swrite\n", $access[$chan->read_access], $access[$chan->write_access]; printf " Data type: %s\n", $chan->field_type; printf " Element count: %d\n", $chan->element_count; } sub HELP_MESSAGE { print STDERR "\nUsage: cainfo.pl [options] ...\n", "\n", " -h: Help: Print this message\n", "Channel Access options:\n", " -w : Wait time, specifies CA timeout, default is $opt_w second\n", "\n", "Example: cainfo my_channel another_channel\n", "\n", "Base version: ", CA->version, "\n"; exit 1; } base-7.0.3.1/modules/ca/src/template/top/caPerlApp/camonitor.pl0000664000577000060420000001211313557101274023047 0ustar anjaesctl#!/usr/bin/env perl use strict; # This construct sets @INC to search lib/perl of all RELEASE entries use FindBin qw($Bin); use lib ($Bin, "$Bin/../../lib/perl"); use _APPNAME_ModuleDirs; no lib $Bin; use Getopt::Std; use Scalar::Util qw(looks_like_number); use CA; our ($opt_0, $opt_e, $opt_f, $opt_g, $opt_h, $opt_n, $opt_s, $opt_S); our $opt_c = 0; our $opt_F = ' '; our $opt_w = 1; our $opt_m = 'va'; $Getopt::Std::OUTPUT_HELP_VERSION = 1; HELP_MESSAGE() unless getopts('0:c:e:f:F:g:hm:nsSw:'); HELP_MESSAGE() if $opt_h; die "camonitor.pl: -c option takes a positive number\n" unless looks_like_number($opt_c) && $opt_c >= 0; die "No pv name specified. ('camonitor.pl -h' gives help.)\n" unless @ARGV; my %monitors; my @chans = map { CA->new($_, \&conn_callback); } grep { $_ ne '' } @ARGV; die "camonitor.pl: Please provide at least one non-empty pv name\n" unless @chans; my $fmt = ($opt_F eq ' ') ? "%-30s %s\n" : "%s$opt_F%s\n"; CA->pend_event($opt_w); map { printf $fmt, $_->name, '*** Not connected (PV not found)' unless $monitors{$_}; } @chans; CA->pend_event(0); sub conn_callback { my ($chan, $up) = @_; if ($up && ! $monitors{$chan}) { my $type = $chan->field_type; $type = 'DBR_STRING' if $opt_s && $type =~ m/ ^ DBR_ ( DOUBLE | FLOAT ) $ /x; $type = 'DBR_LONG' if ($opt_n && $type eq 'DBR_ENUM') || (!$opt_S && $type eq 'DBR_CHAR'); $type =~ s/^DBR_/DBR_TIME_/; $monitors{$chan} = $chan->create_subscription($opt_m, \&mon_callback, $type, 0+$opt_c); } } sub mon_callback { my ($chan, $status, $data) = @_; if ($status) { printf $fmt, $chan->name, $status; } else { display($chan, $data); } } sub format_number { my ($data, $type) = @_; if ($type =~ m/_DOUBLE$/) { return sprintf "%.${opt_e}e", $data if $opt_e; return sprintf "%.${opt_f}f", $data if $opt_f; return sprintf "%.${opt_g}g", $data if $opt_g; } if ($type =~ m/_LONG$/) { return sprintf "%lx", $data if $opt_0 eq 'x'; return sprintf "%lo", $data if $opt_0 eq 'o'; if ($opt_0 eq 'b') { my $bin = unpack "B*", pack "l", $data; $bin =~ s/^0*//; return $bin; } } return $data; } sub display { my ($chan, $data) = @_; die "Internal error" unless ref $data eq 'HASH'; my $type = $data->{TYPE}; my $value = $data->{value}; if (ref $value eq 'ARRAY') { $value = join($opt_F, $data->{COUNT}, map { format_number($_, $type); } @{$value}); } else { $value = format_number($value, $type); } my $stamp; if (exists $data->{stamp}) { my @t = localtime $data->{stamp}; splice @t, 6; $t[5] += 1900; $t[0] += $data->{stamp_fraction}; $stamp = sprintf "%4d-%02d-%02d %02d:%02d:%09.6f", reverse @t; } printf $fmt, $chan->name, join($opt_F, $stamp, $value, $data->{status}, $data->{severity}); } sub HELP_MESSAGE { print STDERR "\nUsage: camonitor.pl [options] ...\n", "\n", " -h: Help: Print this message\n", "Channel Access options:\n", " -w : Wait time, specifies CA timeout, default is $opt_w second\n", " -m : Specify CA event mask to use, with being any combination of\n", " 'v' (value), 'a' (alarm), 'l' (log/archive), 'p' (property)", " Default: '$opt_m'\n", # "Timestamps:\n", # " Default: Print absolute timestamps (as reported by CA)\n", # " -r: Relative timestamps (time elapsed since start of program)\n", # " -i: Incremental timestamps (time elapsed since last update)\n", # " -I: Incremental timestamps (time elapsed since last update for this channel)\n", "Enum format:\n", " -n: Print DBF_ENUM values as number (default are enum string values)\n", "Arrays: Value format: print number of values, then list of values\n", " Default: Print all values\n", " -c : Print first elements of an array\n", " -S: Print array of char as a string (long string)\n", "Floating point type format:\n", " Default: Use %g format\n", " -e : Use %e format, with a precision of digits\n", " -f : Use %f format, with a precision of digits\n", " -g : Use %g format, with a precision of digits\n", " -s: Get value as string (may honour server-side precision)\n", "Integer number format:\n", " Default: Print as decimal number\n", " -0x: Print as hex number\n", " -0o: Print as octal number\n", " -0b: Print as binary number\n", "Alternate output field separator:\n", " -F : Use to separate fields on output\n", "\n", "Example: camonitor -f8 my_channel another_channel\n", " (doubles are printed as %f with 8 decimal digits)\n", "\n", "Base version: ", CA->version, "\n"; exit 1; } base-7.0.3.1/modules/ca/src/template/top/caPerlApp/caput.pl0000664000577000060420000001261613557101274022200 0ustar anjaesctl#!/usr/bin/env perl use strict; # This construct sets @INC to search lib/perl of all RELEASE entries use FindBin qw($Bin); use lib ($Bin, "$Bin/../../lib/perl"); use _APPNAME_ModuleDirs; no lib $Bin; use Getopt::Std; use CA; our ($opt_0, $opt_c, $opt_e, $opt_f, $opt_g, $opt_h, $opt_l, $opt_n, $opt_s, $opt_S, $opt_t); our $opt_w = 1; $Getopt::Std::OUTPUT_HELP_VERSION = 1; HELP_MESSAGE() unless getopts('achlnsStw:'); HELP_MESSAGE() if $opt_h; die "No pv name specified. ('caput.pl -h' gives help.)\n" unless @ARGV; my $pv = shift; die "caput.pl: Empty pv name given.\n" unless $pv ne ''; die "No value specified. ('caput.pl -h' gives help.)\n" unless @ARGV; my $chan = CA->new($pv); eval { CA->pend_io($opt_w); }; if ($@) { if ($@ =~ m/^ECA_TIMEOUT/) { print "Channel connect timed out: '$pv' not found.\n"; exit 2; } else { die $@; } } die "Write access denied for '$pv'.\n" unless $chan->write_access; my $n = $chan->element_count(); die "Too many values given, '$pv' limit is $n\n" unless $n >= @ARGV; my $type = $chan->field_type; $type = 'DBR_STRING' if $opt_s && $type =~ m/ ^ DBR_ (ENUM | FLOAT | DOUBLE) $ /x; $type = 'DBR_LONG' if ($opt_n && $type eq 'DBR_ENUM') || (!$opt_S && $type eq 'DBR_CHAR'); $type =~ s/^DBR_/DBR_TIME_/ if $opt_l; my @values; if ($type !~ m/ ^ DBR_ (STRING | ENUM | CHAR) $ /x) { # Make @ARGV strings numeric @values = map { +$_; } @ARGV; } else { # Use strings @values = @ARGV; } my $done = 0; if ($opt_t) { do_put(); } else { $chan->get_callback(\&old_callback, $type); } CA->pend_event(0.1) until $done; sub old_callback { my ($chan, $status, $data) = @_; die $status if $status; display($chan, $type, $data, 'Old'); do_put(); } sub do_put { if ($opt_c) { $chan->put_callback(\&put_callback, @values); } else { $chan->put(@values); $chan->get_callback(\&new_callback, $type); } } sub put_callback { my ($chan, $status) = @_; die $status if $status; $chan->get_callback(\&new_callback, $type); } sub new_callback { my ($chan, $status, $data) = @_; die $status if $status; display($chan, $type, $data, 'New'); $done = 1; } sub format_number { my ($data, $type) = @_; if ($type =~ m/_DOUBLE$/) { return sprintf "%.${opt_e}e", $data if $opt_e; return sprintf "%.${opt_f}f", $data if $opt_f; return sprintf "%.${opt_g}g", $data if $opt_g; } if ($type =~ m/_LONG$/) { return sprintf "%lx", $data if $opt_0 eq 'x'; return sprintf "%lo", $data if $opt_0 eq 'o'; if ($opt_0 eq 'b') { my $bin = unpack "B*", pack "l", $data; $bin =~ s/^0*//; return $bin; } } return $data; } sub display { my ($chan, $type, $data, $prefix) = @_; if (ref $data eq 'ARRAY') { display($chan, $type, join(' ', @{$data}), $prefix); } elsif (ref $data eq 'HASH') { $type = $data->{TYPE}; # Can differ from request my $value = $data->{value}; if (ref $value eq 'ARRAY') { $value = join(' ', map { format_number($_, $type); } @{$value}); } else { $value = format_number($value, $type); } my $stamp; if (exists $data->{stamp}) { my @t = localtime $data->{stamp}; splice @t, 6; $t[5] += 1900; $t[0] += $data->{stamp_fraction}; $stamp = sprintf "%4d-%02d-%02d %02d:%02d:%09.6f", reverse @t; } printf "%-30s %s %s %s %s\n", $chan->name, $stamp, $value, $data->{status}, $data->{severity}; } else { my $value = format_number($data, $type); if ($opt_t) { print "$value\n"; } else { printf "$prefix : %-30s %s\n", $chan->name, $value; } } } sub HELP_MESSAGE { print STDERR "\nUsage: caput.pl [options] ...\n", "\n", " -h: Help: Print this message\n", "Channel Access options:\n", " -w : Wait time, specifies CA timeout, default is $opt_w second\n", " -c: Use put_callback to wait for completion\n", "Format options:\n", " -t: Terse mode - print only sucessfully written value, without name\n", " -l: Long mode \"name timestamp value stat sevr\" (read PVs as DBR_TIME_xxx)\n", " -S: Put string as an array of char (long string)\n", "Enum format:\n", " Default: Auto - try value as ENUM string, then as index number\n", " -n: Force interpretation of values as numbers\n", " -s: Force interpretation of values as strings\n", "Floating point type format:\n", " Default: Use %g format\n", " -e : Use %e format, with a precision of digits\n", " -f : Use %f format, with a precision of digits\n", " -g : Use %g format, with a precision of digits\n", " -s: Get value as string (may honour server-side precision)\n", "Integer number format:\n", " Default: Print as decimal number\n", " -0x: Print as hex number\n", " -0o: Print as octal number\n", " -0b: Print as binary number\n", "\n", "Examples:\n", " caput my_channel 1.2\n", " caput my_waveform 1.2 2.4 3.6 4.8 6.0\n", "\n", "Base version: ", CA->version, "\n"; exit 1; } base-7.0.3.1/modules/ca/src/tools/Makefile0000664000577000060420000000162713557101274017043 0ustar anjaesctl#************************************************************************* # Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer # Synchrotronstrahlung. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* TOP = ../../../.. include $(TOP)/configure/CONFIG PROD_DEFAULT += caget camonitor cainfo caput PROD_vxWorks = -nil- PROD_RTEMS = -nil- PROD_iOS = -nil- PROD_SRCS = tool_lib.c caget_SRCS = caget.c caput_SRCS = caput.c camonitor_SRCS = camonitor.c cainfo_SRCS = cainfo.c PROD_LIBS = ca Com include $(TOP)/configure/RULES base-7.0.3.1/modules/ca/src/tools/caget.c0000664000577000060420000005363713557101274016642 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. * Copyright (c) 2006 Diamond Light Source Ltd. * 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) 2002 Berliner Elektronenspeicherringgesellschaft fuer * Synchrotronstrahlung. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange (BESSY) * * Modification History * 2006/01/17 Malcolm Walters (Tessella/Diamond Light Source) * Fixed problem with "-c -w 0" hanging forever * 2008/04/16 Ralph Lange (BESSY) * Updated usage info * 2009/03/31 Larry Hoff (BNL) * Added field separators * 2009/04/01 Ralph Lange (HZB/BESSY) * Added support for long strings (array of char) and quoting of nonprintable characters * */ #include #include #include #include #include #include #include #include "epicsVersion.h" #include "tool_lib.h" #define VALID_DOUBLE_DIGITS 18 /* Max usable precision for a double */ #define PEND_EVENT_SLICES 5 /* No. of pend_event slices for callback requests */ /* Different output formats */ typedef enum { plain, terse, all, specifiedDbr } OutputT; /* Different request types */ typedef enum { get, callback } RequestT; static int nConn = 0; /* Number of connected PVs */ static int nRead = 0; /* Number of channels that were read */ static int floatAsString = 0; /* Flag: fetch floats as string */ static void usage (void) { fprintf (stderr, "\nUsage: caget [options] ...\n\n" " -h: Help: Print this message\n" " -V: Version: Show EPICS and CA versions\n" "Channel Access options:\n" " -w : Wait time, specifies CA timeout, default is %f second(s)\n" " -c: Asynchronous get (use ca_get_callback and wait for completion)\n" " -p : CA priority (0-%u, default 0=lowest)\n" "Format options:\n" " Default output format is \"name value\"\n" " -t: Terse mode - print only value, without name\n" " -a: Wide mode \"name timestamp value stat sevr\" (read PVs as DBR_TIME_xxx)\n" " -d : Request specific dbr type; use string (DBR_ prefix may be omitted)\n" " or number of one of the following types:\n" " DBR_STRING 0 DBR_STS_FLOAT 9 DBR_TIME_LONG 19 DBR_CTRL_SHORT 29\n" " DBR_INT 1 DBR_STS_ENUM 10 DBR_TIME_DOUBLE 20 DBR_CTRL_INT 29\n" " DBR_SHORT 1 DBR_STS_CHAR 11 DBR_GR_STRING 21 DBR_CTRL_FLOAT 30\n" " DBR_FLOAT 2 DBR_STS_LONG 12 DBR_GR_SHORT 22 DBR_CTRL_ENUM 31\n" " DBR_ENUM 3 DBR_STS_DOUBLE 13 DBR_GR_INT 22 DBR_CTRL_CHAR 32\n" " DBR_CHAR 4 DBR_TIME_STRING 14 DBR_GR_FLOAT 23 DBR_CTRL_LONG 33\n" " DBR_LONG 5 DBR_TIME_INT 15 DBR_GR_ENUM 24 DBR_CTRL_DOUBLE 34\n" " DBR_DOUBLE 6 DBR_TIME_SHORT 15 DBR_GR_CHAR 25 DBR_STSACK_STRING 37\n" " DBR_STS_STRING 7 DBR_TIME_FLOAT 16 DBR_GR_LONG 26 DBR_CLASS_NAME 38\n" " DBR_STS_SHORT 8 DBR_TIME_ENUM 17 DBR_GR_DOUBLE 27\n" " DBR_STS_INT 8 DBR_TIME_CHAR 18 DBR_CTRL_STRING 28\n" "Enum format:\n" " -n: Print DBF_ENUM value as number (default is enum string)\n" "Arrays: Value format: print number of requested values, then list of values\n" " Default: Print all values\n" " -# : Print first elements of an array\n" " -S: Print array of char as a string (long string)\n" "Floating point type format:\n" " Default: Use %%g format\n" " -e : Use %%e format, with a precision of digits\n" " -f : Use %%f format, with a precision of digits\n" " -g : Use %%g format, with a precision of digits\n" " -s: Get value as string (honors server-side precision)\n" " -lx: Round to long integer and print as hex number\n" " -lo: Round to long integer and print as octal number\n" " -lb: Round to long integer and print as binary number\n" "Integer number format:\n" " Default: Print as decimal number\n" " -0x: Print as hex number\n" " -0o: Print as octal number\n" " -0b: Print as binary number\n" "Alternate output field separator:\n" " -F : Use as an alternate output field separator\n" "\nExample: caget -a -f8 my_channel another_channel\n" " (uses wide output format, doubles are printed as %%f with precision of 8)\n\n" , DEFAULT_TIMEOUT, CA_PRIORITY_MAX); } /*+************************************************************************** * * Function: event_handler * * Description: CA event_handler for request type callback * Allocates the dbr structure and copies the data * * Arg(s) In: args - event handler args (see CA manual) * **************************************************************************-*/ static void event_handler (evargs args) { pv* ppv = args.usr; ppv->status = args.status; if (args.status == ECA_NORMAL) { ppv->dbrType = args.type; ppv->value = calloc(1, dbr_size_n(args.type, args.count)); memcpy(ppv->value, args.dbr, dbr_size_n(args.type, args.count)); ppv->nElems = args.count; nRead++; } } /*+************************************************************************** * * Function: caget * * Description: Issue read requests, wait for incoming data * and print the data according to the selected format * * Arg(s) In: pvs - Pointer to an array of pv structures * nPvs - Number of elements in the pvs array * request - Request type * format - Output format * dbrType - Requested dbr type * reqElems - Requested number of (array) elements * * Return(s): Error code: 0 = OK, 1 = Error * **************************************************************************-*/ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format, chtype dbrType, unsigned long reqElems) { unsigned int i; int n, result; for (n = 0; n < nPvs; n++) { unsigned long nElems; /* Set up pvs structure */ /* -------------------- */ /* Get natural type and array count */ nElems = ca_element_count(pvs[n].chid); pvs[n].dbfType = ca_field_type(pvs[n].chid); pvs[n].dbrType = dbrType; /* Set up value structures */ if (format != specifiedDbr) { pvs[n].dbrType = dbf_type_to_DBR_TIME(pvs[n].dbfType); /* Use native type */ if (dbr_type_is_ENUM(pvs[n].dbrType)) /* Enums honour -n option */ { if (enumAsNr) pvs[n].dbrType = DBR_TIME_INT; else pvs[n].dbrType = DBR_TIME_STRING; } else if (floatAsString && (dbr_type_is_FLOAT(pvs[n].dbrType) || dbr_type_is_DOUBLE(pvs[n].dbrType))) { pvs[n].dbrType = DBR_TIME_STRING; } } /* Issue CA request */ /* ---------------- */ if (ca_state(pvs[n].chid) == cs_conn) { nConn++; pvs[n].onceConnected = 1; if (request == callback) { /* Event handler will allocate value and set nElems */ pvs[n].reqElems = reqElems > nElems ? nElems : reqElems; result = ca_array_get_callback(pvs[n].dbrType, pvs[n].reqElems, pvs[n].chid, event_handler, (void*)&pvs[n]); } else { /* We allocate value structure and set nElems */ pvs[n].nElems = reqElems && reqElems < nElems ? reqElems : nElems; pvs[n].value = calloc(1, dbr_size_n(pvs[n].dbrType, pvs[n].nElems)); if (!pvs[n].value) { fprintf(stderr,"Memory allocation failed\n"); return 1; } result = ca_array_get(pvs[n].dbrType, pvs[n].nElems, pvs[n].chid, pvs[n].value); } pvs[n].status = result; } else { pvs[n].status = ECA_DISCONN; } } if (!nConn) return 1; /* No connection? We're done. */ /* Wait for completion */ /* ------------------- */ result = ca_pend_io(caTimeout); if (result == ECA_TIMEOUT) fprintf(stderr, "Read operation timed out: some PV data was not read.\n"); if (request == callback) /* Also wait for callbacks */ { if (caTimeout != 0) { double slice = caTimeout / PEND_EVENT_SLICES; for (n = 0; n < PEND_EVENT_SLICES; n++) { ca_pend_event(slice); if (nRead >= nConn) break; } if (nRead < nConn) fprintf(stderr, "Read operation timed out: some PV data was not read.\n"); } else { /* For 0 timeout keep waiting until all are done */ while (nRead < nConn) { ca_pend_event(1.0); } } } /* Print the data */ /* -------------- */ for (n = 0; n < nPvs; n++) { switch (format) { case plain: /* Emulate old caget behaviour */ if (pvs[n].nElems <= 1 && fieldSeparator == ' ') printf("%-30s", pvs[n].name); else printf("%s", pvs[n].name); printf("%c", fieldSeparator); case terse: if (pvs[n].status == ECA_DISCONN) printf("*** not connected\n"); else if (pvs[n].status == ECA_NORDACCESS) printf("*** no read access\n"); else if (pvs[n].status != ECA_NORMAL) printf("*** CA error %s\n", ca_message(pvs[n].status)); else if (pvs[n].value == 0) printf("*** no data available (timeout)\n"); else { if (charArrAsStr && dbr_type_is_CHAR(pvs[n].dbrType) && (reqElems || pvs[n].nElems > 1)) { dbr_char_t *s = (dbr_char_t*) dbr_value_ptr(pvs[n].value, pvs[n].dbrType); int dlen = epicsStrnEscapedFromRawSize((char*)s, strlen((char*)s)); char *d = calloc(dlen+1, sizeof(char)); if(d) { epicsStrnEscapedFromRaw(d, dlen+1, (char*)s, strlen((char*)s)); printf("%s", d); free(d); } else { fprintf(stderr,"Failed to allocate space for escaped string\n"); } } else { if (reqElems || pvs[n].nElems > 1) printf("%lu%c", pvs[n].nElems, fieldSeparator); for (i=0; i 1)) { dbr_char_t *s = (dbr_char_t*) dbr_value_ptr(pvs[n].value, pvs[n].dbrType); int dlen = epicsStrnEscapedFromRawSize((char*)s, strlen((char*)s)); char *d = calloc(dlen+1, sizeof(char)); if(d) { epicsStrnEscapedFromRaw(d, dlen+1, (char*)s, strlen((char*)s)); printf("%s", d); free(d); } else { fprintf(stderr,"Failed to allocate space for escaped string\n"); } } else { for (i=0; i DBR_DOUBLE) /* Extended type extra info */ printf("%s\n", dbr2str(pvs[n].value, pvs[n].dbrType)); } } break; default : break; } } return 0; } /*+************************************************************************** * * Function: main * * Description: caget main() * Evaluate command line options, set up CA, connect the * channels, collect and print the data as requested * * Arg(s) In: [options] ... * * Arg(s) Out: none * * Return(s): Standard return code (0=success, 1=error) * **************************************************************************-*/ static void complainIfNotPlainAndSet (OutputT *current, const OutputT requested) { if (*current != plain) fprintf(stderr, "Options t,d,a are mutually exclusive. " "('caget -h' for help.)\n"); *current = requested; } int main (int argc, char *argv[]) { int n; int result; /* CA result */ OutputT format = plain; /* User specified format */ RequestT request = get; /* User specified request type */ IntFormatT outType; /* Output type */ int count = 0; /* 0 = not specified by -# option */ int opt; /* getopt() current option */ int type = -1; /* getopt() data type argument */ int digits = 0; /* getopt() no. of float digits */ int nPvs; /* Number of PVs */ pv* pvs; /* Array of PV structures */ LINE_BUFFER(stdout); /* Configure stdout buffering */ while ((opt = getopt(argc, argv, ":taicnhsSVe:f:g:l:#:d:0:w:p:F:")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; case 'V': printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); return 0; case 't': /* Terse output mode */ complainIfNotPlainAndSet(&format, terse); break; case 'a': /* Wide output mode */ complainIfNotPlainAndSet(&format, all); break; case 'c': /* Callback mode */ request = callback; break; case 'd': /* Data type specification */ complainIfNotPlainAndSet(&format, specifiedDbr); /* Argument (type) may be text or number */ if (sscanf(optarg, "%d", &type) != 1) { dbr_text_to_type(optarg, type); if (type == -1) /* Invalid? Try prefix DBR_ */ { char str[30] = "DBR_"; strncat(str, optarg, 25); dbr_text_to_type(str, type); } } if (type < DBR_STRING || type > DBR_CLASS_NAME || type == DBR_PUT_ACKT || type == DBR_PUT_ACKS) { fprintf(stderr, "Requested dbr type out of range " "or invalid - ignored. ('caget -h' for help.)\n"); format = plain; } break; case 'n': /* Print ENUM as index numbers */ enumAsNr = 1; break; case 'w': /* Set CA timeout value */ if(epicsScanDouble(optarg, &caTimeout) != 1) { fprintf(stderr, "'%s' is not a valid timeout value " "- ignored. ('caget -h' for help.)\n", optarg); caTimeout = DEFAULT_TIMEOUT; } break; case '#': /* Array count */ if (sscanf(optarg,"%d", &count) != 1) { fprintf(stderr, "'%s' is not a valid array element count " "- ignored. ('caget -h' for help.)\n", optarg); count = 0; } break; case 'p': /* CA priority */ if (sscanf(optarg,"%u", &caPriority) != 1) { fprintf(stderr, "'%s' is not a valid CA priority " "- ignored. ('caget -h' for help.)\n", optarg); caPriority = DEFAULT_CA_PRIORITY; } if (caPriority > CA_PRIORITY_MAX) caPriority = CA_PRIORITY_MAX; break; case 's': /* Select string dbr for floating type data */ floatAsString = 1; break; case 'S': /* Treat char array as (long) string */ charArrAsStr = 1; break; case 'e': /* Select %e/%f/%g format, using digits */ case 'f': case 'g': if (sscanf(optarg, "%d", &digits) != 1) fprintf(stderr, "Invalid precision argument '%s' " "for option '-%c' - ignored.\n", optarg, opt); else { if (digits>=0 && digits<=VALID_DOUBLE_DIGITS) sprintf(dblFormatStr, "%%-.%d%c", digits, opt); else fprintf(stderr, "Precision %d for option '-%c' " "out of range - ignored.\n", digits, opt); } break; case 'l': /* Convert to long and use integer format */ case '0': /* Select integer format */ switch ((char) *optarg) { case 'x': outType = hex; break; /* x print Hex */ case 'b': outType = bin; break; /* b print Binary */ case 'o': outType = oct; break; /* o print Octal */ default : outType = dec; fprintf(stderr, "Invalid argument '%s' " "for option '-%c' - ignored.\n", optarg, opt); } if (outType != dec) { if (opt == '0') { type = DBR_LONG; outTypeI = outType; } else { outTypeF = outType; } } break; case 'F': /* Store this for output and tool_lib formatting */ fieldSeparator = (char) *optarg; break; case '?': fprintf(stderr, "Unrecognized option: '-%c'. ('caget -h' for help.)\n", optopt); return 1; case ':': fprintf(stderr, "Option '-%c' requires an argument. ('caget -h' for help.)\n", optopt); return 1; default : usage(); return 1; } } nPvs = argc - optind; /* Remaining arg list are PV names */ if (nPvs < 1) { fprintf(stderr, "No pv name specified. ('caget -h' for help.)\n"); return 1; } /* Start up Channel Access */ result = ca_context_create(ca_disable_preemptive_callback); if (result != ECA_NORMAL) { fprintf(stderr, "CA error %s occurred while trying " "to start channel access.\n", ca_message(result)); return 1; } /* Allocate PV structure array */ pvs = calloc (nPvs, sizeof(pv)); if (!pvs) { fprintf(stderr, "Memory allocation for channel structures failed.\n"); return 1; } /* Connect channels */ for (n = 0; optind < argc; n++, optind++) pvs[n].name = argv[optind] ; /* Copy PV names from command line */ result = connect_pvs(pvs, nPvs); /* Read and print data */ if (!result) result = caget(pvs, nPvs, request, format, type, count); /* Shut down Channel Access */ ca_context_destroy(); return result; } base-7.0.3.1/modules/ca/src/tools/cainfo.c0000664000577000060420000001645413557101274017012 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. * 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) 2002 Berliner Elektronenspeicherringgesellschaft fuer * Synchrotronstrahlung. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange (BESSY) * * Modification History * 2008/04/16 Ralph Lange (BESSY) * Updated usage info * 2009/04/01 Ralph Lange (HZB/BESSY) * Clarified output for native data type * */ #include #include #include "epicsVersion.h" #include #include #include "tool_lib.h" static unsigned statLevel = 0; /* ca_client_status() interest level */ void usage (void) { fprintf (stderr, "\nUsage: cainfo [options] ...\n\n" " -h: Help: Print this message\n" " -V: Version: Show EPICS and CA versions\n" "Channel Access options:\n" " -w : Wait time, specifies CA timeout, default is %f second(s)\n" " -s : Call ca_client_status with the specified interest level\n" " -p : CA priority (0-%u, default 0=lowest)\n" "\nExample: cainfo my_channel another_channel\n\n" , DEFAULT_TIMEOUT, CA_PRIORITY_MAX); fprintf (stderr, "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); } /*+************************************************************************** * * Function: cainfo * * Description: Print CA info data or call ca_client_status * * Arg(s) In: pvs - Pointer to an array of pv structures * nPvs - Number of elements in the pvs array * * Return(s): Error code: 0 = OK, 1 = Error * **************************************************************************-*/ int cainfo (pv *pvs, int nPvs) { int n; long dbfType; long dbrType; unsigned long nElems; enum channel_state state; char *stateStrings[] = { "never connected", "previously connected", "connected", "closed" }; char *boolStrings[] = { "no ", "" }; if (statLevel) { ca_client_status(statLevel); } else { for (n = 0; n < nPvs; n++) { /* Print the status data */ /* --------------------- */ state = ca_state(pvs[n].chid); nElems = ca_element_count(pvs[n].chid); dbfType = ca_field_type(pvs[n].chid); dbrType = dbf_type_to_DBR(dbfType); printf("%s\n" " State: %s\n" " Host: %s\n" " Access: %sread, %swrite\n" " Native data type: %s\n" " Request type: %s\n" " Element count: %lu\n" , pvs[n].name, stateStrings[state], ca_host_name(pvs[n].chid), boolStrings[ca_read_access(pvs[n].chid)], boolStrings[ca_write_access(pvs[n].chid)], dbf_type_to_text(dbfType), dbr_type_to_text(dbrType), nElems ); } } return 0; } /*+************************************************************************** * * Function: main * * Description: cainfo main() * Evaluate command line options, set up CA, connect the * channels, print the data as requested * * Arg(s) In: [options] ... * * Arg(s) Out: none * * Return(s): Standard return code (0=success, 1=error) * **************************************************************************-*/ int main (int argc, char *argv[]) { int n; int result; /* CA result */ int opt; /* getopt() current option */ int nPvs; /* Number of PVs */ pv* pvs; /* Array of PV structures */ LINE_BUFFER(stdout); /* Configure stdout buffering */ while ((opt = getopt(argc, argv, ":nhVw:s:p:")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; case 'V': printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); return 0; case 'w': /* Set CA timeout value */ if(epicsScanDouble(optarg, &caTimeout) != 1) { fprintf(stderr, "'%s' is not a valid timeout value " "- ignored. ('cainfo -h' for help.)\n", optarg); caTimeout = DEFAULT_TIMEOUT; } break; case 's': /* ca_client_status interest level */ if (sscanf(optarg,"%du", &statLevel) != 1) { fprintf(stderr, "'%s' is not a valid interest level " "- ignored. ('cainfo -h' for help.)\n", optarg); statLevel = 0; } break; case 'p': /* CA priority */ if (sscanf(optarg,"%u", &caPriority) != 1) { fprintf(stderr, "'%s' is not a valid CA priority " "- ignored. ('cainfo -h' for help.)\n", optarg); caPriority = DEFAULT_CA_PRIORITY; } if (caPriority > CA_PRIORITY_MAX) caPriority = CA_PRIORITY_MAX; break; case '?': fprintf(stderr, "Unrecognized option: '-%c'. ('cainfo -h' for help.)\n", optopt); return 1; case ':': fprintf(stderr, "Option '-%c' requires an argument. ('cainfo -h' for help.)\n", optopt); return 1; default : usage(); return 1; } } nPvs = argc - optind; /* Remaining arg list are PV names */ if (!statLevel && nPvs < 1) { fprintf(stderr, "No pv name specified. ('cainfo -h' for help.)\n"); return 1; } /* Start up Channel Access */ result = ca_context_create(ca_disable_preemptive_callback); if (result != ECA_NORMAL) { fprintf(stderr, "CA error %s occurred while trying " "to start channel access.\n", ca_message(result)); return 1; } /* Allocate PV structure array */ pvs = calloc (nPvs, sizeof(pv)); if (!pvs) { fprintf(stderr, "Memory allocation for channel structures failed.\n"); return 1; } /* Connect channels */ for (n = 0; optind < argc; n++, optind++) pvs[n].name = argv[optind] ; /* Copy PV names from command line */ result = connect_pvs(pvs, nPvs); /* Print data */ if (!result) result = cainfo(pvs, nPvs); /* Shut down Channel Access */ ca_context_destroy(); return result; } base-7.0.3.1/modules/ca/src/tools/camonitor.c0000664000577000060420000003545513557101274017550 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. * 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) 2002 Berliner Elektronenspeicherringgesellschaft fuer * Synchrotronstrahlung. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange (BESSY) * * Modification History * 2008/04/16 Ralph Lange (BESSY) * Updated usage info * 2009/03/31 Larry Hoff (BNL) * Added field separators * 2009/04/01 Ralph Lange (HZB/BESSY) * Added support for long strings (array of char) and quoting of nonprintable characters * */ #include #include #include #include "epicsVersion.h" #include #include #include "tool_lib.h" #define VALID_DOUBLE_DIGITS 18 /* Max usable precision for a double */ static unsigned long reqElems = 0; static unsigned long eventMask = DBE_VALUE | DBE_ALARM; /* Event mask used */ static int floatAsString = 0; /* Flag: fetch floats as string */ static int nConn = 0; /* Number of connected PVs */ void usage (void) { fprintf (stderr, "\nUsage: camonitor [options] ...\n" "\n" " -h: Help: Print this message\n" " -V: Version: Show EPICS and CA versions\n" "Channel Access options:\n" " -w : Wait time, specifies CA timeout, default is %f second(s)\n" " -m : Specify CA event mask to use. is any combination of\n" " 'v' (value), 'a' (alarm), 'l' (log/archive), 'p' (property).\n" " Default event mask is 'va'\n" " -p : CA priority (0-%u, default 0=lowest)\n" "Timestamps:\n" " Default: Print absolute timestamps (as reported by CA server)\n" " -t : Specify timestamp source(s) and type, with containing\n" " 's' = CA server (remote) timestamps\n" " 'c' = CA client (local) timestamps (shown in '()'s)\n" " 'n' = no timestamps\n" " 'r' = relative timestamps (time elapsed since start of program)\n" " 'i' = incremental timestamps (time elapsed since last update)\n" " 'I' = incremental timestamps (time since last update, by channel)\n" " 'r', 'i' or 'I' require 's' or 'c' to select the time source\n" "Enum format:\n" " -n: Print DBF_ENUM values as number (default is enum string)\n" "Array values: Print number of elements, then list of values\n" " Default: Request and print all elements (dynamic arrays supported)\n" " -# : Request and print up to elements\n" " -S: Print arrays of char as a string (long string)\n" "Floating point format:\n" " Default: Use %%g format\n" " -e : Use %%e format, with a precision of digits\n" " -f : Use %%f format, with a precision of digits\n" " -g : Use %%g format, with a precision of digits\n" " -s: Get value as string (honors server-side precision)\n" " -lx: Round to long integer and print as hex number\n" " -lo: Round to long integer and print as octal number\n" " -lb: Round to long integer and print as binary number\n" "Integer number format:\n" " Default: Print as decimal number\n" " -0x: Print as hex number\n" " -0o: Print as octal number\n" " -0b: Print as binary number\n" "Alternate output field separator:\n" " -F : Use to separate fields in output\n" "\n" "Example: camonitor -f8 my_channel another_channel\n" " (doubles are printed as %%f with precision of 8)\n\n" , DEFAULT_TIMEOUT, CA_PRIORITY_MAX); } /*+************************************************************************** * * Function: event_handler * * Description: CA event_handler for request type callback * Prints the event data * * Arg(s) In: args - event handler args (see CA manual) * **************************************************************************-*/ static void event_handler (evargs args) { pv* pv = args.usr; pv->status = args.status; if (args.status == ECA_NORMAL) { pv->dbrType = args.type; pv->nElems = args.count; pv->value = (void *) args.dbr; /* casting away const */ print_time_val_sts(pv, reqElems); fflush(stdout); pv->value = NULL; } } /*+************************************************************************** * * Function: connection_handler * * Description: CA connection_handler * * Arg(s) In: args - connection_handler_args (see CA manual) * **************************************************************************-*/ static void connection_handler ( struct connection_handler_args args ) { pv *ppv = ( pv * ) ca_puser ( args.chid ); if ( args.op == CA_OP_CONN_UP ) { nConn++; if (!ppv->onceConnected) { ppv->onceConnected = 1; /* Set up pv structure */ /* ------------------- */ /* Get natural type and array count */ ppv->dbfType = ca_field_type(ppv->chid); ppv->dbrType = dbf_type_to_DBR_TIME(ppv->dbfType); /* Use native type */ if (dbr_type_is_ENUM(ppv->dbrType)) /* Enums honour -n option */ { if (enumAsNr) ppv->dbrType = DBR_TIME_INT; else ppv->dbrType = DBR_TIME_STRING; } else if (floatAsString && (dbr_type_is_FLOAT(ppv->dbrType) || dbr_type_is_DOUBLE(ppv->dbrType))) { ppv->dbrType = DBR_TIME_STRING; } /* Set request count */ ppv->nElems = ca_element_count(ppv->chid); ppv->reqElems = reqElems > ppv->nElems ? ppv->nElems : reqElems; /* Issue CA request */ /* ---------------- */ /* install monitor once with first connect */ ppv->status = ca_create_subscription(ppv->dbrType, ppv->reqElems, ppv->chid, eventMask, event_handler, (void*)ppv, NULL); } } else if ( args.op == CA_OP_CONN_DOWN ) { nConn--; ppv->status = ECA_DISCONN; print_time_val_sts(ppv, reqElems); } } /*+************************************************************************** * * Function: main * * Description: camonitor main() * Evaluate command line options, set up CA, connect the * channels, collect and print the data as requested * * Arg(s) In: [options] ... * * Arg(s) Out: none * * Return(s): Standard return code (0=success, 1=error) * **************************************************************************-*/ int main (int argc, char *argv[]) { int returncode = 0; int n; int result; /* CA result */ IntFormatT outType; /* Output type */ int opt; /* getopt() current option */ int digits = 0; /* getopt() no. of float digits */ int nPvs; /* Number of PVs */ pv* pvs; /* Array of PV structures */ LINE_BUFFER(stdout); /* Configure stdout buffering */ while ((opt = getopt(argc, argv, ":nhVm:sSe:f:g:l:#:0:w:t:p:F:")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; case 'V': printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); return 0; case 'n': /* Print ENUM as index numbers */ enumAsNr=1; break; case 't': /* Select timestamp source(s) and type */ tsSrcServer = 0; tsSrcClient = 0; { int i = 0; char c; while ((c = optarg[i++])) switch (c) { case 's': tsSrcServer = 1; break; case 'c': tsSrcClient = 1; break; case 'n': break; case 'r': tsType = relative; break; case 'i': tsType = incremental; break; case 'I': tsType = incrementalByChan; break; default : fprintf(stderr, "Invalid argument '%c' " "for option '-t' - ignored.\n", c); } } break; case 'w': /* Set CA timeout value */ if(epicsScanDouble(optarg, &caTimeout) != 1) { fprintf(stderr, "'%s' is not a valid timeout value " "- ignored. ('camonitor -h' for help.)\n", optarg); caTimeout = DEFAULT_TIMEOUT; } break; case '#': /* Array count */ if (sscanf(optarg,"%ld", &reqElems) != 1) { fprintf(stderr, "'%s' is not a valid array element count " "- ignored. ('camonitor -h' for help.)\n", optarg); reqElems = 0; } break; case 'p': /* CA priority */ if (sscanf(optarg,"%u", &caPriority) != 1) { fprintf(stderr, "'%s' is not a valid CA priority " "- ignored. ('camonitor -h' for help.)\n", optarg); caPriority = DEFAULT_CA_PRIORITY; } if (caPriority > CA_PRIORITY_MAX) caPriority = CA_PRIORITY_MAX; break; case 'm': /* Select CA event mask */ eventMask = 0; { int i = 0; char c, err = 0; while ((c = optarg[i++]) && !err) switch (c) { case 'v': eventMask |= DBE_VALUE; break; case 'a': eventMask |= DBE_ALARM; break; case 'l': eventMask |= DBE_LOG; break; case 'p': eventMask |= DBE_PROPERTY; break; default : fprintf(stderr, "Invalid argument '%s' " "for option '-m' - ignored.\n", optarg); eventMask = DBE_VALUE | DBE_ALARM; err = 1; } } break; case 's': /* Select string dbr for floating type data */ floatAsString = 1; break; case 'S': /* Treat char array as (long) string */ charArrAsStr = 1; break; case 'e': /* Select %e/%f/%g format, using digits */ case 'f': case 'g': if (sscanf(optarg, "%d", &digits) != 1) fprintf(stderr, "Invalid precision argument '%s' " "for option '-%c' - ignored.\n", optarg, opt); else { if (digits>=0 && digits<=VALID_DOUBLE_DIGITS) sprintf(dblFormatStr, "%%-.%d%c", digits, opt); else fprintf(stderr, "Precision %d for option '-%c' " "out of range - ignored.\n", digits, opt); } break; case 'l': /* Convert to long and use integer format */ case '0': /* Select integer format */ switch ((char) *optarg) { case 'x': outType = hex; break; /* x print Hex */ case 'b': outType = bin; break; /* b print Binary */ case 'o': outType = oct; break; /* o print Octal */ default : outType = dec; fprintf(stderr, "Invalid argument '%s' " "for option '-%c' - ignored.\n", optarg, opt); } if (outType != dec) { if (opt == '0') outTypeI = outType; else outTypeF = outType; } break; case 'F': /* Store this for output and tool_lib formatting */ fieldSeparator = (char) *optarg; break; case '?': fprintf(stderr, "Unrecognized option: '-%c'. ('camonitor -h' for help.)\n", optopt); return 1; case ':': fprintf(stderr, "Option '-%c' requires an argument. ('camonitor -h' for help.)\n", optopt); return 1; default : usage(); return 1; } } nPvs = argc - optind; /* Remaining arg list are PV names */ if (nPvs < 1) { fprintf(stderr, "No pv name specified. ('camonitor -h' for help.)\n"); return 1; } /* Start up Channel Access */ result = ca_context_create(ca_disable_preemptive_callback); if (result != ECA_NORMAL) { fprintf(stderr, "CA error %s occurred while trying " "to start channel access.\n", ca_message(result)); return 1; } /* Allocate PV structure array */ pvs = calloc (nPvs, sizeof(pv)); if (!pvs) { fprintf(stderr, "Memory allocation for channel structures failed.\n"); return 1; } /* Connect channels */ /* Copy PV names from command line */ for (n = 0; optind < argc; n++, optind++) { pvs[n].name = argv[optind]; } /* Create CA connections */ returncode = create_pvs(pvs, nPvs, connection_handler); if ( returncode ) { return returncode; } /* Check for channels that didn't connect */ ca_pend_event(caTimeout); for (n = 0; n < nPvs; n++) { if (!pvs[n].onceConnected) print_time_val_sts(&pvs[n], reqElems); } /* Read and print data forever */ ca_pend_event(0); /* Shut down Channel Access */ ca_context_destroy(); return result; } base-7.0.3.1/modules/ca/src/tools/caput.c0000664000577000060420000004771313557101274016671 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. * Copyright (c) 2006 Diamond Light Source Ltd. * 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) 2002 Berliner Elektronenspeicherringgesellschaft fuer * Synchrotronstrahlung. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange (BESSY) * * Modification History * 2006/01/17 Malcolm Walters (Tessella/Diamond Light Source) * Added put_callback option - heavily based on caget * 2008/03/06 Andy Foster (OSL/Diamond Light Source) * Remove timeout dependency of ca_put_callback by using an EPICS event * (semaphore), i.e. remove ca_pend_event time slicing. * 2008/04/16 Ralph Lange (BESSY) * Updated usage info * 2009/03/31 Larry Hoff (BNL) * Added field separators * 2009/04/01 Ralph Lange (HZB/BESSY) * Added support for long strings (array of char) and quoting of nonprintable characters * */ #include #include #include #include #include #include #include #include "epicsVersion.h" #include "tool_lib.h" #define VALID_DOUBLE_DIGITS 18 /* Max usable precision for a double */ /* Different output formats */ typedef enum { plain, terse, all } OutputT; /* Different request types */ typedef enum { get, callback } RequestT; /* Valid EPICS string */ typedef char EpicsStr[MAX_STRING_SIZE]; static int nConn = 0; /* Number of connected PVs */ static epicsEventId epId; void usage (void) { fprintf (stderr, "\nUsage: caput [options] ...\n" " caput -a [options] ...\n\n" " -h: Help: Print this message\n" " -V: Version: Show EPICS and CA versions\n" "Channel Access options:\n" " -w : Wait time, specifies CA timeout, default is %f second(s)\n" " -c: Asynchronous put (use ca_put_callback and wait for completion)\n" " -p : CA priority (0-%u, default 0=lowest)\n" "Format options:\n" " -t: Terse mode - print only sucessfully written value, without name\n" " -l: Long mode \"name timestamp value stat sevr\" (read PVs as DBR_TIME_xxx)\n" "Enum format:\n" " Default: Auto - try value as ENUM string, then as index number\n" " -n: Force interpretation of values as numbers\n" " -s: Force interpretation of values as strings\n" "Arrays:\n" " Default: Put scalar\n" " Value format: all value arguments concatenated with spaces\n" " -S: Put string as an array of chars (long string)\n" " -a: Put array\n" " Value format: number of values, then list of values\n" "Alternate output field separator:\n" " -F : Use as an alternate output field separator\n" "\nExample: caput my_channel 1.2\n" " (puts 1.2 to my_channel)\n\n" , DEFAULT_TIMEOUT, CA_PRIORITY_MAX); } /*+************************************************************************** * * Function: put_event_handler * * Description: CA event_handler for request type callback * Sets status flags and marks as done. * * Arg(s) In: args - event handler args (see CA manual) * **************************************************************************-*/ void put_event_handler ( struct event_handler_args args ) { /* Retrieve pv from event handler structure */ pv* pPv = args.usr; /* Store status, then give EPICS event */ pPv->status = args.status; epicsEventSignal( epId ); } /*+************************************************************************** * * Function: caget * * Description: Issue read request, wait for incoming data * and print the data * * Arg(s) In: pvs - Pointer to an array of pv structures * nPvs - Number of elements in the pvs array * format - Output format * dbrType - Requested dbr type * reqElems - Requested number of (array) elements * * Return(s): Error code: 0 = OK, 1 = Error * **************************************************************************-*/ int caget (pv *pvs, int nPvs, OutputT format, chtype dbrType, unsigned long reqElems) { unsigned int i; int n, result; for (n = 0; n < nPvs; n++) { /* Set up pvs structure */ /* -------------------- */ /* Get natural type and array count */ pvs[n].nElems = ca_element_count(pvs[n].chid); pvs[n].dbfType = ca_field_type(pvs[n].chid); pvs[n].dbrType = dbrType; /* Set up value structures */ pvs[n].dbrType = dbf_type_to_DBR_TIME(pvs[n].dbfType); /* Use native type */ if (dbr_type_is_ENUM(pvs[n].dbrType)) /* Enums honour -n option */ { if (enumAsNr) pvs[n].dbrType = DBR_TIME_INT; else pvs[n].dbrType = DBR_TIME_STRING; } if (reqElems == 0 || pvs[n].nElems < reqElems) /* Adjust array count */ pvs[n].reqElems = pvs[n].nElems; else pvs[n].reqElems = reqElems; /* Issue CA request */ /* ---------------- */ if (ca_state(pvs[n].chid) == cs_conn) { nConn++; pvs[n].onceConnected = 1; /* Allocate value structure */ pvs[n].value = calloc(1, dbr_size_n(pvs[n].dbrType, pvs[n].reqElems)); if(!pvs[n].value){ fprintf(stderr,"Allocation failed\n"); exit(1); } result = ca_array_get(pvs[n].dbrType, pvs[n].reqElems, pvs[n].chid, pvs[n].value); pvs[n].status = result; } else { pvs[n].status = ECA_DISCONN; } } if (!nConn) return 1; /* No connection? We're done. */ /* Wait for completion */ /* ------------------- */ result = ca_pend_io(caTimeout); if (result == ECA_TIMEOUT) fprintf(stderr, "Read operation timed out: PV data was not read.\n"); /* Print the data */ /* -------------- */ for (n = 0; n < nPvs; n++) { switch (format) { case plain: /* Emulate old caput behaviour */ if (pvs[n].reqElems <= 1 && fieldSeparator == ' ') printf("%-30s", pvs[n].name); else printf("%s", pvs[n].name); printf("%c", fieldSeparator); case terse: if (pvs[n].status == ECA_DISCONN) printf("*** not connected\n"); else if (pvs[n].status == ECA_NORDACCESS) printf("*** no read access\n"); else if (pvs[n].status != ECA_NORMAL) printf("*** CA error %s\n", ca_message(pvs[n].status)); else if (pvs[n].value == 0) printf("*** no data available (timeout)\n"); else { if (charArrAsStr && dbr_type_is_CHAR(pvs[n].dbrType) && (reqElems || pvs[n].reqElems > 1)) { dbr_char_t *s = (dbr_char_t*) dbr_value_ptr(pvs[n].value, pvs[n].dbrType); int dlen = epicsStrnEscapedFromRawSize((char*)s, strlen((char*)s)); char *d = calloc(dlen+1, sizeof(char)); if(!d){ fprintf(stderr,"Allocation failed\n"); exit(1); } epicsStrnEscapedFromRaw(d, dlen+1, (char*)s, strlen((char*)s)); printf("%s", d); free(d); } else { if (reqElems || pvs[n].nElems > 1) printf("%lu%c", pvs[n].reqElems, fieldSeparator); for (i=0; i ... * * Arg(s) Out: none * * Return(s): Standard return code (0=success, 1=error) * **************************************************************************-*/ int main (int argc, char *argv[]) { int i; int result; /* CA result */ OutputT format = plain; /* User specified format */ RequestT request = get; /* User specified request type */ int isArray = 0; /* Flag for array operation */ int enumAsString = 0; /* Force ENUM values to be strings */ int count = 1; int opt; /* getopt() current option */ chtype dbrType = DBR_STRING; char *pend; EpicsStr *sbuf; double *dbuf; char *cbuf = 0; char *ebuf = 0; void *pbuf; int len = 0; int waitStatus; struct dbr_gr_enum bufGrEnum; int nPvs; /* Number of PVs */ pv* pvs; /* Array of PV structures */ LINE_BUFFER(stdout); /* Configure stdout buffering */ putenv("POSIXLY_CORRECT="); /* Behave correct on GNU getopt systems */ while ((opt = getopt(argc, argv, ":cnlhatsVS#:w:p:F:")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; case 'V': printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); return 0; case 'n': /* Force interpret ENUM as index number */ enumAsNr = 1; enumAsString = 0; break; case 's': /* Force interpret ENUM as menu string */ enumAsString = 1; enumAsNr = 0; break; case 'S': /* Treat char array as (long) string */ charArrAsStr = 1; isArray = 0; break; case 't': /* Select terse output format */ format = terse; break; case 'l': /* Select long output format */ format = all; break; case 'a': /* Select array mode */ isArray = 1; charArrAsStr = 0; break; case 'c': /* Select put_callback mode */ request = callback; break; case 'w': /* Set CA timeout value */ if(epicsScanDouble(optarg, &caTimeout) != 1) { fprintf(stderr, "'%s' is not a valid timeout value " "- ignored. ('caput -h' for help.)\n", optarg); caTimeout = DEFAULT_TIMEOUT; } break; case '#': /* Array count */ if (sscanf(optarg,"%d", &count) != 1) { fprintf(stderr, "'%s' is not a valid array element count " "- ignored. ('caput -h' for help.)\n", optarg); count = 0; } break; case 'p': /* CA priority */ if (sscanf(optarg,"%u", &caPriority) != 1) { fprintf(stderr, "'%s' is not a valid CA priority " "- ignored. ('caget -h' for help.)\n", optarg); caPriority = DEFAULT_CA_PRIORITY; } if (caPriority > CA_PRIORITY_MAX) caPriority = CA_PRIORITY_MAX; break; case 'F': /* Store this for output and tool_lib formatting */ fieldSeparator = (char) *optarg; break; case '?': fprintf(stderr, "Unrecognized option: '-%c'. ('caput -h' for help.)\n", optopt); return 1; case ':': fprintf(stderr, "Option '-%c' requires an argument. ('caput -h' for help.)\n", optopt); return 1; default : usage(); return 1; } } nPvs = argc - optind; /* Remaining arg list are PV names and values */ if (nPvs < 1) { fprintf(stderr, "No pv name specified. ('caput -h' for help.)\n"); return 1; } if (nPvs == 1) { fprintf(stderr, "No value specified. ('caput -h' for help.)\n"); return 1; } nPvs = 1; /* One PV - the rest is value(s) */ epId = epicsEventCreate(epicsEventEmpty); /* Create empty EPICS event (semaphore) */ /* Start up Channel Access */ result = ca_context_create(ca_enable_preemptive_callback); if (result != ECA_NORMAL) { fprintf(stderr, "CA error %s occurred while trying " "to start channel access.\n", ca_message(result)); return 1; } /* Allocate PV structure array */ pvs = calloc (nPvs, sizeof(pv)); if (!pvs) { fprintf(stderr, "Memory allocation for channel structure failed.\n"); return 1; } /* Connect channels */ pvs[0].name = argv[optind] ; /* Copy PV name from command line */ result = connect_pvs(pvs, nPvs); /* If the connection fails, we're done */ if (result) { ca_context_destroy(); return result; } /* Get values from command line */ optind++; if (isArray) { optind++; /* In case of array skip first value (nr * of elements) - actual number of values is used */ count = argc - optind; } else { /* Concatenate the remaining line to one string * (sucks but is compatible to the former version) */ for (i = optind; i < argc; i++) { len += strlen(argv[i]); len++; } cbuf = calloc(len, sizeof(char)); if (!cbuf) { fprintf(stderr, "Memory allocation failed.\n"); return 1; } strcpy(cbuf, argv[optind]); if (argc > optind+1) { for (i = optind + 1; i < argc; i++) { strcat(cbuf, " "); strcat(cbuf, argv[i]); } } if ((argc - optind) >= 1) count = 1; argv[optind] = cbuf; } sbuf = calloc (count, sizeof(EpicsStr)); dbuf = calloc (count, sizeof(double)); if(!sbuf || !dbuf) { fprintf(stderr, "Memory allocation failed\n"); return 1; } /* ENUM? Special treatment */ if (ca_field_type(pvs[0].chid) == DBR_ENUM) { /* Get the ENUM strings */ result = ca_array_get (DBR_GR_ENUM, 1, pvs[0].chid, &bufGrEnum); result = ca_pend_io(caTimeout); if (result == ECA_TIMEOUT) { fprintf(stderr, "Read operation timed out: ENUM data was not read.\n"); return 1; } if (enumAsNr) { /* Interpret values as numbers */ for (i = 0; i < count; ++i) { dbuf[i] = epicsStrtod(*(argv+optind+i), &pend); if (*(argv+optind+i) == pend) { /* Conversion didn't work */ fprintf(stderr, "Enum index value '%s' is not a number.\n", *(argv+optind+i)); return 1; } if (dbuf[i] >= bufGrEnum.no_str) { fprintf(stderr, "Warning: enum index value '%s' may be too large.\n", *(argv+optind+i)); } } dbrType = DBR_DOUBLE; } else { /* Interpret values as strings */ for (i = 0; i < count; ++i) { epicsStrnRawFromEscaped(sbuf[i], sizeof(EpicsStr), *(argv+optind+i), sizeof(EpicsStr)); *( sbuf[i]+sizeof(EpicsStr)-1 ) = '\0'; dbrType = DBR_STRING; /* Compare to ENUM strings */ for (len = 0; len < bufGrEnum.no_str; len++) if (!strcmp(sbuf[i], bufGrEnum.strs[len])) break; if (len >= bufGrEnum.no_str) { /* Not a string? Try as number */ dbuf[i] = epicsStrtod(sbuf[i], &pend); if (sbuf[i] == pend || enumAsString) { fprintf(stderr, "Enum string value '%s' invalid.\n", sbuf[i]); return 1; } if (dbuf[i] >= bufGrEnum.no_str) { fprintf(stderr, "Warning: enum index value '%s' may be too large.\n", sbuf[i]); } dbrType = DBR_DOUBLE; } } } } else { /* Not an ENUM */ if (charArrAsStr) { dbrType = DBR_CHAR; ebuf = calloc(len, sizeof(char)); if(!ebuf) { fprintf(stderr, "Memory allocation failed\n"); return 1; } count = epicsStrnRawFromEscaped(ebuf, len, cbuf, len-1) + 1; } else { for (i = 0; i < count; ++i) { epicsStrnRawFromEscaped(sbuf[i], sizeof(EpicsStr), *(argv+optind+i), sizeof(EpicsStr)); *( sbuf[i]+sizeof(EpicsStr)-1 ) = '\0'; } dbrType = DBR_STRING; } } /* Read and print old data */ if (format != terse) { printf("Old : "); result = caget(pvs, nPvs, format, 0, 0); } /* Write new data */ if (dbrType == DBR_STRING) pbuf = sbuf; else if (dbrType == DBR_CHAR) pbuf = ebuf; else pbuf = dbuf; if (request == callback) { /* Use callback version of put */ pvs[0].status = ECA_NORMAL; /* All ok at the moment */ result = ca_array_put_callback ( dbrType, count, pvs[0].chid, pbuf, put_event_handler, (void *) pvs); } else { /* Use standard put with defined timeout */ result = ca_array_put (dbrType, count, pvs[0].chid, pbuf); } if (result != ECA_NORMAL) { fprintf(stderr, "Error from put operation: %s\n", ca_message(result)); return 1; } result = ca_pend_io(caTimeout); if (result == ECA_TIMEOUT) { fprintf(stderr, "Write operation timed out: Data was not written.\n"); return 1; } if (request == callback) { /* Also wait for callbacks */ waitStatus = epicsEventWaitWithTimeout( epId, caTimeout ); if (waitStatus) fprintf(stderr, "Write callback operation timed out\n"); /* retrieve status from callback */ result = pvs[0].status; } if (result != ECA_NORMAL) { fprintf(stderr, "Error occured writing data: %s\n", ca_message(result)); return 1; } /* Read and print new data */ if (format != terse) printf("New : "); result = caget(pvs, nPvs, format, 0, 0); /* Shut down Channel Access */ ca_context_destroy(); return result; } base-7.0.3.1/modules/ca/src/tools/tool_lib.c0000664000577000060420000006421313557101274017352 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. * 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) 2002 Berliner Elektronenspeicherringgesellschaft fuer * Synchrotronstrahlung. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange (BESSY) * * Modification History * 2009/03/31 Larry Hoff (BNL) * Added field separators * 2009/04/01 Ralph Lange (HZB/BESSY) * Added support for long strings (array of char) and quoting of nonprintable characters * */ #include #include #include #include #include #include #include #include "tool_lib.h" /* Time stamps for program start, first incoming monitor, previous value (client and server stamp): used for relative resp. incremental timestamps with monitors */ static epicsTimeStamp tsStart, tsFirst, tsPreviousC, tsPreviousS; static int tsInitS = 0; /* Flag: Server timestamps init'd */ static int tsInitC = 0; /* Flag: Client timestamps init'd */ TimeT tsType = absolute; /* Timestamp type flag (-t option) */ int tsSrcServer = 1; /* Timestamp source flag (-t option) */ int tsSrcClient = 0; /* Timestamp source flag (-t option) */ IntFormatT outTypeI = dec; /* For -0.. output format option */ IntFormatT outTypeF = dec; /* For -l.. output format option */ char dblFormatStr[30] = "%g"; /* Format string to print doubles (-efg options) */ char timeFormatStr[30] = "%Y-%m-%d %H:%M:%S.%06f"; /* Time format string */ char fieldSeparator = ' '; /* OFS default is whitespace */ int enumAsNr = 0; /* used for -n option - get DBF_ENUM as number */ int charArrAsStr = 0; /* used for -S option - treat char array as (long) string */ double caTimeout = 1.0; /* wait time default (see -w option) */ capri caPriority = DEFAULT_CA_PRIORITY; /* CA Priority */ #define TIMETEXTLEN 28 /* Length of timestamp text buffer */ static void sprint_long (char *ret, dbr_long_t val, IntFormatT outType) { if (outType == bin && val != 0) { /* sprintf doesn't do binary; this code doesn't handle 0 */ int i, skip = -1; for (i = 31; i >= 0; i--) { int bit = (val >> i) & 1; if (skip < 0 && bit) { skip = 31 - i; /* skip leading 0's */ ret[i+1] = '\0'; } if (skip >= 0) { ret[31-i-skip] = '0' + bit; } } } else { const char *fmt[4] = { /* Order must match the enum IntFormatT */ "%d" /* dec */, "0" /* bin and val is 0 */, "0o%o" /* oct */, "0x%X" /* hex */ }; /* dbr_long_t is actually an int on all supported platforms */ sprintf(ret, fmt[outType], (int) val); } } /*+************************************************************************** * * Function: val2str * * Description: Print (convert) value to a string * * Arg(s) In: v - Pointer to dbr_... structure * type - Numeric dbr type * index - Index of element to print (for arrays) * * Return(s): Pointer to static output string * **************************************************************************-*/ char *val2str (const void *v, unsigned type, int index) { #define STR 500 static char str[STR]; char ch; void *val_ptr; unsigned base_type; dbr_long_t val_long; if (!dbr_type_is_valid(type)) { strcpy (str, "*** invalid type"); return str; } strcpy (str, "!!!"); base_type = type % (LAST_TYPE+1); if (type == DBR_STSACK_STRING || type == DBR_CLASS_NAME) base_type = DBR_STRING; val_ptr = dbr_value_ptr(v, type); switch (base_type) { case DBR_STRING: epicsStrnEscapedFromRaw(str, STR, ((dbr_string_t*) val_ptr)[index], strlen(((dbr_string_t*) val_ptr)[index])); break; case DBR_FLOAT: if (outTypeF == dec) { sprintf(str, dblFormatStr, ((dbr_float_t*) val_ptr)[index]); } else { if (((dbr_float_t*) val_ptr)[index] > 0.0) val_long = ((dbr_float_t*) val_ptr)[index] + 0.5; else val_long = ((dbr_float_t*) val_ptr)[index] - 0.5; sprint_long(str, val_long, outTypeF); } break; case DBR_DOUBLE: if (outTypeF == dec) { sprintf(str, dblFormatStr, ((dbr_double_t*) val_ptr)[index]); } else { if (((dbr_double_t*) val_ptr)[index] > 0.0) val_long = ((dbr_double_t*) val_ptr)[index] + 0.5; else val_long = ((dbr_double_t*) val_ptr)[index] - 0.5; sprint_long(str, val_long, outTypeF); } break; case DBR_CHAR: ch = ((dbr_char_t*) val_ptr)[index]; sprintf(str, "%d", ch); break; case DBR_INT: sprint_long(str, ((dbr_int_t*) val_ptr)[index], outTypeI); break; case DBR_LONG: sprint_long(str, ((dbr_long_t*) val_ptr)[index], outTypeI); break; case DBR_ENUM: { dbr_enum_t *val = (dbr_enum_t *)val_ptr; if (dbr_type_is_GR(type) && !enumAsNr) { if (val[index] >= MAX_ENUM_STATES) sprintf(str, "Illegal Value (%d)", val[index]); else if (val[index] >= ((struct dbr_gr_enum *)v)->no_str) sprintf(str, "Enum Index Overflow (%d)", val[index]); else sprintf(str, "%s", ((struct dbr_gr_enum *)v)->strs[val[index]]); } else if (dbr_type_is_CTRL(type) && !enumAsNr) { if (val[index] >= MAX_ENUM_STATES) sprintf(str, "Illegal Value (%d)", val[index]); else if (val[index] >= ((struct dbr_ctrl_enum *)v)->no_str) sprintf(str, "Enum Index Overflow (%d)", val[index]); else sprintf(str, "%s", ((struct dbr_ctrl_enum *)v)->strs[val[index]]); } else sprintf(str, "%d", val[index]); } } return str; } /*+************************************************************************** * * Function: dbr2str * * Description: Print (convert) additional information contained in dbr_... * * Arg(s) In: value - Pointer to dbr_... structure * type - Numeric dbr type * * Return(s): Pointer to static output string * **************************************************************************-*/ /* Definitions for sprintf format strings and matching argument lists */ #define FMT_TIME \ " Timestamp: %s" #define ARGS_TIME(T) \ timeText #define FMT_STS \ " Status: %s\n" \ " Severity: %s" #define ARGS_STS(T) \ stat_to_str(((struct T *)value)->status), \ sevr_to_str(((struct T *)value)->severity) #define ARGS_STS_UNSIGNED(T) \ stat_to_str_unsigned(((struct T *)value)->status), \ sevr_to_str_unsigned(((struct T *)value)->severity) #define FMT_ACK \ " Ack transient?: %s\n" \ " Ack severity: %s" #define ARGS_ACK(T) \ ((struct T *)value)->ackt ? "YES" : "NO", \ sevr_to_str_unsigned(((struct T *)value)->acks) #define FMT_UNITS \ " Units: %s" #define ARGS_UNITS(T) \ ((struct T *)value)->units #define FMT_PREC \ " Precision: %d" #define ARGS_PREC(T) \ ((struct T *)value)->precision #define FMT_GR(FMT) \ " Lo disp limit: " #FMT "\n" \ " Hi disp limit: " #FMT "\n" \ " Lo alarm limit: " #FMT "\n" \ " Lo warn limit: " #FMT "\n" \ " Hi warn limit: " #FMT "\n" \ " Hi alarm limit: " #FMT #define ARGS_GR(T,F) \ (F)((struct T *)value)->lower_disp_limit, \ (F)((struct T *)value)->upper_disp_limit, \ (F)((struct T *)value)->lower_alarm_limit, \ (F)((struct T *)value)->lower_warning_limit, \ (F)((struct T *)value)->upper_warning_limit, \ (F)((struct T *)value)->upper_alarm_limit #define FMT_CTRL(FMT) \ " Lo ctrl limit: " #FMT "\n" \ " Hi ctrl limit: " #FMT #define ARGS_CTRL(T,F) \ (F)((struct T *)value)->lower_ctrl_limit, \ (F)((struct T *)value)->upper_ctrl_limit /* Definitions for the actual sprintf calls */ #define PRN_DBR_STS(T) \ sprintf(str, \ FMT_STS, \ ARGS_STS(T)) #define PRN_DBR_TIME(T) \ epicsTimeToStrftime(timeText, TIMETEXTLEN, timeFormatStr, \ &(((struct T *)value)->stamp)); \ sprintf(str, \ FMT_TIME "\n" FMT_STS, \ ARGS_TIME(T), ARGS_STS(T)) #define PRN_DBR_GR(T,F,FMT) \ sprintf(str, \ FMT_STS "\n" FMT_UNITS "\n" FMT_GR(FMT), \ ARGS_STS(T), ARGS_UNITS(T), ARGS_GR(T,F)) #define PRN_DBR_GR_PREC(T,F,FMT) \ sprintf(str, \ FMT_STS "\n" FMT_UNITS "\n" FMT_PREC "\n" FMT_GR(FMT), \ ARGS_STS(T), ARGS_UNITS(T), ARGS_PREC(T), ARGS_GR(T,F)) #define PRN_DBR_CTRL(T,F,FMT) \ sprintf(str, \ FMT_STS "\n" FMT_UNITS "\n" FMT_GR(FMT) "\n" FMT_CTRL(FMT), \ ARGS_STS(T), ARGS_UNITS(T), ARGS_GR(T,F), ARGS_CTRL(T,F)) #define PRN_DBR_CTRL_PREC(T,F,FMT) \ sprintf(str, \ FMT_STS "\n" FMT_UNITS "\n" FMT_PREC "\n" FMT_GR(FMT) "\n" FMT_CTRL(FMT), \ ARGS_STS(T), ARGS_UNITS(T), ARGS_PREC(T), ARGS_GR(T,F), ARGS_CTRL(T,F)) #define PRN_DBR_STSACK(T) \ sprintf(str, \ FMT_STS "\n" FMT_ACK, \ ARGS_STS_UNSIGNED(T), ARGS_ACK(T)) #define PRN_DBR_X_ENUM(T) \ n = ((struct T *)value)->no_str; \ PRN_DBR_STS(T); \ sprintf(str+strlen(str), \ "\n Enums: (%2d)", n); \ for (i=0; istrs[i]); /* Make a good guess how long the dbr_... stuff might get as worst case */ #define DBR_PRINT_BUFFER_SIZE \ 50 /* timestamp */ \ + 2 * 30 /* status / Severity */ \ + 2 * 30 /* acks / ackt */ \ + 20 + MAX_UNITS_SIZE /* units */ \ + 30 /* precision */ \ + 6 * 45 /* graphic limits */ \ + 2 * 45 /* control limits */ \ + 30 + (MAX_ENUM_STATES * (20 + MAX_ENUM_STRING_SIZE)) /* enums */ \ + 50 /* just to be sure */ char *dbr2str (const void *value, unsigned type) { static char str[DBR_PRINT_BUFFER_SIZE]; char timeText[TIMETEXTLEN]; int n, i; switch (type) { case DBR_STRING: /* no additional information for basic data types */ case DBR_INT: case DBR_FLOAT: case DBR_ENUM: case DBR_CHAR: case DBR_LONG: case DBR_DOUBLE: break; case DBR_CTRL_STRING: /* see db_access.h: not implemented */ case DBR_GR_STRING: /* see db_access.h: not implemented */ case DBR_STS_STRING: PRN_DBR_STS(dbr_sts_string); break; case DBR_STS_SHORT: PRN_DBR_STS(dbr_sts_short); break; case DBR_STS_FLOAT: PRN_DBR_STS(dbr_sts_float); break; case DBR_STS_ENUM: PRN_DBR_STS(dbr_sts_enum); break; case DBR_STS_CHAR: PRN_DBR_STS(dbr_sts_char); break; case DBR_STS_LONG: PRN_DBR_STS(dbr_sts_long); break; case DBR_STS_DOUBLE: PRN_DBR_STS(dbr_sts_double); break; case DBR_TIME_STRING: PRN_DBR_TIME(dbr_time_string); break; case DBR_TIME_SHORT: PRN_DBR_TIME(dbr_time_short); break; case DBR_TIME_FLOAT: PRN_DBR_TIME(dbr_time_float); break; case DBR_TIME_ENUM: PRN_DBR_TIME(dbr_time_enum); break; case DBR_TIME_CHAR: PRN_DBR_TIME(dbr_time_char); break; case DBR_TIME_LONG: PRN_DBR_TIME(dbr_time_long); break; case DBR_TIME_DOUBLE: PRN_DBR_TIME(dbr_time_double); break; case DBR_GR_CHAR: PRN_DBR_GR(dbr_gr_char, char, %8d); break; case DBR_GR_INT: PRN_DBR_GR(dbr_gr_int, int, %8d); break; case DBR_GR_LONG: PRN_DBR_GR(dbr_gr_long, long int, %8ld); break; case DBR_GR_FLOAT: PRN_DBR_GR_PREC(dbr_gr_float, float, %g); break; case DBR_GR_DOUBLE: PRN_DBR_GR_PREC(dbr_gr_double, double, %g); break; case DBR_GR_ENUM: PRN_DBR_X_ENUM(dbr_gr_enum); break; case DBR_CTRL_CHAR: PRN_DBR_CTRL(dbr_ctrl_char, char, %8d); break; case DBR_CTRL_INT: PRN_DBR_CTRL(dbr_ctrl_int, int, %8d); break; case DBR_CTRL_LONG: PRN_DBR_CTRL(dbr_ctrl_long, long int, %8ld); break; case DBR_CTRL_FLOAT: PRN_DBR_CTRL_PREC(dbr_ctrl_float, float, %g); break; case DBR_CTRL_DOUBLE: PRN_DBR_CTRL_PREC(dbr_ctrl_double, double, %g); break; case DBR_CTRL_ENUM: PRN_DBR_X_ENUM(dbr_ctrl_enum); break; case DBR_STSACK_STRING: PRN_DBR_STSACK(dbr_stsack_string); break; default : strcpy (str, "can't print data type"); } return str; } /*+************************************************************************** * * Function: print_time_val_sts * * Description: Print (to stdout) one wide output line * (name, timestamp, value, status, severity) * * Arg(s) In: pv - Pointer to pv structure * nElems - Number of elements (array) * **************************************************************************-*/ #define PRN_TIME_VAL_STS(TYPE,TYPE_ENUM) \ printAbs = !pv->firstStampPrinted; \ \ ptsNewS = &((struct TYPE *)value)->stamp; \ ptsNewC = &tsNow; \ \ if (!tsInitS) \ { \ tsFirst = *ptsNewS; \ tsInitS = 1; \ } \ \ switch (tsType) { \ case relative: \ ptsRefC = &tsStart; \ ptsRefS = &tsFirst; \ break; \ case incremental: \ ptsRefC = &tsPreviousC; \ ptsRefS = &tsPreviousS; \ break; \ case incrementalByChan: \ ptsRefC = &pv->tsPreviousC; \ ptsRefS = &pv->tsPreviousS; \ break; \ default : \ printAbs = 1; \ } \ \ if (printAbs) { \ if (tsSrcServer) { \ epicsTimeToStrftime(timeText, TIMETEXTLEN, timeFormatStr, ptsNewS); \ printf("%s", timeText); \ } \ if (tsSrcClient) { \ epicsTimeToStrftime(timeText, TIMETEXTLEN, timeFormatStr, ptsNewC); \ printf("(%s)", timeText); \ } \ pv->firstStampPrinted = 1; \ } else { \ if (tsSrcServer) { \ printf(" %+12.6f", epicsTimeDiffInSeconds(ptsNewS, ptsRefS) ); \ } \ if (tsSrcClient) { \ printf(" (%+12.6f)", epicsTimeDiffInSeconds(ptsNewC, ptsRefC) ); \ } \ } \ \ if (tsType == incrementalByChan) { \ pv->tsPreviousC = *ptsNewC; \ pv->tsPreviousS = *ptsNewS; \ } \ \ tsPreviousC = *ptsNewC; \ tsPreviousS = *ptsNewS; \ \ if (charArrAsStr && dbr_type_is_CHAR(TYPE_ENUM) && (reqElems || pv->nElems > 1)) { \ dbr_char_t *s = (dbr_char_t*) dbr_value_ptr(pv->value, pv->dbrType); \ size_t len = strlen((char*)s); \ unsigned long elems = reqElems && (reqElems < pv->nElems) ? reqElems : pv->nElems; \ int dlen; \ char *d; \ if (len < elems) elems = len; \ dlen = epicsStrnEscapedFromRawSize((char*)s, elems); \ d = calloc(dlen+1, sizeof(char)); \ if(d) { \ epicsStrnEscapedFromRaw(d, dlen+1, (char*)s, elems); \ printf("%c%s", fieldSeparator, d); \ free(d); \ } else { \ printf("Failed to allocate for print_time_val_sts\n"); \ } \ } else { \ if (reqElems || pv->nElems > 1) printf("%c%lu", fieldSeparator, pv->nElems); \ for (i=0; inElems; ++i) { \ printf("%c%s", fieldSeparator, val2str(value, TYPE_ENUM, i)); \ } \ } \ /* Print Status, Severity - if not NO_ALARM */ \ if ( ((struct TYPE *)value)->status || ((struct TYPE *)value)->severity ) \ { \ printf("%c%s%c%s\n", \ fieldSeparator, \ stat_to_str(((struct TYPE *)value)->status), \ fieldSeparator, \ sevr_to_str(((struct TYPE *)value)->severity)); \ } else { \ printf("%c%c\n", \ fieldSeparator, fieldSeparator); \ } void print_time_val_sts (pv* pv, unsigned long reqElems) { char timeText[2*TIMETEXTLEN+2]; int i, printAbs; void* value = pv->value; epicsTimeStamp *ptsRefC, *ptsRefS; /* Reference timestamps (client, server) */ epicsTimeStamp *ptsNewC, *ptsNewS; /* Update timestamps (client, server) */ epicsTimeStamp tsNow; epicsTimeGetCurrent(&tsNow); epicsTimeToStrftime(timeText, TIMETEXTLEN, timeFormatStr, &tsNow); if (pv->nElems <= 1 && fieldSeparator == ' ') printf("%-30s", pv->name); else printf("%s", pv->name); printf("%c", fieldSeparator); if (!pv->onceConnected) printf("*** Not connected (PV not found)\n"); else if (pv->status == ECA_DISCONN) printf("%s *** disconnected\n", timeText); else if (pv->status == ECA_NORDACCESS) printf("%s *** no read access\n", timeText); else if (pv->status != ECA_NORMAL) printf("%s *** CA error %s\n", timeText, ca_message(pv->status)); else if (pv->value == 0) printf("%s *** no data available (timeout)\n", timeText); else switch (pv->dbrType) { case DBR_TIME_STRING: PRN_TIME_VAL_STS(dbr_time_string, DBR_TIME_STRING); break; case DBR_TIME_SHORT: PRN_TIME_VAL_STS(dbr_time_short, DBR_TIME_SHORT); break; case DBR_TIME_FLOAT: PRN_TIME_VAL_STS(dbr_time_float, DBR_TIME_FLOAT); break; case DBR_TIME_ENUM: PRN_TIME_VAL_STS(dbr_time_enum, DBR_TIME_ENUM); break; case DBR_TIME_CHAR: PRN_TIME_VAL_STS(dbr_time_char, DBR_TIME_CHAR); break; case DBR_TIME_LONG: PRN_TIME_VAL_STS(dbr_time_long, DBR_TIME_LONG); break; case DBR_TIME_DOUBLE: PRN_TIME_VAL_STS(dbr_time_double, DBR_TIME_DOUBLE); break; default: printf("can't print data type\n"); } } /*+************************************************************************** * * Function: create_pvs * * Description: Creates an arbitrary number of PVs * * Arg(s) In: pvs - Pointer to an array of pv structures * nPvs - Number of elements in the pvs array * pCB - Connection state change callback * * Arg(s) Out: none * * Return(s): Error code: * 0 - All PVs created * 1 - Some PV(s) not created * **************************************************************************-*/ int create_pvs (pv* pvs, int nPvs, caCh *pCB) { int n; int result; int returncode = 0; if (!tsInitC) /* Initialize start timestamp */ { epicsTimeGetCurrent(&tsStart); tsInitC = 1; } /* Issue channel connections */ for (n = 0; n < nPvs; n++) { result = ca_create_channel (pvs[n].name, pCB, &pvs[n], caPriority, &pvs[n].chid); if (result != ECA_NORMAL) { fprintf(stderr, "CA error %s occurred while trying " "to create channel '%s'.\n", ca_message(result), pvs[n].name); pvs[n].status = result; returncode = 1; } } return returncode; } /*+************************************************************************** * * Function: connect_pvs * * Description: Connects an arbitrary number of PVs * * Arg(s) In: pvs - Pointer to an array of pv structures * nPvs - Number of elements in the pvs array * * Arg(s) Out: none * * Return(s): Error code: * 0 - All PVs connected * 1 - Some PV(s) not connected * **************************************************************************-*/ int connect_pvs (pv* pvs, int nPvs) { int returncode = create_pvs ( pvs, nPvs, 0); if ( returncode == 0 ) { /* Wait for channels to connect */ int result = ca_pend_io (caTimeout); if (result == ECA_TIMEOUT) { if (nPvs > 1) { fprintf(stderr, "Channel connect timed out: some PV(s) not found.\n"); } else { fprintf(stderr, "Channel connect timed out: '%s' not found.\n", pvs[0].name); } returncode = 1; } } return returncode; } base-7.0.3.1/modules/ca/src/tools/tool_lib.h0000664000577000060420000000736013557101274017357 0ustar anjaesctl/*************************************************************************\ * 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) 2002 Berliner Elektronenspeicherringgesellschaft fuer * Synchrotronstrahlung. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange (BESSY) * * Modification History * 2009/03/31 Larry Hoff (BNL) * Added field separators * */ #ifndef INCLtool_libh #define INCLtool_libh #include /* Convert status and severity to strings */ #define stat_to_str(stat) \ ((stat) >= 0 && (stat) <= (signed)lastEpicsAlarmCond) ? \ epicsAlarmConditionStrings[stat] : "??" #define sevr_to_str(stat) \ ((stat) >= 0 && (stat) <= (signed)lastEpicsAlarmSev) ? \ epicsAlarmSeverityStrings[stat] : "??" #define stat_to_str_unsigned(stat) \ ((stat) <= lastEpicsAlarmCond) ? \ epicsAlarmConditionStrings[stat] : "??" #define sevr_to_str_unsigned(stat) \ ((stat) <= lastEpicsAlarmSev) ? \ epicsAlarmSeverityStrings[stat] : "??" /* The different versions are necessary because stat and sevr are * defined unsigned in CA's DBR_STSACK structure and signed in all the * others. Some compilers generate warnings if you check an unsigned * being >=0 */ #define DEFAULT_CA_PRIORITY 0 /* Default CA priority */ #define DEFAULT_TIMEOUT 1.0 /* Default CA timeout */ #ifndef _WIN32 # define LINE_BUFFER(stream) setvbuf(stream, NULL, _IOLBF, BUFSIZ) #else /* Windows doesn't support line mode, turn buffering off completely */ # define LINE_BUFFER(stream) setvbuf(stream, NULL, _IONBF, 0) #endif /* Type of timestamp */ typedef enum { absolute, relative, incremental, incrementalByChan } TimeT; /* Output formats for integer data types */ typedef enum { dec, bin, oct, hex } IntFormatT; /* Structure representing one PV (= channel) */ typedef struct { char* name; chid chid; long dbfType; long dbrType; unsigned long nElems; // True length of data in value unsigned long reqElems; // Requested length of data int status; void* value; epicsTimeStamp tsPreviousC; epicsTimeStamp tsPreviousS; char firstStampPrinted; char onceConnected; } pv; extern TimeT tsType; /* Timestamp type flag (-t option) */ extern int tsSrcServer; /* Timestamp source flag (-t option) */ extern int tsSrcClient; /* Timestamp source flag (-t option) */ extern IntFormatT outTypeI; /* Flag used for -0.. output format option */ extern IntFormatT outTypeF; /* Flag used for -l.. output format option */ extern int enumAsNr; /* Used for -n option (get DBF_ENUM as number) */ extern int charArrAsStr; /* used for -S option - treat char array as (long) string */ extern double caTimeout; /* Wait time default (see -w option) */ extern char dblFormatStr[]; /* Format string to print doubles (see -e -f option) */ extern char fieldSeparator; /* Output field separator */ extern capri caPriority; /* CA priority */ extern char *val2str (const void *v, unsigned type, int index); extern char *dbr2str (const void *value, unsigned type); extern void print_time_val_sts (pv *pv, unsigned long reqElems); extern int create_pvs (pv *pvs, int nPvs, caCh *pCB ); extern int connect_pvs (pv *pvs, int nPvs ); /* * no additions below this endif */ #endif /* ifndef INCLtool_libh */ base-7.0.3.1/modules/database/Makefile0000664000577000060420000000116713557101274016274 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* TOP = ../.. include $(TOP)/configure/CONFIG DIRS += src DIRS += test test_DEPEND_DIRS = src include $(TOP)/configure/RULES_TOP base-7.0.3.1/modules/database/src/Makefile0000664000577000060420000000137213557101274017061 0ustar anjaesctl#************************************************************************* # Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* TOP = ../../.. include $(TOP)/configure/CONFIG # PDB Tools DIRS += tools # PDB Core DIRS += ioc ioc_DEPEND_DIRS = tools # PDB Standard Record Definitions DIRS += std std_DEPEND_DIRS = ioc # Templates DIRS += template include $(TOP)/configure/RULES_DIRS base-7.0.3.1/modules/database/src/ioc/Makefile0000664000577000060420000000340513557101274017632 0ustar anjaesctl#************************************************************************* # Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* IOCDIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) TOP = ../../../.. include $(TOP)/configure/CONFIG USR_CPPFLAGS += -DUSE_TYPED_RSET SHRLIB_VERSION = 3.17.0 LIBRARY_IOC += dbCore dbCore_LIBS += ca Com dbCore_SYS_LIBS_WIN32 += ws2_32 dbCore_RCS += dbCore.rc dbStaticHost_RCS = dbStaticHost.rc INC += databaseVersion.h INC += databaseVersionNum.h PROD_LIBS = Com include $(IOCDIR)/as/Makefile include $(IOCDIR)/bpt/Makefile include $(IOCDIR)/db/Makefile include $(IOCDIR)/dbStatic/Makefile include $(IOCDIR)/dbtemplate/Makefile include $(IOCDIR)/misc/Makefile include $(IOCDIR)/registry/Makefile include $(IOCDIR)/rsrv/Makefile EXPANDVARS += EPICS_DATABASE_MAJOR_VERSION EXPANDVARS += EPICS_DATABASE_MINOR_VERSION EXPANDVARS += EPICS_DATABASE_MAINTENANCE_VERSION EXPANDVARS += EPICS_DATABASE_DEVELOPMENT_FLAG EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))") include $(TOP)/configure/RULES include $(IOCDIR)/dbStatic/RULES include $(IOCDIR)/bpt/RULES include $(IOCDIR)/db/RULES include $(IOCDIR)/dbtemplate/RULES # Can't use EXPAND as generated headers must appear # in O.Common, but EXPAND emits rules for O.$(T_A) ../O.Common/databaseVersionNum.h: ../databaseVersionNum.h@ $(MKDIR) $(COMMON_DIR) $(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@ base-7.0.3.1/modules/database/src/ioc/as/Makefile0000664000577000060420000000156413557101274020241 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2010 Brookhaven Science Associates, as Operator of # Brookhaven National Lab. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. SRC_DIRS += $(IOCDIR)/as INC += asDbLib.h INC += asCa.h INC += asIocRegister.h dbCore_SRCS += asDbLib.c dbCore_SRCS += asCa.c dbCore_SRCS += asIocRegister.c PROD_HOST += ascheck ascheck_SRCS = ascheck.c ascheck_LIBS = dbCore ca Com base-7.0.3.1/modules/database/src/ioc/as/asCa.c0000664000577000060420000002477113557101274017621 0ustar anjaesctl/*asCa.c*/ /*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Marty Kraimer Date: 10-15-93 */ /*This module is separate from asDbLib because CA uses old database access*/ #include #include #include #include #include "alarm.h" #include "asLib.h" #include "cantProceed.h" #include "db_access.h" #include "dbDefs.h" #include "ellLib.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "epicsStdio.h" #include "epicsThread.h" #include "errlog.h" #include "taskwd.h" #include "cadef.h" #include "caerr.h" #include "caeventmask.h" #define epicsExportSharedSymbols #include "asCa.h" #include "asDbLib.h" #include "callback.h" #include "epicsExport.h" int asCaDebug = 0; epicsExportAddress(int,asCaDebug); static int firstTime = TRUE; static epicsThreadId threadid=0; static int caInitializing=FALSE; static epicsMutexId asCaTaskLock; /*lock access to task */ static epicsEventId asCaTaskWait; /*Wait for task to respond*/ static epicsEventId asCaTaskAddChannels; /*Tell asCaTask to add channels*/ static epicsEventId asCaTaskClearChannels;/*Tell asCaTask to clear channels*/ typedef struct { struct dbr_sts_double rtndata; chid chid; } CAPVT; static void exceptionCallback(struct exception_handler_args args) { chid chid = args.chid; long stat = args.stat; /* Channel access status code*/ const char *channel; const char *context; static char *unknown = "unknown"; const char *nativeType; const char *requestType; long nativeCount; long requestCount; int readAccess; int writeAccess; channel = (chid ? ca_name(chid) : unknown); context = (args.ctx ? args.ctx : unknown); nativeType = dbr_type_to_text((chid ? ca_field_type(chid) : -1)); requestType = dbr_type_to_text(args.type); nativeCount = (chid ? ca_element_count(chid) : 0); requestCount = args.count; readAccess = (chid ? ca_read_access(chid) : 0); writeAccess = (chid ? ca_write_access(chid) : 0); errlogPrintf("dbCa:exceptionCallback stat \"%s\" channel \"%s\"" " context \"%s\"\n" " nativeType %s requestType %s" " nativeCount %ld requestCount %ld %s %s\n", ca_message(stat),channel,context, nativeType,requestType, nativeCount,requestCount, (readAccess ? "readAccess" : "noReadAccess"), (writeAccess ? "writeAccess" : "noWriteAccess")); } /*connectCallback only handles disconnects*/ static void connectCallback(struct connection_handler_args arg) { chid chid = arg.chid; ASGINP *pasginp = (ASGINP *)ca_puser(chid); ASG *pasg = pasginp->pasg; if(ca_state(chid)!=cs_conn) { if(!(pasg->inpBad & (1<inpIndex))) { /*was good so lets make it bad*/ pasg->inpBad |= (1<inpIndex); if(!caInitializing) asComputeAsg(pasg); if(asCaDebug) printf("as connectCallback disconnect %s\n", ca_name(chid)); } } } static void eventCallback(struct event_handler_args arg) { int caStatus = arg.status; chid chid = arg.chid; ASGINP *pasginp = (ASGINP *)arg.usr; ASG *pasg; CAPVT *pcapvt; const struct dbr_sts_double *pdata; if(caStatus!=ECA_NORMAL) { if(chid) { epicsPrintf("asCa: eventCallback error %s channel %s\n", ca_message(caStatus),ca_name(chid)); } else { epicsPrintf("asCa: eventCallback error %s chid is null\n", ca_message(caStatus)); } return; } pasg = pasginp->pasg; pcapvt = (CAPVT *)pasginp->capvt; if(chid!=pcapvt->chid) { epicsPrintf("asCa: eventCallback error pcapvt->chid != arg.chid\n"); return; } if(ca_state(chid)!=cs_conn || !ca_read_access(chid)) { if(!(pasg->inpBad & (1<inpIndex))) { /*was good so lets make it bad*/ pasg->inpBad |= (1<inpIndex); if(!caInitializing) asComputeAsg(pasg); if(asCaDebug) { printf("as eventCallback %s inpBad ca_state %d" " ca_read_access %d\n", ca_name(chid),ca_state(chid),ca_read_access(chid)); } } return; } pdata = arg.dbr; pcapvt->rtndata = *pdata; /*structure copy*/ if(pdata->severity==INVALID_ALARM) { pasg->inpBad |= (1<inpIndex); if(asCaDebug) printf("as eventCallback %s inpBad because INVALID_ALARM\n", ca_name(chid)); } else { pasg->inpBad &= ~((1<inpIndex)); pasg->pavalue[pasginp->inpIndex] = pdata->value; if(asCaDebug) printf("as eventCallback %s inpGood data %f\n", ca_name(chid),pdata->value); } pasg->inpChanged |= (1<inpIndex); if(!caInitializing) asComputeAsg(pasg); } static void asCaTask(void) { ASG *pasg; ASGINP *pasginp; CAPVT *pcapvt; int status; taskwdInsert(epicsThreadGetIdSelf(),NULL,NULL); SEVCHK(ca_context_create(ca_enable_preemptive_callback), "asCaTask calling ca_context_create"); SEVCHK(ca_add_exception_event(exceptionCallback,NULL), "ca_add_exception_event"); while(TRUE) { epicsEventMustWait(asCaTaskAddChannels); caInitializing = TRUE; pasg = (ASG *)ellFirst(&pasbase->asgList); while(pasg) { pasginp = (ASGINP *)ellFirst(&pasg->inpList); while(pasginp) { pasg->inpBad |= (1<inpIndex); pcapvt = pasginp->capvt = asCalloc(1,sizeof(CAPVT)); /*Note calls connectCallback immediately for local Pvs*/ status = ca_search_and_connect(pasginp->inp,&pcapvt->chid, connectCallback,pasginp); if(status!=ECA_NORMAL) { epicsPrintf("asCa ca_search_and_connect error %s\n", ca_message(status)); } /*Note calls eventCallback immediately for local Pvs*/ status = ca_add_event(DBR_STS_DOUBLE,pcapvt->chid, eventCallback,pasginp,0); if(status!=ECA_NORMAL) { epicsPrintf("asCa ca_add_event error %s\n", ca_message(status)); } pasginp = (ASGINP *)ellNext((ELLNODE *)pasginp); } pasg = (ASG *)ellNext((ELLNODE *)pasg); } SEVCHK(ca_flush_io(),"asCaTask"); caInitializing = FALSE; asComputeAllAsg(); if(asCaDebug) printf("asCaTask initialized\n"); epicsEventSignal(asCaTaskWait); epicsEventMustWait(asCaTaskClearChannels); pasg = (ASG *)ellFirst(&pasbase->asgList); while(pasg) { pasginp = (ASGINP *)ellFirst(&pasg->inpList); while(pasginp) { pcapvt = (CAPVT *)pasginp->capvt; status = ca_clear_channel(pcapvt->chid); if(status!=ECA_NORMAL) { epicsPrintf("asCa ca_clear_channel error %s\n", ca_message(status)); } free(pasginp->capvt); pasginp->capvt = 0; pasginp = (ASGINP *)ellNext((ELLNODE *)pasginp); } pasg = (ASG *)ellNext((ELLNODE *)pasg); } if(asCaDebug) printf("asCaTask has cleared all channels\n"); epicsEventSignal(asCaTaskWait); } } void asCaStart(void) { epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; opts.stackSize = epicsThreadGetStackSize(epicsThreadStackBig); opts.priority = epicsThreadPriorityScanLow - 3; opts.joinable = 1; if(asCaDebug) printf("asCaStart called\n"); if(firstTime) { firstTime = FALSE; asCaTaskLock=epicsMutexMustCreate(); asCaTaskWait=epicsEventMustCreate(epicsEventEmpty); asCaTaskAddChannels=epicsEventMustCreate(epicsEventEmpty); asCaTaskClearChannels=epicsEventMustCreate(epicsEventEmpty); threadid = epicsThreadCreateOpt("asCaTask", (EPICSTHREADFUNC)asCaTask, 0, &opts); if(threadid==0) { errMessage(0,"asCaStart: taskSpawn Failure\n"); } } epicsMutexMustLock(asCaTaskLock); epicsEventSignal(asCaTaskAddChannels); epicsEventMustWait(asCaTaskWait); if(asCaDebug) printf("asCaStart done\n"); epicsMutexUnlock(asCaTaskLock); } void asCaStop(void) { if(threadid==0) return; if(asCaDebug) printf("asCaStop called\n"); epicsMutexMustLock(asCaTaskLock); epicsEventSignal(asCaTaskClearChannels); epicsEventMustWait(asCaTaskWait); if(asCaDebug) printf("asCaStop done\n"); epicsMutexUnlock(asCaTaskLock); epicsThreadMustJoin(threadid); threadid = 0; } int ascar(int level) { return ascarFP(stdout,level);} int ascarFP(FILE *fp,int level) { ASG *pasg; int n=0,nbad=0; enum channel_state state; if(!pasbase) { fprintf(fp,"access security not started\n"); return(0); } pasg = (ASG *)ellFirst(&pasbase->asgList); while(pasg) { ASGINP *pasginp; pasginp = (ASGINP *)ellFirst(&pasg->inpList); while(pasginp) { CAPVT *pcapvt = (CAPVT *)pasginp->capvt; chid chid = pcapvt->chid; pcapvt = pasginp->capvt; ++n; state = ca_state(chid); if(state!=cs_conn) ++nbad; if(level>1 || (level==1 && state!=cs_conn)) { fprintf(fp,"connected:"); if(state==cs_never_conn) fprintf(fp,"never "); else if(state==cs_prev_conn) fprintf(fp,"prev "); else if(state==cs_conn) fprintf(fp,"yes "); else if(state==cs_closed) fprintf(fp,"closed"); else fprintf(fp,"unknown"); fprintf(fp," read:%s write:%s", (ca_read_access(chid) ? "yes" : "no "), (ca_write_access(chid) ? "yes" : "no ")); fprintf(fp," %s %s\n", ca_name(chid),ca_host_name(chid)); } pasginp = (ASGINP *)ellNext((ELLNODE *)pasginp); } pasg = (ASG *)ellNext((ELLNODE *)pasg); } fprintf(fp,"%d channels %d not connected\n",n,nbad); return(0); } void ascaStats(int *pchans, int *pdiscon) { ASG *pasg; int n = 0; int nbad = 0; if(!pasbase) { if (pchans) *pchans = n; if (pdiscon) *pdiscon = nbad; return; } pasg = (ASG *)ellFirst(&pasbase->asgList); while (pasg) { ASGINP *pasginp; pasginp = (ASGINP *)ellFirst(&pasg->inpList); while (pasginp) { CAPVT *pcapvt = (CAPVT *)pasginp->capvt; chid chid = pcapvt->chid; ++n; if (ca_state(chid) != cs_conn) ++nbad; pasginp = (ASGINP *)ellNext((ELLNODE *)pasginp); } pasg = (ASG *)ellNext((ELLNODE *)pasg); } if (pchans) *pchans = n; if (pdiscon) *pdiscon = nbad; } base-7.0.3.1/modules/database/src/ioc/as/asCa.h0000664000577000060420000000155513557101274017621 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* asCa.h */ #ifndef INCasCah #define INCasCah #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void asCaStart(void); epicsShareFunc void asCaStop(void); epicsShareFunc int ascar(int level); epicsShareFunc int ascarFP(FILE *fp, int level); epicsShareFunc void ascaStats(int *pchans, int *pdiscon); #ifdef __cplusplus } #endif #endif /*INCasCah*/ base-7.0.3.1/modules/database/src/ioc/as/asDbLib.c0000664000577000060420000001761613557101274020252 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Marty Kraimer Date: 02-11-94*/ #include #include #include #include #include "alarm.h" #include "asLib.h" #include "cantProceed.h" #include "dbDefs.h" #include "epicsStdio.h" #include "epicsThread.h" #include "errlog.h" #include "taskwd.h" #include "caeventmask.h" #define epicsExportSharedSymbols #include "asCa.h" #include "asDbLib.h" #include "callback.h" #include "dbAccess.h" #include "dbChannel.h" #include "dbCommon.h" #include "dbEvent.h" #include "db_field_log.h" #include "dbStaticLib.h" #include "recSup.h" static char *pacf=NULL; static char *psubstitutions=NULL; static epicsThreadId asInitTheadId=0; static int firstTime = TRUE; static long asDbAddRecords(void) { DBENTRY dbentry; DBENTRY *pdbentry=&dbentry; long status; dbCommon *precord; dbInitEntry(pdbbase,pdbentry); status = dbFirstRecordType(pdbentry); while(!status) { status = dbFirstRecord(pdbentry); while(!status) { precord = pdbentry->precnode->precord; if(!precord->asp) { status = asAddMember(&precord->asp, precord->asg); if(status) errMessage(status,"asDbAddRecords:asAddMember"); asPutMemberPvt(precord->asp,precord); } status = dbNextRecord(pdbentry); } status = dbNextRecordType(pdbentry); } dbFinishEntry(pdbentry); return(0); } int asSetFilename(const char *acf) { if (pacf) free (pacf); if (acf) { pacf = calloc(1, strlen(acf)+1); if (!pacf) { errMessage(0, "asSetFilename calloc failure"); } else { strcpy(pacf, acf); if (*pacf != '/' && !strchr(pacf, ':')) { printf("asSetFilename: Warning - relative paths won't usually " "work\n"); } } } else { pacf = NULL; } return 0; } int asSetSubstitutions(const char *substitutions) { if(psubstitutions) free ((void *)psubstitutions); if(substitutions) { psubstitutions = calloc(1,strlen(substitutions)+1); if(!psubstitutions) { errMessage(0,"asSetSubstitutions calloc failure"); } else { strcpy(psubstitutions,substitutions); } } else { psubstitutions = NULL; } return(0); } static void asSpcAsCallback(struct dbCommon *precord) { asChangeGroup(&precord->asp, precord->asg); } static void asInitCommonOnce(void *arg) { int *firstTime = (int *)arg; *firstTime = FALSE; } static long asInitCommon(void) { long status; int asWasActive = asActive; int wasFirstTime = firstTime; static epicsThreadOnceId asInitCommonOnceFlag = EPICS_THREAD_ONCE_INIT; epicsThreadOnce(&asInitCommonOnceFlag,asInitCommonOnce,(void *)&firstTime); if(wasFirstTime) { if(!pacf) return(0); /*access security will NEVER be turned on*/ } else { if(!asActive) { printf("Access security is NOT enabled." " Was asSetFilename specified before iocInit?\n"); return(S_asLib_asNotActive); } if(pacf) { asCaStop(); } else { /*Just leave everything as is */ return(S_asLib_badConfig); } } status = asInitFile(pacf,psubstitutions); if(asActive) { if(!asWasActive) { dbSpcAsRegisterCallback(asSpcAsCallback); asDbAddRecords(); } asCaStart(); } return(status); } int asInit(void) { return(asInitCommon()); } int asShutdown(void) { volatile ASBASE *pbase = pasbase; pasbase = NULL; firstTime = TRUE; if(pbase) asFreeAll((ASBASE*)pbase); return 0; } static void wdCallback(void *arg) { ASDBCALLBACK *pcallback = (ASDBCALLBACK *)arg; pcallback->status = S_asLib_InitFailed; callbackRequest(&pcallback->callback); } static void asInitTask(ASDBCALLBACK *pcallback) { long status; taskwdInsert(epicsThreadGetIdSelf(), wdCallback, (void *)pcallback); status = asInitCommon(); taskwdRemove(epicsThreadGetIdSelf()); asInitTheadId = 0; if(pcallback) { pcallback->status = status; callbackRequest(&pcallback->callback); } } int asInitAsyn(ASDBCALLBACK *pcallback) { if(!pacf) return(0); if(asInitTheadId) { errMessage(-1,"asInit: asInitTask already active"); if(pcallback) { pcallback->status = S_asLib_InitFailed; callbackRequest(&pcallback->callback); } return(-1); } asInitTheadId = epicsThreadCreate("asInitTask", (epicsThreadPriorityCAServerHigh + 1), epicsThreadGetStackSize(epicsThreadStackBig), (EPICSTHREADFUNC)asInitTask,(void *)pcallback); if(asInitTheadId==0) { errMessage(0,"asInit: epicsThreadCreate Error"); if(pcallback) { pcallback->status = S_asLib_InitFailed; callbackRequest(&pcallback->callback); } asInitTheadId = 0; } return(0); } int asDbGetAsl(struct dbChannel *chan) { return dbChannelFldDes(chan)->as_level; } void * asDbGetMemberPvt(struct dbChannel *chan) { return dbChannelRecord(chan)->asp; } static void astacCallback(ASCLIENTPVT clientPvt,asClientStatus status) { char *recordname; recordname = (char *)asGetClientPvt(clientPvt); printf("astac callback %s: status=%d",recordname,status); printf(" get %s put %s\n",(asCheckGet(clientPvt) ? "Yes" : "No"), (asCheckPut(clientPvt) ? "Yes" : "No")); } int astac(const char *pname,const char *user,const char *location) { DBADDR *paddr; long status; ASCLIENTPVT *pasclientpvt=NULL; dbCommon *precord; dbFldDes *pflddes; char *puser; char *plocation; if (!pname || !user || !location){ printf("Usage: astac \"record name\", \"user\", \"host\"\n"); return(1); } paddr = dbCalloc(1,sizeof(DBADDR) + sizeof(ASCLIENTPVT)); pasclientpvt = (ASCLIENTPVT *)(paddr + 1); status=dbNameToAddr(pname,paddr); if(status) { errMessage(status,"dbNameToAddr error"); return(1); } precord = paddr->precord; pflddes = paddr->pfldDes; puser = asCalloc(1,strlen(user)+1); strcpy(puser,user); plocation = asCalloc(1,strlen(location)+1); strcpy(plocation,location); status = asAddClient(pasclientpvt,precord->asp, (int)pflddes->as_level,puser,plocation); if(status) { errMessage(status,"asAddClient error"); return(1); } else { asPutClientPvt(*pasclientpvt,(void *)precord->name); asRegisterClientCallback(*pasclientpvt,astacCallback); } return(0); } static void myMemberCallback(ASMEMBERPVT memPvt,FILE *fp) { dbCommon *precord; precord = asGetMemberPvt(memPvt); if(precord) fprintf(fp," Record:%s",precord->name); } int asdbdump(void) { asDumpFP(stdout,myMemberCallback,NULL,1); return(0); } int asdbdumpFP(FILE *fp) { asDumpFP(fp,myMemberCallback,NULL,1); return(0); } int aspuag(const char *uagname) { asDumpUagFP(stdout,uagname); return(0); } int aspuagFP(FILE *fp,const char *uagname) { asDumpUagFP(fp,uagname); return(0); } int asphag(const char *hagname) { asDumpHagFP(stdout,hagname); return(0); } int asphagFP(FILE *fp,const char *hagname) { asDumpHagFP(fp,hagname); return(0); } int asprules(const char *asgname) { asDumpRulesFP(stdout,asgname); return(0); } int asprulesFP(FILE *fp,const char *asgname) { asDumpRulesFP(fp,asgname); return(0); } int aspmem(const char *asgname,int clients) { asDumpMemFP(stdout,asgname,myMemberCallback,clients); return(0); } int aspmemFP(FILE *fp,const char *asgname,int clients) { asDumpMemFP(fp,asgname,myMemberCallback,clients); return(0); } base-7.0.3.1/modules/database/src/ioc/as/asDbLib.h0000664000577000060420000000347013557101274020250 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* Author: Marty Kraimer Date: 02-23-94*/ #ifndef INCdbAsLibh #define INCdbAsLibh #include "callback.h" #include "shareLib.h" typedef struct { epicsCallback callback; long status; } ASDBCALLBACK; struct dbChannel; #ifdef __cplusplus extern "C" { #endif epicsShareFunc int asSetFilename(const char *acf); epicsShareFunc int asSetSubstitutions(const char *substitutions); epicsShareFunc int asInit(void); epicsShareFunc int asInitAsyn(ASDBCALLBACK *pcallback); epicsShareFunc int asShutdown(void); epicsShareFunc int asDbGetAsl(struct dbChannel *chan); epicsShareFunc void * asDbGetMemberPvt(struct dbChannel *chan); epicsShareFunc int asdbdump(void); epicsShareFunc int asdbdumpFP(FILE *fp); epicsShareFunc int aspuag(const char *uagname); epicsShareFunc int aspuagFP(FILE *fp,const char *uagname); epicsShareFunc int asphag(const char *hagname); epicsShareFunc int asphagFP(FILE *fp,const char *hagname); epicsShareFunc int asprules(const char *asgname); epicsShareFunc int asprulesFP(FILE *fp,const char *asgname); epicsShareFunc int aspmem(const char *asgname,int clients); epicsShareFunc int aspmemFP( FILE *fp,const char *asgname,int clients); epicsShareFunc int astac( const char *recordname,const char *user,const char *location); #ifdef __cplusplus } #endif #endif /*INCdbAsLibh*/ base-7.0.3.1/modules/database/src/ioc/as/asIocRegister.c0000664000577000060420000001056413557101274021510 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "asLib.h" #include "iocsh.h" #define epicsExportSharedSymbols #include "asCa.h" #include "asDbLib.h" #include "asIocRegister.h" /* asSetFilename */ static const iocshArg asSetFilenameArg0 = { "ascf",iocshArgString}; static const iocshArg * const asSetFilenameArgs[] = {&asSetFilenameArg0}; static const iocshFuncDef asSetFilenameFuncDef = {"asSetFilename",1,asSetFilenameArgs}; static void asSetFilenameCallFunc(const iocshArgBuf *args) { asSetFilename(args[0].sval); } /* asSetSubstitutions */ static const iocshArg asSetSubstitutionsArg0 = { "substitutions",iocshArgString}; static const iocshArg * const asSetSubstitutionsArgs[] = {&asSetSubstitutionsArg0}; static const iocshFuncDef asSetSubstitutionsFuncDef = {"asSetSubstitutions",1,asSetSubstitutionsArgs}; static void asSetSubstitutionsCallFunc(const iocshArgBuf *args) { asSetSubstitutions(args[0].sval); } /* asInit */ static const iocshFuncDef asInitFuncDef = {"asInit",0}; static void asInitCallFunc(const iocshArgBuf *args) { iocshSetError(asInit()); } /* asdbdump */ static const iocshFuncDef asdbdumpFuncDef = {"asdbdump",0}; static void asdbdumpCallFunc(const iocshArgBuf *args) { asdbdump(); } /* aspuag */ static const iocshArg aspuagArg0 = { "uagname",iocshArgString}; static const iocshArg * const aspuagArgs[] = {&aspuagArg0}; static const iocshFuncDef aspuagFuncDef = {"aspuag",1,aspuagArgs}; static void aspuagCallFunc(const iocshArgBuf *args) { aspuag(args[0].sval); } /* asphag */ static const iocshArg asphagArg0 = { "hagname",iocshArgString}; static const iocshArg * const asphagArgs[] = {&asphagArg0}; static const iocshFuncDef asphagFuncDef = {"asphag",1,asphagArgs}; static void asphagCallFunc(const iocshArgBuf *args) { asphag(args[0].sval); } /* asprules */ static const iocshArg asprulesArg0 = { "asgname",iocshArgString}; static const iocshArg * const asprulesArgs[] = {&asprulesArg0}; static const iocshFuncDef asprulesFuncDef = {"asprules",1,asprulesArgs}; static void asprulesCallFunc(const iocshArgBuf *args) { asprules(args[0].sval); } /* aspmem */ static const iocshArg aspmemArg0 = { "asgname",iocshArgString}; static const iocshArg aspmemArg1 = { "clients",iocshArgInt}; static const iocshArg * const aspmemArgs[] = {&aspmemArg0,&aspmemArg1}; static const iocshFuncDef aspmemFuncDef = {"aspmem",2,aspmemArgs}; static void aspmemCallFunc(const iocshArgBuf *args) { aspmem(args[0].sval,args[1].ival); } /* astac */ static const iocshArg astacArg0 = { "recordname",iocshArgString}; static const iocshArg astacArg1 = { "user",iocshArgString}; static const iocshArg astacArg2 = { "location",iocshArgString}; static const iocshArg * const astacArgs[] = {&astacArg0,&astacArg1,&astacArg2}; static const iocshFuncDef astacFuncDef = {"astac",3,astacArgs}; static void astacCallFunc(const iocshArgBuf *args) { astac(args[0].sval,args[1].sval,args[2].sval); } /* ascar */ static const iocshArg ascarArg0 = { "level",iocshArgInt}; static const iocshArg * const ascarArgs[] = {&ascarArg0}; static const iocshFuncDef ascarFuncDef = {"ascar",1,ascarArgs}; static void ascarCallFunc(const iocshArgBuf *args) { ascar(args[0].ival); } /* asDumpHash */ static const iocshFuncDef asDumpHashFuncDef = {"asDumpHash",0,0}; static void asDumpHashCallFunc(const iocshArgBuf *args) { asDumpHash(); } void asIocRegister(void) { iocshRegister(&asSetFilenameFuncDef,asSetFilenameCallFunc); iocshRegister(&asSetSubstitutionsFuncDef,asSetSubstitutionsCallFunc); iocshRegister(&asInitFuncDef,asInitCallFunc); iocshRegister(&asdbdumpFuncDef,asdbdumpCallFunc); iocshRegister(&aspuagFuncDef,aspuagCallFunc); iocshRegister(&asphagFuncDef,asphagCallFunc); iocshRegister(&asprulesFuncDef,asprulesCallFunc); iocshRegister(&aspmemFuncDef,aspmemCallFunc); iocshRegister(&astacFuncDef,astacCallFunc); iocshRegister(&ascarFuncDef,ascarCallFunc); iocshRegister(&asDumpHashFuncDef,asDumpHashCallFunc); } base-7.0.3.1/modules/database/src/ioc/as/asIocRegister.h0000664000577000060420000000132313557101274021506 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_asIocRegister_H #define INC_asIocRegister_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void asIocRegister(void); #ifdef __cplusplus } #endif #endif /* INC_asIocRegister_H */ base-7.0.3.1/modules/database/src/ioc/as/ascheck.c0000664000577000060420000000307613557101274020346 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Marty Kraimer Date: 03-24-94 */ #include #include #include #include #include "asLib.h" #include "dbStaticLib.h" #include "errlog.h" int main(int argc,char **argv) { int argn = 1; char *sub = NULL; int subLength = 0; char **pstr; char *psep; int *len; long status = 0; static char *subSep = ","; /* Look for -Smacro=value options */ while (argc>argn && (strncmp(argv[argn], "-S", 2)==0)) { pstr = ⊂ psep = subSep; len = &subLength; if (strlen(argv[argn])==2) { dbCatString(pstr, len, argv[++argn], psep); } else { dbCatString(pstr, len, argv[argn]+2, psep); } argn++; } if (argc == argn) { status = asInitFP(stdin, sub); if(status) errlogPrintf("ascheck: Access Security File failed.\n"); } else if (argc == argn+1) { status = asInitFile(argv[argn], sub); if(status) errlogPrintf("ascheck: Access Security File failed.\n"); } else { printf("usage: ascheck [-Smac=sub ...] [<] file\n"); status = -1; } errlogFlush(); return status; } base-7.0.3.1/modules/database/src/ioc/bpt/Makefile0000664000577000060420000000146713557101274020425 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. SRC_DIRS += $(IOCDIR)/bpt INC += cvtTable.h DBDINC += menuConvert BPT_DBD += bptTypeJdegC.dbd BPT_DBD += bptTypeJdegF.dbd BPT_DBD += bptTypeKdegC.dbd BPT_DBD += bptTypeKdegF.dbd DBD += $(BPT_DBD) PROD_HOST += makeBpt makeBpt_SRCS = makeBpt HTMLS += menuConvert.html base-7.0.3.1/modules/database/src/ioc/bpt/RULES0000664000577000060420000000133613557101274017575 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2010 Brookhaven Science Associates, as Operator of # Brookhaven National Lab. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. $(patsubst %,$(COMMON_DIR)/%,$(BPT_DBD)) : \ $(COMMON_DIR)/bpt%.dbd : $(MAKEBPT) base-7.0.3.1/modules/database/src/ioc/bpt/bptTypeJdegC.data0000664000577000060420000001506013557101274022136 0ustar anjaesctl!header "typeJdegC" 0 0 700 4095 .5 -210 760 1 !data -8.096 -8.076 -8.057 -8.037 -8.017 -7.996 -7.976 -7.955 -7.934 -7.912 -7.890 -7.868 -7.846 -7.824 -7.801 -7.778 -7.755 -7.731 -7.707 -7.683 -7.659 -7.634 -7.609 -7.584 -7.559 -7.533 -7.508 -7.482 -7.455 -7.429 -7.402 -7.375 -7.348 -7.321 -7.293 -7.265 -7.237 -7.209 -7.180 -7.151 -7.122 -7.093 -7.064 -7.034 -7.004 -6.974 -6.944 -6.914 -6.883 -6.852 -6.821 -6.790 -6.758 -6.727 -6.695 -6.663 -6.630 -6.598 -6.565 -6.532 -6.499 -6.466 -6.433 -6.399 -6.365 -6.331 -6.297 -6.263 -6.228 -6.194 -6.159 -6.124 -6.089 -6.053 -6.018 -5.982 -5.946 -5.910 -5.874 -5.837 -5.801 -5.764 -5.727 -5.690 -5.653 -5.615 -5.578 -5.540 -5.502 -5.464 -5.426 -5.388 -5.349 -5.311 -5.272 -5.233 -5.194 -5.155 -5.115 -5.076 -5.036 -4.996 -4.956 -4.916 -4.876 -4.836 -4.795 -4.755 -4.714 -4.673 -4.632 -4.591 -4.550 -4.508 -4.467 -4.425 -4.383 -4.341 -4.299 -4.257 -4.215 -4.172 -4.130 -4.087 -4.044 -4.001 -3.958 -3.915 -3.872 -3.829 -3.785 -3.742 -3.698 -3.654 -3.610 -3.566 -3.522 -3.478 -3.433 -3.389 -3.344 -3.299 -3.255 -3.210 -3.165 -3.120 -3.074 -3.029 -2.984 -2.938 -2.892 -2.847 -2.801 -2.755 -2.709 -2.663 -2.617 -2.570 -2.524 -2.478 -2.431 -2.384 -2.338 -2.291 -2.244 -2.197 -2.150 -2.102 -2.055 -2.008 -1.960 -1.913 -1.865 -1.818 -1.770 -1.722 -1.674 -1.626 -1.578 -1.530 -1.481 -1.433 -1.385 -1.336 -1.288 -1.239 -1.190 -1.141 -1.093 -1.044 -0.995 -0.945 -0.896 -0.847 -0.798 -0.748 -0.699 -0.650 -0.600 -0.550 -0.501 -0.451 -0.401 -0.351 -0.301 -0.251 -0.201 -0.151 -0.101 -0.050 0.0 0.050 0.101 0.151 0.202 0.253 0.303 0.354 0.405 0.456 0.507 0.558 0.609 0.660 0.711 0.762 0.813 0.865 0.916 0.967 1.019 1.070 1.122 1.174 1.225 1.277 1.329 1.381 1.432 1.484 1.536 1.588 1.640 1.693 1.745 1.797 1.849 1.901 1.954 2.006 2.058 2.111 2.163 2.216 2.268 2.321 2.374 2.426 2.479 2.532 2.585 2.638 2.691 2.743 2.796 2.849 2.902 2.956 3.009 3.062 3.115 3.168 3.221 3.275 3.328 3.381 3.435 3.488 3.542 3.595 3.649 3.702 3.756 3.809 3.863 3.917 3.971 4.024 4.078 4.132 4.186 4.239 4.293 4.347 4.401 4.455 4.509 4.563 4.617 4.671 4.725 4.780 4.834 4.888 4.942 4.996 5.050 5.105 5.159 5.213 5.268 5.322 5.376 5.431 5.485 5.540 5.594 5.649 5.703 5.758 5.812 5.867 5.921 5.976 6.031 6.085 6.140 6.195 6.249 6.304 6.359 6.414 6.468 6.523 6.578 6.633 6.688 6.742 6.797 6.852 6.907 6.962 7.017 7.072 7.127 7.182 7.237 7.292 7.347 7.402 7.457 7.512 7.567 7.622 7.677 7.732 7.787 7.843 7.898 7.953 8.008 8.063 8.118 8.174 8.229 8.284 8.339 8.394 8.450 8.505 8.560 8.616 8.671 8.726 8.781 8.837 8.892 8.947 9.003 9.058 9.113 9.169 9.224 9.279 9.335 9.390 9.446 9.501 9.556 9.612 9.667 9.723 9.778 9.834 9.889 9.944 10.000 10.055 10.111 10.166 10.222 10.277 10.333 10.388 10.444 10.499 10.555 10.610 10.666 10.721 10.777 10.832 10.888 10.943 10.999 11.054 11.110 11.165 11.221 11.276 11.332 11.387 11.443 11.498 11.554 11.609 11.665 11.720 11.776 11.831 11.887 11.943 11.998 12.054 12.109 12.165 12.220 12.276 12.331 12.387 12.442 12.498 12.553 12.609 12.664 12.720 12.776 12.831 12.887 12.942 12.998 13.053 13.109 13.164 13.220 13.275 13.331 13.386 13.442 13.497 13.553 13.608 13.664 13.719 13.775 13.830 13.886 13.941 13.997 14.052 14.108 14.163 14.219 14.274 14.330 14.385 14.441 14.496 14.552 14.607 14.663 14.718 14.774 14.829 14.885 14.940 14.995 15.051 15.106 15.162 15.217 15.273 15.328 15.383 15.439 15.494 15.550 15.605 15.661 15.716 15.771 15.827 15.882 15.938 15.993 16.048 16.104 16.159 16.214 16.270 16.325 16.380 16.436 16.491 16.547 16.602 16.657 16.713 16.768 16.823 16.879 16.934 16.989 17.044 17.100 17.155 17.210 17.266 17.321 17.376 17.432 17.487 17.542 17.597 17.653 17.708 17.763 17.818 17.874 17.929 17.984 18.039 18.095 18.150 18.205 18.260 18.316 18.371 18.426 18.481 18.537 18.592 18.647 18.702 18.757 18.813 18.868 18.923 18.978 19.033 19.089 19.144 19.199 19.254 19.309 19.364 19.420 19.475 19.530 19.585 19.640 19.695 19.751 19.806 19.861 19.916 19.971 20.026 20.081 20.137 20.192 20.247 20.302 20.357 20.412 20.467 20.523 20.578 20.633 20.688 20.743 20.798 20.853 20.909 20.964 21.019 21.074 21.129 21.184 21.239 21.295 21.350 21.405 21.460 21.515 21.570 21.625 21.680 21.736 21.791 21.846 21.901 21.956 22.011 22.066 22.122 22.177 22.232 22.287 22.342 22.397 22.453 22.508 22.563 22.618 22.673 22.728 22.784 22.839 22.894 22.949 23.004 23.060 23.115 23.170 23.225 23.280 23.336 23.391 23.446 23.501 23.556 23.612 23.667 23.722 23.777 23.833 23.888 23.943 23.999 24.054 24.109 24.164 24.220 24.275 24.330 24.386 24.441 24.496 24.552 24.607 24.662 24.718 24.773 24.829 24.884 24.939 24.995 25.050 25.106 25.161 25.217 25.272 25.327 25.383 25.438 25.494 25.549 25.605 25.661 25.716 25.772 25.827 25.883 25.938 25.994 26.050 26.105 26.161 26.216 26.272 26.328 26.383 26.439 26.495 26.551 26.606 26.662 26.718 26.774 26.829 26.885 26.941 26.997 27.053 27.109 27.165 27.220 27.276 27.332 27.388 27.444 27.500 27.556 27.612 27.668 27.724 27.780 27.836 27.893 27.949 28.005 28.061 28.117 28.173 28.230 28.286 28.342 28.398 28.455 28.511 28.567 28.624 28.680 28.736 28.793 28.849 28.906 28.962 29.019 29.075 29.132 29.188 29.245 29.301 29.358 29.415 29.471 29.528 29.585 29.642 29.698 29.755 29.812 29.869 29.926 29.983 30.039 30.096 30.153 30.210 30.267 30.324 30.381 30.439 30.496 30.553 30.610 30.667 30.724 30.782 30.839 30.896 30.954 31.011 31.068 31.126 31.183 31.241 31.298 31.356 31.413 31.471 31.528 31.586 31.644 31.702 31.759 31.817 31.875 31.933 31.991 32.048 32.106 32.164 32.222 32.280 32.338 32.396 32.455 32.513 32.571 32.629 32.687 32.746 32.804 32.862 32.921 32.979 33.038 33.096 33.155 33.213 33.272 33.330 33.389 33.448 33.506 33.565 33.624 33.683 33.742 33.800 33.859 33.918 33.977 34.036 34.095 34.155 34.214 34.273 34.332 34.391 34.451 34.510 34.569 34.629 34.688 34.748 34.807 34.867 34.926 34.986 35.046 35.105 35.165 35.225 35.285 35.344 35.404 35.464 35.524 35.584 35.644 35.704 35.764 35.825 35.885 35.945 36.005 36.066 36.126 36.186 36.247 36.307 36.368 36.428 36.489 36.549 36.610 36.671 36.732 36.792 36.853 36.914 36.975 37.036 37.097 37.158 37.219 37.280 37.341 37.402 37.463 37.525 37.586 37.647 37.709 37.770 37.831 37.893 37.954 38.016 38.078 38.139 38.201 38.262 38.324 38.386 38.448 38.510 38.572 38.633 38.695 38.757 38.819 38.882 38.944 39.006 39.068 39.130 39.192 39.255 39.317 39.379 39.442 39.504 39.567 39.629 39.692 39.754 39.817 39.880 39.942 40.005 40.068 40.131 40.193 40.256 40.319 40.382 40.445 40.508 40.571 40.634 40.697 40.760 40.823 40.886 40.950 41.013 41.076 41.139 41.203 41.266 41.329 41.393 41.456 41.520 41.583 41.647 41.710 41.774 41.837 41.901 41.965 42.028 42.092 42.156 42.219 42.283 42.347 42.411 42.475 42.538 42.602 42.666 42.730 42.794 42.858 42.922 base-7.0.3.1/modules/database/src/ioc/bpt/bptTypeJdegF.data0000664000577000060420000002737013557101274022150 0ustar anjaesctl! cvtTypeJdegF.data "typeJdegF" 32 0 1200 4095 1.0 -350 1400 1 ! -8.137 -8.127 -8.117 -8.106 -8.096 -8.085 -8.074 -8.063 -8.052 -8.041 -8.030 -8.019 -8.008 -7.996 -7.985 -7.973 -7.962 -7.950 -7.938 -7.927 -7.915 -7.903 -7.890 -7.878 -7.866 -7.854 -7.841 -7.829 -7.816 -7.803 -7.791 -7.778 -7.765 -7.752 -7.739 -7.726 -7.712 -7.699 -7.686 -7.672 -7.659 -7.645 -7.631 -7.618 -7.604 -7.590 -7.576 -7.562 -7.548 -7.533 -7.519 -7.505 -7.490 -7.476 -7.461 -7.447 -7.432 -7.417 -7.402 -7.387 -7.372 -7.357 -7.342 -7.327 -7.311 -7.296 -7.281 -7.265 -7.250 -7.234 -7.218 -7.202 -7.187 -7.171 -7.155 -7.139 -7.122 -7.106 -7.090 -7.074 -7.057 -7.041 -7.024 -7.008 -6.991 -6.974 -6.958 -6.941 -6.924 -6.907 -6.890 -6.873 -6.856 -6.838 -6.821 -6.804 -6.786 -6.769 -6.751 -6.734 -6.716 -6.698 -6.680 -6.663 -6.645 -6.627 -6.609 -6.591 -6.572 -6.554 -6.536 -6.518 -6.499 -6.481 -6.462 -6.444 -6.425 -6.407 -6.388 -6.369 -6.350 -6.331 -6.312 -6.293 -6.274 -6.255 -6.236 -6.217 -6.198 -6.178 -6.159 -6.139 -6.120 -6.100 -6.081 -6.061 -6.041 -6.022 -6.002 -5.982 -5.962 -5.942 -5.922 -5.902 -5.882 -5.861 -5.841 -5.821 -5.801 -5.780 -5.760 -5.739 -5.719 -5.698 -5.678 -5.657 -5.636 -5.615 -5.594 -5.574 -5.553 -5.532 -5.511 -5.490 -5.468 -5.447 -5.426 -5.405 -5.383 -5.362 -5.341 -5.319 -5.298 -5.276 -5.255 -5.233 -5.211 -5.190 -5.168 -5.146 -5.124 -5.102 -5.080 -5.058 -5.036 -5.014 -4.992 -4.970 -4.948 -4.925 -4.903 -4.881 -4.858 -4.836 -4.813 -4.791 -4.768 -4.746 -4.723 -4.700 -4.678 -4.655 -4.632 -4.609 -4.586 -4.563 -4.540 -4.517 -4.494 -4.471 -4.448 -4.425 -4.402 -4.379 -4.355 -4.332 -4.309 -4.285 -4.262 -4.238 -4.215 -4.191 -4.168 -4.144 -4.120 -4.097 -4.073 -4.049 -4.025 -4.001 -3.978 -3.954 -3.930 -3.906 -3.882 -3.858 -3.833 -3.809 -3.785 -3.761 -3.737 -3.712 -3.688 -3.664 -3.639 -3.615 -3.590 -3.566 -3.541 -3.517 -3.492 -3.468 -3.443 -3.418 -3.394 -3.369 -3.344 -3.319 -3.294 -3.270 -3.245 -3.220 -3.195 -3.170 -3.145 -3.120 -3.094 -3.069 -3.044 -3.019 -2.994 -2.968 -2.943 -2.918 -2.892 -2.867 -2.842 -2.816 -2.791 -2.765 -2.740 -2.714 -2.689 -2.663 -2.637 -2.612 -2.586 -2.560 -2.534 -2.509 -2.483 -2.457 -2.431 -2.405 -2.379 -2.353 -2.327 -2.301 -2.275 -2.249 -2.223 -2.197 -2.171 -2.144 -2.118 -2.092 -2.066 -2.039 -2.013 -1.987 -1.960 -1.934 -1.908 -1.881 -1.855 -1.828 -1.802 -1.775 -1.748 -1.722 -1.695 -1.669 -1.642 -1.615 -1.589 -1.562 -1.535 -1.508 -1.481 -1.455 -1.428 -1.401 -1.374 -1.347 -1.320 -1.293 -1.266 -1.239 -1.212 -1.185 -1.158 -1.131 -1.103 -1.076 -1.049 -1.022 -0.995 -0.967 -0.940 -0.913 -0.885 -0.858 -0.831 -0.803 -0.776 -0.748 -0.721 -0.694 -0.666 -0.639 -0.611 -0.583 -0.556 -0.528 -0.501 -0.473 -0.445 -0.418 -0.390 -0.362 -0.334 -0.307 -0.279 -0.251 -0.223 -0.195 -0.168 -0.140 -0.112 -0.084 -0.056 -0.028 0.000 0.028 0.056 0.084 0.112 0.140 0.168 0.196 0.224 0.253 0.281 0.309 0.337 0.365 0.394 0.422 0.450 0.478 0.507 0.535 0.563 0.592 0.620 0.648 0.677 0.705 0.734 0.762 0.791 0.819 0.848 0.876 0.905 0.933 0.962 0.990 1.019 1.048 1.076 1.105 1.134 1.162 1.191 1.220 1.248 1.277 1.306 1.335 1.363 1.392 1.421 1.450 1.479 1.507 1.536 1.565 1.594 1.623 1.652 1.681 1.710 1.739 1.768 1.797 1.826 1.855 1.884 1.913 1.942 1.971 2.000 2.029 2.058 2.088 2.117 2.146 2.175 2.204 2.233 2.263 2.292 2.321 2.350 2.380 2.409 2.438 2.467 2.497 2.526 2.555 2.585 2.614 2.644 2.673 2.702 2.732 2.761 2.791 2.820 2.849 2.879 2.908 2.938 2.967 2.997 3.026 3.056 3.085 3.115 3.145 3.174 3.204 3.233 3.263 3.293 3.322 3.352 3.381 3.411 3.441 3.470 3.500 3.530 3.560 3.589 3.619 3.649 3.678 3.708 3.738 3.768 3.798 3.827 3.857 3.887 3.917 3.947 3.976 4.006 4.036 4.066 4.096 4.126 4.156 4.186 4.216 4.245 4.275 4.305 4.335 4.365 4.395 4.425 4.455 4.485 4.515 4.545 4.575 4.605 4.635 4.665 4.695 4.725 4.755 4.786 4.816 4.846 4.876 4.906 4.936 4.966 4.996 5.026 5.057 5.087 5.117 5.147 5.177 5.207 5.238 5.268 5.298 5.328 5.358 5.389 5.419 5.449 5.479 5.509 5.540 5.570 5.600 5.630 5.661 5.691 5.721 5.752 5.782 5.812 5.843 5.873 5.903 5.934 5.964 5.994 6.025 6.055 6.085 6.116 6.146 6.176 6.207 6.237 6.268 6.298 6.328 6.359 6.389 6.420 6.450 6.481 6.511 6.541 6.572 6.602 6.633 6.663 6.694 6.724 6.755 6.785 6.816 6.846 6.877 6.907 6.938 6.968 6.999 7.029 7.060 7.090 7.121 7.151 7.182 7.212 7.243 7.274 7.304 7.335 7.365 7.396 7.426 7.457 7.488 7.518 7.549 7.579 7.610 7.641 7.671 7.702 7.732 7.763 7.794 7.824 7.855 7.885 7.916 7.947 7.977 8.008 8.039 8.069 8.100 8.131 8.161 8.192 8.223 8.253 8.284 8.315 8.345 8.376 8.407 8.437 8.468 8.499 8.530 8.560 8.591 8.622 8.652 8.683 8.714 8.745 8.775 8.806 8.837 8.867 8.898 8.929 8.960 8.990 9.021 9.052 9.083 9.113 9.144 9.175 9.206 9.236 9.267 9.298 9.329 9.359 9.390 9.421 9.452 9.483 9.513 9.544 9.575 9.606 9.636 9.667 9.698 9.729 9.760 9.790 9.821 9.852 9.883 9.914 9.944 9.975 10.006 10.037 10.068 10.098 10.129 10.160 10.191 10.222 10.252 10.283 10.314 10.345 10.376 10.407 10.437 10.468 10.499 10.530 10.561 10.592 10.622 10.653 10.684 10.715 10.746 10.777 10.807 10.838 10.869 10.900 10.931 10.962 10.992 11.023 11.054 11.085 11.116 11.147 11.177 11.208 11.239 11.270 11.301 11.332 11.363 11.393 11.424 11.455 11.486 11.517 11.548 11.578 11.609 11.640 11.671 11.702 11.733 11.764 11.794 11.825 11.856 11.887 11.918 11.949 11.980 12.010 12.041 12.072 12.103 12.134 12.165 12.196 12.226 12.257 12.288 12.319 12.350 12.381 12.411 12.442 12.473 12.504 12.535 12.566 12.597 12.627 12.658 12.689 12.720 12.751 12.782 12.813 12.843 12.874 12.905 12.936 12.967 12.998 13.029 13.059 13.090 13.121 13.152 13.183 13.214 13.244 13.275 13.306 13.337 13.368 13.399 13.430 13.460 13.491 13.522 13.553 13.584 13.615 13.645 13.676 13.707 13.738 13.769 13.800 13.830 13.861 13.892 13.923 13.954 13.985 14.015 14.046 14.077 14.108 14.139 14.170 14.200 14.231 14.262 14.293 14.324 14.355 14.385 14.416 14.447 14.478 14.509 14.539 14.570 14.601 14.632 14.663 14.694 14.724 14.755 14.786 14.817 14.848 14.878 14.909 14.940 14.971 15.002 15.032 15.063 15.094 15.125 15.156 15.186 15.217 15.248 15.279 15.310 15.340 15.371 15.402 15.433 15.464 15.494 15.525 15.556 15.587 15.617 15.648 15.679 15.710 15.741 15.771 15.802 15.833 15.864 15.894 15.925 15.956 15.987 16.018 16.048 16.079 16.110 16.141 16.171 16.202 16.233 16.264 16.294 16.325 16.356 16.387 16.417 16.448 16.479 16.510 16.540 16.571 16.602 16.633 16.663 16.694 16.725 16.756 16.786 16.817 16.848 16.879 16.909 16.940 16.971 17.001 17.032 17.063 17.094 17.124 17.155 17.186 17.217 17.247 17.278 17.309 17.339 17.370 17.401 17.432 17.462 17.493 17.524 17.554 17.585 17.616 17.646 17.677 17.708 17.739 17.769 17.800 17.831 17.861 17.892 17.923 17.953 17.984 18.015 18.046 18.076 18.107 18.138 18.168 18.199 18.230 18.260 18.291 18.322 18.352 18.383 18.414 18.444 18.475 18.506 18.537 18.567 18.598 18.629 18.659 18.690 18.721 18.751 18.782 18.813 18.843 18.874 18.905 18.935 18.966 18.997 19.027 19.058 19.089 19.119 19.150 19.180 19.211 19.242 19.272 19.303 19.334 19.364 19.395 19.426 19.456 19.487 19.518 19.548 19.579 19.610 19.640 19.671 19.702 19.732 19.763 19.793 19.824 19.855 19.885 19.916 19.947 19.977 20.008 20.039 20.069 20.100 20.131 20.161 20.192 20.222 20.253 20.284 20.314 20.345 20.376 20.406 20.437 20.467 20.498 20.529 20.559 20.590 20.621 20.651 20.682 20.713 20.743 20.774 20.804 20.835 20.866 20.896 20.927 20.958 20.988 21.019 21.049 21.080 21.111 21.141 21.172 21.203 21.233 21.264 21.295 21.325 21.356 21.386 21.417 21.448 21.478 21.509 21.540 21.570 21.601 21.631 21.662 21.693 21.723 21.754 21.785 21.815 21.846 21.877 21.907 21.938 21.968 21.999 22.030 22.060 22.091 22.122 22.152 22.183 22.214 22.244 22.275 22.305 22.336 22.367 22.397 22.428 22.459 22.489 22.520 22.551 22.581 22.612 22.643 22.673 22.704 22.735 22.765 22.796 22.826 22.857 22.888 22.918 22.949 22.980 23.010 23.041 23.072 23.102 23.133 23.164 23.194 23.225 23.256 23.286 23.317 23.348 23.378 23.409 23.440 23.471 23.501 23.532 23.563 23.593 23.624 23.655 23.685 23.716 23.747 23.777 23.808 23.839 23.870 23.900 23.931 23.962 23.992 24.023 24.054 24.085 24.115 24.146 24.177 24.207 24.238 24.269 24.300 24.330 24.361 24.392 24.423 24.453 24.484 24.515 24.546 24.576 24.607 24.638 24.669 24.699 24.730 24.761 24.792 24.822 24.853 24.854 24.915 24.946 24.976 25.007 25.038 25.069 25.099 25.130 25.161 25.192 25.223 25.254 25.284 25.315 25.346 25.377 25.408 25.438 25.469 25.500 25.531 25.562 25.593 25.623 25.654 25.685 25.716 25.747 25.778 25.809 25.840 25.870 25.901 25.932 25.963 25.994 26.025 26.056 26.087 26.118 26.148 26.179 26.210 26.241 26.272 26.303 26.334 26.365 26.396 26.427 26.458 26.489 26.520 26.551 26.582 26.613 26.644 26.675 26.705 26.736 26.767 26.798 26.829 26.860 26.891 26.922 26.954 26.985 27.016 27.047 27.078 27.109 27.140 27.171 27.202 27.233 27.264 27.295 27.326 27.357 27.388 27.419 27.450 27.482 27.513 27.544 27.575 27.606 27.637 27.668 27.699 27.731 27.762 27.793 27.824 27.855 27.886 27.917 27.949 27.980 28.011 28.042 28.073 28.105 28.136 28.167 28.198 28.230 28.261 28.292 28.323 28.355 28.386 28.417 28.448 28.480 28.511 28.542 28.573 28.605 28.636 28.667 28.699 28.730 28.761 28.793 28.824 28.855 28.887 28.918 28.950 28.981 29.012 29.044 29.075 29.107 29.138 29.169 29.201 29.232 29.264 29.295 29.327 29.358 29.390 29.421 29.452 29.484 29.515 29.547 29.578 29.610 29.642 29.673 29.705 29.736 29.768 29.799 29.831 29.862 29.894 29.926 29.957 29.989 30.020 30.052 30.084 30.115 30.147 30.179 30.210 30.242 30.274 30.305 30.337 30.369 30.400 30.432 30.464 30.496 30.527 30.559 30.591 30.623 30.654 30.686 30.718 30.750 30.782 30.813 30.845 30.877 30.909 30.941 30.973 31.005 31.036 31.068 31.100 31.132 31.164 31.196 31.228 31.260 31.292 31.324 31.356 31.388 31.420 31.452 31.484 31.516 31.548 31.580 31.612 31.644 31.676 31.708 31.740 31.772 31.804 31.836 31.868 31.901 31.933 31.965 31.997 32.029 32.061 32.094 32.126 32.158 32.190 32.222 32.255 32.287 32.319 32.351 32.384 32.416 32.448 32.480 32.513 32.545 32.577 32.610 32.642 32.674 32.707 32.739 32.772 32.804 32.836 32.869 32.901 32.934 32.966 32.999 33.031 33.064 33.096 33.129 33.161 33.194 33.226 33.259 33.291 33.324 33.356 33.389 33.422 33.454 33.487 33.519 33.552 33.585 33.617 33.650 33.683 33.715 33.748 33.781 33.814 33.846 33.879 33.912 33.945 33.977 34.010 34.043 34.076 34.109 34.141 34.174 34.207 34.240 34.273 34.306 34.339 34.372 34.405 34.437 34.470 34.503 34.536 34.569 34.602 34.635 34.668 34.701 34.734 34.767 34.801 34.834 34.867 34.900 34.933 34.966 34.999 35.032 35.065 35.099 35.132 35.165 35.198 35.231 35.265 35.298 35.331 35.364 35.398 35.431 35.464 35.498 35.531 35.564 35.598 35.631 35.664 35.698 35.731 35.764 35.798 35.831 35.865 35.898 35.932 35.965 35.999 36.032 36.066 36.099 36.133 36.166 36.200 36.233 36.267 36.301 36.334 36.368 36.401 36.435 36.469 36.502 36.536 36.570 36.603 36.637 36.671 36.705 36.738 36.772 36.806 36.840 36.873 36.907 36.941 36.975 37.009 37.043 37.076 37.110 37.144 37.178 37.212 37.246 37.280 37.314 37.348 37.382 37.416 37.450 37.484 37.518 37.552 37.586 37.620 37.654 37.688 37.722 37.756 37.790 37.825 37.859 37.893 37.927 37.961 37.995 38.030 38.064 38.098 38.132 38.167 38.201 38.235 38.269 38.304 38.338 38.372 38.407 38.441 38.475 38.510 38.544 38.578 38.613 38.647 38.682 38.716 38.751 38.785 38.819 38.854 38.888 38.923 38.957 38.992 39.027 39.061 39.096 39.130 39.165 39.199 39.234 39.269 39.303 39.338 39.373 39.407 39.442 39.477 39.511 39.546 39.581 39.615 39.650 39.685 39.720 39.754 39.789 39.824 39.859 39.894 39.928 39.963 39.998 40.033 40.068 40.103 40.138 40.172 40.207 40.242 40.277 40.312 40.347 40.382 40.417 40.452 40.487 40.522 40.557 40.592 40.627 40.662 40.697 40.732 40.767 40.802 40.837 40.872 40.908 40.943 40.978 41.013 41.048 41.083 41.118 41.154 41.189 41.224 41.259 41.294 41.329 41.365 41.400 41.435 41.470 41.506 41.541 41.576 41.611 41.647 41.682 41.717 41.753 41.788 41.823 41.859 41.894 41.929 41.965 42.000 42.035 42.071 42.106 42.142 42.177 42.212 42.248 42.283 42.319 42.354 42.390 42.425 42.460 42.496 42.531 42.567 42.602 42.638 42.673 42.709 42.744 42.780 42.815 42.851 42.886 42.922 base-7.0.3.1/modules/database/src/ioc/bpt/bptTypeKdegC.data0000664000577000060420000002612613557101274022144 0ustar anjaesctl! cvtTypeKdegC.data "typeKdegC" 0 0 1000 4095 .5 -270 1372 1 ! -6.458 -6.457 -6.456 -6.455 -6.453 -6.452 -6.450 -6.448 -6.446 -6.444 -6.441 -6.438 -6.435 -6.432 -6.429 -6.425 -6.421 -6.417 -6.413 -6.408 -6.404 -6.399 -6.394 -6.388 -6.382 -6.377 -6.371 -6.364 -6.358 -6.351 -6.344 -6.337 -6.329 -6.322 -6.314 -6.306 -6.297 -6.289 -6.280 -6.271 -6.262 -6.253 -6.243 -6.233 -6.223 -6.213 -6.202 -6.192 -6.181 -6.170 -6.158 -6.147 -6.135 -6.123 -6.111 -6.099 -6.087 -6.074 -6.061 -6.048 -6.035 -6.021 -6.007 -5.994 -5.980 -5.965 -5.951 -5.936 -5.922 -5.907 -5.891 -5.876 -5.860 -5.845 -5.829 -5.813 -5.796 -5.780 -5.763 -5.747 -5.730 -5.712 -5.695 -5.678 -5.660 -5.642 -5.624 -5.606 -5.587 -5.569 -5.550 -5.531 -5.512 -5.493 -5.474 -5.454 -5.434 -5.414 -5.394 -5.374 -5.354 -5.333 -5.313 -5.292 -5.271 -5.249 -5.228 -5.207 -5.185 -5.163 -5.141 -5.119 -5.097 -5.074 -5.051 -5.029 -5.006 -4.983 -4.959 -4.936 -4.912 -4.889 -4.865 -4.841 -4.817 -4.792 -4.768 -4.743 -4.719 -4.694 -4.669 -4.644 -4.618 -4.593 -4.567 -4.541 -4.515 -4.489 -4.463 -4.437 -4.410 -4.384 -4.357 -4.330 -4.303 -4.276 -4.248 -4.221 -4.193 -4.166 -4.138 -4.110 -4.082 -4.053 -4.025 -3.997 -3.968 -3.939 -3.910 -3.881 -3.852 -3.823 -3.793 -3.764 -3.734 -3.704 -3.674 -3.644 -3.614 -3.584 -3.553 -3.523 -3.492 -3.461 -3.430 -3.399 -3.368 -3.337 -3.305 -3.274 -3.242 -3.211 -3.179 -3.147 -3.115 -3.082 -3.050 -3.018 -2.985 -2.953 -2.920 -2.887 -2.854 -2.821 -2.788 -2.754 -2.721 -2.687 -2.654 -2.620 -2.586 -2.552 -2.518 -2.484 -2.450 -2.416 -2.381 -2.347 -2.312 -2.277 -2.243 -2.208 -2.173 -2.137 -2.102 -2.067 -2.032 -1.996 -1.961 -1.925 -1.889 -1.853 -1.817 -1.781 -1.745 -1.709 -1.673 -1.636 -1.600 -1.563 -1.527 -1.490 -1.453 -1.416 -1.379 -1.342 -1.305 -1.268 -1.231 -1.193 -1.156 -1.118 -1.081 -1.043 -1.005 -0.968 -0.930 -0.892 -0.854 -0.816 -0.777 -0.739 -0.701 -0.662 -0.624 -0.585 -0.547 -0.508 -0.469 -0.431 -0.392 -0.353 -0.314 -0.275 -0.236 -0.197 -0.157 -0.118 -0.079 -0.039 0.000 0.039 0.079 0.119 0.158 0.198 0.238 0.277 0.317 0.357 0.397 0.437 0.477 0.517 0.557 0.597 0.637 0.677 0.718 0.758 0.798 0.838 0.879 0.919 0.960 1.000 1.041 1.081 1.122 1.162 1.203 1.244 1.285 1.325 1.366 1.407 1.448 1.489 1.529 1.570 1.611 1.652 1.693 1.734 1.776 1.817 1.858 1.899 1.940 1.981 2.022 2.064 2.105 2.146 2.188 2.229 2.270 2.312 2.353 2.394 2.436 2.477 2.519 2.560 2.601 2.643 2.684 2.726 2.767 2.809 2.850 2.892 2.933 2.975 3.016 3.058 3.100 3.141 3.183 3.224 3.266 3.307 3.349 3.390 3.432 3.473 3.515 3.556 3.598 3.639 3.681 3.722 3.764 3.805 3.847 3.888 3.930 3.971 4.012 4.054 4.095 4.137 4.178 4.219 4.261 4.302 4.343 4.384 4.426 4.467 4.508 4.549 4.590 4.632 4.673 4.714 4.755 4.796 4.837 4.878 4.919 4.960 5.001 5.042 5.083 5.124 5.164 5.205 5.246 5.287 5.327 5.368 5.409 5.450 5.490 5.531 5.571 5.612 5.652 5.693 5.733 5.774 5.814 5.855 5.895 5.936 5.976 6.016 6.057 6.097 6.137 6.177 6.218 6.258 6.298 6.338 6.378 6.419 6.459 6.499 6.539 6.579 6.619 6.659 6.699 6.739 6.779 6.819 6.859 6.899 6.939 6.979 7.019 7.059 7.099 7.139 7.179 7.219 7.259 7.299 7.338 7.378 7.418 7.458 7.498 7.538 7.578 7.618 7.658 7.697 7.737 7.777 7.817 7.857 7.897 7.937 7.977 8.017 8.057 8.097 8.137 8.177 8.216 8.256 8.296 8.336 8.376 8.416 8.456 8.497 8.537 8.577 8.617 8.657 8.697 8.737 8.777 8.817 8.857 8.898 8.938 8.978 9.018 9.058 9.099 9.139 9.179 9.220 9.260 9.300 9.341 9.381 9.421 9.462 9.502 9.543 9.583 9.624 9.664 9.705 9.745 9.786 9.826 9.867 9.907 9.948 9.989 10.029 10.070 10.111 10.151 10.192 10.233 10.274 10.315 10.355 10.396 10.437 10.478 10.519 10.560 10.600 10.641 10.682 10.723 10.764 10.805 10.846 10.887 10.928 10.969 11.010 11.051 11.093 11.134 11.175 11.216 11.257 11.298 11.339 11.381 11.422 11.463 11.504 11.546 11.587 11.628 11.669 11.711 11.752 11.793 11.835 11.876 11.918 11.959 12.000 12.042 12.083 12.125 12.166 12.207 12.249 12.290 12.332 12.373 12.415 12.456 12.498 12.539 12.581 12.623 12.664 12.706 12.747 12.789 12.831 12.872 12.914 12.955 12.997 13.039 13.080 13.122 13.164 13.205 13.247 13.289 13.331 13.372 13.414 13.456 13.497 13.539 13.581 13.623 13.665 13.706 13.748 13.790 13.832 13.874 13.915 13.957 13.999 14.041 14.083 14.125 14.167 14.208 14.250 14.292 14.334 14.376 14.418 14.460 14.502 14.544 14.586 14.628 14.670 14.712 14.754 14.796 14.838 14.880 14.922 14.964 15.006 15.048 15.090 15.132 15.174 15.216 15.258 15.300 15.342 15.384 15.426 15.468 15.510 15.552 15.594 15.636 15.679 15.721 15.763 15.805 15.847 15.889 15.931 15.974 16.016 16.058 16.100 16.142 16.184 16.227 16.269 16.311 16.353 16.395 16.438 16.480 16.522 16.564 16.607 16.649 16.691 16.733 16.776 16.818 16.860 16.902 16.945 16.987 17.029 17.072 17.114 17.156 17.199 17.241 17.283 17.326 17.368 17.410 17.453 17.495 17.537 17.580 17.622 17.664 17.707 17.749 17.792 17.834 17.876 17.919 17.961 18.004 18.046 18.088 18.131 18.173 18.216 18.258 18.301 18.343 18.385 18.428 18.470 18.513 18.555 18.598 18.640 18.683 18.725 18.768 18.810 18.853 18.895 18.938 18.980 19.023 19.065 19.108 19.150 19.193 19.235 19.278 19.320 19.363 19.405 19.448 19.490 19.533 19.576 19.618 19.661 19.703 19.746 19.788 19.831 19.873 19.916 19.959 20.001 20.044 20.086 20.129 20.172 20.214 20.257 20.299 20.342 20.385 20.427 20.470 20.512 20.555 20.598 20.640 20.683 20.725 20.768 20.811 20.853 20.896 20.938 20.981 21.024 21.066 21.109 21.152 21.194 21.237 21.280 21.322 21.365 21.407 21.450 21.493 21.535 21.578 21.621 21.663 21.706 21.749 21.791 21.834 21.876 21.919 21.962 22.004 22.047 22.090 22.132 22.175 22.218 22.260 22.303 22.346 22.388 22.431 22.473 22.516 22.559 22.601 22.644 22.687 22.729 22.772 22.815 22.857 22.900 22.942 22.985 23.028 23.070 23.113 23.156 23.198 23.241 23.284 23.326 23.369 23.411 23.454 23.497 23.539 23.582 23.624 23.667 23.710 23.752 23.795 23.837 23.880 23.923 23.965 24.008 24.050 24.093 24.136 24.178 24.221 24.263 24.306 24.348 24.391 24.434 24.476 24.519 24.561 24.604 24.646 24.689 24.731 24.774 24.817 24.859 24.902 24.944 24.987 25.029 25.072 25.114 25.157 25.199 25.242 25.284 25.327 25.369 25.412 25.454 25.497 25.539 25.582 25.624 25.666 25.709 25.751 25.794 25.836 25.879 25.921 25.964 26.006 26.048 26.091 26.133 26.176 26.218 26.260 26.303 26.345 26.387 26.430 26.472 26.515 26.557 26.599 26.642 26.684 26.726 26.769 26.811 26.853 26.896 26.938 26.980 27.022 27.065 27.107 27.149 27.192 27.234 27.276 27.318 27.361 27.403 27.445 27.487 27.529 27.572 27.614 27.656 27.698 27.740 27.783 27.825 27.867 27.909 27.951 27.993 28.035 28.078 28.120 28.162 28.204 28.246 28.288 28.330 28.372 28.414 28.456 28.498 28.540 28.583 28.625 28.667 28.709 28.751 28.793 28.835 28.877 28.919 28.961 29.002 29.044 29.086 29.128 29.170 29.212 29.254 29.296 29.338 29.380 29.422 29.464 29.505 29.547 29.589 29.631 29.673 29.715 29.756 29.798 29.840 29.882 29.924 29.965 30.007 30.049 30.091 30.132 30.174 30.216 30.257 30.299 30.341 30.383 30.424 30.466 30.508 30.549 30.591 30.632 30.674 30.716 30.757 30.799 30.840 30.882 30.924 30.965 31.007 31.048 31.090 31.131 31.173 31.214 31.256 31.297 31.339 31.380 31.422 31.463 31.504 31.546 31.587 31.629 31.670 31.712 31.753 31.794 31.836 31.877 31.918 31.960 32.001 32.042 32.084 32.125 32.166 32.207 32.249 32.290 32.331 32.372 32.414 32.455 32.496 32.537 32.578 32.619 32.661 32.702 32.743 32.784 32.825 32.866 32.907 32.948 32.990 33.031 33.072 33.113 33.154 33.195 33.236 33.277 33.318 33.359 33.400 33.441 33.482 33.523 33.564 33.604 33.645 33.686 33.727 33.768 33.809 33.850 33.891 33.931 33.972 34.013 34.054 34.095 34.136 34.176 34.217 34.258 34.299 34.339 34.380 34.421 34.461 34.502 34.543 34.583 34.624 34.665 34.705 34.746 34.787 34.827 34.868 34.909 34.949 34.990 35.030 35.071 35.111 35.152 35.192 35.233 35.273 35.314 35.354 35.395 35.435 35.476 35.516 35.557 35.597 35.637 35.678 35.718 35.758 35.799 35.839 35.880 35.920 35.960 36.000 36.041 36.081 36.121 36.162 36.202 36.242 36.282 36.323 36.363 36.403 36.443 36.483 36.524 36.564 36.604 36.644 36.684 36.724 36.764 36.804 36.844 36.885 36.925 36.965 37.005 37.045 37.085 37.125 37.165 37.205 37.245 37.285 37.325 37.365 37.405 37.445 37.484 37.524 37.564 37.604 37.644 37.684 37.724 37.764 37.803 37.843 37.883 37.923 37.963 38.002 38.042 38.082 38.122 38.162 38.201 38.241 38.281 38.320 38.360 38.400 38.439 38.479 38.519 38.558 38.598 38.638 38.677 38.717 38.756 38.796 38.836 38.875 38.915 38.954 38.994 39.033 39.073 39.112 39.152 39.191 39.231 39.270 39.310 39.349 39.388 39.428 39.467 39.507 39.546 39.585 39.625 39.644 39.703 39.743 39.782 39.821 39.861 39.900 39.939 39.979 40.018 40.057 40.096 40.136 40.175 40.214 40.253 40.292 40.332 40.371 40.410 40.449 40.488 40.527 40.566 40.605 40.645 40.684 40.723 40.762 40.801 40.840 40.879 40.918 40.957 40.996 41.035 41.074 41.113 41.152 41.191 41.230 41.269 41.308 41.347 41.385 41.424 41.463 41.502 41.541 41.580 41.619 41.657 41.696 41.735 41.774 41.813 41.851 41.890 41.929 41.968 42.006 42.045 42.084 42.123 42.161 42.200 42.239 42.277 42.316 42.355 42.393 42.432 42.470 42.509 42.548 42.586 42.625 42.663 42.702 42.740 42.779 42.817 42.856 42.894 42.933 42.971 43.010 43.048 43.087 43.125 43.164 43.202 43.240 43.279 43.317 43.356 43.394 43.432 43.471 43.509 43.547 43.585 43.624 43.662 43.700 43.739 43.777 43.815 43.853 43.891 43.930 43.968 44.006 44.044 44.082 44.121 44.159 44.197 44.235 44.273 44.311 44.349 44.387 44.425 44.463 44.501 44.539 44.577 44.615 44.653 44.691 44.729 44.767 44.805 44.843 44.881 44.919 44.957 44.995 45.033 45.070 45.108 45.146 45.184 45.222 45.260 45.297 45.335 45.373 45.411 45.448 45.486 45.524 45.561 45.599 45.637 45.675 45.712 45.750 45.787 45.825 45.863 45.900 45.938 45.975 46.013 46.051 46.088 46.126 46.163 46.201 46.238 46.275 46.313 46.350 46.388 46.425 46.463 46.500 46.537 46.575 46.612 46.649 46.687 46.724 46.761 46.799 46.836 46.873 46.910 46.948 46.985 47.022 47.059 47.096 47.134 47.171 47.208 47.245 47.282 47.319 47.356 47.393 47.430 47.468 47.505 47.542 47.579 47.616 47.653 47.689 47.726 47.763 47.800 47.837 47.874 47.911 47.948 47.985 48.021 48.058 48.095 48.132 48.169 48.205 48.242 48.279 48.316 48.352 48.389 48.426 48.462 48.499 48.536 48.572 48.609 48.645 48.682 48.718 48.755 48.792 48.828 48.865 48.901 48.937 48.974 49.010 49.047 49.083 49.120 49.156 49.192 49.229 49.265 49.301 49.338 49.374 49.410 49.446 49.483 49.519 49.555 49.591 49.627 49.663 49.700 49.736 49.772 49.808 49.844 49.880 49.916 49.952 49.988 50.024 50.060 50.096 50.132 50.168 50.204 50.240 50.276 50.311 50.347 50.383 50.419 50.455 50.491 50.526 50.562 50.598 50.633 50.669 50.705 50.741 50.776 50.812 50.847 50.883 50.919 50.954 50.990 51.025 51.061 51.096 51.132 51.167 51.203 51.238 51.274 51.309 51.344 51.380 51.415 51.450 51.486 51.521 51.556 51.592 51.627 51.662 51.697 51.733 51.768 51.803 51.838 51.873 51.908 51.943 51.979 52.014 52.049 52.084 52.119 52.154 52.189 52.224 52.259 52.294 52.329 52.364 52.398 52.433 52.468 52.503 52.538 52.573 52.608 52.642 52.677 52.712 52.747 52.781 52.816 52.851 52.886 52.920 52.955 52.989 53.024 53.059 53.093 53.128 53.162 53.197 53.232 53.266 53.301 53.335 53.370 53.404 53.439 53.473 53.507 53.542 53.576 53.611 53.645 53.679 53.714 53.748 53.782 53.817 53.851 53.885 53.920 53.954 53.988 54.022 54.057 54.091 54.125 54.159 54.193 54.228 54.262 54.296 54.330 54.364 54.398 54.432 54.466 54.501 54.535 54.569 54.603 54.637 54.671 54.705 54.739 54.773 54.807 54.841 54.875 base-7.0.3.1/modules/database/src/ioc/bpt/bptTypeKdegF.data0000664000577000060420000004763213557101274022154 0ustar anjaesctl! cvtTypeKdegF.data "typeKdegF" 32 0 1832 4095 1.0 -454 2500 1 ! -6.458 -6.457 -6.457 -6.456 -6.456 -6.455 -6.454 -6.454 -6.453 -6.452 -6.451 -6.450 -6.449 -6.448 -6.447 -6.445 -6.444 -6.443 -6.441 -6.440 -6.438 -6.436 -6.435 -6.433 -6.431 -6.429 -6.427 -6.425 -6.423 -6.421 -6.419 -6.416 -6.414 -6.411 -6.409 -6.406 -6.404 -6.401 -6.398 -6.395 -6.392 -6.389 -6.386 -6.383 -6.380 -6.377 -6.373 -6.370 -6.366 -6.363 -6.359 -6.355 -6.352 -6.348 -6.344 -6.340 -6.336 -6.332 -6.328 -6.323 -6.319 -6.315 -6.310 -6.306 -6.301 -6.296 -6.292 -6.287 -6.282 -6.277 -6.272 -6.267 -6.262 -6.257 -6.251 -6.246 -6.241 -6.235 -6.230 -6.224 -6.219 -6.213 -6.207 -6.201 -6.195 -6.189 -6.183 -6.177 -6.171 -6.165 -6.158 -6.152 -6.146 -6.139 -6.133 -6.126 -6.119 -6.113 -6.106 -6.099 -6.092 -6.085 -6.078 -6.071 -6.064 -6.057 -6.049 -6.042 -6.035 -6.027 -6.020 -6.012 -6.004 -5.997 -5.989 -5.981 -5.973 -5.965 -5.957 -5.949 -5.941 -5.933 -5.925 -5.917 -5.908 -5.900 -5.891 -5.883 -5.874 -5.866 -5.857 -5.848 -5.839 -5.831 -5.822 -5.813 -5.804 -5.795 -5.786 -5.776 -5.767 -5.758 -5.748 -5.739 -5.730 -5.720 -5.711 -5.701 -5.691 -5.682 -5.672 -5.662 -5.652 -5.642 -5.632 -5.622 -5.612 -5.602 -5.592 -5.581 -5.571 -5.561 -5.550 -5.540 -5.529 -5.519 -5.508 -5.497 -5.487 -5.476 -5.465 -5.454 -5.443 -5.432 -5.421 -5.410 -5.399 -5.388 -5.376 -5.365 -5.354 -5.342 -5.331 -5.319 -5.308 -5.296 -5.285 -5.273 -5.261 -5.249 -5.238 -5.226 -5.214 -5.202 -5.190 -5.178 -5.165 -5.153 -5.141 -5.129 -5.116 -5.104 -5.092 -5.079 -5.067 -5.054 -5.041 -5.029 -5.016 -5.003 -4.990 -4.978 -4.965 -4.952 -4.939 -4.926 -4.912 -4.899 -4.886 -4.873 -4.860 -4.846 -4.833 -4.819 -4.806 -4.792 -4.779 -4.765 -4.752 -4.738 -4.724 -4.710 -4.697 -4.683 -4.669 -4.655 -4.641 -4.627 -4.613 -4.598 -4.584 -4.570 -4.556 -4.541 -4.527 -4.512 -4.498 -4.484 -4.469 -4.454 -4.440 -4.425 -4.410 -4.396 -4.381 -4.366 -4.351 -4.336 -4.321 -4.306 -4.291 -4.276 -4.261 -4.245 -4.230 -4.215 -4.200 -4.184 -4.169 -4.153 -4.138 -4.122 -4.107 -4.091 -4.075 -4.060 -4.044 -4.028 -4.012 -3.997 -3.981 -3.965 -3.949 -3.933 -3.917 -3.901 -3.884 -3.868 -3.852 -3.836 -3.819 -3.803 -3.787 -3.770 -3.754 -3.737 -3.721 -3.704 -3.688 -3.671 -3.654 -3.637 -3.621 -3.604 -3.587 -3.570 -3.553 -3.536 -3.519 -3.502 -3.485 -3.468 -3.451 -3.434 -3.417 -3.399 -3.382 -3.365 -3.347 -3.330 -3.312 -3.295 -3.277 -3.260 -3.242 -3.225 -3.207 -3.189 -3.172 -3.154 -3.136 -3.118 -3.100 -3.082 -3.065 -3.047 -3.029 -3.010 -2.992 -2.974 -2.956 -2.938 -2.920 -2.902 -2.883 -2.865 -2.847 -2.828 -2.810 -2.791 -2.773 -2.754 -2.736 -2.717 -2.699 -2.680 -2.661 -2.643 -2.624 -2.605 -2.586 -2.567 -2.549 -2.530 -2.511 -2.492 -2.473 -2.454 -2.435 -2.416 -2.397 -2.377 -2.358 -2.339 -2.320 -2.300 -2.281 -2.262 -2.243 -2.223 -2.204 -2.184 -2.165 -2.145 -2.126 -2.106 -2.087 -2.067 -2.047 -2.028 -2.008 -1.988 -1.968 -1.949 -1.929 -1.909 -1.889 -1.869 -1.849 -1.829 -1.809 -1.789 -1.769 -1.749 -1.729 -1.709 -1.689 -1.669 -1.648 -1.628 -1.608 -1.588 -1.567 -1.547 -1.527 -1.506 -1.486 -1.465 -1.445 -1.424 -1.404 -1.383 -1.363 -1.342 -1.322 -1.301 -1.280 -1.260 -1.239 -1.218 -1.197 -1.177 -1.156 -1.135 -1.114 -1.093 -1.072 -1.051 -1.031 -1.010 -0.989 -0.968 -0.946 -0.925 -0.904 -0.883 -0.862 -0.841 -0.820 -0.799 -0.777 -0.756 -0.735 -0.714 -0.692 -0.671 -0.650 -0.628 -0.607 -0.585 -0.564 -0.543 -0.521 -0.500 -0.478 -0.457 -0.435 -0.413 -0.392 -0.370 -0.349 -0.327 -0.305 -0.284 -0.262 -0.240 -0.218 -0.197 -0.175 -0.153 -0.131 -0.109 -0.088 -0.066 -0.044 -0.022 0.000 0.022 0.044 0.066 0.088 0.110 0.132 0.154 0.176 0.198 0.220 0.242 0.264 0.286 0.308 0.331 0.353 0.375 0.397 0.419 0.441 0.464 0.486 0.508 0.530 0.553 0.575 0.597 0.619 0.642 0.664 0.686 0.709 0.731 0.753 0.776 0.798 0.821 0.843 0.865 0.888 0.910 0.933 0.955 0.978 1.000 1.023 1.045 1.068 1.090 1.113 1.135 1.158 1.181 1.203 1.226 1.248 1.271 1.294 1.316 1.339 1.362 1.384 1.407 1.430 1.452 1.475 1.498 1.520 1.543 1.566 1.589 1.611 1.634 1.657 1.680 1.703 1.725 1.748 1.771 1.794 1.817 1.839 1.862 1.885 1.908 1.931 1.954 1.977 2.000 2.022 2.045 2.068 2.091 2.114 2.137 2.160 2.183 2.206 2.229 2.252 2.275 2.298 2.321 2.344 2.367 2.390 2.413 2.436 2.459 2.482 2.505 2.528 2.551 2.574 2.597 2.620 2.643 2.666 2.689 2.712 2.735 2.758 2.781 2.804 2.827 2.850 2.873 2.896 2.920 2.943 2.966 2.989 3.012 3.035 3.058 3.081 3.104 3.127 3.150 3.173 3.196 3.220 3.243 3.266 3.289 3.312 3.335 3.358 3.381 3.404 3.427 3.450 3.473 3.496 3.519 3.543 3.566 3.589 3.612 3.635 3.658 3.681 3.704 3.727 3.750 3.773 3.796 3.819 3.842 3.865 3.888 3.911 3.934 3.957 3.980 4.003 4.026 4.049 4.072 4.095 4.118 4.141 4.164 4.187 4.210 4.233 4.256 4.279 4.302 4.325 4.348 4.371 4.394 4.417 4.439 4.462 4.485 4.508 4.531 4.554 4.577 4.600 4.622 4.645 4.668 4.691 4.714 4.737 4.759 4.782 4.805 4.828 4.851 4.873 4.896 4.919 4.942 4.964 4.987 5.010 5.033 5.055 5.078 5.101 5.124 5.146 5.169 5.192 5.214 5.237 5.260 5.282 5.305 5.327 5.350 5.373 5.395 5.418 5.440 5.463 5.486 5.508 5.531 5.553 5.576 5.598 5.621 5.643 5.666 5.688 5.711 5.733 5.756 5.778 5.801 5.823 5.846 5.868 5.891 5.913 5.936 5.958 5.980 6.003 6.025 6.048 6.070 6.092 6.115 6.137 6.160 6.182 6.204 6.227 6.249 6.271 6.294 6.316 6.338 6.361 6.383 6.405 6.428 6.450 6.472 6.494 6.517 6.539 6.561 6.583 6.606 6.628 6.650 6.672 6.695 6.717 6.739 6.761 6.784 6.806 6.828 6.850 6.873 6.895 6.917 6.939 6.961 6.984 7.006 7.028 7.050 7.072 7.094 7.117 7.139 7.161 7.183 7.205 7.228 7.250 7.272 7.294 7.316 7.338 7.361 7.383 7.405 7.427 7.449 7.471 7.494 7.516 7.538 7.560 7.582 7.604 7.627 7.649 7.671 7.693 7.715 7.737 7.760 7.782 7.804 7.826 7.848 7.870 7.893 7.915 7.937 7.959 7.981 8.003 8.026 8.048 8.070 8.092 8.114 8.137 8.159 8.181 8.203 8.225 8.248 8.270 8.292 8.314 8.336 8.359 8.381 8.403 8.425 8.448 8.470 8.492 8.514 8.537 8.559 8.581 8.603 8.626 8.648 8.670 8.692 8.715 8.737 8.759 8.782 8.804 8.826 8.849 8.871 8.893 8.916 8.938 8.960 8.983 9.005 9.027 9.050 9.072 9.094 9.117 9.139 9.161 9.184 9.206 9.229 9.251 9.273 9.296 9.318 9.341 9.363 9.385 9.408 9.430 9.453 9.475 9.498 9.520 9.543 9.565 9.588 9.610 9.633 9.655 9.678 9.700 9.723 9.745 9.768 9.790 9.813 9.835 9.858 9.880 9.903 9.926 9.948 9.971 9.993 10.016 10.038 10.061 10.084 10.106 10.129 10.151 10.174 10.197 10.219 10.242 10.265 10.287 10.310 10.333 10.355 10.378 10.401 10.423 10.446 10.469 10.491 10.514 10.537 10.560 10.582 10.605 10.628 10.650 10.673 10.696 10.719 10.741 10.764 10.787 10.810 10.833 10.855 10.878 10.901 10.924 10.947 10.969 10.992 11.015 11.038 11.061 11.083 11.106 11.129 11.152 11.175 11.198 11.221 11.243 11.266 11.289 11.312 11.335 11.358 11.381 11.404 11.426 11.449 11.472 11.495 11.518 11.541 11.564 11.587 11.610 11.633 11.656 11.679 11.702 11.725 11.748 11.770 11.793 11.816 11.839 11.862 11.885 11.908 11.931 11.954 11.977 12.000 12.023 12.046 12.069 12.092 12.115 12.138 12.161 12.184 12.207 12.230 12.254 12.277 12.300 12.323 12.346 12.369 12.392 12.415 12.438 12.461 12.484 12.507 12.530 12.553 12.576 12.599 12.623 12.646 12.669 12.692 12.715 12.738 12.761 12.784 12.807 12.831 12.854 12.877 12.900 12.923 12.946 12.969 12.992 13.016 13.039 13.062 13.085 13.108 13.131 13.154 13.178 13.201 13.224 13.247 13.270 13.293 13.317 13.340 13.363 13.386 13.409 13.433 13.456 13.479 13.502 13.525 13.549 13.572 13.595 13.618 13.641 13.665 13.688 13.711 13.734 13.757 13.781 13.804 13.827 13.850 13.874 13.897 13.920 13.943 13.967 13.990 14.013 14.036 14.060 14.083 14.106 14.129 14.153 14.176 14.199 14.222 14.246 14.269 14.292 14.316 14.339 14.362 14.385 14.409 14.432 14.455 14.479 14.502 14.525 14.548 14.572 14.595 14.618 14.642 14.665 14.688 14.712 14.735 14.758 14.782 14.805 14.828 14.852 14.875 14.898 14.922 14.945 14.968 14.992 15.015 15.038 15.062 15.085 15.108 15.132 15.155 15.178 15.202 15.225 15.248 15.272 15.295 15.318 15.342 15.365 15.389 15.412 15.435 15.459 15.482 15.505 15.529 15.552 15.576 15.599 15.622 15.646 15.669 15.693 15.716 15.739 15.763 15.786 15.810 15.833 15.856 15.880 15.903 15.927 15.950 15.974 15.997 16.020 16.044 16.067 16.091 16.114 16.138 16.161 16.184 16.208 16.231 16.255 16.278 16.302 16.325 16.349 16.372 16.395 16.419 16.442 16.466 16.489 16.513 16.536 16.560 16.583 16.607 16.630 16.654 16.677 16.700 16.724 16.747 16.771 16.794 16.818 16.841 16.865 16.888 16.912 16.935 16.959 16.982 17.006 17.029 17.053 17.076 17.100 17.123 17.147 17.170 17.194 17.217 17.241 17.264 17.288 17.311 17.335 17.358 17.382 17.406 17.429 17.453 17.476 17.500 17.523 17.547 17.570 17.594 17.617 17.641 17.664 17.688 17.711 17.735 17.759 17.782 17.806 17.829 17.853 17.876 17.900 17.923 17.947 17.971 17.994 18.018 18.041 18.065 18.088 18.112 18.136 18.159 18.183 18.206 18.230 18.253 18.277 18.301 18.324 18.348 18.371 18.395 18.418 18.442 18.466 18.489 18.513 18.536 18.560 18.584 18.607 18.631 18.654 18.678 18.702 18.725 18.749 18.772 18.796 18.820 18.843 18.867 18.890 18.914 18.938 18.961 18.985 19.008 19.032 19.056 19.079 19.103 19.127 19.150 19.174 19.197 19.221 19.245 19.268 19.292 19.316 19.339 19.363 19.386 19.410 19.434 19.457 19.481 19.505 19.528 19.552 19.576 19.599 19.623 19.646 19.670 19.694 19.717 19.741 19.765 19.788 19.812 19.836 19.859 19.883 19.907 19.930 19.954 19.978 20.001 20.025 20.049 20.072 20.096 20.120 20.143 20.167 20.190 20.214 20.238 20.261 20.285 20.309 20.332 20.356 20.380 20.403 20.427 20.451 20.474 20.498 20.522 20.545 20.569 20.593 20.616 20.640 20.664 20.688 20.711 20.735 20.759 20.782 20.806 20.830 20.853 20.877 20.901 20.924 20.948 20.972 20.995 21.019 21.043 21.066 21.090 21.114 21.137 21.161 21.185 21.208 21.232 21.256 21.280 21.303 21.327 21.351 21.374 21.398 21.422 21.445 21.469 21.493 21.516 21.540 21.564 21.587 21.611 21.635 21.659 21.682 21.706 21.730 21.753 21.777 21.801 21.824 21.848 21.872 21.895 21.919 21.943 21.966 21.990 22.014 22.038 22.061 22.085 22.109 22.132 22.156 22.180 22.203 22.227 22.251 22.274 22.298 22.322 22.346 22.369 22.393 22.417 22.440 22.464 22.488 22.511 22.535 22.559 22.582 22.606 22.630 22.654 22.677 22.701 22.725 22.748 22.772 22.796 22.819 22.843 22.867 22.890 22.914 22.938 22.961 22.985 23.009 23.032 23.056 23.080 23.104 23.127 23.151 23.175 23.198 23.222 23.246 23.269 23.293 23.317 23.340 23.364 23.388 23.411 23.435 23.459 23.482 23.506 23.530 23.553 23.577 23.601 23.624 23.648 23.672 23.695 23.719 23.743 23.766 23.790 23.814 23.837 23.861 23.885 23.908 23.932 23.956 23.979 24.003 24.027 24.050 24.074 24.098 24.121 24.145 24.169 24.192 24.216 24.240 24.263 24.287 24.311 24.334 24.358 24.382 24.405 24.429 24.453 24.476 24.500 24.523 24.547 24.571 24.594 24.618 24.642 24.665 24.689 24.713 24.736 24.760 24.783 24.807 24.831 24.854 24.878 24.902 24.925 24.949 24.972 24.996 25.020 25.043 25.067 25.091 25.114 25.138 25.161 25.185 25.209 25.232 25.256 25.279 25.303 25.327 25.350 25.374 25.397 25.421 25.445 25.468 25.492 25.515 25.539 25.563 25.586 25.610 25.633 25.657 25.681 25.704 25.728 25.751 25.775 25.799 25.822 25.846 25.869 25.893 25.916 25.940 25.964 25.987 26.011 26.034 26.058 26.081 26.105 26.128 26.152 26.176 26.199 26.223 26.246 26.270 26.293 26.317 26.340 26.364 26.387 26.411 26.435 26.458 26.482 26.505 26.529 26.552 26.576 26.599 26.623 26.646 26.670 26.693 26.717 26.740 26.764 26.787 26.811 26.834 26.858 26.881 26.905 26.928 26.952 26.975 26.999 27.022 27.046 27.069 27.093 27.116 27.140 27.163 27.187 27.210 27.234 27.257 27.281 27.304 27.328 27.351 27.375 27.398 27.422 27.445 27.468 27.492 27.515 27.539 27.562 27.586 27.609 27.633 27.656 27.679 27.703 27.726 27.750 27.773 27.797 27.820 27.843 27.867 27.890 27.914 27.937 27.961 27.984 28.007 28.031 28.054 28.078 28.101 28.124 28.148 28.171 28.195 28.218 28.241 28.265 28.288 28.311 28.335 28.358 28.382 28.405 28.428 28.452 28.475 28.498 28.522 28.545 28.569 28.592 28.615 28.639 28.662 28.685 28.709 28.732 28.755 28.779 28.802 28.825 28.849 28.872 28.895 28.919 28.942 28.965 28.988 29.012 29.035 29.058 29.082 29.105 29.128 29.152 29.175 29.198 29.221 29.245 29.268 29.291 29.315 29.338 29.361 29.384 29.408 29.431 29.454 29.477 29.501 29.524 29.547 29.570 29.594 29.617 29.640 29.663 29.687 29.710 29.733 29.756 29.780 29.803 29.826 29.849 29.872 29.896 29.919 29.942 29.965 29.989 30.012 30.035 30.058 30.081 30.104 30.128 30.151 30.174 30.197 30.220 30.244 30.267 30.290 30.313 30.336 30.359 30.383 30.406 30.429 30.452 30.475 30.498 30.521 30.545 30.568 30.591 30.614 30.637 30.660 30.683 30.706 30.730 30.753 30.776 30.799 30.822 30.845 30.868 30.891 30.914 30.937 30.961 30.984 31.007 31.030 31.053 31.076 31.099 31.122 31.145 31.168 31.191 31.214 31.237 31.260 31.283 31.306 31.329 31.353 31.376 31.399 31.422 31.445 31.468 31.491 31.514 31.537 31.560 31.583 31.606 31.629 31.652 31.675 31.698 31.721 31.744 31.767 31.790 31.813 31.836 31.859 31.882 31.905 31.927 31.950 31.973 31.996 32.019 32.042 32.065 32.088 32.111 32.134 32.157 32.180 32.203 32.226 32.249 32.272 32.294 32.317 32.340 32.363 32.386 32.409 32.432 32.455 32.478 32.501 32.523 32.546 32.569 32.592 32.615 32.638 32.661 32.683 32.706 32.729 32.752 32.775 32.798 32.821 32.843 32.866 32.889 32.912 32.935 32.958 32.980 33.003 33.026 33.049 33.072 33.094 33.117 33.140 33.163 33.186 33.208 33.231 33.254 33.277 33.300 33.322 33.345 33.368 33.391 33.413 33.436 33.459 33.482 33.504 33.527 33.550 33.573 33.595 33.618 33.641 33.664 33.686 33.709 33.732 33.754 33.777 33.800 33.823 33.845 33.868 33.891 33.913 33.936 33.959 33.981 34.004 34.027 34.049 34.072 34.095 34.117 34.140 34.163 34.185 34.208 34.231 34.253 34.276 34.299 34.321 34.344 34.366 34.389 34.412 34.434 34.457 34.480 34.502 34.525 34.547 34.570 34.593 34.615 34.638 34.660 34.683 34.705 34.728 34.751 34.773 34.796 34.818 34.841 34.863 34.886 34.909 34.931 34.954 34.976 34.999 35.021 35.044 35.066 35.089 35.111 35.134 35.156 35.179 35.201 35.224 35.246 35.269 35.291 35.314 35.336 35.359 35.381 35.404 35.426 35.449 35.471 35.494 35.516 35.539 35.561 35.583 35.606 35.628 35.651 35.673 35.696 35.718 35.741 35.763 35.785 35.808 35.830 35.853 35.875 35.897 35.920 35.942 35.965 35.987 36.009 36.032 36.054 36.077 36.099 36.121 36.144 36.166 36.188 36.211 36.233 36.256 36.278 36.300 36.323 36.345 36.367 36.390 36.412 36.434 36.457 36.479 36.501 36.524 36.546 36.568 36.590 36.613 36.635 36.657 36.680 36.702 36.724 36.746 36.769 36.791 36.813 36.836 36.858 36.880 36.902 36.925 36.947 36.969 36.991 37.014 37.036 37.058 37.080 37.103 37.125 37.147 37.169 37.191 37.214 37.236 37.258 37.280 37.303 37.325 37.347 37.369 37.391 37.413 37.436 37.458 37.480 37.502 37.524 37.547 37.569 37.591 37.613 37.635 37.657 37.679 37.702 37.724 37.746 37.768 37.790 37.812 37.834 37.857 37.879 37.901 37.923 37.945 37.967 37.989 38.011 38.033 38.055 38.078 38.100 38.122 38.144 38.166 38.188 38.210 38.232 38.254 38.276 38.298 38.320 38.342 38.364 38.387 38.409 38.431 38.453 38.475 38.497 38.519 38.541 38.563 38.585 38.607 38.629 38.651 38.673 38.695 38.717 38.739 38.761 38.783 38.805 38.827 38.849 38.871 38.893 38.915 38.937 38.959 38.981 39.003 39.024 39.046 39.068 39.090 39.112 39.134 39.156 39.178 39.200 39.222 39.244 39.266 39.288 39.310 39.331 39.353 39.375 39.397 39.419 39.441 39.463 39.485 39.507 39.529 39.550 39.572 39.594 39.616 39.638 39.660 39.682 39.703 39.725 39.747 39.769 39.791 39.813 39.835 39.856 39.878 39.900 39.922 39.944 39.965 39.987 40.009 40.031 40.053 40.075 40.096 40.118 40.140 40.162 40.183 40.205 40.227 40.249 40.271 40.292 40.314 40.336 40.358 40.379 40.401 40.423 40.445 40.466 40.488 40.510 40.532 40.553 40.575 40.597 40.619 40.640 40.662 40.684 40.705 40.727 40.749 40.770 40.792 40.814 40.836 40.857 40.879 40.901 40.922 40.944 40.966 40.987 41.009 41.031 41.052 41.074 41.096 41.117 41.139 41.161 41.182 41.204 41.225 41.247 41.269 41.290 41.312 41.334 41.355 41.377 41.398 41.420 41.442 41.463 41.485 41.506 41.528 41.550 41.571 41.593 41.614 41.636 41.657 41.679 41.701 41.722 41.744 41.765 41.787 41.808 41.830 41.851 41.873 41.895 41.916 41.938 41.959 41.981 42.002 42.024 42.045 42.067 42.088 42.110 42.131 42.153 42.174 42.196 42.217 42.239 42.260 42.282 42.303 42.325 42.346 42.367 42.389 42.410 42.432 42.453 42.475 42.496 42.518 42.539 42.560 42.582 42.603 42.625 42.646 42.668 42.689 42.710 42.732 42.753 42.775 42.796 42.817 42.839 42.860 42.882 42.903 42.924 42.946 42.967 42.989 43.010 43.031 43.053 43.074 43.095 43.117 43.138 43.159 43.181 43.202 43.223 43.245 43.266 43.287 43.309 43.330 43.351 43.373 43.394 43.415 43.436 43.458 43.479 43.500 43.522 43.543 43.564 43.585 43.607 43.628 43.649 43.671 43.692 43.713 43.734 43.756 43.777 43.798 43.819 43.841 43.862 43.883 43.904 43.925 43.947 43.968 43.989 44.010 44.031 44.053 44.074 44.095 44.116 44.137 44.159 44.180 44.201 44.222 44.243 44.265 44.286 44.307 44.328 44.349 44.370 44.391 44.413 44.434 44.455 44.476 44.497 44.518 44.539 44.560 44.582 44.603 44.624 44.645 44.666 44.687 44.708 44.729 44.750 44.771 44.793 44.814 44.835 44.856 44.877 44.898 44.919 44.940 44.961 44.982 45.003 45.024 45.045 45.066 45.087 45.108 45.129 45.150 45.171 45.192 45.213 45.234 45.255 45.276 45.297 45.318 45.339 45.360 45.381 45.402 45.423 45.444 45.465 45.486 45.507 45.528 45.549 45.570 45.591 45.612 45.633 45.654 45.675 45.695 45.716 45.737 45.758 45.779 45.800 45.821 45.842 45.863 45.884 45.904 45.925 45.946 45.967 45.988 46.009 46.030 46.051 46.071 46.092 46.113 46.134 46.155 46.176 46.196 46.217 46.238 46.259 46.280 46.300 46.321 46.342 46.363 46.384 46.404 46.425 46.446 46.467 46.488 46.508 46.529 46.550 46.571 46.591 46.612 46.633 46.654 46.674 46.695 46.716 46.737 46.757 46.778 46.799 46.819 46.840 46.861 46.881 46.902 46.923 46.944 46.964 46.985 47.006 47.026 47.047 47.068 47.088 47.109 47.130 47.150 47.171 47.191 47.212 47.233 47.253 47.274 47.295 47.315 47.336 47.356 47.377 47.398 47.418 47.439 47.459 47.480 47.500 47.521 47.542 47.562 47.583 47.603 47.624 47.644 47.665 47.685 47.706 47.726 47.747 47.767 47.788 47.808 47.829 47.849 47.870 47.890 47.911 47.931 47.952 47.972 47.993 48.013 48.034 48.054 48.075 48.095 48.116 48.136 48.156 48.177 48.197 48.218 48.238 48.258 48.279 48.299 48.320 48.340 48.360 48.381 48.401 48.422 48.442 48.462 48.483 48.503 48.523 48.544 48.564 48.584 48.605 48.625 48.645 48.666 48.686 48.706 48.727 48.747 48.767 48.787 48.808 48.828 48.848 48.869 48.889 48.909 48.929 48.950 48.970 48.990 49.010 49.031 49.051 49.071 49.091 49.111 49.132 49.152 49.172 49.192 49.212 49.233 49.253 49.273 49.293 49.313 49.333 49.354 49.374 49.394 49.414 49.434 49.454 49.474 49.495 49.515 49.535 49.555 49.575 49.595 49.615 49.635 49.655 49.675 49.696 49.716 49.736 49.756 49.776 49.796 49.816 49.836 49.856 49.876 49.896 49.916 49.936 49.956 49.976 49.996 50.016 50.036 50.056 50.076 50.096 50.116 50.136 50.156 50.176 50.196 50.216 50.236 50.256 50.276 50.296 50.315 50.335 50.355 50.375 50.395 50.415 50.435 50.455 50.475 50.494 50.514 50.534 50.554 50.574 50.594 50.614 50.633 50.653 50.673 50.693 50.713 50.733 50.752 50.772 50.792 50.812 50.832 50.851 50.871 50.891 50.911 50.930 50.950 50.970 50.990 51.009 51.029 51.049 51.069 51.088 51.108 51.128 51.148 51.167 51.187 51.207 51.226 51.246 51.266 51.285 51.305 51.325 51.344 51.364 51.384 51.403 51.423 51.443 51.462 51.482 51.501 51.521 51.541 51.560 51.580 51.599 51.619 51.639 51.658 51.678 51.697 51.717 51.736 51.756 51.776 51.795 51.815 51.834 51.854 51.873 51.893 51.912 51.932 51.951 51.971 51.990 52.010 52.029 52.049 52.068 52.088 52.107 52.127 52.146 52.165 52.185 52.204 52.224 52.243 52.263 52.282 52.301 52.321 52.340 52.360 52.379 52.398 52.418 52.437 52.457 52.476 52.495 52.515 52.534 52.553 52.573 52.592 52.611 52.631 52.650 52.669 52.689 52.708 52.727 52.747 52.766 52.785 52.805 52.824 52.843 52.862 52.882 52.901 52.920 52.939 52.959 52.978 52.997 53.016 53.036 53.055 53.074 53.093 53.113 53.132 53.151 53.170 53.189 53.209 53.228 53.247 53.266 53.285 53.304 53.324 53.343 53.362 53.381 53.400 53.419 53.439 53.458 53.477 53.496 53.515 53.534 53.553 53.572 53.592 53.611 53.630 53.649 53.668 53.687 53.706 53.725 53.744 53.763 53.782 53.801 53.821 53.840 53.859 53.878 53.897 53.916 53.935 53.954 53.973 53.992 54.011 54.030 54.049 54.068 54.087 54.106 54.125 54.144 54.163 54.182 54.201 54.220 54.239 54.258 54.277 54.296 54.315 54.334 54.353 54.372 54.391 54.410 54.429 54.447 54.466 54.485 54.504 54.523 54.542 54.561 54.580 54.599 54.618 54.637 54.656 54.675 54.694 54.712 54.731 54.750 54.769 54.788 54.807 54.826 54.845 base-7.0.3.1/modules/database/src/ioc/bpt/cvtTable.h0000664000577000060420000000176313557101274020701 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Breakpoint Tables * * Author: Marty Kraimer * Date: 11-7-90 */ #ifndef INCcvtTableh #define INCcvtTableh 1 #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /* Global Routines*/ epicsShareFunc long cvtEngToRawBpt( double *pval,short linr,short init,void **ppbrk,short *plbrk); epicsShareFunc long cvtRawToEngBpt( double *pval,short linr,short init,void **ppbrk, short *plbrk); #ifdef __cplusplus } #endif #endif base-7.0.3.1/modules/database/src/ioc/bpt/makeBpt.c0000664000577000060420000003216313557101274020511 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Marty Kraimer * Date: 9/28/95 * Replacement for old bldCvtTable */ #include #include #include #include #include #include #include "dbDefs.h" #include "ellLib.h" #include "cvtTable.h" #define MAX_LINE_SIZE 160 #define MAX_BREAKS 100 struct brkCreateInfo { double engLow; /* Lowest value desired: engineering units */ double engHigh; /* Highest value desired: engineering units */ double rawLow; /* Raw value for EngLow */ double rawHigh; /* Raw value for EngHigh */ double accuracy; /* accuracy desired in engineering units */ double tblEngFirst;/* First table value: engineering units */ double tblEngLast; /* Last table value: engineering units */ double tblEngDelta;/* Change per table entry: eng units */ long nTable; /* number of table entries */ /* (last-first)/delta + 1 */ double *pTable; /* addr of data table */ } brkCreateInfo; typedef struct brkInt { /* breakpoint interval */ double raw; /* raw value for beginning of interval */ double slope; /* slope for interval */ double eng; /* converted value for beginning of interval */ } brkInt; brkInt brkint[MAX_BREAKS]; static int create_break(struct brkCreateInfo *pbci, brkInt *pabrkInt, int max_breaks, int *n_breaks); static char inbuf[MAX_LINE_SIZE]; static int linenum=0; typedef struct dataList{ struct dataList *next; double value; }dataList; static int getNumber(char **pbeg, double *value) { int nchars=0; while(isspace((int)**pbeg) && **pbeg!= '\0') (*pbeg)++; if(**pbeg == '!' || **pbeg == '\0') return(-1); if(sscanf(*pbeg,"%lf%n",value,&nchars)!=1) return(-1); *pbeg += nchars; return(0); } static void errExit(char *pmessage) { fprintf(stderr, "%s\n", pmessage); fflush(stderr); exit(-1); } int main(int argc, char **argv) { char *pbeg; char *pend; double value; char *pname = NULL; dataList *phead; dataList *pdataList; dataList *pnext; double *pdata; long ndata; int nBreak,n; size_t len; char *outFilename; char *pext; FILE *outFile; FILE *inFile; char *plastSlash; if(argc<2) { fprintf(stderr,"usage: makeBpt file.data [outfile]\n"); exit(-1); } if (argc==2) { plastSlash = strrchr(argv[1],'/'); plastSlash = (plastSlash ? plastSlash+1 : argv[1]); outFilename = calloc(1,strlen(plastSlash)+2); if(!outFilename) { fprintf(stderr,"calloc failed\n"); exit(-1); } strcpy(outFilename,plastSlash); pext = strstr(outFilename,".data"); if(!pext) { fprintf(stderr,"Input file MUST have .data extension\n"); exit(-1); } strcpy(pext,".dbd"); } else { outFilename = calloc(1,strlen(argv[2])+1); if(!outFilename) { fprintf(stderr,"calloc failed\n"); exit(-1); } strcpy(outFilename,argv[2]); } inFile = fopen(argv[1],"r"); if(!inFile) { fprintf(stderr,"Error opening %s\n",argv[1]); exit(-1); } outFile = fopen(outFilename,"w"); if(!outFile) { fprintf(stderr,"Error opening %s\n",outFilename); exit(-1); } while(fgets(inbuf,MAX_LINE_SIZE,inFile)) { linenum++; pbeg = inbuf; while(isspace((int)*pbeg) && *pbeg!= '\0') pbeg++; if(*pbeg == '!' || *pbeg == '\0') continue; while(*pbeg!='"' && *pbeg!= '\0') pbeg++; if(*pbeg!='"' ) errExit("Illegal Header"); pbeg++; pend = pbeg; while(*pend!='"' && *pend!= '\0') pend++; if(*pend!='"') errExit("Illegal Header"); len = pend - pbeg; if(len<=1) errExit("Illegal Header"); pname = calloc(len+1,sizeof(char)); if(!pname) { fprintf(stderr,"calloc failed while processing line %d\n",linenum); exit(-1); } strncpy(pname,pbeg,len); pname[len]='\0'; pbeg = pend + 1; if(getNumber(&pbeg,&value)) errExit("Illegal Header"); brkCreateInfo.engLow = value; if(getNumber(&pbeg,&value)) errExit("Illegal Header"); brkCreateInfo.rawLow = value; if(getNumber(&pbeg,&value)) errExit("Illegal Header"); brkCreateInfo.engHigh = value; if(getNumber(&pbeg,&value)) errExit("Illegal Header"); brkCreateInfo.rawHigh = value; if(getNumber(&pbeg,&value)) errExit("Illegal Header"); brkCreateInfo.accuracy = value; if(getNumber(&pbeg,&value)) errExit("Illegal Header"); brkCreateInfo.tblEngFirst = value; if(getNumber(&pbeg,&value)) errExit("Illegal Header"); brkCreateInfo.tblEngLast = value; if(getNumber(&pbeg,&value)) errExit("Illegal Header"); brkCreateInfo.tblEngDelta = value; goto got_header; } errExit("Illegal Header"); got_header: phead = pnext = 0; ndata = 0; errno = 0; while(fgets(inbuf,MAX_LINE_SIZE,inFile)) { double value; linenum++; pbeg = inbuf; while(!getNumber(&pbeg,&value)) { ndata++; pdataList = (dataList *)calloc(1,sizeof(dataList)); if(!pdataList) { fprintf(stderr,"calloc failed (after header)" " while processing line %d\n",linenum); exit(-1); } if(!phead) phead = pdataList; else pnext->next = pdataList; pdataList->value = value; pnext = pdataList; } } if(!pname) { errExit("create_break failed: no name specified\n"); } brkCreateInfo.nTable = ndata; pdata = (double *)calloc(brkCreateInfo.nTable,sizeof(double)); if(!pdata) { fprintf(stderr,"calloc failed for table length %ld\n",brkCreateInfo.nTable); exit(-1); } pnext = phead; for(n=0; nvalue; pdataList = pnext; pnext = pnext->next; free((void *)pdataList); } brkCreateInfo.pTable = pdata; if(create_break(&brkCreateInfo,&brkint[0],MAX_BREAKS,&nBreak)) errExit("create_break failed\n"); fprintf(outFile,"breaktable(%s) {\n",pname); for(n=0; npTable; long ntable = pbci->nTable; double ilow, ihigh, tbllow, tblhigh, slope, offset; int ibeg, iend, i, inc, imax, n; double rawBeg, engBeg, rawEnd, engEnd, engCalc, engActual, error; int valid, all_ok, expanding; /* make checks to ensure that brkCreateInfo makes sense */ if (pbci->engLow >= pbci->engHigh) { errExit("create_break: engLow >= engHigh"); return (-1); } if ((pbci->engLow < pbci->tblEngFirst) || (pbci->engHigh > pbci->tblEngLast)) { errExit("create_break: engLow > engHigh"); return (-1); } if (pbci->tblEngDelta <= 0.0) { errExit("create_break: tblEngDelta <= 0.0"); return (-1); } if (ntable < 3) { errExit("raw data must have at least 3 elements"); return (-1); } /*************************************************************************** Convert Table to raw values * * raw and table values are assumed to be related by an equation of the form: * * raw = slope*table + offset * * The following algorithm converts each table value to raw units * * 1) Finds the locations in Table corresponding to engLow and engHigh * Note that these locations need not be exact integers * 2) Interpolates to obtain table values corresponding to engLow and enghigh * we now have the equations: * rawLow = slope*tblLow + offset * rawHigh = slope*tblHigh + offset * 4) Solving these equations for slope and offset gives: * slope=(rawHigh-rawLow)/(tblHigh-tblLow) * offset=rawHigh-slope*tblHigh * 5) for each table value set table[i]=table[i]*slope+offset *************************************************************************/ /* Find engLow in Table and then compute tblLow */ ilow = (pbci->engLow - pbci->tblEngFirst) / (pbci->tblEngDelta); i = (int) ilow; if (i >= ntable - 1) i = ntable - 2; tbllow = table[i] + (table[i + 1] - table[i]) * (ilow - (double) i); /* Find engHigh in Table and then compute tblHigh */ ihigh = (pbci->engHigh - pbci->tblEngFirst) / (pbci->tblEngDelta); i = (int) ihigh; if (i >= ntable - 1) i = ntable - 2; tblhigh = table[i] + (table[i + 1] - table[i]) * (ihigh - (double) i); /* compute slope and offset */ slope = (pbci->rawHigh - pbci->rawLow) / (tblhigh - tbllow); offset = pbci->rawHigh - slope * tblhigh; /* convert table to raw units */ for (i = 0; i < ntable; i++) table[i] = table[i] * slope + offset; /***************************************************************************** * Now create break point table * * The algorithm does the following: * * It finds one breakpoint interval at a time. For each it does the following: * * 1) Use a relatively large portion of the remaining table as an interval * 2) It attempts to use the entire interval as a breakpoint interval * Success is determined by the following algorithm: * a) compute the slope using the entire interval * b) for each table entry in the interval determine the eng value * using the slope just determined. * c) compare the computed value with eng value associated with table * d) if all table entries are within the accuracy desired then success. * 3) If successful then attempt to expand the interval and try again. * Note that it is expanded by up to 1/10 of the table size. * 4) If not successful reduce the interval by 1 and try again. * Once the interval is being decreased it will never be increased again. * 5) The algorithm will ultimately fail or will have determined the optimum * breakpoint interval *************************************************************************/ /* Must start with table entry corresponding to engLow; */ i = (int) ilow; if (i >= ntable - 1) i = ntable - 2; rawBeg = table[i] + (table[i + 1] - table[i]) * (ilow - (double) i); engBeg = pbci->engLow; ibeg = (int) (ilow); /* Make sure that ibeg > ilow */ if( ibeg < ilow ) ibeg = ibeg + 1; /* start first breakpoint interval */ n = 1; pbrkInt = pabrkInt; pbrkInt->raw = rawBeg; pbrkInt->eng = engBeg; /* determine next breakpoint interval */ while ((engBeg <= pbci->engHigh) && (ibeg < ntable - 1)) { /* determine next interval to try. Up to 1/10 full range */ rawEnd = rawBeg; engEnd = engBeg; iend = ibeg; inc = (int) ((ihigh - ilow) / 10.0); if (inc < 1) inc = 1; valid = TRUE; /* keep trying intervals until cant do better */ expanding = TRUE; /* originally we are trying larger and larger * intervals */ while (valid) { imax = iend + inc; if (imax >= ntable) { /* don't go past end of table */ imax = ntable - 1; inc = ntable - iend - 1; expanding = FALSE; } if (imax > (int) (ihigh + 1.0)) { /* Don't go to far past * engHigh */ imax = (int) (ihigh + 1.0); inc = (int) (ihigh + 1.0) - iend; expanding = FALSE; } if (imax <= ibeg) break; /* failure */ rawEnd = table[imax]; engEnd = pbci->tblEngFirst + (double) imax *(pbci->tblEngDelta); slope = (engEnd - engBeg) / (rawEnd - rawBeg); all_ok = TRUE; for (i = ibeg + 1; i <= imax; i++) { engCalc = engBeg + slope * (table[i] - rawBeg); engActual = pbci->tblEngFirst + ((double) i) * (pbci->tblEngDelta); error = engCalc - engActual; if (error < 0.0) error = -error; if (error >= pbci->accuracy) { /* we will be trying smaller intervals */ expanding = FALSE; /* just decrease inc and let while(valid) try again */ inc--; all_ok = FALSE; break; } } /* end for */ if (all_ok) { iend = imax; /* if not expanding we found interval */ if (!expanding) break; /* will automatically try larger interval */ } } /* end while(valid) */ /* either we failed or optimal interval has been found */ if ((iend <= ibeg) && (iend < (int) ihigh)) { errExit("Could not meet accuracy criteria"); return (-1); } pbrkInt->slope = slope; /* get ready for next breakpoint interval */ if (n++ >= max_breaks) { errExit("Break point table too large"); return (-1); } ibeg = iend; pbrkInt++; rawBeg = rawEnd; engBeg = engEnd; pbrkInt->raw = rawBeg; pbrkInt->eng = engBeg + (pbrkInt->raw - rawBeg) * slope; } pbrkInt->slope = 0.0; *n_breaks = n; return (0); } base-7.0.3.1/modules/database/src/ioc/bpt/menuConvert.dbd.pod0000664000577000060420000000277413557101274022530 0ustar anjaesctl#************************************************************************* # Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =head1 Menu menuConvert This menu defines the standard analog conversions which are included with Base. IOC applications may add choices or replace the later choices in this menu, although the first three choices must not be renamed or moved to different positions. The breakpoint table name must exactly match the choice string listed here. =menu menuConvert =cut menu(menuConvert) { choice(menuConvertNO_CONVERSION,"NO CONVERSION") choice(menuConvertSLOPE,"SLOPE") choice(menuConvertLINEAR,"LINEAR") choice(menuConverttypeKdegF,"typeKdegF") choice(menuConverttypeKdegC,"typeKdegC") choice(menuConverttypeJdegF,"typeJdegF") choice(menuConverttypeJdegC,"typeJdegC") choice(menuConverttypeEdegF,"typeEdegF(ixe only)") choice(menuConverttypeEdegC,"typeEdegC(ixe only)") choice(menuConverttypeTdegF,"typeTdegF") choice(menuConverttypeTdegC,"typeTdegC") choice(menuConverttypeRdegF,"typeRdegF") choice(menuConverttypeRdegC,"typeRdegC") choice(menuConverttypeSdegF,"typeSdegF") choice(menuConverttypeSdegC,"typeSdegC") } base-7.0.3.1/modules/database/src/ioc/databaseVersion.h0000664000577000060420000000166213557101274021460 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef DATABASEVERSION_H #define DATABASEVERSION_H #include #ifndef VERSION_INT # define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P)) #endif /* include generated headers with: * EPICS_DATABASE_MAJOR_VERSION * EPICS_DATABASE_MINOR_VERSION * EPICS_DATABASE_MAINTENANCE_VERSION * EPICS_DATABASE_DEVELOPMENT_FLAG */ #include "databaseVersionNum.h" #define DATABASE_VERSION_INT VERSION_INT(EPICS_DATABASE_MAJOR_VERSION, EPICS_DATABASE_MINOR_VERSION, EPICS_DATABASE_MAINTENANCE_VERSION, 0) #endif // DATABASEVERSION_H base-7.0.3.1/modules/database/src/ioc/databaseVersionNum.h@0000664000577000060420000000056713557101274022243 0ustar anjaesctl#ifndef DATABASEVERSION_H # error include databaseVersion.h, not this header #endif #define EPICS_DATABASE_MAJOR_VERSION @EPICS_DATABASE_MAJOR_VERSION@ #define EPICS_DATABASE_MINOR_VERSION @EPICS_DATABASE_MINOR_VERSION@ #define EPICS_DATABASE_MAINTENANCE_VERSION @EPICS_DATABASE_MAINTENANCE_VERSION@ #define EPICS_DATABASE_DEVELOPMENT_FLAG @EPICS_DATABASE_DEVELOPMENT_FLAG@ base-7.0.3.1/modules/database/src/ioc/db/Makefile0000664000577000060420000000517413557101274020224 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. SRC_DIRS += $(IOCDIR)/db INC += callback.h INC += dbAccess.h INC += dbAccessDefs.h INC += dbAddr.h INC += dbBkpt.h INC += dbCa.h INC += dbChannel.h INC += dbConstLink.h INC += dbConvert.h INC += dbConvertFast.h INC += dbConvertJSON.h INC += dbDbLink.h INC += dbExtractArray.h INC += dbEvent.h INC += dbJLink.h INC += dbLink.h INC += dbLock.h INC += dbNotify.h INC += dbScan.h INC += dbServer.h INC += dbTest.h INC += dbCaTest.h INC += db_test.h INC += db_field_log.h INC += recGbl.h INC += dbIocRegister.h INC += chfPlugin.h INC += dbState.h INC += db_access_routines.h INC += db_convert.h INC += dbUnitTest.h # Generate menuGlobal.dbd, not really by concatenation, see RULES DBDCAT += menuGlobal.dbd menuGlobal_DBD += menuAlarmSevr.dbd menuGlobal_DBD += menuAlarmStat.dbd menuGlobal_DBD += menuFtype.dbd menuGlobal_DBD += menuIvoa.dbd menuGlobal_DBD += menuOmsl.dbd menuGlobal_DBD += menuPini.dbd menuGlobal_DBD += menuPost.dbd menuGlobal_DBD += menuPriority.dbd menuGlobal_DBD += menuYesNo.dbd menuGlobal_DBD += menuSimm.dbd DBDINC += $(basename $(menuGlobal_DBD)) DBDINC += menuScan DBDINC += dbCommon dbMenusPod = $(notdir $(wildcard ../db/menu*.dbd.pod)) HTMLS += $(patsubst %.dbd.pod,%.html,$(dbMenusPod)) dbCore_SRCS += dbLock.c dbCore_SRCS += dbAccess.c dbCore_SRCS += dbBkpt.c dbCore_SRCS += dbChannel.c dbCore_SRCS += dbConstLink.c dbCore_SRCS += dbConvert.c dbCore_SRCS += dbConvertJSON.c dbCore_SRCS += dbDbLink.c dbCore_SRCS += dbFastLinkConv.c dbCore_SRCS += dbExtractArray.c dbCore_SRCS += dbJLink.c dbCore_SRCS += dbLink.c dbCore_SRCS += dbNotify.c dbCore_SRCS += dbScan.c dbCore_SRCS += dbEvent.c dbCore_SRCS += dbTest.c dbCore_SRCS += db_access.c dbCore_SRCS += db_test.c dbCore_SRCS += recGbl.c dbCore_SRCS += callback.c dbCore_SRCS += dbCa.c dbCore_SRCS += dbCaTest.c dbCore_SRCS += cvtBpt.c dbCore_SRCS += dbContext.cpp dbCore_SRCS += dbChannelIO.cpp dbCore_SRCS += dbSubscriptionIO.cpp dbCore_SRCS += dbPutNotifyBlocker.cpp dbCore_SRCS += dbContextReadNotifyCache.cpp dbCore_SRCS += dbIocRegister.c dbCore_SRCS += chfPlugin.c dbCore_SRCS += dbState.c dbCore_SRCS += dbUnitTest.c dbCore_SRCS += dbServer.c base-7.0.3.1/modules/database/src/ioc/db/RULES0000664000577000060420000000225513557101274017376 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2010 Brookhaven Science Associates, as Operator of # Brookhaven National Lab. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. dbCommon.h$(DEP): $(IOCDIR)/db/dbCommonRecord.dbd $(IOCDIR)/db/RULES @$(RM) $@ @$(DBTORECORDTYPEH) -D -I ../db -o $(COMMONDEP_TARGET) $< > $@ $(COMMON_DIR)/dbCommon.h: $(IOCDIR)/db/dbCommonRecord.dbd $(IOCDIR)/db/RULES @$(RM) $(notdir $@) $(DBTORECORDTYPEH) -I ../db -o $(notdir $@) $< @$(MV) $(notdir $@) $@ $(COMMON_DIR)/menuGlobal.dbd: $(IOCDIR)/db/Makefile $(IOCDIR)/db/RULES # This is a target-specific variable $(COMMON_DIR)/menuGlobal.dbd: DBDCAT_COMMAND = \ $(PERL) $(INSTALL_HOST_BIN)/makeIncludeDbd.pl $(menuGlobal_DBD) $(@F) base-7.0.3.1/modules/database/src/ioc/db/callback.c0000664000577000060420000003430713557101274020464 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * Copyright (c) 2013 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* callback.c */ /* general purpose callback tasks */ /* * Original Author: Marty Kraimer * Date: 07-18-91 */ #include #include #include #include #include "cantProceed.h" #include "dbDefs.h" #include "epicsAtomic.h" #include "epicsEvent.h" #include "epicsInterrupt.h" #include "epicsRingPointer.h" #include "epicsString.h" #include "epicsThread.h" #include "epicsTimer.h" #include "errlog.h" #include "errMdef.h" #include "taskwd.h" #define epicsExportSharedSymbols #include "callback.h" #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" #include "dbCommon.h" #include "dbFldTypes.h" #include "dbLock.h" #include "dbStaticLib.h" #include "epicsExport.h" #include "link.h" #include "recSup.h" #include "dbUnitTest.h" /* for testSyncCallback() */ static int callbackQueueSize = 2000; typedef struct cbQueueSet { epicsEventId semWakeUp; epicsRingPointerId queue; int queueOverflow; int queueOverflows; int shutdown; // use atomic int threadsConfigured; int threadsRunning; } cbQueueSet; static cbQueueSet callbackQueue[NUM_CALLBACK_PRIORITIES]; int callbackThreadsDefault = 1; /* Don't know what a reasonable default is (yet). * For the time being: parallel means 2 if not explicitly specified */ epicsShareDef int callbackParallelThreadsDefault = 2; epicsExportAddress(int,callbackParallelThreadsDefault); /* Timer for Delayed Requests */ static epicsTimerQueueId timerQueue; enum cbState_t { cbInit, /* before callbackInit() and after callbackCleanup() */ cbRun, /* after callbackInit() and before callbackStop() */ cbStop, /* after callbackStop() and before callbackCleanup() */ }; static int cbState; // holdscbState_t, use atomic ops static epicsEventId startStopEvent; /* Static data */ static char *threadNamePrefix[NUM_CALLBACK_PRIORITIES] = { "cbLow", "cbMedium", "cbHigh" }; #define FULL_MSG(name) "callbackRequest: " name " ring buffer full\n" static char *fullMessage[NUM_CALLBACK_PRIORITIES] = { FULL_MSG("cbLow"), FULL_MSG("cbMedium"), FULL_MSG("cbHigh") }; static unsigned int threadPriority[NUM_CALLBACK_PRIORITIES] = { epicsThreadPriorityScanLow - 1, epicsThreadPriorityScanLow + 4, epicsThreadPriorityScanHigh + 1 }; static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2}; int callbackSetQueueSize(int size) { if (epicsAtomicGetIntT(&cbState)!=cbInit) { fprintf(stderr, "Callback system already initialized\n"); return -1; } callbackQueueSize = size; return 0; } int callbackQueueStatus(const int reset, callbackQueueStats *result) { int ret; if (epicsAtomicGetIntT(&cbState)==cbInit) return -1; if (result) { int prio; result->size = callbackQueueSize; for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { epicsRingPointerId qId = callbackQueue[prio].queue; result->numUsed[prio] = epicsRingPointerGetUsed(qId); result->maxUsed[prio] = epicsRingPointerGetHighWaterMark(qId); result->numOverflow[prio] = epicsAtomicGetIntT(&callbackQueue[prio].queueOverflows); } ret = 0; } else { ret = -2; } if (reset) { int prio; for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { epicsRingPointerResetHighWaterMark(callbackQueue[prio].queue); } } return ret; } void callbackQueueShow(const int reset) { callbackQueueStats stats; if (callbackQueueStatus(reset, &stats) == -1) { fprintf(stderr, "Callback system not initialized, yet. Please run " "iocInit before using this command.\n"); } else { int prio; printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n"); for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { double qusage = 100.0 * stats.numUsed[prio] / stats.size; printf("%8s %15d %10d %6d %6.1f %11d\n", threadNamePrefix[prio], stats.maxUsed[prio], stats.numUsed[prio], stats.size, qusage, stats.numOverflow[prio]); } } } int callbackParallelThreads(int count, const char *prio) { if (epicsAtomicGetIntT(&cbState)!=cbInit) { fprintf(stderr, "Callback system already initialized\n"); return -1; } if (count < 0) count = epicsThreadGetCPUs() + count; else if (count == 0) count = callbackParallelThreadsDefault; if (count < 1) count = 1; if (!prio || *prio == 0 || strcmp(prio, "*") == 0) { int i; for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) { callbackQueue[i].threadsConfigured = count; } } else { dbMenu *pdbMenu; int i; if (!pdbbase) { fprintf(stderr, "callbackParallelThreads: pdbbase not set\n"); return -1; } /* Find prio in menuPriority */ pdbMenu = dbFindMenu(pdbbase, "menuPriority"); if (!pdbMenu) { fprintf(stderr, "callbackParallelThreads: No Priority menu\n"); return -1; } for (i = 0; i < pdbMenu->nChoice; i++) { if (epicsStrCaseCmp(prio, pdbMenu->papChoiceValue[i]) == 0) goto found; } fprintf(stderr, "callbackParallelThreads: " "Unknown priority \"%s\"\n", prio); return -1; found: callbackQueue[i].threadsConfigured = count; } return 0; } static void callbackTask(void *arg) { int prio = *(int*)arg; cbQueueSet *mySet = &callbackQueue[prio]; taskwdInsert(0, NULL, NULL); epicsEventSignal(startStopEvent); while(!epicsAtomicGetIntT(&mySet->shutdown)) { void *ptr; if (epicsRingPointerIsEmpty(mySet->queue)) epicsEventMustWait(mySet->semWakeUp); while ((ptr = epicsRingPointerPop(mySet->queue))) { epicsCallback *pcallback = (epicsCallback *)ptr; if(!epicsRingPointerIsEmpty(mySet->queue)) epicsEventMustTrigger(mySet->semWakeUp); mySet->queueOverflow = FALSE; (*pcallback->callback)(pcallback); } } if(!epicsAtomicDecrIntT(&mySet->threadsRunning)) epicsEventSignal(startStopEvent); taskwdRemove(0); } void callbackStop(void) { int i; if (epicsAtomicCmpAndSwapIntT(&cbState, cbRun, cbStop)!=cbRun) return; for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) { epicsAtomicSetIntT(&callbackQueue[i].shutdown, 1); epicsEventSignal(callbackQueue[i].semWakeUp); } for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) { cbQueueSet *mySet = &callbackQueue[i]; while (epicsAtomicGetIntT(&mySet->threadsRunning)) { epicsEventSignal(mySet->semWakeUp); epicsEventWaitWithTimeout(startStopEvent, 0.1); } } } void callbackCleanup(void) { int i; if(epicsAtomicCmpAndSwapIntT(&cbState, cbStop, cbInit)!=cbStop) { fprintf(stderr, "callbackCleanup() but not stopped\n"); } for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) { cbQueueSet *mySet = &callbackQueue[i]; assert(epicsAtomicGetIntT(&mySet->threadsRunning)==0); epicsEventDestroy(mySet->semWakeUp); epicsRingPointerDelete(mySet->queue); } epicsTimerQueueRelease(timerQueue); memset(callbackQueue, 0, sizeof(callbackQueue)); } void callbackInit(void) { int i; int j; char threadName[32]; if (epicsAtomicCmpAndSwapIntT(&cbState, cbInit, cbRun)!=cbInit) { fprintf(stderr, "Warning: callbackInit called again before callbackCleanup\n"); return; } if(!startStopEvent) startStopEvent = epicsEventMustCreate(epicsEventEmpty); timerQueue = epicsTimerQueueAllocate(0, epicsThreadPriorityScanHigh); for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) { epicsThreadId tid; callbackQueue[i].semWakeUp = epicsEventMustCreate(epicsEventEmpty); callbackQueue[i].queue = epicsRingPointerLockedCreate(callbackQueueSize); if (callbackQueue[i].queue == 0) cantProceed("epicsRingPointerLockedCreate failed for %s\n", threadNamePrefix[i]); callbackQueue[i].queueOverflow = FALSE; if (callbackQueue[i].threadsConfigured == 0) callbackQueue[i].threadsConfigured = callbackThreadsDefault; for (j = 0; j < callbackQueue[i].threadsConfigured; j++) { if (callbackQueue[i].threadsConfigured > 1 ) sprintf(threadName, "%s-%d", threadNamePrefix[i], j); else strcpy(threadName, threadNamePrefix[i]); tid = epicsThreadCreate(threadName, threadPriority[i], epicsThreadGetStackSize(epicsThreadStackBig), (EPICSTHREADFUNC)callbackTask, &priorityValue[i]); if (tid == 0) { cantProceed("Failed to spawn callback thread %s\n", threadName); } else { epicsEventWait(startStopEvent); epicsAtomicIncrIntT(&callbackQueue[i].threadsRunning); } } } } /* This routine can be called from interrupt context */ int callbackRequest(epicsCallback *pcallback) { int priority; int pushOK; cbQueueSet *mySet; if (!pcallback) { epicsInterruptContextMessage("callbackRequest: pcallback was NULL\n"); return S_db_notInit; } priority = pcallback->priority; if (priority < 0 || priority >= NUM_CALLBACK_PRIORITIES) { epicsInterruptContextMessage("callbackRequest: Bad priority\n"); return S_db_badChoice; } mySet = &callbackQueue[priority]; if (mySet->queueOverflow) return S_db_bufFull; pushOK = epicsRingPointerPush(mySet->queue, pcallback); if (!pushOK) { epicsInterruptContextMessage(fullMessage[priority]); mySet->queueOverflow = TRUE; epicsAtomicIncrIntT(&mySet->queueOverflows); return S_db_bufFull; } epicsEventSignal(mySet->semWakeUp); return 0; } static void ProcessCallback(epicsCallback *pcallback) { dbCommon *pRec; callbackGetUser(pRec, pcallback); if (!pRec) return; dbScanLock(pRec); (*pRec->rset->process)(pRec); dbScanUnlock(pRec); } void callbackSetProcess(epicsCallback *pcallback, int Priority, void *pRec) { callbackSetCallback(ProcessCallback, pcallback); callbackSetPriority(Priority, pcallback); callbackSetUser(pRec, pcallback); } int callbackRequestProcessCallback(epicsCallback *pcallback, int Priority, void *pRec) { callbackSetProcess(pcallback, Priority, pRec); return callbackRequest(pcallback); } static void notify(void *pPrivate) { epicsCallback *pcallback = (epicsCallback *)pPrivate; callbackRequest(pcallback); } void callbackRequestDelayed(epicsCallback *pcallback, double seconds) { epicsTimerId timer = (epicsTimerId)pcallback->timer; if (timer == 0) { timer = epicsTimerQueueCreateTimer(timerQueue, notify, pcallback); pcallback->timer = timer; } epicsTimerStartDelay(timer, seconds); } void callbackCancelDelayed(epicsCallback *pcallback) { epicsTimerId timer = (epicsTimerId)pcallback->timer; if (timer != 0) { epicsTimerCancel(timer); } } void callbackRequestProcessCallbackDelayed(epicsCallback *pcallback, int Priority, void *pRec, double seconds) { callbackSetProcess(pcallback, Priority, pRec); callbackRequestDelayed(pcallback, seconds); } /* Sync. process of testSyncCallback() * * 1. For each priority, make a call to callbackRequest() for each worker. * 2. Wait until all callbacks are concurrently being executed * 3. Last worker to begin executing signals success and begins waking up other workers * 4. Last worker to wake signals testSyncCallback() to complete */ typedef struct { epicsEventId wait_phase2, wait_phase4; int nphase2, nphase3; epicsCallback cb; } sync_helper; static void sync_callback(epicsCallback *cb) { sync_helper *helper; callbackGetUser(helper, cb); testGlobalLock(); assert(helper->nphase2 > 0); if(--helper->nphase2!=0) { /* we are _not_ the last to start. */ testGlobalUnlock(); epicsEventMustWait(helper->wait_phase2); testGlobalLock(); } /* we are either the last to start, or have been * woken by the same and must pass the wakeup along */ epicsEventMustTrigger(helper->wait_phase2); assert(helper->nphase2 == 0); assert(helper->nphase3 > 0); if(--helper->nphase3==0) { /* we are the last to wake up. wake up testSyncCallback() */ epicsEventMustTrigger(helper->wait_phase4); } testGlobalUnlock(); } void testSyncCallback(void) { sync_helper helper[NUM_CALLBACK_PRIORITIES]; unsigned i; testDiag("Begin testSyncCallback()"); for(i=0; icallback = (PFUN) ) #define callbackSetPriority(PRIORITY, PCALLBACK) \ ( (PCALLBACK)->priority = (PRIORITY) ) #define callbackGetPriority(PRIORITY, PCALLBACK) \ ( (PRIORITY) = (PCALLBACK)->priority ) #define callbackSetUser(USER, PCALLBACK) \ ( (PCALLBACK)->user = (void *) (USER) ) #define callbackGetUser(USER, PCALLBACK) \ ( (USER) = (PCALLBACK)->user ) epicsShareFunc void callbackInit(void); epicsShareFunc void callbackStop(void); epicsShareFunc void callbackCleanup(void); epicsShareFunc int callbackRequest(epicsCallback *pCallback); epicsShareFunc void callbackSetProcess( epicsCallback *pcallback, int Priority, void *pRec); epicsShareFunc int callbackRequestProcessCallback( epicsCallback *pCallback,int Priority, void *pRec); epicsShareFunc void callbackRequestDelayed( epicsCallback *pCallback,double seconds); epicsShareFunc void callbackCancelDelayed(epicsCallback *pcallback); epicsShareFunc void callbackRequestProcessCallbackDelayed( epicsCallback *pCallback, int Priority, void *pRec, double seconds); epicsShareFunc int callbackSetQueueSize(int size); epicsShareFunc int callbackQueueStatus(const int reset, callbackQueueStats *result); epicsShareFunc void callbackQueueShow(const int reset); epicsShareFunc int callbackParallelThreads(int count, const char *prio); #ifdef __cplusplus } #endif #endif /*INCcallbackh*/ base-7.0.3.1/modules/database/src/ioc/db/chfPlugin.c0000664000577000060420000004355713557101274020656 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * für Materialien und Energie GmbH. * Copyright (c) 2014 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ /* Based on the linkoptions utility by Michael Davidsaver (BNL) */ #include #include #include #include #include #include "dbDefs.h" #include "epicsStdio.h" #include "epicsStdlib.h" #include "epicsString.h" #include "epicsTypes.h" #include "errlog.h" #define epicsExportSharedSymbols #include "chfPlugin.h" #include "dbStaticLib.h" /* * Data for a chfPlugin */ typedef struct chfPlugin { const chfPluginArgDef *opts; size_t nopts; epicsUInt32 *required; const chfPluginIf *pif; } chfPlugin; /* * Parser state data for a chfFilter (chfPlugin instance) */ typedef struct chfFilter { const chfPlugin *plugin; epicsUInt32 *found; void *puser; epicsInt16 nextParam; } chfFilter; /* Data types we get from the parser */ typedef enum chfPluginType { chfPluginTypeBool, chfPluginTypeInt, chfPluginTypeDouble, chfPluginTypeString } chfPluginType; /* * Convert the (epicsInt32) integer value 'val' to the type named in * 'opt->optType' and store the result at 'user + opt->offset'. */ static int store_integer_value(const chfPluginArgDef *opt, char *user, epicsInt32 val) { epicsInt32 *ival; int *eval; const chfPluginEnumType *emap; double *dval; char *sval; int ret; char buff[22]; /* 2^64 = 1.8e+19, so 20 digits plus sign max */ #ifdef DEBUG_CHF printf("Got an integer for %s (type %d): %ld\n", opt->name, opt->optType, (long) val); #endif if (!opt->convert && opt->optType != chfPluginArgInt32) { return -1; } switch (opt->optType) { case chfPluginArgInt32: ival = (epicsInt32 *) ((char *)user + opt->dataOffset); *ival = val; break; case chfPluginArgBoolean: sval = user + opt->dataOffset; *sval = !!val; break; case chfPluginArgDouble: dval = (double*) (user + opt->dataOffset); *dval = val; break; case chfPluginArgString: sval = user + opt->dataOffset; ret = sprintf(buff, "%ld", (long)val); if (ret < 0 || (unsigned) ret > opt->size - 1) { return -1; } strncpy(sval, buff, opt->size-1); sval[opt->size-1]='\0'; break; case chfPluginArgEnum: eval = (int*) (user + opt->dataOffset); for (emap = opt->enums; emap && emap->name; emap++) { if (val == emap->value) { *eval = val; break; } } if (!emap || !emap->name) { return -1; } break; case chfPluginArgInvalid: return -1; } return 0; } /* * Convert the (int) boolean value 'val' to the type named in 'opt->optType' * and store the result at 'user + opt->offset'. */ static int store_boolean_value(const chfPluginArgDef *opt, char *user, int val) { epicsInt32 *ival; double *dval; char *sval; #ifdef DEBUG_CHF printf("Got a boolean for %s (type %d): %d\n", opt->name, opt->optType, val); #endif if (!opt->convert && opt->optType != chfPluginArgBoolean) { return -1; } switch (opt->optType) { case chfPluginArgInt32: ival = (epicsInt32 *) (user + opt->dataOffset); *ival = val; break; case chfPluginArgBoolean: sval = user + opt->dataOffset; *sval = val; break; case chfPluginArgDouble: dval = (double*) (user + opt->dataOffset); *dval = !!val; break; case chfPluginArgString: sval = user + opt->dataOffset; if ((unsigned) (val ? 4 : 5) > opt->size - 1) { return -1; } strncpy(sval, val ? "true" : "false", opt->size - 1); sval[opt->size - 1] = '\0'; break; case chfPluginArgEnum: case chfPluginArgInvalid: return -1; } return 0; } /* * Convert the double value 'val' to the type named in 'opt->optType' * and store the result at 'user + opt->offset'. */ static int store_double_value(const chfPluginArgDef *opt, void *vuser, double val) { char *user = vuser; epicsInt32 *ival; double *dval; char *sval; int i; #ifdef DEBUG_CHF printf("Got a double for %s (type %d, convert: %s): %g\n", opt->name, opt->optType, opt->convert ? "yes" : "no", val); #endif if (!opt->convert && opt->optType != chfPluginArgDouble) { return -1; } switch (opt->optType) { case chfPluginArgInt32: if (val < INT_MIN || val > INT_MAX) { return -1; } ival = (epicsInt32 *) (user + opt->dataOffset); *ival = (epicsInt32) val; break; case chfPluginArgBoolean: sval = user + opt->dataOffset; *sval = !!val; break; case chfPluginArgDouble: dval = (double*) (user + opt->dataOffset); *dval = val; break; case chfPluginArgString: sval = user + opt->dataOffset; if (opt->size <= 8) { /* Play it safe: 3 exp + 2 sign + 'e' + '.' */ return -1; } i = epicsSnprintf(sval, opt->size, "%.*g", (int) opt->size - 7, val); if (i < 0 || (unsigned) i >= opt->size) { return -1; } break; case chfPluginArgEnum: case chfPluginArgInvalid: return -1; } return 0; } /* * Convert the (char*) string value 'val' to the type named in 'opt->optType' * and store the result at 'user + opt->offset'. */ static int store_string_value(const chfPluginArgDef *opt, char *user, const char *val, size_t len) { epicsInt32 *ival; int *eval; const chfPluginEnumType *emap; double *dval; char *sval; char *end; size_t i; #ifdef DEBUG_CHF printf("Got a string for %s (type %d): %.*s\n", opt->name, opt->optType, (int) len, val); #endif if (!opt->convert && opt->optType != chfPluginArgString && opt->optType != chfPluginArgEnum) { return -1; } switch (opt->optType) { case chfPluginArgInt32: ival = (epicsInt32 *) (user + opt->dataOffset); return epicsParseInt32(val, ival, 0, &end); case chfPluginArgBoolean: sval = user + opt->dataOffset; if (epicsStrnCaseCmp(val, "true", len) == 0) { *sval = 1; } else if (epicsStrnCaseCmp(val, "false", len) == 0) { *sval = 0; } else { epicsInt8 i8; if (epicsParseInt8(val, &i8, 0, &end)) return -1; *sval = !!i8; } break; case chfPluginArgDouble: dval = (double*) (user + opt->dataOffset); return epicsParseDouble(val, dval, &end); case chfPluginArgString: i = opt->size-1 < len ? opt->size-1 : (int) len; sval = user + opt->dataOffset; strncpy(sval, val, i); sval[i] = '\0'; break; case chfPluginArgEnum: eval = (int*) (user + opt->dataOffset); for (emap = opt->enums; emap && emap->name; emap++) { if (strncmp(emap->name, val, len) == 0) { *eval = emap->value; break; } } if( !emap || !emap->name ) { return -1; } break; case chfPluginArgInvalid: return -1; } return 0; } static void freeInstanceData(chfFilter *f) { free(f->found); free(f); /* FIXME: Use a free-list */ } /* * chFilterIf callbacks */ /* First entry point when a new filter instance is created. * All per-instance allocations happen here. */ static parse_result parse_start(chFilter *filter) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f; /* Filter context */ /* FIXME: Use a free-list */ f = calloc(1, sizeof(chfFilter)); if (!f) { errlogPrintf("chfFilterCtx calloc failed\n"); goto errfctx; } f->nextParam = -1; /* Bit array to find missing required keys */ f->found = calloc( (p->nopts/32)+1, sizeof(epicsUInt32) ); if (!f->found) { errlogPrintf("chfConfigParseStart: bit array calloc failed\n"); goto errbitarray; } /* Call the plugin to allocate its structure, it returns NULL on error */ if (p->pif->allocPvt) { if ((f->puser = p->pif->allocPvt()) == NULL) { errlogPrintf("chfConfigParseStart: plugin pvt alloc failed\n"); goto errplugin; } } filter->puser = (void*) f; return parse_continue; errplugin: free(f->found); errbitarray: free(f); /* FIXME: Use a free-list */ errfctx: return parse_stop; } static void parse_abort(chFilter *filter) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; /* Call the plugin to tell it we're aborting */ if (p->pif->parse_error) p->pif->parse_error(f->puser); if (p->pif->freePvt) p->pif->freePvt(f->puser); freeInstanceData(f); } static parse_result parse_end(chFilter *filter) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; int i; /* Check if all required arguments were supplied */ for(i = 0; i < (p->nopts/32)+1; i++) { if ((f->found[i] & p->required[i]) != p->required[i]) { if (p->pif->parse_error) p->pif->parse_error(f->puser); if (p->pif->freePvt) p->pif->freePvt(f->puser); freeInstanceData(f); return parse_stop; } } /* Call the plugin to tell it we're done */ if (p->pif->parse_ok) { if (p->pif->parse_ok(f->puser)) { if (p->pif->freePvt) p->pif->freePvt(f->puser); freeInstanceData(f); return parse_stop; } } return parse_continue; } static parse_result parse_boolean(chFilter *filter, int boolVal) { const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; chfFilter *f = (chfFilter*)filter->puser; if (f->nextParam < 0 || store_boolean_value(&opts[f->nextParam], f->puser, boolVal)) { return parse_stop; } else { return parse_continue; } } static parse_result parse_integer(chFilter *filter, long integerVal) { const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; chfFilter *f = (chfFilter*)filter->puser; if(sizeof(long)>sizeof(epicsInt32)) { epicsInt32 temp=integerVal; if(integerVal !=temp) return parse_stop; } if (f->nextParam < 0 || store_integer_value(&opts[f->nextParam], f->puser, integerVal)) { return parse_stop; } else { return parse_continue; } } static parse_result parse_double(chFilter *filter, double doubleVal) { const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; chfFilter *f = (chfFilter*)filter->puser; if (f->nextParam < 0 || store_double_value(&opts[f->nextParam], f->puser, doubleVal)) { return parse_stop; } else { return parse_continue; } } static parse_result parse_string(chFilter *filter, const char *stringVal, size_t stringLen) { const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; chfFilter *f = (chfFilter*)filter->puser; if (f->nextParam < 0 || store_string_value(&opts[f->nextParam], f->puser, stringVal, stringLen)) { return parse_stop; } else { return parse_continue; } } static parse_result parse_start_map(chFilter *filter) { return parse_continue; } static parse_result parse_map_key(chFilter *filter, const char *key, size_t stringLen) { const chfPluginArgDef *cur; const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; chfFilter *f = (chfFilter*)filter->puser; int *tag; int i; int j; f->nextParam = -1; for (cur = opts, i = 0; cur && cur->name; cur++, i++) { if (strncmp(key, cur->name, stringLen) == 0) { f->nextParam = i; break; } } if (f->nextParam == -1) { return parse_stop; } if (opts[i].tagged) { tag = (int*) ((char*) f->puser + opts[i].tagOffset); *tag = opts[i].choice; } f->found[i/32] |= 1<<(i%32); /* Mark tag and all other options pointing to the same data as found */ for (cur = opts, j = 0; cur && cur->name; cur++, j++) { if ((opts[i].tagged && cur->dataOffset == opts[i].tagOffset) || cur->dataOffset == opts[i].dataOffset) f->found[j/32] |= 1<<(j%32); } return parse_continue; } static parse_result parse_end_map(chFilter *filter) { return parse_continue; } static long channel_open(chFilter *filter) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; if (p->pif->channel_open) return p->pif->channel_open(filter->chan, f->puser); else return 0; } static void channel_register_pre(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; if (p->pif->channelRegisterPre) p->pif->channelRegisterPre(filter->chan, f->puser, cb_out, arg_out, probe); } static void channel_register_post(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; if (p->pif->channelRegisterPost) p->pif->channelRegisterPost(filter->chan, f->puser, cb_out, arg_out, probe); } static void channel_report(chFilter *filter, int level, const unsigned short indent) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; if (p->pif->channel_report) p->pif->channel_report(filter->chan, f->puser, level, indent); } static void channel_close(chFilter *filter) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; if (p->pif->channel_close) p->pif->channel_close(filter->chan, f->puser); if (p->pif->freePvt) p->pif->freePvt(f->puser); free(f->found); free(f); /* FIXME: Use a free-list */ } static void plugin_free(void* puser) { chfPlugin *p=puser; free(p->required); free(p); } /* * chFilterIf for the wrapper * we just support a simple one-level map, and no arrays */ static chFilterIf wrapper_fif = { plugin_free, parse_start, parse_abort, parse_end, NULL, /* parse_null, */ parse_boolean, parse_integer, parse_double, parse_string, parse_start_map, parse_map_key, parse_end_map, NULL, /* parse_start_array, */ NULL, /* parse_end_array, */ channel_open, channel_register_pre, channel_register_post, channel_report, channel_close }; const char* chfPluginEnumString(const chfPluginEnumType *emap, int i, const char* def) { for(; emap && emap->name; emap++) { if ( i == emap->value ) { return emap->name; } } return def; } int chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts) { chfPlugin *p; size_t i; const chfPluginArgDef *cur; epicsUInt32 *reqd; /* Check and count options */ for (i = 0, cur = opts; cur && cur->name; i++, cur++) { switch(cur->optType) { case chfPluginArgInt32: if (cur->size < sizeof(epicsInt32)) { errlogPrintf("Plugin %s: %d bytes too small for epicsInt32 %s\n", key, cur->size, cur->name); return -1; } break; case chfPluginArgBoolean: if (cur->size < 1) { errlogPrintf("Plugin %s: %d bytes too small for boolean %s\n", key, cur->size, cur->name); return -1; } break; case chfPluginArgDouble: if (cur->size < sizeof(double)) { errlogPrintf("Plugin %s: %d bytes too small for double %s\n", key, cur->size, cur->name); return -1; } break; case chfPluginArgString: if (cur->size < sizeof(char*)) { /* Catch if someone has given us a char* instead of a char[] * Also means that char buffers must be >=4. */ errlogPrintf("Plugin %s: %d bytes too small for string %s\n", key, cur->size, cur->name); return -1; } break; case chfPluginArgEnum: if (cur->size < sizeof(int)) { errlogPrintf("Plugin %s: %d bytes too small for enum %s\n", key, cur->size, cur->name); return -1; } break; case chfPluginArgInvalid: errlogPrintf("Plugin %s: storage type for %s is not defined\n", key, cur->name); return -1; break; } } /* Bit array used to find missing required keys */ reqd = dbCalloc((i/32)+1, sizeof(epicsUInt32)); if (!reqd) { errlogPrintf("Plugin %s: bit array calloc failed\n", key); return -1; } for (i = 0, cur = opts; cur && cur->name; i++, cur++) { if (cur->required) reqd[i/32] |= 1 << (i%32); } /* Plugin data */ p = dbCalloc(1, sizeof(chfPlugin)); p->pif = pif; p->opts = opts; p->nopts = i; p->required = reqd; dbRegisterFilter(key, &wrapper_fif, p); return 0; } base-7.0.3.1/modules/database/src/ioc/db/chfPlugin.h0000664000577000060420000002655313557101274020660 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * für Materialien und Energie GmbH. * Copyright (c) 2014 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ /* Based on the linkoptions utility by Michael Davidsaver (BNL) */ #ifndef CHFPLUGIN_H #define CHFPLUGIN_H #include #include #include #include struct db_field_log; /** @file chfPlugin.h * @brief Channel filter simplified plugins. * * Utility layer to allow an easier (reduced) interface for * channel filter plugins. * * Parsing the configuration arguments of a channel filter plugin * is done according to an argument description table provided by the plugin. * The parser stores the results directly into a user supplied structure * after appropriate type conversion. * * To specify the arguments, a chfPluginArgDef table must be defined * for the user structure. This table has to be specified when the plugin registers. * * The plugin is responsible to register an init function using * epicsExportRegistrar() and the accompanying registrar() directive in the dbd, * and call chfPluginRegister() from within the init function. * * For example: * * typedef struct myStruct { * ... other stuff * char mode; * epicsInt32 ival; * double dval; * epicsInt32 ival2; * int enumval; * char strval[20]; * char boolval; * } myStruct; * * static const * chfPluginEnumType colorEnum[] = { {"Red",1}, {"Green",2}, {"Blue",3}, {NULL,0} }; * * static const * chfPluginDef myStructDef[] = { * chfTagInt32(myStruct, ival, "Integer" , ival2, 3, 0, 0), * chfInt32 (myStruct, ival2, "Second" , 1, 0), * chfDouble (myStruct, dval, "Double" , 1, 0), * chfString (myStruct, strval , "String" , 1, 0), * chfEnum (myStruct, enumval, "Color" , 1, 0, colorEnum), * chfBoolean (myStruct, boolval, "Bool" , 1, 0), * chfPluginEnd * }; * * Note: The 4th argument specifies the parameter to be required (1) or optional (0), * the 5th whether converting to the required type is allowed (1), or * type mismatches are an error (0). * Note: The "Tag" version has two additional arguments. the 4th arg specifies the tag * field (integer type) inside the structure to be set, the 5th arg specifies the * value to set the tag field to. Arguments 6 and 7 specify "required" and * "conversion" as described above. * */ #ifdef __cplusplus extern "C" { #endif /** @brief Channel filter simplified plugin interface. * * The routines in this structure must be implemented by each filter plugin. */ typedef struct chfPluginIf { /* Memory management */ /** @brief Allocate private resources. * * Called before parsing starts. * The plugin should allocate its per-instance structures, * returning a pointer to them or NULL requesting an abort of the operation. * * allocPvt may be set to NULL, if no resource allocation is needed. * * @return Pointer to private structure, NULL if operation is to be aborted. */ void * (* allocPvt) (void); /** @brief Free private resources. * * Called as part of abort or shutdown. * The plugin should release any resources allocated for this filter; * no further calls through this interface will be made. * * freePvt may be set to NULL, if no resources need to be released. * * @param pvt Pointer to private structure. */ void (* freePvt) (void *pvt); /* Parameter parsing results */ /** @brief A parsing error occurred. * * Called after parsing failed with an error. * * @param pvt Pointer to private structure. */ void (* parse_error) (void *pvt); /** @brief Configuration has been parsed successfully. * * Called after parsing has finished ok. * The plugin may check the validity of the parsed data, * returning -1 to request an abort of the operation. * * @param pvt Pointer to private structure. * @return 0 for success, -1 if operation is to be aborted. */ int (* parse_ok) (void *pvt); /* Channel operations */ /** @brief Open channel. * * Called as part of the channel connection setup. * * @param chan dbChannel for which the connection is being made. * @param pvt Pointer to private structure. * @return 0 for success, -1 if operation is to be aborted. */ long (* channel_open) (dbChannel *chan, void *pvt); /** @brief Register callbacks for pre-event-queue operation. * * Called as part of the channel connection setup. * * This function is called to establish the stack of plugins that an event * is passed through between the database and the event queue. * * The plugin must set pe_out to point to its own post-event callback in order * to be called when a data update is sent from the database towards the * event queue. * * The plugin may find out the type of data it will receive by looking at 'probe'. * If the plugin will change the data type and/or size, it must update 'probe' * accordingly. * * @param chan dbChannel for which the connection is being made. * @param pvt Pointer to private structure. * @param cb_out Pointer to this plugin's post-event callback (NULL to bypass * this plugin). * @param arg_out Argument that must be supplied when calling * this plugin's post-event callback. */ void (* channelRegisterPre) (dbChannel *chan, void *pvt, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); /** @brief Register callbacks for post-event-queue operation. * * Called as part of the channel connection setup. * * This function is called to establish the stack of plugins that an event * is passed through between the event queue and the final user (CA server or * database access). * * The plugin must set pe_out to point to its own post-event callback in order * to be called when a data update is sent from the event queue towards the * final user. * * The plugin may find out the type of data it will receive by looking at 'probe'. * If the plugin will change the data type and/or size, it must update 'probe' * accordingly. * * @param chan dbChannel for which the connection is being made. * @param pvt Pointer to private structure. * @param cb_out Pointer to this plugin's post-event callback (NULL to bypass * this plugin). * @param arg_out Argument that must be supplied when calling * this plugin's post-event callback. */ void (* channelRegisterPost) (dbChannel *chan, void *pvt, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); /** @brief Channel report request. * * Called as part of show... routines. * * @param chan dbChannel for which the report is requested. * @param pvt Pointer to private structure. * @param level Interest level. * @param indent Number of spaces to print before each output line. */ void (* channel_report) (dbChannel *chan, void *pvt, int level, const unsigned short indent); /** @brief Channel close request. * * Called as part of connection shutdown. * @param chan dbChannel for which the connection is being shut down. * @param pvt Pointer to private structure. */ void (* channel_close) (dbChannel *chan, void *pvt); } chfPluginIf; typedef enum chfPluginArg { chfPluginArgInvalid=0, chfPluginArgBoolean, chfPluginArgInt32, chfPluginArgDouble, chfPluginArgString, chfPluginArgEnum } chfPluginArg; typedef struct chfPluginEnumType { const char *name; const int value; } chfPluginEnumType; typedef struct chfPluginArgDef { const char * name; chfPluginArg optType; unsigned int required:1; unsigned int convert:1; unsigned int tagged:1; epicsUInt32 tagOffset; epicsUInt32 choice; epicsUInt32 dataOffset; epicsUInt32 size; const chfPluginEnumType *enums; } chfPluginArgDef; /* Simple arguments */ #define chfInt32(Struct, Member, Name, Req, Conv) \ {Name, chfPluginArgInt32, Req, Conv, 0, 0, 0, \ OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} #define chfBoolean(Struct, Member, Name, Req, Conv) \ {Name, chfPluginArgBoolean, Req, Conv, 0, 0, 0, \ OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} #define chfDouble(Struct, Member, Name, Req, Conv) \ {Name, chfPluginArgDouble, Req, Conv, 0, 0, 0, \ OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} #define chfString(Struct, Member, Name, Req, Conv) \ {Name, chfPluginArgString, Req, Conv, 0, 0, 0, \ OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} #define chfEnum(Struct, Member, Name, Req, Conv, Enums) \ {Name, chfPluginArgEnum, Req, Conv, 0, 0, 0, \ OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums} /* Tagged arguments */ #define chfTagInt32(Struct, Member, Name, Tag, Choice, Req, Conv) \ {Name, chfPluginArgInt32, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \ OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} #define chfTagBoolean(Struct, Member, Name, Tag, Choice, Req, Conv) \ {Name, chfPluginArgBoolean, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \ OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} #define chfTagDouble(Struct, Member, Name, Tag, Choice, Req, Conv) \ {Name, chfPluginArgDouble, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \ OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} #define chfTagString(Struct, Member, Name, Tag, Choice, Req, Conv) \ {Name, chfPluginArgString, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \ OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} #define chfTagEnum(Struct, Member, Name, Tag, Choice, Req, Conv, Enums) \ {Name, chfPluginArgEnum, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \ OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums} #define chfPluginArgEnd {0} /* Extra output when parsing and converting */ #define CHFPLUGINDEBUG 1 /** @brief Return the string associated with Enum index 'i'. * * @param Enums A null-terminated array of string/integer pairs. * @param i An Enum index. * @param def String to be returned when 'i' isn't a valid Enum index. * @return The string associated with 'i'. */ epicsShareFunc const char* chfPluginEnumString(const chfPluginEnumType *Enums, int i, const char* def); /** @brief Register a plugin. * * @param key The plugin name key that clients will use. * @param pif Pointer to the plugin's interface. * @param opts Pointer to the configuration argument description table. */ epicsShareFunc int chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts); #ifdef __cplusplus } #endif #endif // CHFPLUGIN_H base-7.0.3.1/modules/database/src/ioc/db/cvtBpt.c0000664000577000060420000001030413557101274020161 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* cvtBpt.c - Convert using breakpoint table * * Author: Marty Kraimer * Date: 04OCT95 * This is adaptation of old bldCvtTable */ #include "epicsPrint.h" #define epicsExportSharedSymbols #include "cvtTable.h" #include "dbAccess.h" #include "dbBase.h" #include "dbStaticLib.h" static brkTable *findBrkTable(short linr) { dbMenu *pdbMenu; pdbMenu = dbFindMenu(pdbbase,"menuConvert"); if (!pdbMenu) { epicsPrintf("findBrkTable: menuConvert not loaded!\n"); return NULL; } if (linr < 0 || linr >= pdbMenu->nChoice) { epicsPrintf("findBrkTable: linr=%d but menuConvert only has %d choices\n", linr,pdbMenu->nChoice); return NULL; } return dbFindBrkTable(pdbbase,pdbMenu->papChoiceValue[linr]); } /* Used by both ao and ai record types */ long cvtRawToEngBpt(double *pval, short linr, short init, void **ppbrk, short *plbrk) { double val = *pval; long status = 0; brkTable *pbrkTable; brkInt *pInt, *nInt; short lbrk; int number; if (linr < 2) return -1; if (init || *ppbrk == NULL) { pbrkTable = findBrkTable(linr); if (!pbrkTable) return S_dbLib_badField; *ppbrk = (void *)pbrkTable; *plbrk = 0; } else pbrkTable = (brkTable *)*ppbrk; number = pbrkTable->number; lbrk = *plbrk; /* Limit index to the size of the table */ if (lbrk < 0) lbrk = 0; else if (lbrk > number-2) lbrk = number-2; pInt = & pbrkTable->paBrkInt[lbrk]; nInt = pInt + 1; if (nInt->raw > pInt->raw) { /* raw values increase down the table */ while (val > nInt->raw) { lbrk++; pInt = nInt++; if (lbrk > number-2) { status = 1; break; } } while (val < pInt->raw) { if (lbrk <= 0) { status = 1; break; } lbrk--; nInt = pInt--; } } else { /* raw values decrease down the table */ while (val <= nInt->raw) { lbrk++; pInt = nInt++; if (lbrk > number-2) { status = 1; break; } } while(val > pInt->raw) { if (lbrk <= 0) { status = 1; break; } lbrk--; nInt = pInt--; } } *plbrk = lbrk; *pval = pInt->eng + (val - pInt->raw) * pInt->slope; return status; } /* Used by the ao record type */ long cvtEngToRawBpt(double *pval, short linr, short init, void **ppbrk, short *plbrk) { double val = *pval; long status = 0; brkTable *pbrkTable; brkInt *pInt, *nInt; short lbrk; int number; if (linr < 2) return -1; if (init || *ppbrk == NULL) { /*must find breakpoint table*/ pbrkTable = findBrkTable(linr); if (!pbrkTable) return S_dbLib_badField; *ppbrk = (void *)pbrkTable; /* start at the beginning */ *plbrk = 0; } else pbrkTable = (brkTable *)*ppbrk; number = pbrkTable->number; lbrk = *plbrk; /* Limit index to the size of the table */ if (lbrk < 0) lbrk = 0; else if (lbrk > number-2) lbrk = number-2; pInt = & pbrkTable->paBrkInt[lbrk]; nInt = pInt + 1; if (nInt->eng > pInt->eng) { /* eng values increase down the table */ while (val > nInt->eng) { lbrk++; pInt = nInt++; if (lbrk > number-2) { status = 1; break; } } while (val < pInt->eng) { if (lbrk <= 0) { status = 1; break; } lbrk--; nInt = pInt--; } } else { /* eng values decrease down the table */ while (val <= nInt->eng) { lbrk++; pInt = nInt++; if (lbrk > number-2) { status = 1; break; } } while (val > pInt->eng) { if (lbrk <= 0) { status = 1; break; } lbrk--; nInt = pInt--; } } *plbrk = lbrk; *pval = pInt->raw + (val - pInt->eng) / pInt->slope; return status; } base-7.0.3.1/modules/database/src/ioc/db/dbAccess.c0000664000577000060420000011750313557101274020437 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbAccess.c */ /* * Original Author: Bob Dalesio * Current Author: Marty Kraimer * Andrew Johnson * Ralph Lange */ #include #include #include #include #include #include "alarm.h" #include "cantProceed.h" #include "cvtFast.h" #include "dbDefs.h" #include "ellLib.h" #include "epicsMath.h" #include "epicsThread.h" #include "epicsTime.h" #include "errlog.h" #include "errMdef.h" #include "epicsExport.h" /* #define epicsExportSharedSymbols */ #include "caeventmask.h" #include "callback.h" #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" #include "dbBkpt.h" #include "dbCommonPvt.h" #include "dbConvertFast.h" #include "dbConvert.h" #include "dbEvent.h" #include "db_field_log.h" #include "dbFldTypes.h" #include "dbFldTypes.h" #include "dbLink.h" #include "dbLockPvt.h" #include "dbNotify.h" #include "dbScan.h" #include "dbServer.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "devSup.h" #include "epicsEvent.h" #include "link.h" #include "recGbl.h" #include "recSup.h" #include "special.h" epicsShareDef struct dbBase *pdbbase = 0; epicsShareDef volatile int interruptAccept=FALSE; epicsShareDef int dbAccessDebugPUTF = 0; epicsExportAddress(int, dbAccessDebugPUTF); /* Hook Routines */ epicsShareDef DB_LOAD_RECORDS_HOOK_ROUTINE dbLoadRecordsHook = NULL; static short mapDBFToDBR[DBF_NTYPES] = { /* DBF_STRING => */ DBR_STRING, /* DBF_CHAR => */ DBR_CHAR, /* DBF_UCHAR => */ DBR_UCHAR, /* DBF_SHORT => */ DBR_SHORT, /* DBF_USHORT => */ DBR_USHORT, /* DBF_LONG => */ DBR_LONG, /* DBF_ULONG => */ DBR_ULONG, /* DBF_INT64 => */ DBR_INT64, /* DBF_UINT64 => */ DBR_UINT64, /* DBF_FLOAT => */ DBR_FLOAT, /* DBF_DOUBLE => */ DBR_DOUBLE, /* DBF_ENUM, => */ DBR_ENUM, /* DBF_MENU, => */ DBR_ENUM, /* DBF_DEVICE => */ DBR_ENUM, /* DBF_INLINK => */ DBR_STRING, /* DBF_OUTLINK => */ DBR_STRING, /* DBF_FWDLINK => */ DBR_STRING, /* DBF_NOACCESS => */ DBR_NOACCESS }; /* * The number of consecutive attempts that can be made to process an * active record before a SCAN_ALARM is raised. Active records * (records with the pact flag set) cannot be processed until * that flag becomes unset. */ #define MAX_LOCK 10 /* The following is to handle SPC_AS */ static SPC_ASCALLBACK spcAsCallback = 0; void dbSpcAsRegisterCallback(SPC_ASCALLBACK func) { spcAsCallback = func; } long dbPutSpecial(DBADDR *paddr,int pass) { long int (*pspecial)()=NULL; rset *prset; dbCommon *precord = paddr->precord; long status=0; long special=paddr->special; prset = dbGetRset(paddr); if(special<100) { /*global processing*/ if((special==SPC_NOMOD) && (pass==0)) { status = S_db_noMod; recGblDbaddrError(status,paddr,"dbPut"); return(status); }else if(special==SPC_SCAN){ if(pass==0) scanDelete(precord); else scanAdd(precord); }else if((special==SPC_AS) && (pass==1)) { if(spcAsCallback) (*spcAsCallback)(precord); } }else { if( prset && (pspecial = (prset->special))) { status=(*pspecial)(paddr,pass); if(status) return(status); } else if(pass==0){ recGblRecSupError(S_db_noSupport,paddr,"dbPut", "special"); return(S_db_noSupport); } } return(0); } static void get_enum_strs(DBADDR *paddr, char **ppbuffer, 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; } *ppbuffer = ((char *)*ppbuffer) + dbr_enumStrs_size; return; } static void get_graphics(DBADDR *paddr, char **ppbuffer, rset *prset,long *options) { struct dbr_grDouble grd; int got_data=FALSE; grd.upper_disp_limit = grd.lower_disp_limit = 0.0; if( prset && prset->get_graphic_double ) { (*prset->get_graphic_double)(paddr,&grd); got_data=TRUE; } if( (*options) & (DBR_GR_LONG) ) { char *pbuffer=*ppbuffer; if(got_data) { struct dbr_grLong *pgr=(struct dbr_grLong*)pbuffer; pgr->upper_disp_limit = (epicsInt32)grd.upper_disp_limit; pgr->lower_disp_limit = (epicsInt32)grd.lower_disp_limit; } else { memset(pbuffer,'\0',dbr_grLong_size); *options = (*options) ^ DBR_GR_LONG; /*Turn off option*/ } *ppbuffer = ((char *)*ppbuffer) + dbr_grLong_size; } if( (*options) & (DBR_GR_DOUBLE) ) { char *pbuffer=*ppbuffer; if(got_data) { struct dbr_grDouble *pgr=(struct dbr_grDouble*)pbuffer; pgr->upper_disp_limit = grd.upper_disp_limit; pgr->lower_disp_limit = grd.lower_disp_limit; } else { memset(pbuffer,'\0',dbr_grDouble_size); *options = (*options) ^ DBR_GR_DOUBLE; /*Turn off option*/ } *ppbuffer = ((char *)*ppbuffer) + dbr_grDouble_size; } return; } static void get_control(DBADDR *paddr, char **ppbuffer, rset *prset,long *options) { struct dbr_ctrlDouble ctrld; int got_data=FALSE; ctrld.upper_ctrl_limit = ctrld.lower_ctrl_limit = 0.0; if( prset && prset->get_control_double ) { (*prset->get_control_double)(paddr,&ctrld); got_data=TRUE; } if( (*options) & (DBR_CTRL_LONG) ) { char *pbuffer=*ppbuffer; if(got_data) { struct dbr_ctrlLong *pctrl=(struct dbr_ctrlLong*)pbuffer; pctrl->upper_ctrl_limit = (epicsInt32)ctrld.upper_ctrl_limit; pctrl->lower_ctrl_limit = (epicsInt32)ctrld.lower_ctrl_limit; } else { memset(pbuffer,'\0',dbr_ctrlLong_size); *options = (*options) ^ DBR_CTRL_LONG; /*Turn off option*/ } *ppbuffer = ((char *)*ppbuffer) + dbr_ctrlLong_size; } if( (*options) & (DBR_CTRL_DOUBLE) ) { char *pbuffer=*ppbuffer; if(got_data) { struct dbr_ctrlDouble *pctrl=(struct dbr_ctrlDouble*)pbuffer; pctrl->upper_ctrl_limit = ctrld.upper_ctrl_limit; pctrl->lower_ctrl_limit = ctrld.lower_ctrl_limit; } else { memset(pbuffer,'\0',dbr_ctrlDouble_size); *options = (*options) ^ DBR_CTRL_DOUBLE; /*Turn off option*/ } *ppbuffer = ((char *)*ppbuffer) + dbr_ctrlDouble_size; } return; } static void get_alarm(DBADDR *paddr, char **ppbuffer, rset *prset, long *options) { char *pbuffer = *ppbuffer; struct dbr_alDouble ald = {epicsNAN, epicsNAN, epicsNAN, epicsNAN}; long no_data = TRUE; if (prset && prset->get_alarm_double) no_data = prset->get_alarm_double(paddr, &ald); if (*options & DBR_AL_LONG) { struct dbr_alLong *pal = (struct dbr_alLong*) pbuffer; pal->upper_alarm_limit = finite(ald.upper_alarm_limit) ? (epicsInt32) ald.upper_alarm_limit : 0; pal->upper_warning_limit = finite(ald.upper_warning_limit) ? (epicsInt32) ald.upper_warning_limit : 0; pal->lower_warning_limit = finite(ald.lower_warning_limit) ? (epicsInt32) ald.lower_warning_limit : 0; pal->lower_alarm_limit = finite(ald.lower_alarm_limit) ? (epicsInt32) ald.lower_alarm_limit : 0; if (no_data) *options ^= DBR_AL_LONG; /*Turn off option*/ *ppbuffer += dbr_alLong_size; } if (*options & DBR_AL_DOUBLE) { struct dbr_alDouble *pal = (struct dbr_alDouble*) pbuffer; pal->upper_alarm_limit = ald.upper_alarm_limit; pal->upper_warning_limit = ald.upper_warning_limit; pal->lower_warning_limit = ald.lower_warning_limit; pal->lower_alarm_limit = ald.lower_alarm_limit; if (no_data) *options ^= DBR_AL_DOUBLE; /*Turn off option*/ *ppbuffer += dbr_alDouble_size; } } /* * This code relies on *poriginal being aligned and all increments done by the * blocks only changing the buffer pointer in a way that does not break alignment. */ static void getOptions(DBADDR *paddr, char **poriginal, long *options, void *pflin) { db_field_log *pfl= (db_field_log *)pflin; rset *prset; short field_type; dbCommon *pcommon; char *pbuffer = *poriginal; if (!pfl || pfl->type == dbfl_type_rec) field_type = paddr->field_type; else field_type = pfl->field_type; prset=dbGetRset(paddr); /* Process options */ pcommon = paddr->precord; if( (*options) & DBR_STATUS ) { unsigned short *pushort = (unsigned short *)pbuffer; if (!pfl || pfl->type == dbfl_type_rec) { *pushort++ = pcommon->stat; *pushort++ = pcommon->sevr; } else { *pushort++ = pfl->stat; *pushort++ = pfl->sevr; } *pushort++ = pcommon->acks; *pushort++ = pcommon->ackt; pbuffer = (char *)pushort; } if( (*options) & DBR_UNITS ) { memset(pbuffer,'\0',dbr_units_size); if( prset && prset->get_units ){ (*prset->get_units)(paddr, pbuffer); pbuffer[DB_UNITS_SIZE-1] = '\0'; } else { *options ^= DBR_UNITS; /*Turn off DBR_UNITS*/ } pbuffer += dbr_units_size; } if( (*options) & DBR_PRECISION ) { memset(pbuffer, '\0', dbr_precision_size); if((field_type==DBF_FLOAT || field_type==DBF_DOUBLE) && prset && prset->get_precision ){ (*prset->get_precision)(paddr,(long *)pbuffer); } else { *options ^= DBR_PRECISION; /*Turn off DBR_PRECISION*/ } pbuffer += dbr_precision_size; } if( (*options) & DBR_TIME ) { epicsUInt32 *ptime = (epicsUInt32 *)pbuffer; if (!pfl || pfl->type == dbfl_type_rec) { *ptime++ = pcommon->time.secPastEpoch; *ptime++ = pcommon->time.nsec; } else { *ptime++ = pfl->time.secPastEpoch; *ptime++ = pfl->time.nsec; } pbuffer = (char *)ptime; } if( (*options) & DBR_ENUM_STRS ) get_enum_strs(paddr, &pbuffer, prset, options); if( (*options) & (DBR_GR_LONG|DBR_GR_DOUBLE )) get_graphics(paddr, &pbuffer, prset, options); if((*options) & (DBR_CTRL_LONG | DBR_CTRL_DOUBLE )) get_control(paddr, &pbuffer, prset, options); if((*options) & (DBR_AL_LONG | DBR_AL_DOUBLE )) get_alarm(paddr, &pbuffer, prset, options); *poriginal = pbuffer; } rset * dbGetRset(const struct dbAddr *paddr) { struct dbFldDes *pfldDes = paddr->pfldDes; if(!pfldDes) return(0); return(pfldDes->pdbRecordType->prset); } long dbPutAttribute( const char *recordTypename, const char *name, const char *value) { DBENTRY dbEntry; DBENTRY *pdbEntry = &dbEntry; long status = 0; if (!pdbbase) return S_db_notFound; if (!name) { status = S_db_badField; goto done; } if (!value) value = ""; dbInitEntry(pdbbase, pdbEntry); status = dbFindRecordType(pdbEntry, recordTypename); if (!status) status = dbPutRecordAttribute(pdbEntry, name, value); dbFinishEntry(pdbEntry); done: if (status) errMessage(status, "dbPutAttribute failure"); return status; } int dbIsValueField(const struct dbFldDes *pdbFldDes) { if (pdbFldDes->pdbRecordType->indvalFlddes == pdbFldDes->indRecordType) return TRUE; else return FALSE; } int dbGetFieldIndex(const struct dbAddr *paddr) { return paddr->pfldDes->indRecordType; } /* * Process the record. * 1. Check for breakpoints. * 2. Check the process active flag (PACT). * 3. Check the disable link. * 4. Check the RSET (record support entry table) exists. * 5. Run the process routine specific to the record type. * 6. Check to see if record contents should be automatically printed. */ long dbProcess(dbCommon *precord) { rset *prset = precord->rset; dbRecordType *pdbRecordType = precord->rdes; unsigned char tpro = precord->tpro; char context[40] = ""; long status = 0; int *ptrace; int set_trace = FALSE; dbFldDes *pdbFldDes; int callNotifyCompletion = FALSE; ptrace = dbLockSetAddrTrace(precord); /* * Note that it is likely that if any changes are made * to dbProcess() corresponding changes will have to * be made in the breakpoint handler. */ /* see if there are any stopped records or breakpoints */ if (lset_stack_count != 0) { /* * Check to see if the record should be processed * and activate breakpoint accordingly. If this * function call returns non-zero, skip record * support and fall out of dbProcess(). This is * done so that a dbContTask() can be spawned to * take over record processing for the lock set * containing a breakpoint. */ if (dbBkpt(precord)) goto all_done; } /* check for trace processing*/ if (tpro) { if (!*ptrace) { *ptrace = 1; set_trace = TRUE; } } if (*ptrace) { /* Identify this thread's client from server layer */ if (dbServerClient(context, sizeof(context))) { /* No client, use thread name */ strncpy(context, epicsThreadGetNameSelf(), sizeof(context)); context[sizeof(context) - 1] = 0; } } /* If already active dont process */ if (precord->pact) { unsigned short monitor_mask; if (*ptrace) printf("%s: dbProcess of Active '%s' with RPRO=%d\n", context, precord->name, precord->rpro); /* raise scan alarm after MAX_LOCK times */ if ((precord->stat == SCAN_ALARM) || (precord->lcnt++ < MAX_LOCK) || (precord->sevr >= INVALID_ALARM)) goto all_done; recGblSetSevr(precord, SCAN_ALARM, INVALID_ALARM); monitor_mask = recGblResetAlarms(precord); monitor_mask |= DBE_VALUE|DBE_LOG; pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes]; db_post_events(precord, (void *)(((char *)precord) + pdbFldDes->offset), monitor_mask); goto all_done; } else precord->lcnt = 0; /* * Check the record disable link. A record will not be * processed if the value retrieved through this link * is equal to constant set in the record's disv field. */ status = dbGetLink(&precord->sdis, DBR_SHORT, &precord->disa, 0, 0); /* if disabled check disable alarm severity and return success */ if (precord->disa == precord->disv) { if (*ptrace) printf("%s: dbProcess of Disabled '%s'\n", context, precord->name); /*take care of caching and notifyCompletion*/ precord->rpro = FALSE; precord->putf = FALSE; callNotifyCompletion = TRUE; /* raise disable alarm */ if (precord->stat == DISABLE_ALARM) goto all_done; precord->sevr = precord->diss; precord->stat = DISABLE_ALARM; precord->nsev = 0; precord->nsta = 0; db_post_events(precord, &precord->stat, DBE_VALUE); db_post_events(precord, &precord->sevr, DBE_VALUE); pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes]; db_post_events(precord, (void *)(((char *)precord) + pdbFldDes->offset), DBE_VALUE|DBE_ALARM); goto all_done; } /* locate record processing routine */ /* FIXME: put this in iocInit() !!! */ if (!prset || !prset->process) { callNotifyCompletion = TRUE; precord->pact = 1;/*set pact so error is issued only once*/ recGblRecordError(S_db_noRSET, (void *)precord, "dbProcess"); status = S_db_noRSET; if (*ptrace) printf("%s: No RSET for %s\n", context, precord->name); goto all_done; } if (*ptrace) printf("%s: dbProcess of '%s'\n", context, precord->name); /* process record */ status = prset->process(precord); /* Print record's fields if PRINT_MASK set in breakpoint field */ if (lset_stack_count != 0) { dbPrint(precord); } all_done: if (set_trace) *ptrace = 0; if (callNotifyCompletion && precord->ppn) dbNotifyCompletion(precord); return status; } long dbEntryToAddr(const DBENTRY *pdbentry, DBADDR *paddr) { dbFldDes *pflddes = pdbentry->pflddes; short dbfType = pflddes->field_type; paddr->precord = pdbentry->precnode->precord; paddr->pfield = pdbentry->pfield; paddr->pfldDes = pflddes; paddr->no_elements = 1; paddr->field_type = dbfType; paddr->field_size = pflddes->size; paddr->special = pflddes->special; paddr->dbr_field_type = mapDBFToDBR[dbfType]; if (paddr->special == SPC_DBADDR) { const rset *prset = dbGetRset(paddr); /* Let record type modify paddr */ if (prset && prset->cvt_dbaddr) { return prset->cvt_dbaddr(paddr); } } return 0; } /* * Fill out a database structure (*paddr) for * a record given by the name "pname." * * Returns error codes from StaticLib module, not * from dbAccess. */ long dbNameToAddr(const char *pname, DBADDR *paddr) { DBENTRY dbEntry; long status = 0; if (!pname || !*pname || !pdbbase) return S_db_notFound; dbInitEntry(pdbbase, &dbEntry); status = dbFindRecordPart(&dbEntry, &pname); if (status) goto finish; if (*pname == '.') ++pname; status = dbFindFieldPart(&dbEntry, &pname); if (status == S_dbLib_fieldNotFound) status = dbGetAttributePart(&dbEntry, &pname); if (status) goto finish; status = dbEntryToAddr(&dbEntry, paddr); if (status) goto finish; /* Handle field modifiers */ if (*pname++ == '$') { short dbfType = paddr->field_type; /* Some field types can be accessed as char arrays */ if (dbfType == DBF_STRING) { paddr->no_elements = paddr->field_size; paddr->field_type = DBF_CHAR; paddr->field_size = 1; paddr->dbr_field_type = DBR_CHAR; } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { /* Clients see a char array, but keep original dbfType */ paddr->no_elements = PVLINK_STRINGSZ; paddr->field_size = 1; paddr->dbr_field_type = DBR_CHAR; } else { status = S_dbLib_fieldNotFound; } } finish: dbFinishEntry(&dbEntry); return status; } void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry) { struct dbCommon *prec = paddr->precord; dbCommonPvt *ppvt = dbRec2Pvt(prec); memset((char *)pdbentry,'\0',sizeof(DBENTRY)); pdbentry->pdbbase = pdbbase; pdbentry->precordType = prec->rdes; pdbentry->precnode = ppvt->recnode; pdbentry->pflddes = paddr->pfldDes; pdbentry->pfield = paddr->pfield; pdbentry->indfield = paddr->pfldDes->indRecordType; } void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry) { dbCommonPvt *ppvt = dbRec2Pvt(prec); memset((char *)pdbentry,'\0',sizeof(DBENTRY)); pdbentry->pdbbase = pdbbase; pdbentry->precordType = prec->rdes; pdbentry->precnode = ppvt->recnode; } struct link* dbGetDevLink(struct dbCommon* prec) { DBLINK *plink = 0; DBENTRY entry; dbInitEntryFromRecord(prec, &entry); if(dbFindField(&entry, "INP")==0 || dbFindField(&entry, "OUT")==0) { plink = (DBLINK*)entry.pfield; } dbFinishEntry(&entry); return plink; } long dbValueSize(short dbr_type) { /* sizes for value associated with each DBR request type */ static long size[] = { MAX_STRING_SIZE, /* STRING */ sizeof(epicsInt8), /* CHAR */ sizeof(epicsUInt8), /* UCHAR */ sizeof(epicsInt16), /* SHORT */ sizeof(epicsUInt16), /* USHORT */ sizeof(epicsInt32), /* LONG */ sizeof(epicsUInt32), /* ULONG */ sizeof(epicsInt64), /* INT64 */ sizeof(epicsUInt64), /* UINT64 */ sizeof(epicsFloat32), /* FLOAT */ sizeof(epicsFloat64), /* DOUBLE */ sizeof(epicsEnum16)}; /* ENUM */ return(size[dbr_type]); } long dbBufferSize(short dbr_type, long options, long no_elements) { long nbytes=0; nbytes += dbValueSize(dbr_type) * no_elements; if (options & DBR_STATUS) nbytes += dbr_status_size; if (options & DBR_UNITS) nbytes += dbr_units_size; if (options & DBR_PRECISION) nbytes += dbr_precision_size; if (options & DBR_TIME) nbytes += dbr_time_size; if (options & DBR_ENUM_STRS) nbytes += dbr_enumStrs_size; if (options & DBR_GR_LONG) nbytes += dbr_grLong_size; if (options & DBR_GR_DOUBLE) nbytes += dbr_grDouble_size; if (options & DBR_CTRL_LONG) nbytes += dbr_ctrlLong_size; if (options & DBR_CTRL_DOUBLE) nbytes += dbr_ctrlDouble_size; if (options & DBR_AL_LONG) nbytes += dbr_alLong_size; if (options & DBR_AL_DOUBLE) nbytes += dbr_alDouble_size; return(nbytes); } int dbLoadDatabase(const char *file, const char *path, const char *subs) { if (!file) { printf("Usage: dbLoadDatabase \"file\", \"path\", \"subs\"\n"); return -1; } return dbReadDatabase(&pdbbase, file, path, subs); } int dbLoadRecords(const char* file, const char* subs) { int status; if (!file) { printf("Usage: dbLoadRecords \"file\", \"subs\"\n"); return -1; } status = dbReadDatabase(&pdbbase, file, 0, subs); if (!status && dbLoadRecordsHook) dbLoadRecordsHook(file, subs); return status; } static long getLinkValue(DBADDR *paddr, short dbrType, char *pbuf, long *nRequest) { dbCommon *precord = paddr->precord; dbFldDes *pfldDes = paddr->pfldDes; /* size of pbuf storage in bytes, including space for trailing nil */ int maxlen; DBENTRY dbEntry; long status; long nReq = nRequest ? *nRequest : 1; /* dbFindRecord() below will always succeed as we have a * valid DBADDR, so no point to check again. * Request for zero elements always succeeds */ if(!nReq) return 0; switch (dbrType) { case DBR_STRING: maxlen = MAX_STRING_SIZE; nReq = 1; break; case DBR_DOUBLE: /* Needed for dbCa links */ if (nRequest) *nRequest = 1; *(double *)pbuf = epicsNAN; return 0; case DBR_CHAR: case DBR_UCHAR: maxlen = nReq; break; default: return S_db_badDbrtype; } dbInitEntry(pdbbase, &dbEntry); status = dbFindRecord(&dbEntry, precord->name); if (!status) status = dbFindField(&dbEntry, pfldDes->name); if (!status) { const char *rtnString = dbGetString(&dbEntry); strncpy(pbuf, rtnString, maxlen-1); pbuf[maxlen-1] = 0; if(dbrType!=DBR_STRING) nReq = strlen(pbuf)+1; if(nRequest) *nRequest = nReq; } dbFinishEntry(&dbEntry); return status; } static long getAttrValue(DBADDR *paddr, short dbrType, char *pbuf, long *nRequest) { int maxlen; long nReq = nRequest ? *nRequest : 1; if (!paddr->pfield) return S_db_badField; switch (dbrType) { case DBR_STRING: maxlen = MAX_STRING_SIZE; nReq = 1; break; case DBR_CHAR: case DBR_UCHAR: maxlen = nReq; break; /* else fall through ... */ default: return S_db_badDbrtype; } strncpy(pbuf, paddr->pfield, maxlen-1); pbuf[maxlen-1] = 0; if(dbrType!=DBR_STRING) nReq = strlen(pbuf)+1; if(nRequest) *nRequest = nReq; return 0; } long dbGetField(DBADDR *paddr,short dbrType, void *pbuffer, long *options, long *nRequest, void *pflin) { dbCommon *precord = paddr->precord; long status = 0; dbScanLock(precord); status = dbGet(paddr, dbrType, pbuffer, options, nRequest, pflin); dbScanUnlock(precord); return status; } long dbGet(DBADDR *paddr, short dbrType, void *pbuffer, long *options, long *nRequest, void *pflin) { char *pbuf = pbuffer; void *pfieldsave = paddr->pfield; db_field_log *pfl = (db_field_log *)pflin; short field_type; long capacity, no_elements, offset; rset *prset; long status = 0; if (options && *options) getOptions(paddr, &pbuf, options, pflin); if (nRequest && *nRequest == 0) return 0; if (!pfl || pfl->type == dbfl_type_rec) { field_type = paddr->field_type; no_elements = capacity = paddr->no_elements; /* Update field info from record * may modify paddr->pfield */ if (paddr->pfldDes->special == SPC_DBADDR && (prset = dbGetRset(paddr)) && prset->get_array_info) { status = prset->get_array_info(paddr, &no_elements, &offset); } else offset = 0; } else { field_type = pfl->field_type; no_elements = capacity = pfl->no_elements; offset = 0; } if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK) { status = getLinkValue(paddr, dbrType, pbuf, nRequest); goto done; } if (paddr->special == SPC_ATTRIBUTE) { status = getAttrValue(paddr, dbrType, pbuf, nRequest); goto done; } /* Check for valid request */ if (INVALID_DB_REQ(dbrType) || field_type > DBF_DEVICE) { char message[80]; sprintf(message, "dbGet: Request type is %d\n", dbrType); recGblDbaddrError(S_db_badDbrtype, paddr, message); status = S_db_badDbrtype; goto done; } if (offset == 0 && (!nRequest || no_elements == 1)) { if (nRequest) *nRequest = 1; if (!pfl || pfl->type == dbfl_type_rec) { status = dbFastGetConvertRoutine[field_type][dbrType] (paddr->pfield, pbuf, paddr); } else { DBADDR localAddr = *paddr; /* Structure copy */ localAddr.field_type = pfl->field_type; localAddr.field_size = pfl->field_size; localAddr.no_elements = pfl->no_elements; if (pfl->type == dbfl_type_val) localAddr.pfield = (char *) &pfl->u.v.field; else localAddr.pfield = (char *) pfl->u.r.field; status = dbFastGetConvertRoutine[field_type][dbrType] (localAddr.pfield, pbuf, &localAddr); } } else { long n; GETCONVERTFUNC convert; if (nRequest) { if (no_elements < *nRequest) *nRequest = no_elements; n = *nRequest; } else { n = 1; } convert = dbGetConvertRoutine[field_type][dbrType]; if (!convert) { char message[80]; sprintf(message, "dbGet: Missing conversion for [%d][%d]\n", field_type, dbrType); recGblDbaddrError(S_db_badDbrtype, paddr, message); status = S_db_badDbrtype; goto done; } /* convert data into the caller's buffer */ if (n <= 0) { ;/*do nothing*/ } else if (!pfl || pfl->type == dbfl_type_rec) { status = convert(paddr, pbuf, n, capacity, offset); } else { DBADDR localAddr = *paddr; /* Structure copy */ localAddr.field_type = pfl->field_type; localAddr.field_size = pfl->field_size; localAddr.no_elements = pfl->no_elements; if (pfl->type == dbfl_type_val) localAddr.pfield = (char *) &pfl->u.v.field; else localAddr.pfield = (char *) pfl->u.r.field; status = convert(&localAddr, pbuf, n, capacity, offset); } if(!status && dbrType==DBF_CHAR && nRequest && paddr->pfldDes && paddr->pfldDes->field_type==DBF_STRING) { /* long string ensure nil and truncate to actual length */ long nReq = *nRequest; pbuf[nReq-1] = '\0'; *nRequest = strlen(pbuf)+1; } } done: paddr->pfield = pfieldsave; return status; } devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp) { return (devSup *)ellNth(&prdes->devList, dtyp+1); } devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset) { devSup *pdevSup = (devSup *)ellFirst(&prdes->devList); while (pdevSup) { if (pdset == pdevSup->pdset) return pdevSup; pdevSup = (devSup *)ellNext(&pdevSup->node); } return NULL; } static long dbPutFieldLink(DBADDR *paddr, short dbrType, const void *pbuffer, long nRequest) { dbLinkInfo link_info; DBADDR *pdbaddr = NULL; dbCommon *precord = paddr->precord; dbCommon *lockrecs[2]; dbLocker locker; dbFldDes *pfldDes = paddr->pfldDes; long special = paddr->special; struct link *plink = (struct link *)paddr->pfield; const char *pstring = (const char *)pbuffer; struct dsxt *old_dsxt = NULL; struct dset *new_dset = NULL; struct dsxt *new_dsxt = NULL; devSup *new_devsup = NULL; long status; int isDevLink; short scan; STATIC_ASSERT(DBLOCKER_NALLOC>=2); switch (dbrType) { case DBR_CHAR: case DBR_UCHAR: if (pstring[nRequest - 1] != '\0') return S_db_badDbrtype; break; case DBR_STRING: break; default: return S_db_badDbrtype; } status = dbParseLink(pstring, pfldDes->field_type, &link_info); if (status) return status; if (link_info.ltype == PV_LINK && (link_info.modifiers & (pvlOptCA | pvlOptCP | pvlOptCPP)) == 0) { DBADDR tempaddr; if (dbNameToAddr(link_info.target, &tempaddr)==0) { /* This will become a DB link. */ pdbaddr = malloc(sizeof(*pdbaddr)); if (!pdbaddr) { status = S_db_noMemory; goto cleanup; } *pdbaddr = tempaddr; /* struct copy */ } } isDevLink = ellCount(&precord->rdes->devList) > 0 && pfldDes->isDevLink; memset(&locker, 0, sizeof(locker)); lockrecs[0] = precord; lockrecs[1] = pdbaddr ? pdbaddr->precord : NULL; dbLockerPrepare(&locker, lockrecs, 2); dbScanLockMany(&locker); scan = precord->scan; if (isDevLink) { new_devsup = dbDTYPtoDevSup(precord->rdes, precord->dtyp); if (new_devsup) { new_dset = new_devsup->pdset; new_dsxt = new_devsup->pdsxt; } } if (dbCanSetLink(plink, &link_info, new_devsup)) { /* link type mis-match prevents assignment */ status = S_dbLib_badField; goto unlock; } if (isDevLink) { if (precord->dset) { devSup *old_devsup = dbDSETtoDevSup(precord->rdes, precord->dset); if (old_devsup) old_dsxt = old_devsup->pdsxt; } if (new_dsxt == NULL || new_dsxt->add_record == NULL || (precord->dset && old_dsxt == NULL) || (old_dsxt && old_dsxt->del_record == NULL)) { status = S_db_noSupport; goto unlock; } if (scan == menuScanI_O_Intr) { scanDelete(precord); precord->scan = menuScanPassive; } if (old_dsxt) { status = old_dsxt->del_record(precord); if (status) goto restoreScan; } } if (dbLinkIsDefined(plink)) { dbRemoveLink(&locker, plink); /* Clear out old link */ } else if (!isDevLink) { status = S_db_badHWaddr; goto restoreScan; } if (special) status = dbPutSpecial(paddr, 0); if (!status) status = dbSetLink(plink, &link_info, new_devsup); if (!status && special) status = dbPutSpecial(paddr, 1); if (status) { if (isDevLink) { precord->dset = NULL; precord->pact = TRUE; } goto postScanEvent; } if (isDevLink) { precord->dpvt = NULL; precord->dset = new_dset; precord->pact = FALSE; status = new_dsxt->add_record(precord); if (status) { precord->dset = NULL; precord->pact = TRUE; goto postScanEvent; } } switch (plink->type) { /* New link type */ case PV_LINK: case CONSTANT: case JSON_LINK: dbAddLink(&locker, plink, pfldDes->field_type, pdbaddr); break; case DB_LINK: case CA_LINK: case MACRO_LINK: break; /* should never get here */ default: /* Hardware address */ if (!isDevLink) { status = S_db_badHWaddr; goto postScanEvent; } break; } db_post_events(precord, plink, DBE_VALUE | DBE_LOG); restoreScan: if (isDevLink && scan == menuScanI_O_Intr) { /* undo scanDelete() */ precord->scan = scan; scanAdd(precord); } postScanEvent: if (scan != precord->scan) db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG); unlock: dbScanUnlockMany(&locker); dbLockerFinalize(&locker); cleanup: free(link_info.target); return status; } long dbPutField(DBADDR *paddr, short dbrType, const void *pbuffer, long nRequest) { long status = 0; long special = paddr->special; dbFldDes *pfldDes = paddr->pfldDes; dbCommon *precord = paddr->precord; short dbfType = paddr->field_type; if (special == SPC_ATTRIBUTE) return S_db_noMod; /*check for putField disabled*/ if (precord->disp && paddr->pfield != &precord->disp) return S_db_putDisabled; if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) return dbPutFieldLink(paddr, dbrType, pbuffer, nRequest); dbScanLock(precord); status = dbPut(paddr, dbrType, pbuffer, nRequest); if (status == 0) { if (paddr->pfield == &precord->proc || (pfldDes->process_passive && precord->scan == 0 && dbrType < DBR_PUT_ACKT)) { if (precord->pact) { if (dbAccessDebugPUTF && precord->tpro) printf("%s: dbPutField to Active '%s', setting RPRO=1\n", epicsThreadGetNameSelf(), precord->name); precord->rpro = TRUE; } else { /* indicate that dbPutField called dbProcess */ precord->putf = TRUE; status = dbProcess(precord); } } } dbScanUnlock(precord); return status; } static long putAckt(DBADDR *paddr, const void *pbuffer, long nRequest, long no_elements, long offset) { dbCommon *precord = paddr->precord; const unsigned short *ptrans = pbuffer; if (*ptrans == precord->ackt) return 0; precord->ackt = *ptrans; db_post_events(precord, &precord->ackt, DBE_VALUE | DBE_ALARM); if (!precord->ackt && precord->acks > precord->sevr) { precord->acks = precord->sevr; db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM); } db_post_events(precord, NULL, DBE_ALARM); return 0; } static long putAcks(DBADDR *paddr, const void *pbuffer, long nRequest, long no_elements, long offset) { dbCommon *precord = paddr->precord; const unsigned short *psev = pbuffer; if (*psev >= precord->acks) { precord->acks = 0; db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM); db_post_events(precord, NULL, DBE_ALARM); } return 0; } long dbPut(DBADDR *paddr, short dbrType, const void *pbuffer, long nRequest) { dbCommon *precord = paddr->precord; short field_type = paddr->field_type; long no_elements = paddr->no_elements; long special = paddr->special; void *pfieldsave = paddr->pfield; rset *prset = dbGetRset(paddr); long status = 0; long offset; dbFldDes *pfldDes; int isValueField; if (special == SPC_ATTRIBUTE) return S_db_noMod; if (dbrType == DBR_PUT_ACKT && field_type <= DBF_DEVICE) { return putAckt(paddr, pbuffer, 1, 1, 0); } else if (dbrType == DBR_PUT_ACKS && field_type <= DBF_DEVICE) { return putAcks(paddr, pbuffer, 1, 1, 0); } else if (INVALID_DB_REQ(dbrType) || field_type > DBF_DEVICE) { char message[80]; sprintf(message, "dbPut: Request type is %d", dbrType); recGblDbaddrError(S_db_badDbrtype, paddr, message); return S_db_badDbrtype; } if (special) { status = dbPutSpecial(paddr, 0); if (status) return status; } if (paddr->pfldDes->special == SPC_DBADDR && prset && prset->get_array_info) { long dummy; status = prset->get_array_info(paddr, &dummy, &offset); /* paddr->pfield may be modified */ if (status) goto done; } else offset = 0; if (no_elements <= 1) { status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer, paddr->pfield, paddr); nRequest = 1; } else { if (no_elements < nRequest) nRequest = no_elements; status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer, nRequest, no_elements, offset); } /* update array info */ if (!status && paddr->pfldDes->special == SPC_DBADDR && prset && prset->put_array_info) { status = prset->put_array_info(paddr, nRequest); } /* Always do special processing if needed */ if (special) { long status2 = dbPutSpecial(paddr, 1); if (status2) goto done; } if (status) goto done; /* Propagate monitor events for this field, */ /* unless the field is VAL and PP is true. */ pfldDes = paddr->pfldDes; isValueField = dbIsValueField(pfldDes); if (isValueField) precord->udf = FALSE; 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; } base-7.0.3.1/modules/database/src/ioc/db/dbAccess.h0000664000577000060420000000157513557101274020445 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbAccess.h */ #ifndef INCdbAccessh #define INCdbAccessh #include "dbDefs.h" #include "epicsTime.h" #include "caeventmask.h" #include "dbFldTypes.h" #include "link.h" #include "dbBase.h" #include "shareLib.h" #include "dbAddr.h" #include "dbLock.h" #include "dbAccessDefs.h" #include "dbLink.h" #include "dbCa.h" #include "dbCommon.h" #include "db_field_log.h" #endif /*INCdbAccessh*/ base-7.0.3.1/modules/database/src/ioc/db/dbAccessDefs.h0000664000577000060420000002512313557101274021242 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbAccessDefs.h */ #ifndef INCdbAccessDefsh #define INCdbAccessDefsh #ifdef epicsExportSharedSymbols # define INCLdb_accessh_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "epicsTypes.h" #include "epicsTime.h" #include "dbBase.h" #include "dbAddr.h" #include "recSup.h" #ifdef INCLdb_accessh_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif #ifdef __cplusplus extern "C" { #endif epicsShareExtern struct dbBase *pdbbase; epicsShareExtern volatile int interruptAccept; epicsShareExtern int dbAccessDebugPUTF; /* The database field and request types are defined in dbFldTypes.h*/ /* Data Base Request Options */ #define DBR_STATUS 0x00000001 #define DBR_UNITS 0x00000002 #define DBR_PRECISION 0x00000004 #define DBR_TIME 0x00000008 #define DBR_ENUM_STRS 0x00000010 #define DBR_GR_LONG 0x00000020 #define DBR_GR_DOUBLE 0x00000040 #define DBR_CTRL_LONG 0x00000080 #define DBR_CTRL_DOUBLE 0x00000100 #define DBR_AL_LONG 0x00000200 #define DBR_AL_DOUBLE 0x00000400 /********************************************************************** * The next page contains macros for defining requests. * As an example the following defines a buffer to accept an array * of 10 float values + DBR_STATUS and DBR_TIME options * * struct { * DBRstatus * DBRtime * epicsFloat32 value[10] * } buffer; * * IMPORTANT!! The DBRoptions must be given in the order that they * appear in the Data Base Request Options #defines * * The associated dbGetField call is: * * long options,number_elements; * ... * options = DBR_STATUS|DBR_TIME; * number_elements = 10; * rtnval=dbGetField(paddr,DBR_FLOAT,&buffer,&options,&number_elements); * * When dbGetField returns: * rtnval is error status (0 means success) * options has a bit set for each option that was accepted * number_elements is actual number of elements obtained * * The individual items can be refered to by the expressions:: * * buffer.status * buffer.severity * buffer.err_status * buffer.epoch_seconds * buffer.nano_seconds * buffer.value[i] * * The following is also a valid declaration: * * typedef struct { * DBRstatus * DBRtime * epicsFloat32 value[10] * } MYBUFFER; * * With this definition you can give definitions such as the following: * * MYBUFFER *pbuf1; * MYBUFFER buf; *************************************************************************/ /* Macros for defining each option */ #define DBRstatus \ epicsUInt16 status; /* alarm status */\ epicsUInt16 severity; /* alarm severity*/\ epicsUInt16 acks; /* alarm ack severity*/\ epicsUInt16 ackt; /* Acknowledge transient alarms?*/ #define DB_UNITS_SIZE 16 #define DBRunits \ char units[DB_UNITS_SIZE]; /* units */ #define DBRprecision union { \ long dp; /* number of decimal places*/\ double unused; /* for alignment */\ } precision; /* precision.dp must be long to match the pointer arguments to * RSET->get_precision() and recGblGetPrec(), which it's * too late to change now. DBRprecision must be padded to * maintain 8-byte alignment. */ #define DBRtime \ epicsTimeStamp time; /* time stamp*/ #define DBRenumStrs \ epicsUInt32 no_str; /* number of strings*/\ epicsInt32 padenumStrs; /*padding to force 8 byte align*/\ char strs[DB_MAX_CHOICES][MAX_STRING_SIZE]; /* string values */ #define DBRgrLong \ epicsInt32 upper_disp_limit; /*upper limit of graph*/\ epicsInt32 lower_disp_limit; /*lower limit of graph*/ #define DBRgrDouble \ epicsFloat64 upper_disp_limit; /*upper limit of graph*/\ epicsFloat64 lower_disp_limit; /*lower limit of graph*/ #define DBRctrlLong \ epicsInt32 upper_ctrl_limit; /*upper limit of graph*/\ epicsInt32 lower_ctrl_limit; /*lower limit of graph*/ #define DBRctrlDouble \ epicsFloat64 upper_ctrl_limit; /*upper limit of graph*/\ epicsFloat64 lower_ctrl_limit; /*lower limit of graph*/ #define DBRalLong \ epicsInt32 upper_alarm_limit;\ epicsInt32 upper_warning_limit;\ epicsInt32 lower_warning_limit;\ epicsInt32 lower_alarm_limit; #define DBRalDouble \ epicsFloat64 upper_alarm_limit;\ epicsFloat64 upper_warning_limit;\ epicsFloat64 lower_warning_limit;\ epicsFloat64 lower_alarm_limit; /* structures for each option type */ struct dbr_status {DBRstatus}; struct dbr_units {DBRunits}; struct dbr_precision {DBRprecision}; struct dbr_time {DBRtime}; struct dbr_enumStrs {DBRenumStrs}; struct dbr_grLong {DBRgrLong}; struct dbr_grDouble {DBRgrDouble}; struct dbr_ctrlLong {DBRctrlLong}; struct dbr_ctrlDouble {DBRctrlDouble}; struct dbr_alLong {DBRalLong}; struct dbr_alDouble {DBRalDouble}; /* sizes for each option structure */ #define dbr_status_size sizeof(struct dbr_status) #define dbr_units_size sizeof(struct dbr_units) #define dbr_precision_size sizeof(struct dbr_precision) #define dbr_time_size sizeof(struct dbr_time) #define dbr_enumStrs_size sizeof(struct dbr_enumStrs) #define dbr_grLong_size sizeof(struct dbr_grLong) #define dbr_grDouble_size sizeof(struct dbr_grDouble) #define dbr_ctrlLong_size sizeof(struct dbr_ctrlLong) #define dbr_ctrlDouble_size sizeof(struct dbr_ctrlDouble) #define dbr_alLong_size sizeof(struct dbr_alLong) #define dbr_alDouble_size sizeof(struct dbr_alDouble) #ifndef INCerrMdefh #include "errMdef.h" #endif #define S_db_notFound (M_dbAccess| 1) /*Process Variable Not Found*/ #define S_db_badDbrtype (M_dbAccess| 3) /*Illegal Database Request Type*/ #define S_db_noMod (M_dbAccess| 5) /*Attempt to modify noMod field*/ #define S_db_badLset (M_dbAccess| 7) /*Illegal Lock Set*/ #define S_db_precision (M_dbAccess| 9) /*get precision failed */ #define S_db_onlyOne (M_dbAccess|11) /*Only one element allowed*/ #define S_db_badChoice (M_dbAccess|13) /*Illegal choice*/ #define S_db_badField (M_dbAccess|15) /*Illegal field value*/ #define S_db_lsetLogic (M_dbAccess|17) /*Logic error generating lock sets*/ #define S_db_noLSET (M_dbAccess|21) /*No link support table or entry*/ #define S_db_noRSET (M_dbAccess|31) /*missing record support entry table*/ #define S_db_noSupport (M_dbAccess|33) /*RSET or DSXT routine not defined*/ #define S_db_BadSub (M_dbAccess|35) /*Subroutine not found*/ /*!!!! Do not change next line without changing src/rsrv/server.h!!!!!!!!*/ #define S_db_Pending (M_dbAccess|37) /*Request is pending*/ #define S_db_Blocked (M_dbAccess|39) /*Request is Blocked*/ #define S_db_putDisabled (M_dbAccess|41) /*putFields are disabled*/ #define S_db_badHWaddr (M_dbAccess|43) /*Hardware link type not on INP/OUT*/ #define S_db_bkptSet (M_dbAccess|53) /*Breakpoint already set*/ #define S_db_bkptNotSet (M_dbAccess|55) /*No breakpoint set in record*/ #define S_db_notStopped (M_dbAccess|57) /*Record not stopped*/ #define S_db_errArg (M_dbAccess|59) /*Error in argument*/ #define S_db_bkptLogic (M_dbAccess|61) /*Logic error in breakpoint routine*/ #define S_db_cntSpwn (M_dbAccess|63) /*Cannot spawn dbContTask*/ #define S_db_cntCont (M_dbAccess|65) /*Cannot resume dbContTask*/ #define S_db_noMemory (M_dbAccess|66) /*unable to allocate data structure from pool*/ #define S_db_notInit (M_dbAccess|67) /*Not initialized*/ #define S_db_bufFull (M_dbAccess|68) /*Buffer full*/ struct dbEntry; epicsShareFunc long dbPutSpecial(struct dbAddr *paddr,int pass); epicsShareFunc rset * dbGetRset(const struct dbAddr *paddr); epicsShareFunc long dbPutAttribute( const char *recordTypename,const char *name,const char*value); epicsShareFunc int dbIsValueField(const struct dbFldDes *pdbFldDes); epicsShareFunc int dbGetFieldIndex(const struct dbAddr *paddr); epicsShareFunc long dbScanPassive( struct dbCommon *pfrom,struct dbCommon *pto); epicsShareFunc long dbProcess(struct dbCommon *precord); epicsShareFunc long dbNameToAddr(const char *pname, struct dbAddr *paddr); /** Initialize DBADDR from a dbEntry * Also handles SPC_DBADDR processing. This is really an internal * routine for use by dbNameToAddr() and dbChannelCreate(). */ epicsShareFunc long dbEntryToAddr(const struct dbEntry *pdbentry, struct dbAddr *paddr); /** Initialize DBENTRY from a valid dbAddr* * Constant time equivalent of dbInitEntry() then dbFindRecord(), * and finally dbFollowAlias(). */ epicsShareFunc void dbInitEntryFromAddr(struct dbAddr *paddr, struct dbEntry *pdbentry); /** Initialize DBENTRY from a valid record (dbCommon*) * Constant time equivalent of dbInitEntry() then dbFindRecord(), * and finally dbFollowAlias() when no field is specified. */ epicsShareFunc void dbInitEntryFromRecord(struct dbCommon *prec, struct dbEntry *pdbentry); epicsShareFunc devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp); epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset); epicsShareFunc long dbGetField( struct dbAddr *,short dbrType,void *pbuffer,long *options, long *nRequest,void *pfl); epicsShareFunc long dbGet( struct dbAddr *,short dbrType,void *pbuffer,long *options, long *nRequest,void *pfl); epicsShareFunc long dbPutField( struct dbAddr *,short dbrType,const void *pbuffer,long nRequest); epicsShareFunc long dbPut( struct dbAddr *,short dbrType,const void *pbuffer,long nRequest); typedef void(*SPC_ASCALLBACK)(struct dbCommon *); /*dbSpcAsRegisterCallback called by access security */ epicsShareFunc void dbSpcAsRegisterCallback(SPC_ASCALLBACK func); epicsShareFunc long dbBufferSize( short dbrType,long options,long nRequest); epicsShareFunc long dbValueSize(short dbrType); /* Hook Routine */ typedef void (*DB_LOAD_RECORDS_HOOK_ROUTINE)(const char* filename, const char* substitutions); epicsShareExtern DB_LOAD_RECORDS_HOOK_ROUTINE dbLoadRecordsHook; epicsShareFunc int dbLoadDatabase( const char *filename, const char *path, const char *substitutions); epicsShareFunc int dbLoadRecords( const char* filename, const char* substitutions); #ifdef __cplusplus } #endif #endif /*INCdbAccessDefsh*/ base-7.0.3.1/modules/database/src/ioc/db/dbAddr.h0000664000577000060420000000252613557101274020113 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef dbAddrh #define dbAddrh struct dbCommon; struct dbFldDes; typedef struct dbAddr { struct dbCommon *precord; /* address of record */ void *pfield; /* address of field */ struct dbFldDes *pfldDes; /* address of struct fldDes */ long no_elements; /* number of elements (arrays) */ short field_type; /* type of database field */ short field_size; /* size of the field being accessed */ short special; /* special processing */ short dbr_field_type; /* field type as seen by database request*/ /* DBR_STRING,...,DBR_ENUM,DBR_NOACCESS */ } dbAddr; typedef dbAddr DBADDR; #endif /* dbAddrh */ base-7.0.3.1/modules/database/src/ioc/db/dbBkpt.c0000664000577000060420000006526713557101274020147 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* dbBkpt.c */ /* * Author: Matthew Needes * Date: 8-30-93 */ /* * Database Breakpoint Manipulation and User Interface * * USER COMMANDS * dbb(record_name) Set a breakpoint in a record * dbd(record_name) Delete a record's breakpoint * dbc(record_name) Resume record processing * dbs(record_name) Step through record processing through * IO links, forward process links, etc. * dbstat() Display status of stopped records in lock sets. * dbap(record_name) Toggle automatic print after processing. * dbp(record_name) Print out fields from record currently stopped. * dbprc(record_name) Processes a record once without printing it. * (Unless autoprint is on) * * INTERNAL FUNCTIONS * dbBkpt() Process breakpoints, called by dbProcess(). * dbPrint() Prints record if autoprint enabled. * dbBkptCont() The task that continues and steps through * records that are stopped at a breakpoint. */ /* #define BKPT_DIAG */ #include #include #include #include #include #include "alarm.h" #include "dbDefs.h" #include "ellLib.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "epicsThread.h" #include "epicsTime.h" #include "errlog.h" #include "errMdef.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" #include "dbBkpt.h" #include "dbCommon.h" #include "db_field_log.h" #include "dbFldTypes.h" #include "dbFldTypes.h" #include "dbLink.h" #include "dbLock.h" #include "dbScan.h" #include "dbTest.h" #include "link.h" #include "recGbl.h" #include "recSup.h" #include "special.h" /* private routines */ static void dbBkptCont(dbCommon *precord); static long FIND_CONT_NODE( const char *record_name, struct LS_LIST **ppnode, struct dbCommon **pprecord); /* * Breakpoints are used as a debugging instrument to suspend the * processing of database records. Once suspended, record * processing may continue if either a continue (dbc()) or a * step (dbs()) command is then issued. The current record's * contents may be printed either with dbp(), or immediately * after processing (use dbap() to toggle the BKPT_PRINT bit). * * dbb() and dbd() add a breakpoint to a record or delete one * from a record. dbstat() prints out comprehensive breakpoint * status information. * * Breakpoints may be set on a per lockset basis. When a * breakpoint is set in a lockset, a new task is created. A * separate task gets created for _every_ lockset containing * a breakpoint. Thus multiple locksets may be debugged * simultaneously. The breakpoint handler then schedules future * processing in that lockset to this task. The separate task is * used so that locksets that do not have breakpoints are isolated * from locksets that do. This allows the processing of other * locksets to continue uninterupted, even if they exist on the same * scan list as a lockset containing a breakpoint. * * An entrypoint is the first record that gets processed in a lockset. * This type of record is the basis for subsequent recursive executions * of dbProcess(). The breakpoint handler monitors and schedules * these entrypoints to the breakpoint tasks. * * Two hooks have been inserted in dbProcess() to manage breakpoints, * dbBkpt() and dbPrint(). The former does two things: * * 1. Schedule entrypoints with the breakpoint task. * 2. Suspend record processing when a breakpoint is detected. * * 1 occurs only if dbProcess() is called outside of the breakpoint * task. Number 2 only occurs when dbProcess() is called from * _within_ the breakpoint task's context. Number 1 is used for * detection and scheduling, while 2 is used for suspending the task. * * The dbPrint() hook is used to print out a record's contents immediately * _after_ a record has been processed. * * The dbBkptCont, or breakpoint task, pends on a semaphore that gets * released whenever new entrypoints are scheduled for it. When * released, this task then runs down its entrypoint queue and * processes each entrypoint in turn. In this context, dbProcess * will execute the dbBkpt() hook in mode 2, allowing this task to * be suspended whenever a breakpoint is detected. * * NOTE: This is not a very "real-time" implementation (even for those * locksets not containing a breakpoint). I may fix this later. * * Final comment: The scary thing is, I don't think this can be done * more simply... * */ /* * Flag used by dbProcess() to determine if there are * any breakpoints. This is so that there is only * a single comparison in the critical path during * normal record execution, i.e. when there aren't * any breakpoints set. */ long lset_stack_count = 0; /* * Stack--in which each entry represents a different * lock set with either breakpoints and/or stopped * execution. (Breakpoints may be disabled even * though execution is stopped). The order of the * list is maintained so that the entry on the top * of stack is used as a default for dbc() and dbs(). * The semaphore is used to prevent conflicts while * operating with this stack. */ static ELLLIST lset_stack = ELLLIST_INIT; static epicsMutexId bkpt_stack_sem = 0; /* * Stores the last lockset continued or stepped from. * dbs() and dbc() will print a message if the current * lockset to be continued from differs from this * variable. */ static unsigned long last_lset = 0; /* * FIND_LOCKSET() finds the stack entry * whose l_num field matches precord's * lset field. The node that is found * is returned in "pnode." */ #define FIND_LOCKSET(precord, pnode) \ pnode = (struct LS_LIST *) ellFirst(&lset_stack); \ while ((pnode) != NULL) { \ if (pnode->l_num == dbLockGetLockId(precord)) break; \ pnode = (struct LS_LIST *) ellNext((ELLNODE *)pnode); \ } \ /* * FIND_QUEUE_ENTRY() matches entries in an * entry point queue. pep_queue is the queue * being searched, pqe is the pointer to the * queue entry found, and precord is the record * being searched for in *pep_queue. */ #define FIND_QUEUE_ENTRY(pep_queue, pqe, precord) \ pqe = (struct EP_LIST *) ellFirst(pep_queue); \ while ((pqe) != NULL) { \ if ((pqe)->entrypoint == (precord)) break; \ pqe = (struct EP_LIST *) ellNext((ELLNODE *)pqe); \ } \ /* * Fills out pnode and precord structures for dbc() and dbs() * MUST LOCK OUT STACK BEFORE ENTRY */ static long FIND_CONT_NODE( const char *record_name, struct LS_LIST **ppnode, struct dbCommon **pprecord) { struct dbAddr addr; struct LS_LIST *pnode; struct dbCommon *precord = NULL; long status = 0; if (record_name == NULL) { /* * Search through stack, taking the first entry that * is currently stopped at a breakpoint. */ pnode = (struct LS_LIST *) ellFirst(&lset_stack); while (pnode != NULL) { if (pnode->precord != NULL) { precord = pnode->precord; break; } pnode = (struct LS_LIST *) ellNext((ELLNODE *)pnode); } if (pnode == NULL) { printf(" BKPT> No records are currently stopped\n"); return(S_db_notStopped); } } else { /* * Convert name to address */ status = dbNameToAddr(record_name, &addr); if (status == S_db_notFound) printf(" BKPT> Record %s not found\n", record_name); if (status != 0) return(status); precord = addr.precord; FIND_LOCKSET(precord, pnode); if (pnode == NULL || pnode->precord == NULL) { printf(" BKPT> Currently not stopped in this lockset\n"); return(S_db_notStopped); } } *pprecord = precord; *ppnode = pnode; return(0); } /* * Initialise the breakpoint stack */ void dbBkptInit(void) { if (! bkpt_stack_sem) { bkpt_stack_sem = epicsMutexMustCreate(); lset_stack_count = 0; } } /* * Add breakpoint to a lock set * 1. Convert name to address and check breakpoint mask. * 2. Lock database. * 3. If empty, initialize lock set stack and its semaphore. * 4. Take that semaphore. * 5. Find lockset in the list. If it doesn't exist, create it. * 6. Turn on breakpoint field in record. * 7. Add breakpoint to list of breakpoints in structure. * 8. Spawn continuation task if it isn't already running. */ long dbb(const char *record_name) { struct dbAddr addr; struct LS_LIST *pnode; struct BP_LIST *pbl; struct dbCommon *precord; long status; /* * Convert name to address */ if (!record_name) { printf("Usage: dbb \"record_name\"\n"); return -1; } status = dbNameToAddr(record_name, &addr); if (status == S_db_notFound) printf(" BKPT> Record %s not found\n", record_name); if (status != 0) return(status); precord = addr.precord; if (precord->bkpt & BKPT_ON_MASK) { printf(" BKPT> Breakpoint already set in this record\n"); return(S_db_bkptSet); } dbScanLock(precord); /* * Add lock set to the stack of lock sets that * contain breakpoints and/or stopped records. */ epicsMutexMustLock(bkpt_stack_sem); FIND_LOCKSET(precord, pnode); if (pnode == NULL) { /* lockset not found, create node, add to end of list */ pnode = (struct LS_LIST *) malloc(sizeof(struct LS_LIST)); if (pnode == NULL) { printf(" BKPT> Out of memory\n"); dbScanUnlock(precord); epicsMutexUnlock(bkpt_stack_sem); return(1); } pnode->precord = NULL; /* initialize breakpoint list */ ellInit(&pnode->bp_list); /* initialize entry point queue */ ellInit(&pnode->ep_queue); /* create execution semaphore */ pnode->ex_sem = epicsEventCreate(epicsEventEmpty); if (pnode->ex_sem == NULL) { printf(" BKPT> Out of memory\n"); dbScanUnlock(precord); epicsMutexUnlock(bkpt_stack_sem); return(1); } pnode->taskid = 0; pnode->step = 0; pnode->l_num = dbLockGetLockId(precord); ellAdd(&lset_stack, (ELLNODE *)pnode); ++lset_stack_count; } /* * Add record to breakpoint list */ pbl = (struct BP_LIST *) malloc(sizeof(struct BP_LIST)); if (pbl == NULL) { printf(" BKPT> Out of memory\n"); dbScanUnlock(precord); epicsMutexUnlock(bkpt_stack_sem); return(1); } pbl->precord = precord; ellAdd(&pnode->bp_list, (ELLNODE *)pbl); /* * Turn on breakpoint field in record */ precord->bkpt |= BKPT_ON_MASK; if (! pnode->taskid) { #ifdef BKPT_DIAG printf(" BKPT> Spawning task: %s\n", precord->name); #endif /* * Spawn continuation task */ pnode->taskid = epicsThreadCreate("bkptCont",epicsThreadPriorityScanLow-1, epicsThreadGetStackSize(epicsThreadStackBig), (EPICSTHREADFUNC)dbBkptCont,precord); if (pnode->taskid == 0) { printf(" BKPT> Cannot spawn task to process record\n"); pnode->taskid = 0; dbScanUnlock(precord); epicsMutexUnlock(bkpt_stack_sem); return(1); } } epicsMutexUnlock(bkpt_stack_sem); dbScanUnlock(precord); return(0); } /* * Remove breakpoint from a record * 1. Convert name to address and check breakpoint mask. * 2. Lock database and take stack semaphore. * 3. Find structure for record's lockset (in stack). * 4. Find and delete record from breakpoint list. * 5. Turn off break point field. * 6. Give up semaphore to "signal" bkptCont task to quit. */ long dbd(const char *record_name) { struct dbAddr addr; struct LS_LIST *pnode; struct BP_LIST *pbl; struct dbCommon *precord; long status; /* * Convert name to address */ if (!record_name) { printf("Usage: dbd \"record_name\"\n"); return -1; } status = dbNameToAddr(record_name, &addr); if (status == S_db_notFound) printf(" BKPT> Record %s not found\n", record_name); if (status != 0) return(status); precord = addr.precord; if (!(precord->bkpt & BKPT_ON_MASK)) { printf(" BKPT> No breakpoint set in this record\n"); return(S_db_bkptNotSet); } dbScanLock(precord); epicsMutexMustLock(bkpt_stack_sem); FIND_LOCKSET(precord, pnode); if (pnode == NULL) { /* not found, error ! */ printf(" BKPT> Logic Error in dbd()\n"); precord->bkpt &= BKPT_OFF_MASK; epicsMutexUnlock(bkpt_stack_sem); dbScanUnlock(precord); return(S_db_bkptLogic); } /* * Remove record from breakpoint list */ /* find record in list */ pbl = (struct BP_LIST *) ellFirst(&pnode->bp_list); while (pbl != NULL) { if (pbl->precord == precord) { ellDelete(&pnode->bp_list, (ELLNODE *)pbl); free(pbl); break; } pbl = (struct BP_LIST *) ellNext((ELLNODE *)pbl); } if (pbl == NULL) { printf(" BKPT> Logic Error in dbd()\n"); precord->bkpt &= BKPT_OFF_MASK; epicsMutexUnlock(bkpt_stack_sem); dbScanUnlock(precord); return(S_db_bkptLogic); } /* * Turn off breakpoint field in record */ precord->bkpt &= BKPT_OFF_MASK; /* * If there are no more breakpoints, give up semaphore * to cause the bkptCont task to quit. */ if (ellCount(&pnode->bp_list) == 0) epicsEventSignal(pnode->ex_sem); epicsMutexUnlock(bkpt_stack_sem); dbScanUnlock(precord); return(0); } /* * Continue processing in a lock set * 1. Find top node in the lockset stack. * 2. Turn off stepping mode. * 2. Resume dbBkptCont. */ long dbc(const char *record_name) { struct LS_LIST *pnode; struct dbCommon *precord = NULL; long status = 0; epicsMutexMustLock(bkpt_stack_sem); status = FIND_CONT_NODE(record_name, &pnode, &precord); if (status) { epicsMutexUnlock(bkpt_stack_sem); return(status); } if (record_name == NULL && last_lset != pnode->l_num) printf(" BKPT> Continuing: %s\n", pnode->precord->name); last_lset = pnode->l_num; /* * Turn off stepping mode */ pnode->step = 0; /* * Resume dbBkptCont() until dbProcess() is executed * for a record with a breakpoint. This occurs * because stepping mode has been switched off. */ epicsThreadResume(pnode->taskid); epicsMutexUnlock(bkpt_stack_sem); return(0); } /* * Step through record processing * 1. Find top node in lockset stack. * 2. Resume dbBkptCont. */ long dbs(const char *record_name) { struct LS_LIST *pnode; struct dbCommon *precord = NULL; long status = 0; epicsMutexMustLock(bkpt_stack_sem); status = FIND_CONT_NODE(record_name, &pnode, &precord); if (status) { epicsMutexUnlock(bkpt_stack_sem); return(status); } if (last_lset != pnode->l_num && record_name == NULL) printf(" BKPT> Stepping: %s\n", pnode->precord->name); last_lset = pnode->l_num; epicsThreadResume(pnode->taskid); epicsMutexUnlock(bkpt_stack_sem); return(0); } /* * Task for continuing record processing * 1. Find lockset in stack for precord. * DO 2-3 while breakpoints exist in the lockset. * 2. Wait on execution semaphore ... * 3. Run through every entrypoint in queue, processing * those that are scheduled. * 4. Free resources for lockset, and exit task. */ static void dbBkptCont(dbCommon *precord) { struct LS_LIST *pnode; struct EP_LIST *pqe = NULL; /* * Reset breakpoint, process record, and * reset bkpt field in record */ epicsMutexMustLock(bkpt_stack_sem); FIND_LOCKSET(precord, pnode); if (pnode == NULL) { printf(" BKPT> Logic error in dbBkptCont()\n"); return; } /* * For every entrypoint scheduled, process. Run process * until there are no more breakpoints remaining in a * lock set. */ do { /* Give up semaphore before waiting to run ... */ epicsMutexUnlock(bkpt_stack_sem); /* Wait to run */ epicsEventMustWait(pnode->ex_sem); /* Bkpt stack must still be stable ! */ epicsMutexMustLock(bkpt_stack_sem); pqe = (struct EP_LIST *) ellFirst(&pnode->ep_queue); /* Run through entrypoint queue */ while (pqe != NULL) { /* check if entrypoint is currently scheduled */ if (pqe->sched) { /* save current entrypoint */ pnode->current_ep = pqe->entrypoint; /* lock the lockset, process record, unlock */ dbScanLock(precord); dbProcess(pqe->entrypoint); dbScanUnlock(precord); /* reset schedule and stepping flag - Do this AFTER processing */ pqe->sched = 0; pnode->step = 0; } pqe = (struct EP_LIST *) ellNext((ELLNODE *)pqe); } /* Reset precord. (Since no records are at a breakpoint) */ pnode->precord = NULL; } while (ellCount(&pnode->bp_list) != 0); /* remove node from lockset stack */ ellDelete(&lset_stack, (ELLNODE *)pnode); --lset_stack_count; /* free entrypoint queue */ ellFree(&pnode->ep_queue); /* remove execution semaphore */ epicsEventDestroy(pnode->ex_sem); printf("\n BKPT> End debug of lockset %lu\n-> ", pnode->l_num); /* free list node */ free(pnode); epicsMutexUnlock(bkpt_stack_sem); } /* * Process breakpoint * Returns a zero if dbProcess() is to execute * record support, a one if dbProcess() is to * skip over record support. See dbProcess(). * * 1. See if there is at least a breakpoint set somewhere * in precord's lockset. If not, return immediately. * 2. Check the disable flag. * 3. Add entry points to the queue for future stepping and * schedule new entrypoints for the continuation task. * 4. Check the pact flag. * 5. Check to see if there is a breakpoint set in a record, and * if so, turn on stepping mode. * 6. If stepping mode is set, stop and report the breakpoint. */ int dbBkpt(dbCommon *precord) { struct LS_LIST *pnode; struct EP_LIST *pqe; /* * It is crucial that operations in dbBkpt() execute * in the correct order or certain features in the * breakpoint handler will not work as expected. */ /* * Take and give a semaphore to check for breakpoints * every time a record is processed. Slow. Thank * goodness breakpoint checking is turned off during * normal operation. */ epicsMutexMustLock(bkpt_stack_sem); FIND_LOCKSET(precord, pnode); epicsMutexUnlock(bkpt_stack_sem); if (pnode == NULL) { /* no breakpoints in precord's lockset */ return(0); } /* Check disable flag */ dbGetLink(&(precord->sdis),DBR_SHORT,&(precord->disa),0,0); if (precord->disa == precord->disv) { /* * Do not process breakpoints if the record is disabled, * but allow disable alarms. Alarms will be raised * in dbProcess() because returning 0 allows dbProcess() * to continue. However processing will be prevented * because disa and disv will be examined again in * dbProcess(). Note that checking for pact will occur * before checking for disa and disv in dbProcess(). */ return(0); } /* * Queue entry points for future stepping. The taskid comparison * is used to determine if the source of processing is the * continuation task or an external source. If it is an external * source, queue its execution, but dump out of dbProcess without * calling record support. */ if (pnode->taskid && (epicsThreadGetIdSelf() != pnode->taskid)) { /* CONTINUE TASK CANNOT ENTER HERE */ /* * Add an entry point to queue, if it does * not already exist. */ FIND_QUEUE_ENTRY(&pnode->ep_queue, pqe, precord); if (pqe == NULL) { pqe = (struct EP_LIST *) malloc(sizeof(struct EP_LIST)); if (pqe == NULL) return(1); pqe->entrypoint = precord; pqe->count = 1; epicsTimeGetCurrent(&pqe->time); pqe->sched = 0; #ifdef BKPT_DIAG printf(" BKPT> Adding entrypoint %s to queue\n", precord->name); #endif /* * Take semaphore, wait on continuation task */ epicsMutexMustLock(bkpt_stack_sem); /* Add entry to queue */ ellAdd(&pnode->ep_queue, (ELLNODE *)pqe); epicsMutexUnlock(bkpt_stack_sem); } else { if (pqe->count < MAX_EP_COUNT) pqe->count++; } /* check pact */ if (! precord->pact) { /* schedule if pact not set */ pqe->sched = 1; /* * Release the semaphore, letting the continuation * task begin execution of the new entrypoint. */ epicsEventSignal(pnode->ex_sem); } return(1); } /* * Don't mess with breakpoints if pact set! Skip * over rest of dbProcess() since we don't want * alarms going off. The pact flag is checked * AFTER entry point queuing so that the record * timing feature will work properly. */ if (precord->pact) return(1); /* Turn on stepping mode if a breakpoint is found */ if (precord->bkpt & BKPT_ON_MASK) { pnode->step = 1; #ifdef BKPT_DIAG printf(" BKPT> Bkpt detected: %s\n", precord->name); #endif } /* * If we are currently stepping through the lockset, * suspend task. */ if (pnode->step) { printf("\n BKPT> Stopped at: %s within Entrypoint: %s\n-> ", precord->name, pnode->current_ep->name); pnode->precord = precord; /* Move current lockset to top of stack */ ellDelete(&lset_stack, (ELLNODE *)pnode); ellInsert(&lset_stack, NULL, (ELLNODE *)pnode); /* * Unlock database while the task suspends itself. This * is done so that dbb() dbd() dbc() dbs() may be used * when the task is suspended. Scan tasks that also * use the scan lock feature will not be hung during * a breakpoint, so that records in other locksets will * continue to be processed. Cross your fingers, this * might actually work ! */ epicsMutexUnlock(bkpt_stack_sem); dbScanUnlock(precord); epicsThreadSuspendSelf(); dbScanLock(precord); epicsMutexMustLock(bkpt_stack_sem); } return(0); } /* print record after processing */ void dbPrint(dbCommon *precord ) { struct LS_LIST *pnode; if (! (precord->bkpt & BKPT_PRINT_MASK)) return; FIND_LOCKSET(precord, pnode); /* do not print if lockset does not currently contain breakpoints */ if (pnode == NULL) return; printf("\n"); dbpr(precord->name, 2); printf("-> "); } /* print stopped record */ long dbp(const char *record_name, int interest_level) { struct LS_LIST *pnode; struct dbCommon *precord = NULL; int status; epicsMutexMustLock(bkpt_stack_sem); /* find pnode and precord pointers */ status = FIND_CONT_NODE(record_name, &pnode, &precord); if (status) { epicsMutexUnlock(bkpt_stack_sem); return(status); } /* print out record's fields */ dbpr(precord->name, (interest_level == 0) ? 2 : interest_level); epicsMutexUnlock(bkpt_stack_sem); return(0); } /* toggle printing after processing a certain record */ long dbap(const char *record_name) { struct dbAddr addr; struct dbCommon *precord; long status; /* * Convert name to address */ if (!record_name) { printf("Usage: dbap \"record_name\"\n"); return -1; } status = dbNameToAddr(record_name, &addr); if (status == S_db_notFound) printf(" BKPT> Record %s not found\n", record_name); if (status != 0) return(status); precord = addr.precord; /* * Toggle print after process field in record */ if (precord->bkpt & BKPT_PRINT_MASK) { printf(" BKPT> Auto print off for record %s\n", precord->name); precord->bkpt &= BKPT_PRINT_OFF_MASK; } else { printf(" BKPT> Auto print on for record %s\n", precord->name); precord->bkpt |= BKPT_PRINT_MASK; } return(0); } /* print list of stopped records, and breakpoints set in locksets */ long dbstat(void) { struct LS_LIST *pnode; struct BP_LIST *pbl; struct EP_LIST *pqe; epicsTimeStamp time; epicsMutexMustLock(bkpt_stack_sem); epicsTimeGetCurrent(&time); /* * Traverse list, reporting stopped records */ pnode = (struct LS_LIST *) ellFirst(&lset_stack); while (pnode != NULL) { if (pnode->precord != NULL) { printf("LSet: %lu Stopped at: %-28.28s #B: %5.5d T: %p\n", pnode->l_num, pnode->precord->name, ellCount(&pnode->bp_list), pnode->taskid); /* for each entrypoint detected, print out entrypoint statistics */ pqe = (struct EP_LIST *) ellFirst(&pnode->ep_queue); while (pqe != NULL) { double diff = epicsTimeDiffInSeconds(&time,&pqe->time); if (diff) { printf(" Entrypoint: %-28.28s #C: %5.5lu C/S: %7.1f\n", pqe->entrypoint->name, pqe->count,diff); } pqe = (struct EP_LIST *) ellNext((ELLNODE *)pqe); } } else { printf("LSet: %lu #B: %5.5d T: %p\n", pnode->l_num, ellCount(&pnode->bp_list), pnode->taskid); } /* * Print out breakpoints set in the lock set */ pbl = (struct BP_LIST *) ellFirst(&pnode->bp_list); while (pbl != NULL) { printf(" Breakpoint: %-28.28s", pbl->precord->name); /* display auto print flag */ if (pbl->precord->bkpt & BKPT_PRINT_MASK) printf(" (ap)\n"); else printf("\n"); pbl = (struct BP_LIST *) ellNext((ELLNODE *)pbl); } pnode = (struct LS_LIST *) ellNext((ELLNODE *)pnode); } epicsMutexUnlock(bkpt_stack_sem); return(0); } /* * Process a record without printing it. */ long dbprc(char *record_name) { struct dbAddr addr; struct dbCommon *precord; long status; /* * Convert name to address */ status = dbNameToAddr(record_name, &addr); if (status == S_db_notFound) printf(" BKPT> Record %s not found\n", record_name); if (status != 0) return(status); precord = addr.precord; /* lock lockset, process record, unlock lockset */ dbScanLock(precord); status = dbProcess(precord); dbScanUnlock(precord); return(status); } #ifdef BKPT_DIAG /* Reset breakpoints */ int dbreset() { epicsMutexUnlock(bkpt_stack_sem); return(0); } #endif base-7.0.3.1/modules/database/src/ioc/db/dbBkpt.h0000664000577000060420000000623213557101274020137 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* dbBkpt.h */ /* * Author: Matthew Needes * Date: 8-30-93 */ #ifndef INCdbBkptsh #define INCdbBkptsh 1 #include "ellLib.h" #include "epicsEvent.h" #include "epicsThread.h" #include "epicsTime.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /* * Structure containing a list of set breakpoints * in a lockset */ struct BP_LIST { ELLNODE *next_list; ELLNODE *prev_list; struct dbCommon *precord; }; /* * Structure containing queue of entrypoints * detected for a lockset. */ struct EP_LIST { ELLNODE *next_list; ELLNODE *prev_list; struct dbCommon *entrypoint; /* pointer to entry point in lockset */ unsigned long count; /* number of times record processed */ epicsTimeStamp time; /* time record first logged */ char sched; /* schedule record for next dbContTask() pass */ }; /* * Structure for stack of lock sets that * currently contain breakpoints. (uses ellLib) */ struct LS_LIST { ELLNODE *next_list; ELLNODE *prev_list; struct dbCommon *precord;/* points to where execution is currently stopped */ struct dbCommon *current_ep; /* current entrypoint */ ELLLIST bp_list; /* list of records containing breakpoints in a lockset */ ELLLIST ep_queue; /* queue of entrypoints found so far */ epicsEventId ex_sem; /* semaphore for execution queue */ epicsThreadId taskid; /* saved taskid for the task in stepping mode */ int step; /* one if currently "stepping," else zero */ unsigned long l_num; /* lockset number */ }; /* Values for BKPT (breakpoint) field in record */ /* 1st bit = 0 if breakpoint is not set, */ /* 1 if breakpoint set */ /* 2nd bit = 0 if no printing after processing */ /* 1 if print after processing set */ /* Breakpoint Masks */ #define BKPT_ON_MASK 0x001 #define BKPT_OFF_MASK 0x0FE #define BKPT_PRINT_MASK 0x002 #define BKPT_PRINT_OFF_MASK 0x0FD #define MAX_EP_COUNT 99999 epicsShareFunc void dbBkptInit(void); epicsShareFunc long dbb(const char *recordname); epicsShareFunc long dbd(const char *recordname); epicsShareFunc long dbc(const char *recordname); epicsShareFunc long dbs(const char *recordname); epicsShareFunc long dbstat(void); epicsShareFunc long dbp( const char *record_name, int interest_level); epicsShareFunc long dbap(const char *record_name); epicsShareFunc int dbBkpt(struct dbCommon *precord); epicsShareFunc void dbPrint(struct dbCommon *precord); epicsShareFunc long dbprc(char *record_name); extern long lset_stack_count; #ifdef __cplusplus } #endif #endif base-7.0.3.1/modules/database/src/ioc/db/dbCAC.h0000664000577000060420000002022313557101274017621 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 * * NOTES: * 1) This interface is preliminary and will change in the future */ #ifndef dbCACh #define dbCACh #ifdef epicsExportSharedSymbols # define dbCACh_restore_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "stdlib.h" #include // std::auto_ptr #include "tsDLList.h" #include "tsFreeList.h" #include "resourceLib.h" #include "cacIO.h" #include "compilerDependencies.h" #ifdef dbCACh_restore_epicsExportSharedSymbols # define epicsExportSharedSymbols # include "shareLib.h" #endif #include "db_access.h" #include "dbNotify.h" #include "dbEvent.h" #include "dbChannel.h" #include "dbLock.h" #include "dbCommon.h" #include "db_convert.h" #include "resourceLib.h" extern "C" int putNotifyPut ( processNotify *ppn, notifyPutType notifyPutType ); extern "C" void putNotifyCompletion ( processNotify *ppn ); class dbContext; class dbChannelIO; class dbPutNotifyBlocker; class dbSubscriptionIO; class dbBaseIO : public chronIntIdRes < dbBaseIO > { public: virtual dbSubscriptionIO * isSubscription () = 0; virtual void show ( epicsGuard < epicsMutex > &, unsigned level ) const = 0; virtual void show ( unsigned level ) const = 0; dbBaseIO (); dbBaseIO ( const dbBaseIO & ); dbBaseIO & operator = ( const dbBaseIO & ); protected: virtual ~dbBaseIO() {} }; extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbChannel *dbch, int eventsRemaining, struct db_field_log *pfl ); class dbSubscriptionIO : public tsDLNode < dbSubscriptionIO >, public dbBaseIO { public: dbSubscriptionIO ( epicsGuard < epicsMutex > &, epicsMutex &, dbContext &, dbChannelIO &, struct dbChannel *, cacStateNotify &, unsigned type, unsigned long count, unsigned mask, dbEventCtx ); void destructor ( CallbackGuard &, epicsGuard < epicsMutex > & ); void unsubscribe ( CallbackGuard &, epicsGuard < epicsMutex > & ); void channelDeleteException ( CallbackGuard &, epicsGuard < epicsMutex > & ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; void show ( unsigned level ) const; void * operator new ( size_t size, tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & )) private: epicsMutex & mutex; unsigned long count; cacStateNotify & notify; dbChannelIO & chan; dbEventSubscription es; unsigned type; unsigned id; dbSubscriptionIO * isSubscription (); friend void dbSubscriptionEventCallback ( void * pPrivate, struct dbChannel * dbch, int eventsRemaining, struct db_field_log * pfl ); dbSubscriptionIO ( const dbSubscriptionIO & ); dbSubscriptionIO & operator = ( const dbSubscriptionIO & ); virtual ~dbSubscriptionIO (); void operator delete ( void * ); }; class dbContext; class dbContextPrivateListOfIO { public: dbContextPrivateListOfIO (); ~dbContextPrivateListOfIO (); private: tsDLList < dbSubscriptionIO > eventq; dbPutNotifyBlocker * pBlocker; friend class dbContext; dbContextPrivateListOfIO ( const dbContextPrivateListOfIO & ); dbContextPrivateListOfIO & operator = ( const dbContextPrivateListOfIO & ); }; class dbContextReadNotifyCacheAllocator { public: dbContextReadNotifyCacheAllocator (); ~dbContextReadNotifyCacheAllocator (); char * alloc ( unsigned long size ); void free ( char * pFree ); void show ( unsigned level ) const; private: struct cacheElem_t { size_t size; struct cacheElem_t * pNext; char buf[1]; }; unsigned long _readNotifyCacheSize; cacheElem_t * _pReadNotifyCache; void reclaimAllCacheEntries (); dbContextReadNotifyCacheAllocator ( const dbContextReadNotifyCacheAllocator & ); dbContextReadNotifyCacheAllocator & operator = ( const dbContextReadNotifyCacheAllocator & ); }; class dbContextReadNotifyCache { public: dbContextReadNotifyCache ( epicsMutex & ); void callReadNotify ( epicsGuard < epicsMutex > &, struct dbChannel * dbch, unsigned type, unsigned long count, cacReadNotify & notify ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; private: dbContextReadNotifyCacheAllocator _allocator; epicsMutex & _mutex; dbContextReadNotifyCache ( const dbContextReadNotifyCache & ); dbContextReadNotifyCache & operator = ( const dbContextReadNotifyCache & ); }; class dbContext : public cacContext { public: dbContext ( epicsMutex & cbMutex, epicsMutex & mutex, cacContextNotify & notify ); virtual ~dbContext (); void destroyChannel ( CallbackGuard &,epicsGuard < epicsMutex > &, dbChannelIO & ); void callReadNotify ( epicsGuard < epicsMutex > &, struct dbChannel * dbch, unsigned type, unsigned long count, cacReadNotify & notify ); void callStateNotify ( struct dbChannel * dbch, unsigned type, unsigned long count, const struct db_field_log * pfl, cacStateNotify & notify ); void subscribe ( epicsGuard < epicsMutex > &, struct dbChannel * dbch, dbChannelIO & chan, unsigned type, unsigned long count, unsigned mask, cacStateNotify & notify, cacChannel::ioid * pId ); void initiatePutNotify ( epicsGuard < epicsMutex > &, dbChannelIO &, struct dbChannel *, unsigned type, unsigned long count, const void * pValue, cacWriteNotify & notify, cacChannel::ioid * pId ); void show ( unsigned level ) const; void showAllIO ( const dbChannelIO & chan, unsigned level ) const; void destroyAllIO ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > &, dbChannelIO & chan ); void ioCancel ( CallbackGuard &, epicsGuard < epicsMutex > &, dbChannelIO & chan, const cacChannel::ioid &id ); void ioShow ( epicsGuard < epicsMutex > &, const cacChannel::ioid & id, unsigned level ) const; private: tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > dbPutNotifyBlockerFreeList; tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > dbSubscriptionIOFreeList; tsFreeList < dbChannelIO, 256, epicsMutexNOOP > dbChannelIOFreeList; chronIntIdResTable < dbBaseIO > ioTable; dbContextReadNotifyCache readNotifyCache; dbEventCtx ctx; unsigned long stateNotifyCacheSize; epicsMutex & mutex; epicsMutex & cbMutex; cacContextNotify & notify; std::auto_ptr < cacContext > pNetContext; char * pStateNotifyCache; bool isolated; cacChannel & createChannel ( epicsGuard < epicsMutex > &, const char * pChannelName, cacChannelNotify &, cacChannel::priLev ); void flush ( epicsGuard < epicsMutex > & ); unsigned circuitCount ( epicsGuard < epicsMutex > & ) const; void selfTest ( epicsGuard < epicsMutex > & ) const; unsigned beaconAnomaliesSinceProgramStart ( epicsGuard < epicsMutex > & ) const; void show ( epicsGuard < epicsMutex > &, unsigned level ) const; dbContext ( const dbContext & ); dbContext & operator = ( const dbContext & ); }; inline dbContextPrivateListOfIO::dbContextPrivateListOfIO () : pBlocker ( 0 ) { } inline dbContextPrivateListOfIO::~dbContextPrivateListOfIO () { assert ( ! this->pBlocker ); } inline void dbContext::callReadNotify ( epicsGuard < epicsMutex > & guard, struct dbChannel * dbch, unsigned type, unsigned long count, cacReadNotify & notifyIn ) { guard.assertIdenticalMutex ( this-> mutex ); this->readNotifyCache.callReadNotify ( guard, dbch, type, count, notifyIn ); } #endif // dbCACh base-7.0.3.1/modules/database/src/ioc/db/dbCa.c0000664000577000060420000011127213557101274017556 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Authors: Bob Dalesio and Marty Kraimer * Date: 26MAR96 */ #define EPICS_DBCA_PRIVATE_API #include #include #include #include #include #include "alarm.h" #include "cantProceed.h" #include "dbDefs.h" #include "epicsAssert.h" #include "epicsEvent.h" #include "epicsExit.h" #include "epicsMutex.h" #include "epicsPrint.h" #include "epicsString.h" #include "epicsThread.h" #include "epicsAtomic.h" #include "epicsTime.h" #include "errlog.h" #include "errMdef.h" #include "taskwd.h" #include "cadef.h" /* We can't include dbStaticLib.h here */ #define dbCalloc(nobj,size) callocMustSucceed(nobj,size,"dbCalloc") #define epicsExportSharedSymbols #include "db_access_routines.h" #include "dbCa.h" #include "dbCaPvt.h" #include "dbCommon.h" #include "db_convert.h" #include "dbLink.h" #include "dbLock.h" #include "dbScan.h" #include "link.h" #include "recGbl.h" #include "recSup.h" /* defined in dbContext.cpp * Setup local CA access */ extern void dbServiceIOInit(); extern int dbServiceIsolate; static ELLLIST workList = ELLLIST_INIT; /* Work list for dbCaTask */ static epicsMutexId workListLock; /*Mutual exclusions semaphores for workList*/ static epicsEventId workListEvent; /*wakeup event for dbCaTask*/ static int removesOutstanding = 0; #define removesOutstandingWarning 10000 static volatile enum dbCaCtl_t { ctlInit, ctlRun, ctlPause, ctlExit } dbCaCtl; static epicsEventId startStopEvent; static epicsThreadId dbCaWorker; struct ca_client_context * dbCaClientContext; /* Forward declarations */ static void dbCaTask(void *); static lset dbCa_lset; #define printLinks(pcaLink) \ errlogPrintf("%s has DB CA link to %s\n",\ pcaLink->plink->precord->name, pcaLink->pvname) static int dbca_chan_count; /* caLink locking * * Lock ordering: * dbScanLock -> caLink.lock -> workListLock * * workListLock: * Guards access to workList. * * dbScanLock: * All dbCa* functions operating on a single link may only be called when * the record containing the DBLINK is locked. Including: * dbCaGet*() * isConnected() * dbCaPutLink() * scanForward() * dbCaAddLinkCallback() * dbCaRemoveLink() * * Guard the pointer plink.value.pv_link.pvt, but not the struct caLink * which is pointed to. * * caLink.lock: * Guards the caLink structure (but not the struct DBLINK) * * The dbCaTask only locks caLink, and must not lock the record (a violation of lock order). * * During link modification or IOC shutdown the pca->plink pointer (guarded by caLink.lock) * is used as a flag to indicate that a link is no longer active. * * References to the struct caLink are owned by the dbCaTask, and any scanOnceCallback() * which is in progress. * * The libca and scanOnceCallback callbacks take no action if pca->plink==NULL. * * dbCaPutLinkCallback causes an additional complication because * when dbCaRemoveLink is called the callback may not have occured. * If putComplete sees plink==0 it will not call the user's code. * If pca->putCallback is non-zero, dbCaTask will call the * user's callback AFTER it has called ca_clear_channel. * Thus the user's callback will get called exactly once. */ static void addAction(caLink *pca, short link_action) { int callAdd; epicsMutexMustLock(workListLock); callAdd = (pca->link_action == 0); if (pca->link_action & CA_CLEAR_CHANNEL) { errlogPrintf("dbCa::addAction %d with CA_CLEAR_CHANNEL set\n", link_action); printLinks(pca); link_action = 0; } if (link_action & CA_CLEAR_CHANNEL) { if (++removesOutstanding >= removesOutstandingWarning) { errlogPrintf("dbCa::addAction pausing, %d channels to clear\n", removesOutstanding); } while (removesOutstanding >= removesOutstandingWarning) { epicsMutexUnlock(workListLock); epicsThreadSleep(1.0); epicsMutexMustLock(workListLock); } } pca->link_action |= link_action; if (callAdd) ellAdd(&workList, &pca->node); epicsMutexUnlock(workListLock); if (callAdd) epicsEventSignal(workListEvent); } static void caLinkInc(caLink *pca) { assert(epicsAtomicGetIntT(&pca->refcount)>0); epicsAtomicIncrIntT(&pca->refcount); } static void caLinkDec(caLink *pca) { int cnt; dbCaCallback callback; void *userPvt = 0; cnt = epicsAtomicDecrIntT(&pca->refcount); assert(cnt>=0); if(cnt>0) return; if (pca->chid) { ca_clear_channel(pca->chid); --dbca_chan_count; } callback = pca->putCallback; if (callback) { userPvt = pca->putUserPvt; pca->putCallback = 0; pca->putType = 0; } free(pca->pgetNative); free(pca->pputNative); free(pca->pgetString); free(pca->pputString); free(pca->pvname); epicsMutexDestroy(pca->lock); free(pca); if (callback) callback(userPvt); } /* Block until worker thread has processed all previously queued actions. * Does not prevent additional actions from being queued. */ void dbCaSync(void) { epicsEventId wake; caLink templink; /* we only partially initialize templink. * It has no link field and no subscription * so the worker must handle it early */ memset(&templink, 0, sizeof(templink)); templink.refcount = 1; wake = epicsEventMustCreate(epicsEventEmpty); templink.lock = epicsMutexMustCreate(); templink.userPvt = wake; addAction(&templink, CA_SYNC); epicsEventMustWait(wake); /* Worker holds workListLock when calling epicsEventMustTrigger() * we cycle through 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); } epicsShareFunc unsigned long dbCaGetUpdateCount(struct link *plink) { caLink *pca = (caLink *)plink->value.pv_link.pvt; unsigned long ret; if (!pca) return (unsigned long)-1; epicsMutexMustLock(pca->lock); ret = pca->nUpdate; epicsMutexUnlock(pca->lock); return ret; } void dbCaCallbackProcess(void *userPvt) { struct link *plink = (struct link *)userPvt; dbLinkAsyncComplete(plink); } void dbCaShutdown(void) { enum dbCaCtl_t cur = dbCaCtl; assert(cur == ctlRun || cur == ctlPause); dbCaCtl = ctlExit; epicsEventSignal(workListEvent); epicsEventMustWait(startStopEvent); if(dbCaWorker) epicsThreadMustJoin(dbCaWorker); } static void dbCaLinkInitImpl(int isolate) { epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; opts.stackSize = epicsThreadGetStackSize(epicsThreadStackBig); opts.priority = epicsThreadPriorityMedium; opts.joinable = 1; dbServiceIsolate = isolate; dbServiceIOInit(); if (!workListLock) workListLock = epicsMutexMustCreate(); if (!workListEvent) workListEvent = epicsEventMustCreate(epicsEventEmpty); if(!startStopEvent) startStopEvent = epicsEventMustCreate(epicsEventEmpty); dbCaCtl = ctlPause; dbCaWorker = epicsThreadCreateOpt("dbCaLink", dbCaTask, NULL, &opts); /* wait for worker to startup and initialize dbCaClientContext */ epicsEventMustWait(startStopEvent); } void dbCaLinkInitIsolated(void) { dbCaLinkInitImpl(1); } void dbCaLinkInit(void) { dbCaLinkInitImpl(0); } void dbCaRun(void) { if (dbCaCtl == ctlPause) { dbCaCtl = ctlRun; epicsEventSignal(workListEvent); } } void dbCaPause(void) { if (dbCaCtl == ctlRun) { dbCaCtl = ctlPause; epicsEventSignal(workListEvent); } } void dbCaAddLinkCallback(struct link *plink, dbCaCallback connect, dbCaCallback monitor, void *userPvt) { caLink *pca; assert(!plink->value.pv_link.pvt); pca = (caLink *)dbCalloc(1, sizeof(caLink)); pca->refcount = 1; pca->lock = epicsMutexMustCreate(); pca->plink = plink; pca->pvname = epicsStrDup(plink->value.pv_link.pvname); pca->connect = connect; pca->monitor = monitor; pca->userPvt = userPvt; epicsMutexMustLock(pca->lock); plink->lset = &dbCa_lset; plink->type = CA_LINK; plink->value.pv_link.pvt = pca; addAction(pca, CA_CONNECT); epicsMutexUnlock(pca->lock); } long dbCaAddLink(struct dbLocker *locker, struct link *plink, short dbfType) { dbCaAddLinkCallback(plink, 0, 0, NULL); return 0; } void dbCaRemoveLink(struct dbLocker *locker, struct link *plink) { caLink *pca = (caLink *)plink->value.pv_link.pvt; if (!pca) return; epicsMutexMustLock(pca->lock); pca->plink = 0; plink->value.pv_link.pvt = 0; plink->value.pv_link.pvlMask = 0; plink->type = PV_LINK; plink->lset = NULL; /* Unlock before addAction or dbCaTask might free first */ epicsMutexUnlock(pca->lock); addAction(pca, CA_CLEAR_CHANNEL); } long dbCaGetLink(struct link *plink, short dbrType, void *pdest, long *nelements) { caLink *pca = (caLink *)plink->value.pv_link.pvt; long status = 0; short link_action = 0; int newType; assert(pca); epicsMutexMustLock(pca->lock); assert(pca->plink); if (!pca->isConnected || !pca->hasReadAccess) { pca->sevr = INVALID_ALARM; pca->stat = LINK_ALARM; status = -1; goto done; } if (pca->dbrType == DBR_ENUM && dbDBRnewToDBRold[dbrType] == DBR_STRING){ long (*fConvert)(const void *from, void *to, struct dbAddr *paddr); /* Subscribe as DBR_STRING */ if (!pca->pgetString) { plink->value.pv_link.pvlMask |= pvlOptInpString; link_action |= CA_MONITOR_STRING; } if (!pca->gotInString) { pca->sevr = INVALID_ALARM; pca->stat = LINK_ALARM; status = -1; goto done; } if (nelements) *nelements = 1; fConvert = dbFastGetConvertRoutine[dbDBRoldToDBFnew[DBR_STRING]][dbrType]; status = fConvert(pca->pgetString, pdest, 0); goto done; } if (!pca->pgetNative) { plink->value.pv_link.pvlMask |= pvlOptInpNative; link_action |= CA_MONITOR_NATIVE; } if (!pca->gotInNative){ pca->sevr = INVALID_ALARM; pca->stat = LINK_ALARM; status = -1; goto done; } newType = dbDBRoldToDBFnew[pca->dbrType]; if (!nelements || *nelements == 1) { long (*fConvert)(const void *from, void *to, struct dbAddr *paddr); fConvert = dbFastGetConvertRoutine[newType][dbrType]; assert(pca->pgetNative); status = fConvert(pca->pgetNative, pdest, 0); } else { unsigned long ntoget = *nelements; struct dbAddr dbAddr; long (*aConvert)(struct dbAddr *paddr, void *to, long nreq, long nto, long off); aConvert = dbGetConvertRoutine[newType][dbrType]; assert(pca->pgetNative); if (ntoget > pca->usedelements) ntoget = pca->usedelements; *nelements = ntoget; memset((void *)&dbAddr, 0, sizeof(dbAddr)); dbAddr.pfield = pca->pgetNative; /*Following will only be used for pca->dbrType == DBR_STRING*/ dbAddr.field_size = MAX_STRING_SIZE; /*Ignore error return*/ aConvert(&dbAddr, pdest, ntoget, ntoget, 0); } done: if (link_action) addAction(pca, link_action); if (!status) recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode, plink->precord, pca->stat, pca->sevr); epicsMutexUnlock(pca->lock); return status; } static long dbCaPutAsync(struct link *plink,short dbrType, const void *pbuffer,long nRequest) { return dbCaPutLinkCallback(plink, dbrType, pbuffer, nRequest, dbCaCallbackProcess, plink); } long dbCaPutLinkCallback(struct link *plink,short dbrType, const void *pbuffer,long nRequest,dbCaCallback callback,void *userPvt) { caLink *pca = (caLink *)plink->value.pv_link.pvt; long status = 0; short link_action = 0; assert(pca); /* put the new value in */ epicsMutexMustLock(pca->lock); assert(pca->plink); if (!pca->isConnected || !pca->hasWriteAccess) { epicsMutexUnlock(pca->lock); return -1; } if (pca->dbrType == DBR_ENUM && dbDBRnewToDBRold[dbrType] == DBR_STRING) { long (*fConvert)(const void *from, void *to, struct dbAddr *paddr); /* Send as DBR_STRING */ if (!pca->pputString) { pca->pputString = dbCalloc(1, MAX_STRING_SIZE); /* Disabled by ANJ, needs a link flag to allow user to control this. * Setting these makes the reconnect callback re-do the last CA put. plink->value.pv_link.pvlMask |= pvlOptOutString; */ } fConvert = dbFastPutConvertRoutine[dbrType][dbDBRoldToDBFnew[DBR_STRING]]; status = fConvert(pbuffer, pca->pputString, 0); link_action |= CA_WRITE_STRING; pca->gotOutString = TRUE; if (pca->newOutString) pca->nNoWrite++; pca->newOutString = TRUE; } else { int newType = dbDBRoldToDBFnew[pca->dbrType]; if (!pca->pputNative) { pca->pputNative = dbCalloc(pca->nelements, dbr_value_size[ca_field_type(pca->chid)]); pca->putnelements = 0; /* Fixed and disabled by ANJ, see comment above. plink->value.pv_link.pvlMask |= pvlOptOutNative; */ } if (nRequest == 1 && pca->nelements==1){ long (*fConvert)(const void *from, void *to, struct dbAddr *paddr); fConvert = dbFastPutConvertRoutine[dbrType][newType]; status = fConvert(pbuffer, pca->pputNative, 0); pca->putnelements = 1; } else { struct dbAddr dbAddr; long (*aConvert)(struct dbAddr *paddr, const void *from, long nreq, long nfrom, long off); aConvert = dbPutConvertRoutine[dbrType][newType]; memset((void *)&dbAddr, 0, sizeof(dbAddr)); dbAddr.pfield = pca->pputNative; /*Following only used for DBF_STRING*/ dbAddr.field_size = MAX_STRING_SIZE; if(nRequest>pca->nelements) nRequest = pca->nelements; status = aConvert(&dbAddr, pbuffer, nRequest, pca->nelements, 0); pca->putnelements = nRequest; } link_action |= CA_WRITE_NATIVE; pca->gotOutNative = TRUE; if (pca->newOutNative) pca->nNoWrite++; pca->newOutNative = TRUE; } if (callback) { pca->putType = CA_PUT_CALLBACK; pca->putCallback = callback; pca->putUserPvt = userPvt; } else { pca->putType = CA_PUT; pca->putCallback = 0; } addAction(pca, link_action); epicsMutexUnlock(pca->lock); return status; } long dbCaPutLink(struct link *plink, short dbrType, const void *pbuffer, long nRequest) { return dbCaPutLinkCallback(plink, dbrType, pbuffer, nRequest, 0, NULL); } static int isConnected(const struct link *plink) { caLink *pca; if (!plink || plink->type != CA_LINK) return FALSE; pca = (caLink *)plink->value.pv_link.pvt; if (!pca || !pca->chid) return FALSE; return pca->isConnected; } static void scanForward(struct link *plink) { short fwdLinkValue = 1; if (plink->value.pv_link.pvlMask & pvlOptFWD) dbCaPutLink(plink, DBR_SHORT, &fwdLinkValue, 1); } #define pcaGetCheck \ assert(plink); \ if (plink->type != CA_LINK) return -1; \ pca = (caLink *)plink->value.pv_link.pvt; \ assert(pca); \ epicsMutexMustLock(pca->lock); \ assert(pca->plink); \ if (!pca->isConnected) { \ epicsMutexUnlock(pca->lock); \ return -1; \ } static long getElements(const struct link *plink, long *nelements) { caLink *pca; pcaGetCheck *nelements = pca->nelements; epicsMutexUnlock(pca->lock); return 0; } static long getAlarm(const struct link *plink, epicsEnum16 *pstat, epicsEnum16 *psevr) { caLink *pca; pcaGetCheck if (pstat) *pstat = pca->stat; if (psevr) *psevr = pca->sevr; epicsMutexUnlock(pca->lock); return 0; } static long getTimeStamp(const struct link *plink, epicsTimeStamp *pstamp) { caLink *pca; pcaGetCheck memcpy(pstamp, &pca->timeStamp, sizeof(epicsTimeStamp)); epicsMutexUnlock(pca->lock); return 0; } static int getDBFtype(const struct link *plink) { caLink *pca; int type; pcaGetCheck type = dbDBRoldToDBFnew[pca->dbrType]; epicsMutexUnlock(pca->lock); return type; } long dbCaGetAttributes(const struct link *plink, dbCaCallback callback,void *userPvt) { caLink *pca; int gotAttributes; assert(plink); if (plink->type != CA_LINK) return -1; pca = (caLink *)plink->value.pv_link.pvt; assert(pca); epicsMutexMustLock(pca->lock); assert(pca->plink); pca->getAttributes = callback; pca->getAttributesPvt = userPvt; gotAttributes = pca->gotAttributes; epicsMutexUnlock(pca->lock); if (gotAttributes && callback) callback(userPvt); return 0; } static long getControlLimits(const struct link *plink, double *low, double *high) { caLink *pca; int gotAttributes; pcaGetCheck gotAttributes = pca->gotAttributes; if (gotAttributes) { *low = pca->controlLimits[0]; *high = pca->controlLimits[1]; } epicsMutexUnlock(pca->lock); return gotAttributes ? 0 : -1; } static long getGraphicLimits(const struct link *plink, double *low, double *high) { caLink *pca; int gotAttributes; pcaGetCheck gotAttributes = pca->gotAttributes; if (gotAttributes) { *low = pca->displayLimits[0]; *high = pca->displayLimits[1]; } epicsMutexUnlock(pca->lock); return gotAttributes ? 0 : -1; } static long getAlarmLimits(const struct link *plink, double *lolo, double *low, double *high, double *hihi) { caLink *pca; int gotAttributes; pcaGetCheck gotAttributes = pca->gotAttributes; if (gotAttributes) { *lolo = pca->alarmLimits[0]; *low = pca->alarmLimits[1]; *high = pca->alarmLimits[2]; *hihi = pca->alarmLimits[3]; } epicsMutexUnlock(pca->lock); return gotAttributes ? 0 : -1; } static long getPrecision(const struct link *plink, short *precision) { caLink *pca; int gotAttributes; pcaGetCheck gotAttributes = pca->gotAttributes; if (gotAttributes) *precision = pca->precision; epicsMutexUnlock(pca->lock); return gotAttributes ? 0 : -1; } static long getUnits(const struct link *plink, char *units, int unitsSize) { caLink *pca; int gotAttributes; pcaGetCheck gotAttributes = pca->gotAttributes; if (unitsSize > sizeof(pca->units)) unitsSize = sizeof(pca->units); if (gotAttributes) strncpy(units, pca->units, unitsSize); units[unitsSize-1] = 0; epicsMutexUnlock(pca->lock); return gotAttributes ? 0 : -1; } static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) { caLink *pca; long status; assert(plink); if (plink->type != CA_LINK) return -1; pca = (caLink *)plink->value.pv_link.pvt; assert(pca); epicsMutexMustLock(pca->lock); assert(pca->plink); status = rtn(plink, priv); epicsMutexUnlock(pca->lock); return status; } static void scanComplete(void *raw, dbCommon *prec) { caLink *pca = raw; epicsMutexMustLock(pca->lock); if(!pca->plink) { /* IOC shutdown or link re-targeted. Do nothing. */ } else if(pca->scanningOnce==0) { errlogPrintf("dbCa.c complete callback w/ scanningOnce==0\n"); } else if(--pca->scanningOnce){ /* another scan is queued */ if(scanOnceCallback(prec, scanComplete, raw)) { errlogPrintf("dbCa.c failed to re-queue scanOnce\n"); } else caLinkInc(pca); } epicsMutexUnlock(pca->lock); caLinkDec(pca); } /* must be called with pca->lock held */ static void scanLinkOnce(dbCommon *prec, caLink *pca) { if(pca->scanningOnce==0) { if(scanOnceCallback(prec, scanComplete, pca)) { errlogPrintf("dbCa.c failed to queue scanOnce\n"); } else caLinkInc(pca); } if(pca->scanningOnce<5) pca->scanningOnce++; /* else too many scans queued */ } static lset dbCa_lset = { 0, 1, /* not Constant, Volatile */ NULL, dbCaRemoveLink, NULL, NULL, NULL, isConnected, getDBFtype, getElements, dbCaGetLink, getControlLimits, getGraphicLimits, getAlarmLimits, getPrecision, getUnits, getAlarm, getTimeStamp, dbCaPutLink, dbCaPutAsync, scanForward, doLocked }; static void connectionCallback(struct connection_handler_args arg) { caLink *pca; short link_action = 0; struct link *plink; pca = ca_puser(arg.chid); assert(pca); epicsMutexMustLock(pca->lock); plink = pca->plink; if (!plink) goto done; pca->isConnected = (ca_state(arg.chid) == cs_conn); if (!pca->isConnected) { struct pv_link *ppv_link = &plink->value.pv_link; dbCommon *precord = plink->precord; pca->nDisconnect++; if (precord && ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))) scanLinkOnce(precord, pca); goto done; } pca->hasReadAccess = ca_read_access(arg.chid); pca->hasWriteAccess = ca_write_access(arg.chid); if (pca->gotFirstConnection) { if (pca->nelements != ca_element_count(arg.chid) || pca->dbrType != ca_field_type(arg.chid)) { /* Size or type changed, clear everything and let the next call to dbCaGetLink() and/or dbCaPutLink() reset everything */ if (pca->evidNative) { ca_clear_event(pca->evidNative); pca->evidNative = 0; } if (pca->evidString) { ca_clear_event(pca->evidString); pca->evidString = 0; } plink->value.pv_link.pvlMask &= ~(pvlOptInpNative | pvlOptInpString | pvlOptOutNative | pvlOptOutString); pca->gotInNative = 0; pca->gotOutNative = 0; pca->gotInString = 0; pca->gotOutString = 0; free(pca->pgetNative); pca->pgetNative = 0; free(pca->pgetString); pca->pgetString = 0; free(pca->pputNative); pca->pputNative = 0; free(pca->pputString); pca->pputString = 0; } } pca->gotFirstConnection = TRUE; pca->nelements = ca_element_count(arg.chid); pca->usedelements = 0; pca->dbrType = ca_field_type(arg.chid); if ((plink->value.pv_link.pvlMask & pvlOptInpNative) && !pca->pgetNative) { link_action |= CA_MONITOR_NATIVE; } if ((plink->value.pv_link.pvlMask & pvlOptInpString) && !pca->pgetString) { link_action |= CA_MONITOR_STRING; } if ((plink->value.pv_link.pvlMask & pvlOptOutNative) && pca->gotOutNative) { link_action |= CA_WRITE_NATIVE; } if ((plink->value.pv_link.pvlMask & pvlOptOutString) && pca->gotOutString) { link_action |= CA_WRITE_STRING; } pca->gotAttributes = 0; if (pca->dbrType != DBR_STRING) { link_action |= CA_GET_ATTRIBUTES; } done: if (link_action) addAction(pca, link_action); epicsMutexUnlock(pca->lock); } static void eventCallback(struct event_handler_args arg) { caLink *pca = (caLink *)arg.usr; struct link *plink; size_t size; dbCommon *precord = 0; struct dbr_time_double *pdbr_time_double; dbCaCallback monitor = 0; void *userPvt = 0; int doScan = 1; assert(pca); epicsMutexMustLock(pca->lock); plink = pca->plink; if (!plink) goto done; pca->nUpdate++; monitor = pca->monitor; userPvt = pca->userPvt; precord = plink->precord; if (arg.status != ECA_NORMAL) { if (precord) { if (arg.status != ECA_NORDACCESS && arg.status != ECA_GETFAIL) errlogPrintf("dbCa: eventCallback record %s error %s\n", precord->name, ca_message(arg.status)); } else { errlogPrintf("dbCa: eventCallback error %s\n", ca_message(arg.status)); } goto done; } assert(arg.dbr); assert(arg.count<=pca->nelements); size = arg.count * dbr_value_size[arg.type]; if (arg.type == DBR_TIME_STRING && ca_field_type(pca->chid) == DBR_ENUM) { assert(pca->pgetString); memcpy(pca->pgetString, dbr_value_ptr(arg.dbr, arg.type), size); pca->gotInString = TRUE; } else switch (arg.type){ case DBR_TIME_ENUM: /* Disable the record scan if we also have a string monitor */ doScan = !(plink->value.pv_link.pvlMask & pvlOptInpString); /* fall through */ case DBR_TIME_STRING: case DBR_TIME_SHORT: case DBR_TIME_FLOAT: case DBR_TIME_CHAR: case DBR_TIME_LONG: case DBR_TIME_DOUBLE: assert(pca->pgetNative); memcpy(pca->pgetNative, dbr_value_ptr(arg.dbr, arg.type), size); pca->usedelements = arg.count; pca->gotInNative = TRUE; break; default: errlogPrintf("dbCa: eventCallback Logic Error. dbr=%ld dbf=%d\n", arg.type, ca_field_type(pca->chid)); break; } pdbr_time_double = (struct dbr_time_double *)arg.dbr; pca->sevr = pdbr_time_double->severity; pca->stat = pdbr_time_double->status; memcpy(&pca->timeStamp, &pdbr_time_double->stamp, sizeof(epicsTimeStamp)); if (doScan && precord) { struct pv_link *ppv_link = &plink->value.pv_link; if ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)) scanLinkOnce(precord, pca); } done: epicsMutexUnlock(pca->lock); if (monitor) monitor(userPvt); } static void exceptionCallback(struct exception_handler_args args) { const char *context = (args.ctx ? args.ctx : "unknown"); errlogPrintf("DB CA Link Exception: \"%s\", context \"%s\"\n", ca_message(args.stat), context); if (args.chid) { errlogPrintf( "DB CA Link Exception: channel \"%s\"\n", ca_name(args.chid)); if (ca_state(args.chid) == cs_conn) { errlogPrintf( "DB CA Link Exception: native T=%s, request T=%s," " native N=%ld, request N=%ld, " " access rights {%s%s}\n", dbr_type_to_text(ca_field_type(args.chid)), dbr_type_to_text(args.type), ca_element_count(args.chid), args.count, ca_read_access(args.chid) ? "R" : "", ca_write_access(args.chid) ? "W" : ""); } } } static void putComplete(struct event_handler_args arg) { caLink *pca = (caLink *)arg.usr; struct link *plink; dbCaCallback callback = 0; void *userPvt = 0; epicsMutexMustLock(pca->lock); plink = pca->plink; if (!plink) goto done; callback = pca->putCallback; userPvt = pca->putUserPvt; pca->putCallback = 0; pca->putType = 0; pca->putUserPvt = 0; done: epicsMutexUnlock(pca->lock); if (callback) callback(userPvt); } static void accessRightsCallback(struct access_rights_handler_args arg) { caLink *pca = (caLink *)ca_puser(arg.chid); struct link *plink; struct pv_link *ppv_link; dbCommon *precord; assert(pca); if (ca_state(pca->chid) != cs_conn) return; /* connectionCallback will handle */ epicsMutexMustLock(pca->lock); plink = pca->plink; if (!plink) goto done; pca->hasReadAccess = ca_read_access(arg.chid); pca->hasWriteAccess = ca_write_access(arg.chid); if (pca->hasReadAccess && pca->hasWriteAccess) goto done; ppv_link = &plink->value.pv_link; precord = plink->precord; if (precord && ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))) scanLinkOnce(precord, pca); done: epicsMutexUnlock(pca->lock); } static void getAttribEventCallback(struct event_handler_args arg) { caLink *pca = (caLink *)arg.usr; struct link *plink; struct dbr_ctrl_double *pdbr; dbCaCallback connect = 0; void *userPvt = 0; dbCaCallback getAttributes = 0; void *getAttributesPvt; assert(pca); epicsMutexMustLock(pca->lock); plink = pca->plink; if (!plink) { epicsMutexUnlock(pca->lock); return; } connect = pca->connect; userPvt = pca->userPvt; getAttributes = pca->getAttributes; getAttributesPvt = pca->getAttributesPvt; if (arg.status != ECA_NORMAL) { dbCommon *precord = plink->precord; if (precord) { errlogPrintf("dbCa: getAttribEventCallback record %s error %s\n", precord->name, ca_message(arg.status)); } else { errlogPrintf("dbCa: getAttribEventCallback error %s\n", ca_message(arg.status)); } epicsMutexUnlock(pca->lock); return; } assert(arg.dbr); pdbr = (struct dbr_ctrl_double *)arg.dbr; pca->gotAttributes = TRUE; pca->controlLimits[0] = pdbr->lower_ctrl_limit; pca->controlLimits[1] = pdbr->upper_ctrl_limit; pca->displayLimits[0] = pdbr->lower_disp_limit; pca->displayLimits[1] = pdbr->upper_disp_limit; pca->alarmLimits[0] = pdbr->lower_alarm_limit; pca->alarmLimits[1] = pdbr->lower_warning_limit; pca->alarmLimits[2] = pdbr->upper_warning_limit; pca->alarmLimits[3] = pdbr->upper_alarm_limit; pca->precision = pdbr->precision; memcpy(pca->units, pdbr->units, MAX_UNITS_SIZE); epicsMutexUnlock(pca->lock); if (getAttributes) getAttributes(getAttributesPvt); if (connect) connect(userPvt); } static void dbCaTask(void *arg) { taskwdInsert(0, NULL, NULL); SEVCHK(ca_context_create(ca_enable_preemptive_callback), "dbCaTask calling ca_context_create"); dbCaClientContext = ca_current_context (); SEVCHK(ca_add_exception_event(exceptionCallback,NULL), "ca_add_exception_event"); epicsEventSignal(startStopEvent); /* channel access event loop */ while (TRUE){ do { epicsEventMustWait(workListEvent); } while (dbCaCtl == ctlPause); while (TRUE) { /* process all requests in workList*/ caLink *pca; short link_action; int status; epicsMutexMustLock(workListLock); if (!(pca = (caLink *)ellGet(&workList))){ /* Take off list head */ epicsMutexUnlock(workListLock); if (dbCaCtl == ctlExit) goto shutdown; break; /* workList is empty */ } link_action = pca->link_action; if (link_action&CA_SYNC) epicsEventMustTrigger((epicsEventId)pca->userPvt); /* dbCaSync() requires workListLock to be held here */ pca->link_action = 0; if (link_action & CA_CLEAR_CHANNEL) --removesOutstanding; epicsMutexUnlock(workListLock); /* Give back immediately */ if (link_action&CA_SYNC) continue; if (link_action & CA_CLEAR_CHANNEL) { /* This must be first */ caLinkDec(pca); /* No alarm is raised. Since link is changing so what? */ continue; /* No other link_action makes sense */ } if (link_action & CA_CONNECT) { status = ca_create_channel( pca->pvname,connectionCallback,(void *)pca, CA_PRIORITY_DB_LINKS, &(pca->chid)); if (status != ECA_NORMAL) { errlogPrintf("dbCaTask ca_create_channel %s\n", ca_message(status)); printLinks(pca); continue; } dbca_chan_count++; status = ca_replace_access_rights_event(pca->chid, accessRightsCallback); if (status != ECA_NORMAL) { errlogPrintf("dbCaTask replace_access_rights_event %s\n", ca_message(status)); printLinks(pca); } continue; /*Other options must wait until connect*/ } if (ca_state(pca->chid) != cs_conn) continue; if (link_action & CA_WRITE_NATIVE) { assert(pca->pputNative); if (pca->putType == CA_PUT) { status = ca_array_put( pca->dbrType, pca->putnelements, pca->chid, pca->pputNative); } else if (pca->putType==CA_PUT_CALLBACK) { status = ca_array_put_callback( pca->dbrType, pca->putnelements, pca->chid, pca->pputNative, putComplete, pca); } else { status = ECA_PUTFAIL; } if (status != ECA_NORMAL) { errlogPrintf("dbCaTask ca_array_put %s\n", ca_message(status)); printLinks(pca); } epicsMutexMustLock(pca->lock); if (status == ECA_NORMAL) pca->newOutNative = FALSE; epicsMutexUnlock(pca->lock); } if (link_action & CA_WRITE_STRING) { assert(pca->pputString); if (pca->putType == CA_PUT) { status = ca_array_put( DBR_STRING, 1, pca->chid, pca->pputString); } else if (pca->putType==CA_PUT_CALLBACK) { status = ca_array_put_callback( DBR_STRING, 1, pca->chid, pca->pputString, putComplete, pca); } else { status = ECA_PUTFAIL; } if (status != ECA_NORMAL) { errlogPrintf("dbCaTask ca_array_put %s\n", ca_message(status)); printLinks(pca); } epicsMutexMustLock(pca->lock); if (status == ECA_NORMAL) pca->newOutString = FALSE; epicsMutexUnlock(pca->lock); } /*CA_GET_ATTRIBUTES before CA_MONITOR so that attributes available * before the first monitor callback */ if (link_action & CA_GET_ATTRIBUTES) { status = ca_get_callback(DBR_CTRL_DOUBLE, pca->chid, getAttribEventCallback, pca); if (status != ECA_NORMAL) { errlogPrintf("dbCaTask ca_get_callback %s\n", ca_message(status)); printLinks(pca); } } if (link_action & CA_MONITOR_NATIVE) { epicsMutexMustLock(pca->lock); pca->elementSize = dbr_value_size[ca_field_type(pca->chid)]; pca->pgetNative = dbCalloc(pca->nelements, pca->elementSize); epicsMutexUnlock(pca->lock); status = ca_add_array_event( dbf_type_to_DBR_TIME(ca_field_type(pca->chid)), 0, /* dynamic size */ pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, &pca->evidNative); if (status != ECA_NORMAL) { errlogPrintf("dbCaTask ca_add_array_event %s\n", ca_message(status)); printLinks(pca); } } if (link_action & CA_MONITOR_STRING) { epicsMutexMustLock(pca->lock); pca->pgetString = dbCalloc(1, MAX_STRING_SIZE); epicsMutexUnlock(pca->lock); status = ca_add_array_event(DBR_TIME_STRING, 1, pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, &pca->evidString); if (status != ECA_NORMAL) { errlogPrintf("dbCaTask ca_add_array_event %s\n", ca_message(status)); printLinks(pca); } } } SEVCHK(ca_flush_io(), "dbCaTask"); } shutdown: taskwdRemove(0); if (dbca_chan_count == 0) ca_context_destroy(); else fprintf(stderr, "dbCa: chan_count = %d at shutdown\n", dbca_chan_count); epicsEventSignal(startStopEvent); } base-7.0.3.1/modules/database/src/ioc/db/dbCa.h0000664000577000060420000000571613557101274017570 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbCa.h */ #ifndef INCdbCah #define INCdbCah #include "dbLink.h" #ifdef __cplusplus extern "C" { #endif typedef void (*dbCaCallback)(void *userPvt); epicsShareFunc void dbCaCallbackProcess(void *usrPvt); epicsShareFunc void dbCaLinkInit(void); /* internal initialization for iocBuild() */ epicsShareFunc void dbCaLinkInitIsolated(void); /* internal initialization for iocBuildIsolated() */ epicsShareFunc void dbCaRun(void); epicsShareFunc void dbCaPause(void); epicsShareFunc void dbCaShutdown(void); struct dbLocker; epicsShareFunc void dbCaAddLinkCallback(struct link *plink, dbCaCallback connect, dbCaCallback monitor, void *userPvt); epicsShareFunc long dbCaAddLink(struct dbLocker *locker, struct link *plink, short dbfType); epicsShareFunc void dbCaRemoveLink(struct dbLocker *locker, struct link *plink); epicsShareFunc long dbCaGetLink(struct link *plink, short dbrType, void *pbuffer, long *nRequest); epicsShareFunc long dbCaGetAttributes(const struct link *plink, dbCaCallback callback, void *userPvt); epicsShareFunc long dbCaPutLinkCallback(struct link *plink, short dbrType, const void *pbuffer,long nRequest, dbCaCallback callback, void *userPvt); epicsShareFunc long dbCaPutLink(struct link *plink,short dbrType, const void *pbuffer,long nRequest); extern struct ca_client_context * dbCaClientContext; #ifdef EPICS_DBCA_PRIVATE_API epicsShareFunc void dbCaSync(void); epicsShareFunc unsigned long dbCaGetUpdateCount(struct link *plink); #endif /* These macros are for backwards compatibility */ #define dbCaIsLinkConnected(link) \ dbIsLinkConnected(link) #define dbCaGetLinkDBFtype(link) \ dbGetLinkDBFtype(link) #define dbCaGetNelements(link, nelements) \ dbGetNelements(link, nelements) #define dbCaGetSevr(link, sevr) \ dbGetAlarm(link, NULL, sevr) #define dbCaGetAlarm(link, stat, sevr) \ dbGetAlarm(link, stat, sevr) #define dbCaGetTimeStamp(link, pstamp) \ dbGetTimeStamp(link, pstamp) #define dbCaGetControlLimits(link, low, high) \ dbGetControlLimits(link, low, high) #define dbCaGetGraphicLimits(link, low, high) \ dbGetGraphicLimits(link, low, high) #define dbCaGetAlarmLimits(link, lolo, low, high, hihi) \ dbGetAlarmLimits(link, lolo, low, high, hihi) #define dbCaGetPrecision(link, prec) \ dbGetPrecision(link, prec) #define dbCaGetUnits(link, units, unitSize) \ dbGetUnits(link, units, unitSize) #define dbCaScanFwdLink(link) \ dbScanFwdLink(link) #ifdef __cplusplus } #endif #endif /*INCdbCah*/ base-7.0.3.1/modules/database/src/ioc/db/dbCaPvt.h0000664000577000060420000000602513557101274020254 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbCaPvt.h * * Original Authors: Bob Dalesio, Marty Kraimer * */ #ifndef INC_dbCaPvt_H #define INC_dbCaPvt_H #include "dbCa.h" #include "ellLib.h" #include "epicsMutex.h" #include "epicsTypes.h" #include "link.h" /* link_action mask */ #define CA_CLEAR_CHANNEL 0x1 #define CA_CONNECT 0x2 #define CA_WRITE_NATIVE 0x4 #define CA_WRITE_STRING 0x8 #define CA_MONITOR_NATIVE 0x10 #define CA_MONITOR_STRING 0x20 #define CA_GET_ATTRIBUTES 0x40 #define CA_SYNC 0x1000 /* write type */ #define CA_PUT 0x1 #define CA_PUT_CALLBACK 0x2 typedef struct caLink { ELLNODE node; int refcount; epicsMutexId lock; struct link *plink; char *pvname; chid chid; short link_action; /* The following have new values after each data event*/ epicsEnum16 sevr; epicsEnum16 stat; epicsTimeStamp timeStamp; /* The following have values after connection*/ short dbrType; size_t elementSize; /* size of one element in pgetNative */ unsigned long nelements; /* PVs max array size */ unsigned long usedelements; /* currently used in pgetNative */ unsigned long putnelements; /* currently used in pputNative */ char hasReadAccess; char hasWriteAccess; char isConnected; char gotFirstConnection; /* The following are for dbCaAddLinkCallback */ dbCaCallback connect; dbCaCallback monitor; void *userPvt; /* The following are for write request */ short putType; dbCaCallback putCallback; void *putUserPvt; /* The following are for access to additional attributes*/ char gotAttributes; dbCaCallback getAttributes; void *getAttributesPvt; /* The following have values after getAttribEventCallback*/ double controlLimits[2]; double displayLimits[2]; double alarmLimits[4]; short precision; char units[MAX_UNITS_SIZE]; /* units of value */ /* The following are for handling data*/ void *pgetNative; char *pgetString; void *pputNative; char *pputString; evid evidNative; evid evidString; char gotInNative; char gotInString; char gotOutNative; char gotOutString; char newOutNative; char newOutString; unsigned char scanningOnce; /* The following are for dbcar*/ unsigned long nDisconnect; unsigned long nNoWrite; /*only modified by dbCaPutLink*/ unsigned long nUpdate; }caLink; #endif /* INC_dbCaPvt_H */ base-7.0.3.1/modules/database/src/ioc/db/dbCaTest.c0000664000577000060420000001706613557101274020424 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbCaTest.c */ /**************************************************************** * * Author: Marty Kraimer * Date: 10APR96 * ****************************************************************/ #include #include #include #include #include "dbDefs.h" #include "epicsEvent.h" #include "epicsPrint.h" #include "epicsStdio.h" #define epicsExportSharedSymbols #include "dbStaticLib.h" #undef epicsExportSharedSymbols /*definitions needed because of old vs new database access*/ #undef DBR_SHORT #undef DBR_PUT_ACKT #undef DBR_PUT_ACKS #undef VALID_DB_REQ #undef INVALID_DB_REQ /*end of conflicting definitions*/ #include "cadef.h" /*define DB_CONVERT_GBLSOURCE because db_access.c does not include db_access.h*/ #define DB_CONVERT_GBLSOURCE #define epicsExportSharedSymbols #include "db_access.h" #include "db_access_routines.h" #include "dbCa.h" #include "dbCaPvt.h" #include "dbCaTest.h" #include "dbCommon.h" #include "db_convert.h" #include "dbLock.h" #include "link.h" long dbcar(char *precordname, int level) { DBENTRY dbentry; DBENTRY *pdbentry=&dbentry; long status; dbCommon *precord; dbRecordType *pdbRecordType; dbFldDes *pdbFldDes; DBLINK *plink; int ncalinks=0; int nconnected=0; int noReadAccess=0; int noWriteAccess=0; unsigned long nDisconnect=0; unsigned long nNoWrite=0; caLink *pca; int j; if (!precordname || precordname[0] == '\0' || !strcmp(precordname, "*")) { precordname = NULL; printf("CA links in all records\n\n"); } else { printf("CA links in record named '%s'\n\n", precordname); } dbInitEntry(pdbbase,pdbentry); status = dbFirstRecordType(pdbentry); while (!status) { status = dbFirstRecord(pdbentry); while (!status) { if (precordname ? !strcmp(precordname, dbGetRecordName(pdbentry)) : !dbIsAlias(pdbentry)) { pdbRecordType = pdbentry->precordType; precord = (dbCommon *)pdbentry->precnode->precord; dbScanLock(precord); for (j=0; jno_links; j++) { pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; plink = (DBLINK *)((char *)precord + pdbFldDes->offset); if (plink->type == CA_LINK) { ncalinks++; pca = (caLink *)plink->value.pv_link.pvt; if (pca && pca->chid && (ca_field_type(pca->chid) != TYPENOTCONN)) { nconnected++; nDisconnect += pca->nDisconnect; nNoWrite += pca->nNoWrite; if (!ca_read_access(pca->chid)) noReadAccess++; if (!ca_write_access(pca->chid)) noWriteAccess++; if (level>1) { int rw = ca_read_access(pca->chid) | ca_write_access(pca->chid) << 1; static const char *rights[4] = { "No Access", "Read Only", "Write Only", "Read/Write" }; int mask = plink->value.pv_link.pvlMask; printf("%28s.%-4s ==> %-28s (%lu, %lu)\n", precord->name, pdbFldDes->name, plink->value.pv_link.pvname, pca->nDisconnect, pca->nNoWrite); printf("%21s [%s%s%s%s] host %s, %s\n", "", mask & pvlOptInpNative ? "IN" : " ", mask & pvlOptInpString ? "IS" : " ", mask & pvlOptOutNative ? "ON" : " ", mask & pvlOptOutString ? "OS" : " ", ca_host_name(pca->chid), rights[rw]); } } else { if (level>0) { printf("%28s.%-4s --> %-28s (%lu, %lu)\n", precord->name, pdbFldDes->name, plink->value.pv_link.pvname, pca ? pca->nDisconnect : 0, pca ? pca->nNoWrite : 0); } } } } dbScanUnlock(precord); if (precordname) goto done; } status = dbNextRecord(pdbentry); } status = dbNextRecordType(pdbentry); } done: if ((level > 1 && nconnected > 0) || (level > 0 && ncalinks != nconnected)) printf("\n"); printf("Total %d CA link%s; ", ncalinks, (ncalinks != 1) ? "s" : ""); printf("%d connected, %d not connected.\n", nconnected, (ncalinks - nconnected)); printf(" %d can't read, %d can't write.", noReadAccess, noWriteAccess); printf(" (%lu disconnects, %lu writes prohibited)\n\n", nDisconnect, nNoWrite); dbFinishEntry(pdbentry); if ( level > 2 && dbCaClientContext != 0 ) { ca_context_status ( dbCaClientContext, level - 2 ); } return(0); } void dbcaStats(int *pchans, int *pdiscon) { DBENTRY dbentry; DBENTRY *pdbentry = &dbentry; long status; DBLINK *plink; long ncalinks = 0; long nconnected = 0; dbInitEntry(pdbbase,pdbentry); status = dbFirstRecordType(pdbentry); while (!status) { dbRecordType *pdbRecordType = pdbentry->precordType; status = dbFirstRecord(pdbentry); while (!status) { dbCommon *precord = (dbCommon *)pdbentry->precnode->precord; int j; if (!dbIsAlias(pdbentry)) { for (j=0; jno_links; j++) { int i = pdbRecordType->link_ind[j]; dbFldDes *pdbFldDes = pdbRecordType->papFldDes[i]; plink = (DBLINK *)((char *)precord + pdbFldDes->offset); if (plink->type == CA_LINK) { ncalinks++; if (dbCaIsLinkConnected(plink)) { nconnected++; } } } } status = dbNextRecord(pdbentry); } status = dbNextRecordType(pdbentry); } dbFinishEntry(pdbentry); if (pchans) *pchans = ncalinks; if (pdiscon) *pdiscon = ncalinks - nconnected; } base-7.0.3.1/modules/database/src/ioc/db/dbCaTest.h0000664000577000060420000000141413557101274020417 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_dbCaTest_H #define INC_dbCaTest_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc long dbcar(char *recordname,int level); epicsShareFunc void dbcaStats(int *pchans, int *pdiscon); #ifdef __cplusplus } #endif #endif /* INC_dbCaTest_H */ base-7.0.3.1/modules/database/src/ioc/db/dbChannel.c0000664000577000060420000005317113557101274020606 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Andrew Johnson * Ralph Lange */ #include #include #include #include "cantProceed.h" #include "epicsAssert.h" #include "epicsString.h" #include "epicsStdio.h" #include "errlog.h" #include "freeList.h" #include "gpHash.h" #include "yajl_parse.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "dbBase.h" #include "dbChannel.h" #include "dbCommon.h" #include "dbEvent.h" #include "dbLock.h" #include "dbStaticLib.h" #include "link.h" #include "recSup.h" #include "special.h" typedef struct parseContext { dbChannel *chan; chFilter *filter; int depth; } parseContext; #define CALLIF(rtn) !rtn ? parse_stop : rtn static void *dbChannelFreeList; static void *chFilterFreeList; static void *dbchStringFreeList; void dbChannelExit(void) { freeListCleanup(dbChannelFreeList); freeListCleanup(chFilterFreeList); freeListCleanup(dbchStringFreeList); dbChannelFreeList = chFilterFreeList = dbchStringFreeList = NULL; } void dbChannelInit (void) { if(dbChannelFreeList) return; freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128); freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64); freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128); } static void chf_value(parseContext *parser, parse_result *presult) { chFilter *filter = parser->filter; if (*presult == parse_stop || parser->depth > 0) return; parser->filter = NULL; if (filter->plug->fif->parse_end(filter) == parse_continue) { ellAdd(&parser->chan->filters, &filter->list_node); } else { freeListFree(chFilterFreeList, filter); *presult = parse_stop; } } static int chf_null(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; parse_result result; assert(filter); result = CALLIF(filter->plug->fif->parse_null)(filter ); chf_value(parser, &result); return result; } static int chf_boolean(void * ctx, int boolVal) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; parse_result result; assert(filter); result = CALLIF(filter->plug->fif->parse_boolean)(filter , boolVal); chf_value(parser, &result); return result; } static int chf_integer(void * ctx, long long integerVal) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; parse_result result; assert(filter); result = CALLIF(filter->plug->fif->parse_integer)(filter , integerVal); chf_value(parser, &result); return result; } static int chf_double(void * ctx, double doubleVal) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; parse_result result; assert(filter); result = CALLIF(filter->plug->fif->parse_double)(filter , doubleVal); chf_value(parser, &result); return result; } static int chf_string(void * ctx, const unsigned char * stringVal, size_t stringLen) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; parse_result result; assert(filter); result = CALLIF(filter->plug->fif->parse_string)(filter , (const char *) stringVal, stringLen); chf_value(parser, &result); return result; } static int chf_start_map(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; if (!filter) { assert(parser->depth == 0); return parse_continue; /* Opening '{' */ } ++parser->depth; return CALLIF(filter->plug->fif->parse_start_map)(filter ); } static int chf_map_key(void * ctx, const unsigned char * key, size_t stringLen) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; const chFilterPlugin *plug; parse_result result; if (filter) { assert(parser->depth > 0); return CALLIF(filter->plug->fif->parse_map_key)(filter , (const char *) key, stringLen); } assert(parser->depth == 0); plug = dbFindFilter((const char *) key, stringLen); if (!plug) { errlogPrintf("dbChannelCreate: Channel filter '%.*s' not found\n", (int) stringLen, key); return parse_stop; } filter = freeListCalloc(chFilterFreeList); if (!filter) { errlogPrintf("dbChannelCreate: Out of memory\n"); return parse_stop; } filter->chan = parser->chan; filter->plug = plug; filter->puser = NULL; result = plug->fif->parse_start(filter); if (result == parse_continue) { parser->filter = filter; } else { freeListFree(chFilterFreeList, filter); } return result; } static int chf_end_map(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; parse_result result; if (!filter) { assert(parser->depth == 0); return parse_continue; /* Final closing '}' */ } assert(parser->depth > 0); result = CALLIF(filter->plug->fif->parse_end_map)(filter ); --parser->depth; chf_value(parser, &result); return result; } static int chf_start_array(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; assert(filter); ++parser->depth; return CALLIF(filter->plug->fif->parse_start_array)(filter ); } static int chf_end_array(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; parse_result result; assert(filter); result = CALLIF(filter->plug->fif->parse_end_array)(filter ); --parser->depth; chf_value(parser, &result); return result; } static const yajl_callbacks chf_callbacks = { chf_null, chf_boolean, chf_integer, chf_double, NULL, chf_string, chf_start_map, chf_map_key, chf_end_map, chf_start_array, chf_end_array }; static void * chf_malloc(void *ctx, size_t sz) { return malloc(sz); } static void * chf_realloc(void *ctx, void *ptr, size_t sz) { return realloc(ptr, sz); } static void chf_free(void *ctx, void *ptr) { free(ptr); } static yajl_alloc_funcs chf_alloc = { chf_malloc, chf_realloc, chf_free }; static long chf_parse(dbChannel *chan, const char **pjson) { parseContext parser = { chan, NULL, 0 }; yajl_handle yh = yajl_alloc(&chf_callbacks, &chf_alloc, &parser); const char *json = *pjson; size_t jlen = strlen(json), ylen; yajl_status ys; long status; if (!yh) return S_db_noMemory; ys = yajl_parse(yh, (const unsigned char *) json, jlen); ylen = yajl_get_bytes_consumed(yh); if (ys == yajl_status_ok) ys = yajl_complete_parse(yh); switch (ys) { case yajl_status_ok: *pjson += ylen; status = 0; break; case yajl_status_error: { unsigned char *err; err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen); printf("dbChannelCreate: %s\n", err); yajl_free_error(yh, err); } /* fall through */ default: status = S_db_notFound; } if (parser.filter) { assert(status); parser.filter->plug->fif->parse_abort(parser.filter); freeListFree(chFilterFreeList, parser.filter); } yajl_free(yh); return status; } static long pvNameLookup(DBENTRY *pdbe, const char **ppname) { long status; dbInitEntry(pdbbase, pdbe); status = dbFindRecordPart(pdbe, ppname); if (status) return status; if (**ppname == '.') ++*ppname; status = dbFindFieldPart(pdbe, ppname); if (status == S_dbLib_fieldNotFound) status = dbGetAttributePart(pdbe, ppname); return status; } long dbChannelTest(const char *name) { DBENTRY dbEntry; long status; if (!name || !*name || !pdbbase) return S_db_notFound; status = pvNameLookup(&dbEntry, &name); dbFinishEntry(&dbEntry); return status; } #define TRY(Func, Arg) \ if (Func) { \ result = Func Arg; \ if (result != parse_continue) goto failure; \ } static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppnext) { epicsInt32 start = 0; epicsInt32 end = -1; epicsInt32 incr = 1; epicsInt32 l; char *pnext; ptrdiff_t exist; chFilter *filter; const chFilterPlugin *plug; parse_result result; long status = 0; /* If no number is present, strtol() returns 0 and sets pnext=pname, else pnext points to the first char after the number */ pname++; l = strtol(pname, &pnext, 0); exist = pnext - pname; if (exist) start = l; pname = pnext; if (*pname == ']' && exist) { end = start; goto insertplug; } if (*pname != ':') { status = S_dbLib_fieldNotFound; goto finish; } pname++; l = strtol(pname, &pnext, 0); exist = pnext - pname; pname = pnext; if (*pname == ']') { if (exist) end = l; goto insertplug; } if (exist) incr = l; if (*pname != ':') { status = S_dbLib_fieldNotFound; goto finish; } pname++; l = strtol(pname, &pnext, 0); exist = pnext - pname; if (exist) end = l; pname = pnext; if (*pname != ']') { status = S_dbLib_fieldNotFound; goto finish; } insertplug: pname++; *ppnext = pname; plug = dbFindFilter("arr", 3); if (!plug) { status = S_dbLib_fieldNotFound; goto finish; } filter = freeListCalloc(chFilterFreeList); if (!filter) { status = S_db_noMemory; goto finish; } filter->chan = chan; filter->plug = plug; filter->puser = NULL; TRY(filter->plug->fif->parse_start, (filter)); TRY(filter->plug->fif->parse_start_map, (filter)); if (start != 0) { TRY(filter->plug->fif->parse_map_key, (filter, "s", 1)); TRY(filter->plug->fif->parse_integer, (filter, start)); } if (incr != 1) { TRY(filter->plug->fif->parse_map_key, (filter, "i", 1)); TRY(filter->plug->fif->parse_integer, (filter, incr)); } if (end != -1) { TRY(filter->plug->fif->parse_map_key, (filter, "e", 1)); TRY(filter->plug->fif->parse_integer, (filter, end)); } TRY(filter->plug->fif->parse_end_map, (filter)); TRY(filter->plug->fif->parse_end, (filter)); ellAdd(&chan->filters, &filter->list_node); return 0; failure: freeListFree(chFilterFreeList, filter); status = S_dbLib_fieldNotFound; finish: return status; } /* Stolen from dbAccess.c: */ static short mapDBFToDBR[DBF_NTYPES] = { /* DBF_STRING => */DBR_STRING, /* DBF_CHAR => */DBR_CHAR, /* DBF_UCHAR => */DBR_UCHAR, /* DBF_SHORT => */DBR_SHORT, /* DBF_USHORT => */DBR_USHORT, /* DBF_LONG => */DBR_LONG, /* DBF_ULONG => */DBR_ULONG, /* DBF_INT64 => */DBR_INT64, /* DBF_UINT64 => */DBR_UINT64, /* DBF_FLOAT => */DBR_FLOAT, /* DBF_DOUBLE => */DBR_DOUBLE, /* DBF_ENUM, => */DBR_ENUM, /* DBF_MENU, => */DBR_ENUM, /* DBF_DEVICE => */DBR_ENUM, /* DBF_INLINK => */DBR_STRING, /* DBF_OUTLINK => */DBR_STRING, /* DBF_FWDLINK => */DBR_STRING, /* DBF_NOACCESS => */DBR_NOACCESS }; dbChannel * dbChannelCreate(const char *name) { const char *pname = name; DBENTRY dbEntry; dbChannel *chan = NULL; char *cname; dbAddr *paddr; long status; if (!name || !*name || !pdbbase) return NULL; status = pvNameLookup(&dbEntry, &pname); if (status) goto finish; chan = freeListCalloc(dbChannelFreeList); if (!chan) goto finish; cname = malloc(strlen(name) + 1); if (!cname) goto finish; strcpy(cname, name); chan->name = cname; ellInit(&chan->filters); ellInit(&chan->pre_chain); ellInit(&chan->post_chain); paddr = &chan->addr; status = dbEntryToAddr(&dbEntry, paddr); if (status) goto finish; /* Handle field modifiers */ if (*pname) { short dbfType = paddr->field_type; if (*pname == '$') { /* Some field types can be accessed as char arrays */ if (dbfType == DBF_STRING) { paddr->no_elements = paddr->field_size; paddr->field_type = DBF_CHAR; paddr->field_size = 1; paddr->dbr_field_type = DBR_CHAR; } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { /* Clients see a char array, but keep original dbfType */ paddr->no_elements = PVLINK_STRINGSZ; paddr->field_size = 1; paddr->dbr_field_type = DBR_CHAR; } else { status = S_dbLib_fieldNotFound; goto finish; } pname++; } if (*pname == '[') { status = parseArrayRange(chan, pname, &pname); if (status) goto finish; } /* JSON may follow */ if (*pname == '{') { status = chf_parse(chan, &pname); if (status) goto finish; } /* Make sure there's nothing else */ if (*pname) { status = S_dbLib_fieldNotFound; goto finish; } } finish: if (status && chan) { dbChannelDelete(chan); chan = NULL; } dbFinishEntry(&dbEntry); return chan; } db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn) { chFilter *filter; ELLNODE *node; db_field_log *pLog = pLogIn; for (node = ellFirst(&chan->pre_chain); node && pLog; node = ellNext(node)) { filter = CONTAINER(node, chFilter, pre_node); pLog = filter->pre_func(filter->pre_arg, chan, pLog); } return pLog; } db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn) { chFilter *filter; ELLNODE *node; db_field_log *pLog = pLogIn; for (node = ellFirst(&chan->post_chain); node && pLog; node = ellNext(node)) { filter = CONTAINER(node, chFilter, post_node); pLog = filter->post_func(filter->post_arg, chan, pLog); } return pLog; } long dbChannelOpen(dbChannel *chan) { chFilter *filter; chPostEventFunc *func; void *arg; long status; ELLNODE *node; db_field_log probe; db_field_log p; for (node = ellFirst(&chan->filters); node; node = ellNext(node)) { filter = CONTAINER(node, chFilter, list_node); /* Call channel_open */ status = 0; if (filter->plug->fif->channel_open) status = filter->plug->fif->channel_open(filter); if (status) return status; } /* Set up type probe */ probe.type = dbfl_type_val; probe.ctx = dbfl_context_read; probe.field_type = dbChannelExportType(chan); probe.no_elements = dbChannelElements(chan); probe.field_size = dbChannelFieldSize(chan); p = probe; /* * Build up the pre- and post-event-queue filter chains * Separate loops because the probe must reach the filters in the right order. */ for (node = ellFirst(&chan->filters); node; node = ellNext(node)) { filter = CONTAINER(node, chFilter, list_node); func = NULL; arg = NULL; if (filter->plug->fif->channel_register_pre) { filter->plug->fif->channel_register_pre(filter, &func, &arg, &p); if (func) { ellAdd(&chan->pre_chain, &filter->pre_node); filter->pre_func = func; filter->pre_arg = arg; probe = p; } } } for (node = ellFirst(&chan->filters); node; node = ellNext(node)) { filter = CONTAINER(node, chFilter, list_node); func = NULL; arg = NULL; if (filter->plug->fif->channel_register_post) { filter->plug->fif->channel_register_post(filter, &func, &arg, &p); if (func) { ellAdd(&chan->post_chain, &filter->post_node); filter->post_func = func; filter->post_arg = arg; probe = p; } } } /* Save probe results */ chan->final_no_elements = probe.no_elements; chan->final_field_size = probe.field_size; chan->final_type = probe.field_type; return 0; } /* Only use dbChannelGet() if the record is already locked. */ long dbChannelGet(dbChannel *chan, short type, void *pbuffer, long *options, long *nRequest, void *pfl) { return dbGet(&chan->addr, type, pbuffer, options, nRequest, pfl); } long dbChannelGetField(dbChannel *chan, short dbrType, void *pbuffer, long *options, long *nRequest, void *pfl) { dbCommon *precord = chan->addr.precord; long status = 0; dbScanLock(precord); status = dbChannelGet(chan, dbrType, pbuffer, options, nRequest, pfl); dbScanUnlock(precord); return status; } /* Only use dbChannelPut() if the record is already locked. * This routine doesn't work on link fields, ignores DISP, and * doesn't trigger record processing on PROC or pp(TRUE). */ long dbChannelPut(dbChannel *chan, short type, const void *pbuffer, long nRequest) { return dbPut(&chan->addr, type, pbuffer, nRequest); } long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer, long nRequest) { return dbPutField(&chan->addr, type, pbuffer, nRequest); } void dbChannelShow(dbChannel *chan, int level, const unsigned short indent) { long elems = chan->addr.no_elements; long felems = chan->final_no_elements; int count = ellCount(&chan->filters); int pre = ellCount(&chan->pre_chain); int post = ellCount(&chan->post_chain); printf("%*sChannel: '%s'\n", indent, "", chan->name); if (level > 0) { printf("%*sfield_type=%s (%d bytes), dbr_type=%s, %ld element%s", indent + 4, "", dbGetFieldTypeString(chan->addr.field_type), chan->addr.field_size, dbGetFieldTypeString(chan->addr.dbr_field_type), elems, elems == 1 ? "" : "s"); if (count) printf("\n%*s%d filter%s (%d pre eventq, %d post eventq)\n", indent + 4, "", count, count == 1 ? "" : "s", pre, post); else printf(", no filters\n"); if (level > 1) dbChannelFilterShow(chan, level - 2, indent + 8); if (count) { printf("%*sfinal field_type=%s (%dB), %ld element%s\n", indent + 4, "", dbGetFieldTypeString(chan->final_type), chan->final_field_size, felems, felems == 1 ? "" : "s"); } } } void dbChannelFilterShow(dbChannel *chan, int level, const unsigned short indent) { chFilter *filter = (chFilter *) ellFirst(&chan->filters); while (filter) { filter->plug->fif->channel_report(filter, level, indent); filter = (chFilter *) ellNext(&filter->list_node); } } void dbChannelDelete(dbChannel *chan) { chFilter *filter; /* Close filters in reverse order */ while ((filter = (chFilter *) ellPop(&chan->filters))) { filter->plug->fif->channel_close(filter); freeListFree(chFilterFreeList, filter); } free((char *) chan->name); freeListFree(dbChannelFreeList, chan); } static void freeArray(db_field_log *pfl) { if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) { freeListFree(dbchStringFreeList, pfl->u.r.field); } else { free(pfl->u.r.field); } } void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan) { void *p; struct dbCommon *prec = dbChannelRecord(chan); if (pfl->type != dbfl_type_rec) return; pfl->type = dbfl_type_ref; pfl->stat = prec->stat; pfl->sevr = prec->sevr; pfl->time = prec->time; pfl->field_type = chan->addr.field_type; pfl->no_elements = chan->addr.no_elements; pfl->field_size = chan->addr.field_size; pfl->u.r.dtor = freeArray; pfl->u.r.pvt = pvt; if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) { p = freeListCalloc(dbchStringFreeList); } else { p = calloc(pfl->no_elements, pfl->field_size); } if (p) dbGet(&chan->addr, mapDBFToDBR[pfl->field_type], p, NULL, &pfl->no_elements, NULL); pfl->u.r.field = p; } /* FIXME: Do these belong in a different file? */ void dbRegisterFilter(const char *name, const chFilterIf *fif, void *puser) { GPHENTRY *pgph; chFilterPlugin *pfilt; if (!pdbbase) { printf("dbRegisterFilter: pdbbase not set!\n"); return; } pgph = gphFind(pdbbase->pgpHash, name, &pdbbase->filterList); if (pgph) return; pfilt = dbCalloc(1, sizeof(chFilterPlugin)); pfilt->name = epicsStrDup(name); pfilt->fif = fif; pfilt->puser = puser; ellAdd(&pdbbase->filterList, &pfilt->node); pgph = gphAdd(pdbbase->pgpHash, pfilt->name, &pdbbase->filterList); if (!pgph) { free((void *) pfilt->name); free(pfilt); printf("dbRegisterFilter: gphAdd failed\n"); return; } pgph->userPvt = pfilt; } const chFilterPlugin * dbFindFilter(const char *name, size_t len) { GPHENTRY *pgph = gphFindParse(pdbbase->pgpHash, name, len, &pdbbase->filterList); if (!pgph) return NULL; return (chFilterPlugin *) pgph->userPvt; } base-7.0.3.1/modules/database/src/ioc/db/dbChannel.h0000664000577000060420000002065713557101274020616 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Andrew Johnson * Ralph Lange */ #ifndef INC_dbChannel_H #define INC_dbChannel_H #include "dbDefs.h" #include "dbAddr.h" #include "ellLib.h" #include "epicsTypes.h" #include "errMdef.h" #include "shareLib.h" #include "db_field_log.h" #include "dbEvent.h" #ifdef __cplusplus extern "C" { #endif /* * event subscription */ typedef struct evSubscrip { ELLNODE node; struct dbChannel *chan; EVENTFUNC *user_sub; void *user_arg; struct event_que *ev_que; db_field_log **pLastLog; unsigned long npend; /* n times this event is on the queue */ unsigned long nreplace; /* n times replacing event on the queue */ unsigned char select; char useValque; char callBackInProgress; char enabled; } evSubscrip; typedef struct chFilter chFilter; /* A dbChannel points to a record field, and can have multiple filters */ typedef struct dbChannel { const char *name; dbAddr addr; /* address structure for record/field */ long final_no_elements; /* final number of elements (arrays) */ short final_field_size; /* final size of element */ short final_type; /* final type of database field */ ELLLIST filters; /* list of filters as created from JSON */ ELLLIST pre_chain; /* list of filters to be called pre-event-queue */ ELLLIST post_chain; /* list of filters to be called post-event-queue */ } dbChannel; /* Prototype for the channel event function that is called in filter stacks * * When invoked the scan lock for the record associated with 'chan' _may_ be locked. * If pLog->type==dbfl_type_rec then dbScanLock() must be called before copying * data out of the associated record. * * This function has ownership of the field log pLog, if it wishes to discard * this update it should free the field log with db_delete_field_log() and * then return NULL. */ typedef db_field_log* (chPostEventFunc)(void *pvt, dbChannel *chan, db_field_log *pLog); /* Return values from chFilterIf->parse_* routines: */ typedef enum { parse_stop, parse_continue } parse_result; /* These routines must be implemented by each filter plug-in */ typedef struct chFilterIf { /* cleanup pointer passed to dbRegisterFilter(). * Called during DB shutdown */ void (* priv_free)(void *puser); /* Parsing event handlers: */ parse_result (* parse_start)(chFilter *filter); /* If parse_start() returns parse_continue for a filter, one of * parse_abort() or parse_end() will later be called for that same * filter. */ void (* parse_abort)(chFilter *filter); /* If parse_abort() is called it should release any memory allocated * for this filter; no further parse_...() calls will be made; */ parse_result (* parse_end)(chFilter *filter); /* If parse_end() returns parse_stop it should have released any * memory allocated for this filter; no further parse_...() calls will * be made in this case. */ parse_result (* parse_null)(chFilter *filter); parse_result (* parse_boolean)(chFilter *filter, int boolVal); parse_result (* parse_integer)(chFilter *filter, long integerVal); parse_result (* parse_double)(chFilter *filter, double doubleVal); parse_result (* parse_string)(chFilter *filter, const char *stringVal, size_t stringLen); /* NB: stringVal is not zero-terminated: */ parse_result (* parse_start_map)(chFilter *filter); parse_result (* parse_map_key)(chFilter *filter, const char *key, size_t stringLen); /* NB: key is not zero-terminated: */ parse_result (* parse_end_map)(chFilter *filter); parse_result (* parse_start_array)(chFilter *filter); parse_result (* parse_end_array)(chFilter *filter); /* Channel operations: */ long (* channel_open)(chFilter *filter); void (* channel_register_pre) (chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); void (* channel_register_post)(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); void (* channel_report)(chFilter *filter, int level, const unsigned short indent); void (* channel_close)(chFilter *filter); } chFilterIf; /* A chFilterPlugin holds data for a filter plugin */ typedef struct chFilterPlugin { ELLNODE node; const char *name; const chFilterIf *fif; void *puser; } chFilterPlugin; /* A chFilter holds data for a single filter instance */ struct chFilter { ELLNODE list_node; ELLNODE pre_node; ELLNODE post_node; dbChannel *chan; const chFilterPlugin *plug; chPostEventFunc *pre_func; void *pre_arg; chPostEventFunc *post_func; void *post_arg; void *puser; }; struct dbCommon; struct dbFldDes; epicsShareFunc void dbChannelInit (void); epicsShareFunc void dbChannelExit(void); epicsShareFunc long dbChannelTest(const char *name); epicsShareFunc dbChannel * dbChannelCreate(const char *name); epicsShareFunc long dbChannelOpen(dbChannel *chan); /*Following is also defined in db_convert.h*/ epicsShareExtern unsigned short dbDBRnewToDBRold[]; /* In the following macros pChan is dbChannel* */ /* evaluates to const char* */ #define dbChannelName(pChan) ((pChan)->name) /* evaluates to struct dbCommon* */ #define dbChannelRecord(pChan) ((pChan)->addr.precord) /* evaluates to struct dbFldDes* */ #define dbChannelFldDes(pChan) ((pChan)->addr.pfldDes) /* evaluates to long */ #define dbChannelElements(pChan) ((pChan)->addr.no_elements) /* evaluates to short */ #define dbChannelFieldType(pChan) ((pChan)->addr.field_type) /* evaluates to short */ #define dbChannelExportType(pChan) ((pChan)->addr.dbr_field_type) /* evaluates to short */ #define dbChannelExportCAType(pChan) (dbDBRnewToDBRold[dbChannelExportType(pChan)]) /* evaluates to short */ #define dbChannelFieldSize(pChan) ((pChan)->addr.field_size) /* evaluates to long */ #define dbChannelFinalElements(pChan) ((pChan)->final_no_elements) /* evaluates to short */ #define dbChannelFinalFieldType(pChan) ((pChan)->final_type) /* evaluates to short */ #define dbChannelFinalCAType(pChan) (dbDBRnewToDBRold[(pChan)->final_type]) /* evaluates to short */ #define dbChannelFinalFieldSize(pChan) ((pChan)->final_field_size) /* evaluates to short */ #define dbChannelSpecial(pChan) ((pChan)->addr.special) /* Channel filters do not get to interpose here since there are many * places where the field pointer is compared with the address of a * specific record field, so they can't modify the pointer value. */ /* evaluates to void* */ #define dbChannelField(pChan) ((pChan)->addr.pfield) epicsShareFunc long dbChannelGet(dbChannel *chan, short type, void *pbuffer, long *options, long *nRequest, void *pfl); epicsShareFunc long dbChannelGetField(dbChannel *chan, short type, void *pbuffer, long *options, long *nRequest, void *pfl); epicsShareFunc long dbChannelPut(dbChannel *chan, short type, const void *pbuffer, long nRequest); epicsShareFunc long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer, long nRequest); epicsShareFunc void dbChannelShow(dbChannel *chan, int level, const unsigned short indent); epicsShareFunc void dbChannelFilterShow(dbChannel *chan, int level, const unsigned short indent); epicsShareFunc void dbChannelDelete(dbChannel *chan); epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, void *puser); epicsShareFunc db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn); epicsShareFunc db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn); epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len); epicsShareFunc void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan); #ifdef __cplusplus } #endif #endif /* INC_dbChannel_H */ base-7.0.3.1/modules/database/src/ioc/db/dbChannelIO.cpp0000664000577000060420000001515213557101274021373 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include #include #include "tsFreeList.h" #include "epicsMutex.h" #include "epicsEvent.h" #include "db_access.h" #include "errlog.h" #define epicsExportSharedSymbols #include "db_access_routines.h" #include "dbCAC.h" #include "dbChannelIO.h" #include "dbPutNotifyBlocker.h" dbChannelIO::dbChannelIO ( epicsMutex & mutexIn, cacChannelNotify & notify, dbChannel * dbchIn, dbContext & serviceIO ) : cacChannel ( notify ), mutex ( mutexIn ), serviceIO ( serviceIO ), dbch ( dbchIn ) { } void dbChannelIO::initiateConnect ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->notify().connectNotify ( guard ); } dbChannelIO::~dbChannelIO () { } void dbChannelIO::destructor ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->serviceIO.destroyAllIO ( cbGuard, guard, *this ); dbChannelDelete ( this->dbch ); this->~dbChannelIO (); } void dbChannelIO::destroy ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->serviceIO.destroyChannel ( cbGuard, guard, *this ); // don't access this pointer after above call because // object no longer exists } cacChannel::ioStatus dbChannelIO::read ( epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, cacReadNotify & notify, ioid * ) { guard.assertIdenticalMutex ( this->mutex ); this->serviceIO.callReadNotify ( guard, this->dbch, type, count, notify ); return iosSynch; } void dbChannelIO::write ( epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, const void *pValue ) { epicsGuardRelease < epicsMutex > unguard ( guard ); if ( count > LONG_MAX ) { throw outOfBounds(); } int status = dbChannel_put ( this->dbch, type, pValue, static_cast (count) ); if ( status ) { throw std::logic_error ( "db_put_field() completed unsuccessfully" ); } } cacChannel::ioStatus dbChannelIO::write ( epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, const void * pValue, cacWriteNotify & notify, ioid * pId ) { guard.assertIdenticalMutex ( this->mutex ); if ( count > LONG_MAX ) { throw outOfBounds(); } this->serviceIO.initiatePutNotify ( guard, *this, this->dbch, type, count, pValue, notify, pId ); return iosAsynch; } void dbChannelIO::subscribe ( epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, unsigned mask, cacStateNotify & notify, ioid * pId ) { guard.assertIdenticalMutex ( this->mutex ); this->serviceIO.subscribe ( guard, this->dbch, *this, type, count, mask, notify, pId ); } void dbChannelIO::ioCancel ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & mutualExclusionGuard, const ioid & id ) { mutualExclusionGuard.assertIdenticalMutex ( this->mutex ); this->serviceIO.ioCancel ( cbGuard, mutualExclusionGuard, *this, id ); } void dbChannelIO::ioShow ( epicsGuard < epicsMutex > & guard, const ioid & id, unsigned level ) const { guard.assertIdenticalMutex ( this->mutex ); this->serviceIO.ioShow ( guard, id, level ); } void dbChannelIO::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( this->mutex ); printf ("channel at %p attached to local database record %s\n", static_cast ( this ), dbChannelRecord ( this->dbch ) -> name ); if ( level > 0u ) { printf ( " type %s, element count %li, field at %p\n", dbf_type_to_text ( dbChannelExportCAType ( this->dbch ) ), dbChannelElements ( this->dbch ), dbChannelField ( this->dbch ) ); if ( level > 1u ) { dbChannelFilterShow ( this->dbch, level - 2u, 8 ); this->serviceIO.show ( level - 2u ); this->serviceIO.showAllIO ( *this, level - 2u ); } } } unsigned long dbChannelIO::nativeElementCount ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); long elements = dbChannelElements ( this->dbch ); if ( elements >= 0u ) { return static_cast < unsigned long > ( elements ); } return 0u; } // hopefully to be eventually phased out const char * dbChannelIO::pName ( epicsGuard < epicsMutex > & guard ) const throw () { guard.assertIdenticalMutex ( this->mutex ); return dbChannelName ( this->dbch ); } unsigned dbChannelIO::getName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw () { const char *name = dbChannelName ( this->dbch ); size_t len = strlen ( name ); strncpy ( pBuf, name, bufLen ); if (len < bufLen) return (unsigned) len; pBuf[--bufLen] = '\0'; return bufLen; } short dbChannelIO::nativeType ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); return dbChannelExportCAType( this->dbch ); } void * dbChannelIO::operator new ( size_t size, tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE void dbChannelIO::operator delete ( void *pCadaver, tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif void dbChannelIO::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } void dbChannelIO::flush ( epicsGuard < epicsMutex > & ) { } unsigned dbChannelIO::requestMessageBytesPending ( epicsGuard < epicsMutex > & ) { return 0u; } base-7.0.3.1/modules/database/src/ioc/db/dbChannelIO.h0000664000577000060420000000770513557101274021045 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 * * NOTES: * 1) This interface is preliminary and will change in the future */ #ifndef dbChannelIOh #define dbChannelIOh #ifdef epicsExportSharedSymbols # define dbChannelIOh_restore_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif #include "compilerDependencies.h" #ifdef dbChannelIOh_restore_epicsExportSharedSymbols # define epicsExportSharedSymbols #endif class dbChannelIO : public cacChannel, public dbContextPrivateListOfIO { public: dbChannelIO ( epicsMutex &, cacChannelNotify &, dbChannel *, dbContext & ); void destructor ( CallbackGuard &, epicsGuard < epicsMutex > & ); void destroy ( CallbackGuard &, epicsGuard < epicsMutex > & mutualExclusionGuard ); void callReadNotify ( epicsGuard < epicsMutex > &, unsigned type, unsigned long count, cacReadNotify & notify ); void callStateNotify ( unsigned type, unsigned long count, const struct db_field_log * pfl, cacStateNotify & notify ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; unsigned getName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw (); const char * pName ( epicsGuard < epicsMutex > & ) const throw (); void * operator new ( size_t size, tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & )) protected: ~dbChannelIO (); private: epicsMutex & mutex; dbContext & serviceIO; dbChannel * dbch; void initiateConnect ( epicsGuard < epicsMutex > & ); unsigned requestMessageBytesPending ( epicsGuard < epicsMutex > & ); void flush ( epicsGuard < epicsMutex > & ); ioStatus read ( epicsGuard < epicsMutex > &, unsigned type, unsigned long count, cacReadNotify &, ioid * ); void write ( epicsGuard < epicsMutex > &, unsigned type, unsigned long count, const void * pvalue ); ioStatus write ( epicsGuard < epicsMutex > &, unsigned type, unsigned long count, const void * pvalue, cacWriteNotify &, ioid * ); void subscribe ( epicsGuard < epicsMutex > &, unsigned type, unsigned long count, unsigned mask, cacStateNotify ¬ify, ioid * ); void ioCancel ( CallbackGuard &, epicsGuard < epicsMutex > &, const ioid & ); void ioShow ( epicsGuard < epicsMutex > &, const ioid &, unsigned level ) const; short nativeType ( epicsGuard < epicsMutex > & ) const; unsigned long nativeElementCount ( epicsGuard < epicsMutex > & ) const; dbChannelIO ( const dbChannelIO & ); dbChannelIO & operator = ( const dbChannelIO & ); void operator delete ( void * ); }; inline void dbChannelIO::callReadNotify ( epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, cacReadNotify & notify ) { guard.assertIdenticalMutex ( this->mutex ); this->serviceIO.callReadNotify ( guard, this->dbch, type, count, notify ); } inline void dbChannelIO::callStateNotify ( unsigned type, unsigned long count, const struct db_field_log *pfl, cacStateNotify ¬ify ) { this->serviceIO.callStateNotify ( this->dbch, type, count, pfl, notify ); } #endif // dbChannelIOh base-7.0.3.1/modules/database/src/ioc/db/dbChannelNOOP.h0000664000577000060420000000617513557101274021311 0ustar anjaesctl#ifndef DBCHANNELNOOP_H #define DBCHANNELNOOP_H #include #include #include "cacIO.h" #include "caerr.h" /** @brief A channel which never connects * * Used when dbCa is placed in isolated mode for unittests */ class dbChannelNOOP : public cacChannel { std::string myname; public: dbChannelNOOP(const char *name, cacChannelNotify ¬ify) :cacChannel(notify) ,myname(name) {} virtual void destroy ( CallbackGuard & /*callbackGuard*/, epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ ) { delete this; // goodbye cruel world } virtual unsigned getName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw () { const char* name = myname.c_str(); if(bufLen>myname.size()+1) { bufLen=myname.size()+1; } memcpy(pBuf, name, bufLen); pBuf[--bufLen] = '\0'; return bufLen; } // !! deprecated, avoid use !! virtual const char * pName ( epicsGuard < epicsMutex > & guard ) const throw () {return myname.c_str();} virtual void show ( epicsGuard < epicsMutex > &, unsigned level ) const {} virtual void initiateConnect ( epicsGuard < epicsMutex > & ) {} virtual unsigned requestMessageBytesPending ( epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ ) {return 0;} virtual void flush ( epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ ) {} virtual ioStatus read ( epicsGuard < epicsMutex > &mut, unsigned type, arrayElementCount count, cacReadNotify ¬ify, ioid * = 0 ) { notify.exception(mut, ECA_NORDACCESS, "dbChannelNOOP", type, count); return iosSynch; } virtual void write ( epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void *pValue ) {} virtual ioStatus write ( epicsGuard < epicsMutex > &mut, unsigned type, arrayElementCount count, const void */*pValue*/, cacWriteNotify & notify, ioid * = 0 ) { notify.exception(mut, ECA_NOWTACCESS, "dbChannelNOOP", type, count); return iosSynch; } virtual void subscribe ( epicsGuard < epicsMutex > &mut, unsigned type, arrayElementCount count, unsigned /*mask*/, cacStateNotify & notify, ioid * = 0 ) { // should never subscribe notify.exception(mut, ECA_BADMASK, "dbChannelNOOP", type, count); } virtual void ioCancel ( CallbackGuard & callbackGuard, epicsGuard < epicsMutex > & mutualExclusionGuard, const ioid & ) {} virtual void ioShow ( epicsGuard < epicsMutex > &, const ioid &, unsigned level ) const {} virtual short nativeType ( epicsGuard < epicsMutex > & ) const {return 0;} // DBR_STRING virtual arrayElementCount nativeElementCount ( epicsGuard < epicsMutex > & ) const {return 1;} }; #endif // DBCHANNELNOOP_H base-7.0.3.1/modules/database/src/ioc/db/dbCommon.dbd0000664000577000060420000001302513557101274020767 0ustar anjaesctl#************************************************************************* # Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* %#include "epicsTypes.h" %#include "link.h" field(NAME,DBF_STRING) { prompt("Record Name") special(SPC_NOMOD) size(61) } field(DESC,DBF_STRING) { prompt("Descriptor") promptgroup("10 - Common") size(41) } field(ASG,DBF_STRING) { prompt("Access Security Group") promptgroup("10 - Common") special(SPC_AS) size(29) } field(SCAN,DBF_MENU) { prompt("Scan Mechanism") promptgroup("20 - Scan") special(SPC_SCAN) interest(1) menu(menuScan) } field(PINI,DBF_MENU) { prompt("Process at iocInit") promptgroup("20 - Scan") interest(1) menu(menuPini) } field(PHAS,DBF_SHORT) { prompt("Scan Phase") promptgroup("20 - Scan") special(SPC_SCAN) interest(1) } field(EVNT,DBF_STRING) { prompt("Event Name") promptgroup("20 - Scan") special(SPC_SCAN) size(40) interest(1) } field(TSE,DBF_SHORT) { prompt("Time Stamp Event") promptgroup("20 - Scan") interest(1) } field(TSEL,DBF_INLINK) { prompt("Time Stamp Link") promptgroup("20 - Scan") interest(1) } field(DTYP,DBF_DEVICE) { prompt("Device Type") promptgroup("10 - Common") interest(1) } field(DISV,DBF_SHORT) { prompt("Disable Value") promptgroup("20 - Scan") initial("1") } field(DISA,DBF_SHORT) { prompt("Disable") } field(SDIS,DBF_INLINK) { prompt("Scanning Disable") promptgroup("20 - Scan") interest(1) } %#include "epicsMutex.h" field(MLOK,DBF_NOACCESS) { prompt("Monitor lock") special(SPC_NOMOD) interest(4) extra("epicsMutexId mlok") } %#include "ellLib.h" field(MLIS,DBF_NOACCESS) { prompt("Monitor List") special(SPC_NOMOD) interest(4) extra("ELLLIST mlis") } field(BKLNK,DBF_NOACCESS) { prompt("Backwards link tracking") special(SPC_NOMOD) interest(4) extra("ELLLIST bklnk") } field(DISP,DBF_UCHAR) { prompt("Disable putField") } field(PROC,DBF_UCHAR) { prompt("Force Processing") pp(TRUE) interest(3) } field(STAT,DBF_MENU) { prompt("Alarm Status") special(SPC_NOMOD) menu(menuAlarmStat) initial("UDF") } field(SEVR,DBF_MENU) { prompt("Alarm Severity") special(SPC_NOMOD) menu(menuAlarmSevr) } field(NSTA,DBF_MENU) { prompt("New Alarm Status") special(SPC_NOMOD) interest(2) menu(menuAlarmStat) } field(NSEV,DBF_MENU) { prompt("New Alarm Severity") special(SPC_NOMOD) interest(2) menu(menuAlarmSevr) } field(ACKS,DBF_MENU) { prompt("Alarm Ack Severity") special(SPC_NOMOD) interest(2) menu(menuAlarmSevr) } field(ACKT,DBF_MENU) { prompt("Alarm Ack Transient") promptgroup("70 - Alarm") special(SPC_NOMOD) interest(2) menu(menuYesNo) initial("YES") } field(DISS,DBF_MENU) { prompt("Disable Alarm Sevrty") promptgroup("70 - Alarm") interest(1) menu(menuAlarmSevr) } field(LCNT,DBF_UCHAR) { prompt("Lock Count") special(SPC_NOMOD) interest(2) } field(PACT,DBF_UCHAR) { prompt("Record active") special(SPC_NOMOD) interest(1) } field(PUTF,DBF_UCHAR) { prompt("dbPutField process") special(SPC_NOMOD) interest(1) } field(RPRO,DBF_UCHAR) { prompt("Reprocess ") special(SPC_NOMOD) interest(1) } field(ASP,DBF_NOACCESS) { prompt("Access Security Pvt") special(SPC_NOMOD) interest(4) extra("struct asgMember *asp") } field(PPN,DBF_NOACCESS) { prompt("pprocessNotify") special(SPC_NOMOD) interest(4) extra("struct processNotify *ppn") } field(PPNR,DBF_NOACCESS) { prompt("pprocessNotifyRecord") special(SPC_NOMOD) interest(4) extra("struct processNotifyRecord *ppnr") } field(SPVT,DBF_NOACCESS) { prompt("Scan Private") special(SPC_NOMOD) interest(4) extra("struct scan_element *spvt") } field(RSET,DBF_NOACCESS) { prompt("Address of RSET") special(SPC_NOMOD) interest(4) extra("struct typed_rset *rset") } field(DSET,DBF_NOACCESS) { prompt("DSET address") special(SPC_NOMOD) interest(4) extra("struct dset *dset") } field(DPVT,DBF_NOACCESS) { prompt("Device Private") special(SPC_NOMOD) interest(4) extra("void *dpvt") } field(RDES,DBF_NOACCESS) { prompt("Address of dbRecordType") special(SPC_NOMOD) interest(4) extra("struct dbRecordType *rdes") } field(LSET,DBF_NOACCESS) { prompt("Lock Set") special(SPC_NOMOD) interest(4) extra("struct lockRecord *lset") } field(PRIO,DBF_MENU) { prompt("Scheduling Priority") promptgroup("20 - Scan") special(SPC_SCAN) interest(1) menu(menuPriority) } field(TPRO,DBF_UCHAR) { prompt("Trace Processing") } field(BKPT,DBF_NOACCESS) { prompt("Break Point") special(SPC_NOMOD) interest(1) extra("char bkpt") } field(UDF,DBF_UCHAR) { prompt("Undefined") promptgroup("10 - Common") pp(TRUE) interest(1) initial("1") } field(UDFS,DBF_MENU) { prompt("Undefined Alarm Sevrty") promptgroup("70 - Alarm") interest(1) menu(menuAlarmSevr) initial("INVALID") } %#include "epicsTime.h" field(TIME,DBF_NOACCESS) { prompt("Time") special(SPC_NOMOD) interest(2) extra("epicsTimeStamp time") } field(FLNK,DBF_FWDLINK) { prompt("Forward Process Link") promptgroup("20 - Scan") interest(1) } base-7.0.3.1/modules/database/src/ioc/db/dbCommonPvt.h0000664000577000060420000000106613557101274021161 0ustar anjaesctl#ifndef DBCOMMONPVT_H #define DBCOMMONPVT_H #include #include #include "dbCommon.h" struct epicsThreadOSD; /** Base internal additional information for every record */ typedef struct dbCommonPvt { struct dbRecordNode *recnode; /* Thread which is currently processing this record */ struct epicsThreadOSD* procThread; struct dbCommon common; } dbCommonPvt; static EPICS_ALWAYS_INLINE dbCommonPvt* dbRec2Pvt(struct dbCommon *prec) { return CONTAINER(prec, dbCommonPvt, common); } #endif // DBCOMMONPVT_H base-7.0.3.1/modules/database/src/ioc/db/dbCommonRecord.dbd0000664000577000060420000000110413557101274022121 0ustar anjaesctl#************************************************************************* # 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. # 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. #************************************************************************* recordtype(dbCommon) { include "dbCommon.dbd" } base-7.0.3.1/modules/database/src/ioc/db/dbConstLink.c0000664000577000060420000001331713557101274021140 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbConstLink.c * * Original Authors: Bob Dalesio, Marty Kraimer * Current Author: Andrew Johnson */ #include #include #include "dbDefs.h" #include "epicsStdlib.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbCommon.h" #include "dbConstLink.h" #include "dbConvertJSON.h" #include "dbFldTypes.h" #include "dbLink.h" #include "link.h" /**************************** Convert functions ****************************/ /* The difference between these and dbFastConvert is that constants * may contain hex numbers, whereas database conversions can't. */ /* Copy to STRING */ static long cvt_st_st(const char *from, void *pfield, const dbAddr *paddr) { char *to = pfield; size_t size; if (paddr && paddr->field_size < MAX_STRING_SIZE) { size = paddr->field_size - 1; } else { size = MAX_STRING_SIZE - 1; } strncpy(to, from, size); to[size] = 0; return 0; } /* Most integer conversions are identical */ #define cvt_st_int(TYPE) static long \ cvt_st_ ## TYPE(const char *from, void *pfield, const dbAddr *paddr) { \ epics##TYPE *to = pfield; \ char *end; \ \ if (*from == 0) { \ *to = 0; \ return 0; \ } \ return epicsParse##TYPE(from, to, 0, &end); \ } /* Instanciate for CHAR, UCHAR, SHORT, USHORT and LONG */ cvt_st_int(Int8) cvt_st_int(UInt8) cvt_st_int(Int16) cvt_st_int(UInt16) cvt_st_int(Int32) /* Conversion for ULONG is different... */ static long cvt_st_UInt32(const char *from, void *pfield, const dbAddr *paddr) { epicsUInt32 *to = pfield; char *end; long status; if (*from == 0) { *to = 0; return 0; } status = epicsParseUInt32(from, to, 0, &end); if (status == S_stdlib_noConversion || (!status && (*end == '.' || *end == 'e' || *end == 'E'))) { /* * Convert via double so numbers like 1.0e3 convert properly. * db_access pretends ULONG fields are DOUBLE. */ double dval; status = epicsParseFloat64(from, &dval, &end); if (!status && dval >=0 && dval <= ULONG_MAX) *to = dval; } return status; } /* Instanciate for INT64 and UINT64 */ cvt_st_int(Int64) cvt_st_int(UInt64) /* Float conversions are identical */ #define cvt_st_float(TYPE) static long \ cvt_st_ ## TYPE(const char *from, void *pfield, const dbAddr *paddr) { \ epics##TYPE *to = pfield; \ char *end; \ \ if (*from == 0) { \ *to = 0; \ return 0; \ } \ return epicsParse##TYPE(from, to, &end); \ } /* Instanciate for FLOAT32 and FLOAT64 */ cvt_st_float(Float32) cvt_st_float(Float64) static long (*convert[DBF_DOUBLE+1])(const char *, void *, const dbAddr *) = { cvt_st_st, cvt_st_Int8, cvt_st_UInt8, cvt_st_Int16, cvt_st_UInt16, cvt_st_Int32, cvt_st_UInt32, cvt_st_Int64, cvt_st_UInt64, cvt_st_Float32, cvt_st_Float64 }; /***************************** Constant Links *****************************/ /* Forward definition */ static lset dbConst_lset; void dbConstInitLink(struct link *plink) { plink->lset = &dbConst_lset; } void dbConstAddLink(struct link *plink) { plink->lset = &dbConst_lset; } /**************************** Member functions ****************************/ static long dbConstLoadScalar(struct link *plink, short dbrType, void *pbuffer) { const char *pstr = plink->value.constantStr; size_t len; if (!pstr) return S_db_badField; len = strlen(pstr); /* Choice values must be numeric */ if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE) dbrType = DBF_USHORT; if (*pstr == '[' && pstr[len-1] == ']') { /* Convert from JSON array */ long nReq = 1; return dbPutConvertJSON(pstr, dbrType, pbuffer, &nReq); } return convert[dbrType](pstr, pbuffer, NULL); } static long dbConstLoadLS(struct link *plink, char *pbuffer, epicsUInt32 size, epicsUInt32 *plen) { const char *pstr = plink->value.constantStr; if (!pstr) return S_db_badField; return dbLSConvertJSON(pstr, pbuffer, size, plen); } static long dbConstLoadArray(struct link *plink, short dbrType, void *pbuffer, long *pnReq) { const char *pstr = plink->value.constantStr; if (!pstr) return S_db_badField; /* Choice values must be numeric */ if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE) dbrType = DBF_USHORT; return dbPutConvertJSON(pstr, dbrType, pbuffer, pnReq); } static long dbConstGetNelements(const struct link *plink, long *nelements) { *nelements = 0; return 0; } static long dbConstGetValue(struct link *plink, short dbrType, void *pbuffer, long *pnRequest) { if (pnRequest) *pnRequest = 0; return 0; } static long dbConstPutValue(struct link *plink, short dbrType, const void *pbuffer, long nRequest) { return 0; } static lset dbConst_lset = { 1, 0, /* Constant, not Volatile */ NULL, NULL, dbConstLoadScalar, dbConstLoadLS, dbConstLoadArray, NULL, NULL, dbConstGetNelements, dbConstGetValue, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dbConstPutValue, NULL, NULL, NULL }; base-7.0.3.1/modules/database/src/ioc/db/dbConstLink.h0000664000577000060420000000157213557101274021145 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbConstLink.h * * Created on: April 3rd, 2016 * Author: Andrew Johnson */ #ifndef INC_dbConstLink_H #define INC_dbConstLink_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif struct link; epicsShareFunc void dbConstInitLink(struct link *plink); epicsShareFunc void dbConstAddLink(struct link *plink); #ifdef __cplusplus } #endif #endif /* INC_dbConstLink_H */ base-7.0.3.1/modules/database/src/ioc/db/dbContext.cpp0000664000577000060420000003300013557101274021207 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include "epicsMutex.h" #include "tsFreeList.h" #include "cadef.h" // this can be eliminated when the callbacks use the new interface #include "db_access.h" // should be eliminated here in the future #include "caerr.h" // should be eliminated here in the future #include "epicsEvent.h" #include "epicsThread.h" #include "errlog.h" #define epicsExportSharedSymbols #include "db_access_routines.h" #include "dbCAC.h" #include "dbChannel.h" #include "dbChannelIO.h" #include "dbChannelNOOP.h" #include "dbPutNotifyBlocker.h" class dbService : public cacService { public: ~dbService () {} cacContext & contextCreate ( epicsMutex & mutualExclusion, epicsMutex & callbackControl, cacContextNotify & ); }; static dbService dbs; cacContext & dbService::contextCreate ( epicsMutex & mutualExclusion, epicsMutex & callbackControl, cacContextNotify & notify ) { return * new dbContext ( callbackControl, mutualExclusion, notify ); } extern "C" int dbServiceIsolate; int dbServiceIsolate = 0; extern "C" void dbServiceIOInit () { static int init=0; if(!init) { caInstallDefaultService ( dbs ); init=1; } } dbBaseIO::dbBaseIO () {} dbContext::dbContext ( epicsMutex & cbMutexIn, epicsMutex & mutexIn, cacContextNotify & notifyIn ) : readNotifyCache ( mutexIn ), ctx ( 0 ), stateNotifyCacheSize ( 0 ), mutex ( mutexIn ), cbMutex ( cbMutexIn ), notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 ), isolated(dbServiceIsolate) { } dbContext::~dbContext () { delete [] this->pStateNotifyCache; if ( this->ctx ) { db_close_events ( this->ctx ); } } cacChannel & dbContext::createChannel ( epicsGuard < epicsMutex > & guard, const char * pName, cacChannelNotify & notifyIn, cacChannel::priLev priority ) { guard.assertIdenticalMutex ( this->mutex ); dbChannel *dbch = dbChannel_create ( pName ); if ( ! dbch ) { if ( isolated ) { return *new dbChannelNOOP(pName, notifyIn); } else if ( ! this->pNetContext.get() ) { this->pNetContext.reset ( & this->notify.createNetworkContext ( this->mutex, this->cbMutex ) ); } return this->pNetContext->createChannel ( guard, pName, notifyIn, priority ); } if ( ! ca_preemtive_callback_is_enabled () ) { dbChannelDelete ( dbch ); errlogPrintf ( "dbContext: preemptive callback required for direct in\n" "memory interfacing of CA channels to the DB.\n" ); throw cacChannel::unsupportedByService (); } try { return * new ( this->dbChannelIOFreeList ) dbChannelIO ( this->mutex, notifyIn, dbch, *this ); } catch (...) { dbChannelDelete ( dbch ); throw; } } void dbContext::destroyChannel ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard, dbChannelIO & chan ) { guard.assertIdenticalMutex ( this->mutex ); if ( chan.dbContextPrivateListOfIO::pBlocker ) { this->ioTable.remove ( *chan.dbContextPrivateListOfIO::pBlocker ); chan.dbContextPrivateListOfIO::pBlocker->destructor ( cbGuard, guard ); this->dbPutNotifyBlockerFreeList.release ( chan.dbContextPrivateListOfIO::pBlocker ); chan.dbContextPrivateListOfIO::pBlocker = 0; } chan.destructor ( cbGuard, guard ); this->dbChannelIOFreeList.release ( & chan ); } void dbContext::callStateNotify ( struct dbChannel * dbch, unsigned type, unsigned long count, const struct db_field_log * pfl, cacStateNotify & notifyIn ) { long realcount = (count==0)?dbChannelElements(dbch):count; unsigned long size = dbr_size_n ( type, realcount ); if ( type > INT_MAX ) { epicsGuard < epicsMutex > guard ( this->mutex ); notifyIn.exception ( guard, ECA_BADTYPE, "type code out of range (high side)", type, count ); return; } if ( count > INT_MAX ) { epicsGuard < epicsMutex > guard ( this->mutex ); notifyIn.exception ( guard, ECA_BADCOUNT, "element count out of range (high side)", type, count); return; } // no need to lock this because state notify is // called from only one event queue consumer thread if ( this->stateNotifyCacheSize < size) { char * pTmp = new char [size]; delete [] this->pStateNotifyCache; this->pStateNotifyCache = pTmp; this->stateNotifyCacheSize = size; } void *pvfl = (void *) pfl; int status; if(count==0) /* fetch actual number of elements (dynamic array) */ status = dbChannel_get_count( dbch, static_cast ( type ), this->pStateNotifyCache, &realcount, pvfl ); else /* fetch requested number of elements, truncated or zero padded */ status = dbChannel_get( dbch, static_cast ( type ), this->pStateNotifyCache, realcount, pvfl ); if ( status ) { epicsGuard < epicsMutex > guard ( this->mutex ); notifyIn.exception ( guard, ECA_GETFAIL, "dbChannel_get() completed unsuccessfully", type, count ); } else { epicsGuard < epicsMutex > guard ( this->mutex ); notifyIn.current ( guard, type, realcount, this->pStateNotifyCache ); } } extern "C" void cacAttachClientCtx ( void * pPrivate ) { int status = ca_attach_context ( (ca_client_context *) pPrivate ); assert ( status == ECA_NORMAL ); } void dbContext::subscribe ( epicsGuard < epicsMutex > & guard, struct dbChannel * dbch, dbChannelIO & chan, unsigned type, unsigned long count, unsigned mask, cacStateNotify & notifyIn, cacChannel::ioid * pId ) { guard.assertIdenticalMutex ( this->mutex ); /* * the database uses type "int" to store these parameters */ if ( type > INT_MAX ) { throw cacChannel::badType(); } if ( count > INT_MAX ) { throw cacChannel::outOfBounds(); } if ( ! this->ctx ) { dbEventCtx tmpctx = 0; { epicsGuardRelease < epicsMutex > unguard ( guard ); tmpctx = db_init_events (); if ( ! tmpctx ) { throw std::bad_alloc (); } unsigned selfPriority = epicsThreadGetPrioritySelf (); unsigned above; epicsThreadBooleanStatus tbs = epicsThreadLowestPriorityLevelAbove ( selfPriority, &above ); if ( tbs != epicsThreadBooleanStatusSuccess ) { above = selfPriority; } int status = db_start_events ( tmpctx, "CAC-event", cacAttachClientCtx, ca_current_context (), above ); if ( status ) { db_close_events ( tmpctx ); throw std::bad_alloc (); } } if ( this->ctx ) { // another thread tried to simultaneously setup // the event system db_close_events ( tmpctx ); } else { this->ctx = tmpctx; } } dbSubscriptionIO & subscr = * new ( this->dbSubscriptionIOFreeList ) dbSubscriptionIO ( guard, this->mutex, *this, chan, dbch, notifyIn, type, count, mask, this->ctx ); chan.dbContextPrivateListOfIO::eventq.add ( subscr ); this->ioTable.idAssignAdd ( subscr ); if ( pId ) { *pId = subscr.getId (); } } void dbContext::initiatePutNotify ( epicsGuard < epicsMutex > & guard, dbChannelIO & chan, struct dbChannel * dbch, unsigned type, unsigned long count, const void * pValue, cacWriteNotify & notifyIn, cacChannel::ioid * pId ) { guard.assertIdenticalMutex ( this->mutex ); if ( ! chan.dbContextPrivateListOfIO::pBlocker ) { chan.dbContextPrivateListOfIO::pBlocker = new ( this->dbPutNotifyBlockerFreeList ) dbPutNotifyBlocker ( this->mutex ); this->ioTable.idAssignAdd ( *chan.dbContextPrivateListOfIO::pBlocker ); } chan.dbContextPrivateListOfIO::pBlocker->initiatePutNotify ( guard, notifyIn, dbch, type, count, pValue ); if ( pId ) { *pId = chan.dbContextPrivateListOfIO::pBlocker->getId (); } } void dbContext::destroyAllIO ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard, dbChannelIO & chan ) { guard.assertIdenticalMutex ( this->mutex ); dbSubscriptionIO * pIO; tsDLList < dbSubscriptionIO > tmp; while ( ( pIO = chan.dbContextPrivateListOfIO::eventq.get() ) ) { this->ioTable.remove ( *pIO ); tmp.add ( *pIO ); } if ( chan.dbContextPrivateListOfIO::pBlocker ) { this->ioTable.remove ( *chan.dbContextPrivateListOfIO::pBlocker ); } while ( ( pIO = tmp.get() ) ) { // This prevents a db event callback from coming // through after the notify IO is deleted pIO->unsubscribe ( cbGuard, guard ); // If they call ioCancel() here it will be ignored // because the IO has been unregistered above. pIO->channelDeleteException ( cbGuard, guard ); pIO->destructor ( cbGuard, guard ); this->dbSubscriptionIOFreeList.release ( pIO ); } if ( chan.dbContextPrivateListOfIO::pBlocker ) { chan.dbContextPrivateListOfIO::pBlocker->destructor ( cbGuard, guard ); this->dbPutNotifyBlockerFreeList.release ( chan.dbContextPrivateListOfIO::pBlocker ); chan.dbContextPrivateListOfIO::pBlocker = 0; } } void dbContext::ioCancel ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard, dbChannelIO & chan, const cacChannel::ioid &id ) { guard.assertIdenticalMutex ( this->mutex ); dbBaseIO * pIO = this->ioTable.remove ( id ); if ( pIO ) { dbSubscriptionIO *pSIO = pIO->isSubscription (); if ( pSIO ) { chan.dbContextPrivateListOfIO::eventq.remove ( *pSIO ); pSIO->unsubscribe ( cbGuard, guard ); pSIO->channelDeleteException ( cbGuard, guard ); pSIO->destructor ( cbGuard, guard ); this->dbSubscriptionIOFreeList.release ( pSIO ); } else if ( pIO == chan.dbContextPrivateListOfIO::pBlocker ) { chan.dbContextPrivateListOfIO::pBlocker->cancel ( cbGuard, guard ); } else { errlogPrintf ( "dbContext::ioCancel() unrecognized IO was probably leaked or not canceled\n" ); } } } void dbContext::ioShow ( epicsGuard < epicsMutex > & guard, const cacChannel::ioid &id, unsigned level ) const { guard.assertIdenticalMutex ( this->mutex ); const dbBaseIO * pIO = this->ioTable.lookup ( id ); if ( pIO ) { pIO->show ( guard, level ); } } void dbContext::showAllIO ( const dbChannelIO & chan, unsigned level ) const { epicsGuard < epicsMutex > guard ( this->mutex ); tsDLIterConst < dbSubscriptionIO > pItem = chan.dbContextPrivateListOfIO::eventq.firstIter (); while ( pItem.valid () ) { pItem->show ( guard, level ); pItem++; } if ( chan.dbContextPrivateListOfIO::pBlocker ) { chan.dbContextPrivateListOfIO::pBlocker->show ( guard, level ); } } void dbContext::show ( unsigned level ) const { epicsGuard < epicsMutex > guard ( this->mutex ); this->show ( guard, level ); } void dbContext::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( this->mutex ); printf ( "dbContext at %p\n", static_cast ( this ) ); if ( level > 0u ) { printf ( "\tevent call back cache location %p, and its size %lu\n", static_cast ( this->pStateNotifyCache ), this->stateNotifyCacheSize ); this->readNotifyCache.show ( guard, level - 1 ); } if ( level > 1u ) { this->mutex.show ( level - 2u ); } if ( this->pNetContext.get() ) { this->pNetContext.get()->show ( guard, level ); } } void dbContext::flush ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( this->pNetContext.get() ) { this->pNetContext.get()->flush ( guard ); } } unsigned dbContext::circuitCount ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); if ( this->pNetContext.get() ) { return this->pNetContext.get()->circuitCount ( guard ); } else { return 0u; } } void dbContext::selfTest ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); this->ioTable.verify (); if ( this->pNetContext.get() ) { this->pNetContext.get()->selfTest ( guard ); } } unsigned dbContext::beaconAnomaliesSinceProgramStart ( epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); if ( this->pNetContext.get() ) { return this->pNetContext.get()->beaconAnomaliesSinceProgramStart ( guard ); } else { return 0u; } } base-7.0.3.1/modules/database/src/ioc/db/dbContextReadNotifyCache.cpp0000664000577000060420000001235713557101274024134 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Auther Jeff Hill */ #include #include "epicsMutex.h" #include "dbDefs.h" #include "cadef.h" // this can be eliminated when the callbacks use the new interface #include "db_access.h" // should be eliminated here in the future #define epicsExportSharedSymbols #include "db_access_routines.h" #include "dbCAC.h" #include "epicsAssert.h" dbContextReadNotifyCache::dbContextReadNotifyCache ( epicsMutex & mutexIn ) : _mutex ( mutexIn ) { } class privateAutoDestroyPtr { public: privateAutoDestroyPtr ( dbContextReadNotifyCacheAllocator & allocator, unsigned long size ) : _allocator ( allocator ), _p ( allocator.alloc ( size ) ) {} ~privateAutoDestroyPtr () { _allocator.free ( _p ); } char * get () const { return _p; } private: dbContextReadNotifyCacheAllocator & _allocator; char * _p; privateAutoDestroyPtr ( const privateAutoDestroyPtr & ); privateAutoDestroyPtr & operator = ( const privateAutoDestroyPtr & ); }; // extra effort taken here to not hold the lock when calling the callback void dbContextReadNotifyCache::callReadNotify ( epicsGuard < epicsMutex > & guard, struct dbChannel * dbch, unsigned type, unsigned long count, cacReadNotify & notify ) { guard.assertIdenticalMutex ( _mutex ); if ( type > INT_MAX ) { notify.exception ( guard, ECA_BADTYPE, "type code out of range (high side)", type, count ); return; } const long maxcount = dbChannelElements(dbch); if ( maxcount < 0 ) { notify.exception ( guard, ECA_BADCOUNT, "database has negetive element count", type, count); return; } else if ( count > (unsigned long)maxcount ) { notify.exception ( guard, ECA_BADCOUNT, "element count out of range (high side)", type, count); return; } long realcount = (count==0)?maxcount:count; unsigned long size = dbr_size_n ( type, realcount ); privateAutoDestroyPtr ptr ( _allocator, size ); int status; { epicsGuardRelease < epicsMutex > unguard ( guard ); if ( count==0 ) status = dbChannel_get_count ( dbch, (int)type, ptr.get(), &realcount, 0); else status = dbChannel_get ( dbch, (int)type, ptr.get (), realcount, 0 ); } if ( status ) { notify.exception ( guard, ECA_GETFAIL, "db_get_field() completed unsuccessfuly", type, count ); } else { notify.completion ( guard, type, realcount, ptr.get () ); } } void dbContextReadNotifyCache::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( _mutex ); printf ( "dbContextReadNotifyCache\n" ); if ( level > 0 ) { this->_allocator.show ( level - 1 ); } } dbContextReadNotifyCacheAllocator::dbContextReadNotifyCacheAllocator () : _readNotifyCacheSize ( 0 ), _pReadNotifyCache ( 0 ) { } dbContextReadNotifyCacheAllocator::~dbContextReadNotifyCacheAllocator () { this->reclaimAllCacheEntries (); } void dbContextReadNotifyCacheAllocator::reclaimAllCacheEntries () { while ( _pReadNotifyCache ) { cacheElem_t * pNext = _pReadNotifyCache->pNext; assert(_pReadNotifyCache->size == _readNotifyCacheSize); ::free(_pReadNotifyCache); _pReadNotifyCache = pNext; } } char * dbContextReadNotifyCacheAllocator::alloc ( unsigned long size ) { if ( size > _readNotifyCacheSize ) { this->reclaimAllCacheEntries (); _readNotifyCacheSize = size; } cacheElem_t * pAlloc = _pReadNotifyCache; if ( pAlloc ) { assert(pAlloc->size == _readNotifyCacheSize); _pReadNotifyCache = pAlloc->pNext; } else { pAlloc = (cacheElem_t*)calloc(1, sizeof(cacheElem_t)+_readNotifyCacheSize); if(!pAlloc) throw std::bad_alloc(); pAlloc->size = _readNotifyCacheSize; } return pAlloc->buf; } void dbContextReadNotifyCacheAllocator::free ( char * pFree ) { cacheElem_t * pAlloc = (cacheElem_t*)(pFree - offsetof(cacheElem_t, buf)); if (pAlloc->size == _readNotifyCacheSize) { pAlloc->pNext = _pReadNotifyCache; _pReadNotifyCache = pAlloc; } else { ::free(pAlloc); } } void dbContextReadNotifyCacheAllocator::show ( unsigned level ) const { printf ( "dbContextReadNotifyCacheAlocator\n" ); if ( level > 0 ) { size_t count =0; cacheElem_t * pNext = _pReadNotifyCache; while ( pNext ) { assert(pNext->size == _readNotifyCacheSize); pNext = _pReadNotifyCache->pNext; count++; } printf ( "\tcount %lu and size %lu\n", static_cast < unsigned long > ( count ), _readNotifyCacheSize ); } } base-7.0.3.1/modules/database/src/ioc/db/dbConvert.c0000664000577000060420000016453613557101274020666 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbConvert.c */ /* * Original Author: Bob Dalesio * Date: 11-7-90 */ #include #include #include #include #include #include "cvtFast.h" #include "dbDefs.h" #include "epicsConvert.h" #include "epicsStdlib.h" #include "errlog.h" #include "errMdef.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" #include "dbConvert.h" #include "dbFldTypes.h" #include "dbStaticLib.h" #include "link.h" #include "recGbl.h" #include "recSup.h" /* Helper for copy as bytes with no type conversion. * Assumes nRequest <= no_bytes * nRequest, no_bytes, and offset should be given in bytes. */ static void copyNoConvert(const void *pfrom, void *pto, long nRequest, long no_bytes, long offset) { const void *pfrom_offset = (const char *) pfrom + offset; if (offset > 0 && offset < no_bytes && offset + nRequest > no_bytes) { const size_t N = no_bytes - offset; void *pto_N = (char *) pto + N; /* copy with wrap */ memmove(pto, pfrom_offset, N); memmove(pto_N, pfrom, nRequest - N); } else { /* no wrap, just copy */ memmove(pto, pfrom_offset, nRequest); } } #define COPYNOCONVERT(N, FROM, TO, NREQ, NO_ELEM, OFFSET) \ copyNoConvert(FROM, TO, (N)*(NREQ), (N)*(NO_ELEM), (N)*(OFFSET)) #define GET(typea, typeb) (const dbAddr *paddr, \ void *pto, long nRequest, long no_elements, long offset) \ { \ typea *psrc = (typea *) paddr->pfield; \ typeb *pdst = (typeb *) pto; \ \ if (nRequest==1 && offset==0) { \ *pdst = (typeb) *psrc; \ return 0; \ } \ psrc += offset; \ while (nRequest--) { \ *pdst++ = (typeb) *psrc++; \ if (++offset == no_elements) \ psrc = (typea *) paddr->pfield; \ } \ return 0; \ } #define GET_NOCONVERT(typea, typeb) (const dbAddr *paddr, \ void *pto, long nRequest, long no_elements, long offset) \ { \ if (nRequest==1 && offset==0) { \ typea *psrc = (typea *) paddr->pfield; \ typeb *pdst = (typeb *) pto; \ \ *pdst = (typeb) *psrc; \ return 0; \ } \ COPYNOCONVERT(sizeof(typeb), paddr->pfield, pto, nRequest, no_elements, offset); \ return 0; \ } #define PUT(typea, typeb) (dbAddr *paddr, \ const void *pfrom, long nRequest, long no_elements, long offset) \ { \ const typea *psrc = (const typea *) pfrom; \ typeb *pdst = (typeb *) paddr->pfield; \ \ if (nRequest==1 && offset==0) { \ *pdst = (typeb) *psrc; \ return 0; \ } \ pdst += offset; \ while (nRequest--) { \ *pdst++ = (typeb) *psrc++; \ if (++offset == no_elements) \ pdst = (typeb *) paddr->pfield; \ } \ return 0; \ } #define PUT_NOCONVERT(typea, typeb) (dbAddr *paddr, \ const void *pfrom, long nRequest, long no_elements, long offset) \ { \ if (nRequest==1 && offset==0) { \ const typea *psrc = (const typea *) pfrom; \ typeb *pdst = (typeb *) paddr->pfield; \ \ *pdst = (typeb) *psrc; \ return 0; \ } \ COPYNOCONVERT(sizeof(typeb), pfrom, paddr->pfield, nRequest, no_elements, offset); \ return 0; \ } /* dbAccess Get conversion support routines */ static long getStringString(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *psrc = paddr->pfield; char *pdst = (char *) pto; short size = paddr->field_size; short sizeto; /* always force result string to be null terminated*/ sizeto = size; if (sizeto >= MAX_STRING_SIZE) sizeto = MAX_STRING_SIZE - 1; if (nRequest==1 && offset==0) { strncpy(pdst, psrc, sizeto); pdst[sizeto] = 0; return 0; } psrc += size * offset; while (nRequest--) { strncpy(pdst, psrc, sizeto); pdst[sizeto] = 0; pdst += MAX_STRING_SIZE; if (++offset == no_elements) psrc = paddr->pfield; else psrc += size; } return 0; } static long getStringChar(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *psrc = (char *) paddr->pfield + MAX_STRING_SIZE * offset; epicsInt8 *pdst = pto; while (nRequest--) { if (*psrc == 0) *pdst++ = 0; else { char *end; long status = epicsParseInt8(psrc, pdst++, 10, &end); if (status) return status; } if (++offset == no_elements) psrc = paddr->pfield; else psrc += MAX_STRING_SIZE; } return 0; } static long getStringUchar(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *psrc = (char *) paddr->pfield + MAX_STRING_SIZE * offset; epicsUInt8 *pdst = pto; while (nRequest--) { if (*psrc == 0) *pdst++ = 0; else { char *end; long status = epicsParseUInt8(psrc, pdst++, 10, &end); if (status) return status; } if (++offset == no_elements) psrc = paddr->pfield; else psrc += MAX_STRING_SIZE; } return 0; } static long getStringShort(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *psrc = (char *) paddr->pfield + MAX_STRING_SIZE * offset; epicsInt16 *pdst = pto; while (nRequest--) { if (*psrc == 0) *pdst++ = 0; else { char *end; long status = epicsParseInt16(psrc, pdst++, 10, &end); if (status) return status; } if (++offset == no_elements) psrc = paddr->pfield; else psrc += MAX_STRING_SIZE; } return 0; } static long getStringUshort(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *psrc = (char *) paddr->pfield + MAX_STRING_SIZE * offset; epicsUInt16 *pdst = pto; while (nRequest--) { if (*psrc == 0) *pdst++ = 0; else { char *end; long status = epicsParseUInt16(psrc, pdst++, 10, &end); if (status) return status; } if (++offset == no_elements) psrc = paddr->pfield; else psrc += MAX_STRING_SIZE; } return 0; } static long getStringLong(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *psrc = (char *) paddr->pfield + MAX_STRING_SIZE * offset; epicsInt32 *pdst = pto; while (nRequest--) { if (*psrc == 0) *pdst++ = 0; else { char *end; long status = epicsParseInt32(psrc, pdst++, 10, &end); if (status) return status; } if (++offset == no_elements) psrc = paddr->pfield; else psrc += MAX_STRING_SIZE; } return 0; } static long getStringUlong(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *psrc = (char *) paddr->pfield + MAX_STRING_SIZE * offset; epicsUInt32 *pdst = pto; while (nRequest--) { if (*psrc == 0) *pdst++ = 0; else { char *end; long status = epicsParseUInt32(psrc, pdst, 10, &end); if (status == S_stdlib_noConversion || (!status && (*end == '.' || *end == 'e' || *end == 'E'))) { /* * Convert via double so numbers like 1.0e3 convert properly. * db_access pretends unsigned long is double. */ epicsFloat64 dval; status = epicsParseFloat64(psrc, &dval, &end); if (!status && 0 <= dval && dval <= ULONG_MAX) *pdst = dval; } if (status) return status; pdst++; } if (++offset == no_elements) psrc = paddr->pfield; else psrc += MAX_STRING_SIZE; } return 0; } static long getStringInt64(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *psrc = (char *) paddr->pfield + MAX_STRING_SIZE * offset; epicsInt64 *pdst = pto; while (nRequest--) { if (*psrc == 0) *pdst++ = 0; else { char *end; long status = epicsParseInt64(psrc, pdst++, 10, &end); if (status) return status; } if (++offset == no_elements) psrc = paddr->pfield; else psrc += MAX_STRING_SIZE; } return 0; } static long getStringUInt64(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *psrc = (char *) paddr->pfield + MAX_STRING_SIZE * offset; epicsUInt64 *pdst = pto; while (nRequest--) { if (*psrc == 0) *pdst++ = 0; else { char *end; long status = epicsParseUInt64(psrc, pdst++, 0, &end); if (status) return status; } if (++offset == no_elements) psrc = paddr->pfield; else psrc += MAX_STRING_SIZE; } return 0; } static long getStringFloat(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *psrc = (char *) paddr->pfield + MAX_STRING_SIZE * offset; epicsFloat32 *pdst = pto; while (nRequest--) { if (*psrc == 0) *pdst++ = 0; else { char *end; long status = epicsParseFloat32(psrc, pdst++, &end); if (status) return status; } if (++offset == no_elements) psrc = paddr->pfield; else psrc += MAX_STRING_SIZE; } return 0; } static long getStringDouble(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *psrc = (char *) paddr->pfield + MAX_STRING_SIZE * offset; epicsFloat64 *pdst = pto; while (nRequest--) { if (*psrc == 0) *pdst++ = 0; else { char *end; long status = epicsParseFloat64(psrc, pdst++, &end); if (status) return status; } if (++offset == no_elements) psrc = paddr->pfield; else psrc += MAX_STRING_SIZE; } return 0; } static long getCharString(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *psrc = (char *) paddr->pfield; char *pdst = (char *) pto; if (nRequest==1 && offset==0) { cvtCharToString(*psrc, pdst); return 0; } psrc += offset; while (nRequest--) { cvtCharToString(*psrc, pdst); pdst += MAX_STRING_SIZE; if (++offset == no_elements) psrc = (char *) paddr->pfield; else psrc++; } return 0; } static long getCharChar(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *psrc = (char *) paddr->pfield; char *pdst = (char *) pto; if (paddr->pfldDes && paddr->pfldDes->field_type == DBF_STRING) { /* This is a DBF_STRING field being read as a long string. * The buffer we return must be zero-terminated. */ pdst[--nRequest] = 0; if (nRequest == 0) return 0; } if (nRequest==1 && offset==0) { *pdst = *psrc; return 0; } COPYNOCONVERT(sizeof(char), paddr->pfield, pto, nRequest, no_elements, offset); return 0; } static long getCharUchar(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *psrc = (char *) paddr->pfield; epicsUInt8 *pdst = (epicsUInt8 *) pto; if (paddr->pfldDes && paddr->pfldDes->field_type == DBF_STRING) { /* This is a DBF_STRING field being read as a long string. * The buffer we return must be zero-terminated. */ pdst[--nRequest] = 0; if (nRequest == 0) return 0; } if (nRequest==1 && offset==0) { *pdst = *psrc; return 0; } COPYNOCONVERT(sizeof(char), paddr->pfield, pto, nRequest, no_elements, offset); return 0; } static long getCharShort GET(char, epicsInt16) static long getCharUshort GET(char, epicsUInt16) static long getCharLong GET(char, epicsInt32) static long getCharUlong GET(char, epicsUInt32) static long getCharInt64 GET(char, epicsInt64) static long getCharUInt64 GET(char, epicsUInt64) static long getCharFloat GET(char, epicsFloat32) static long getCharDouble GET(char, epicsFloat64) static long getCharEnum GET(char, epicsEnum16) static long getUcharString(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { epicsUInt8 *psrc = (epicsUInt8 *) paddr->pfield; char *pdst = (char *) pto; if (nRequest==1 && offset==0) { cvtUcharToString(*psrc, pdst); return 0; } psrc += offset; while (nRequest--) { cvtUcharToString(*psrc, pdst); pdst += MAX_STRING_SIZE; if (++offset == no_elements) psrc = (epicsUInt8 *) paddr->pfield; else psrc++; } return 0; } static long getUcharChar GET_NOCONVERT(epicsUInt8, char) static long getUcharUchar GET_NOCONVERT(epicsUInt8, epicsUInt8) static long getUcharShort GET(epicsUInt8, epicsInt16) static long getUcharUshort GET(epicsUInt8, epicsUInt16) static long getUcharLong GET(epicsUInt8, epicsInt32) static long getUcharUlong GET(epicsUInt8, epicsUInt32) static long getUcharInt64 GET(epicsUInt8, epicsInt64) static long getUcharUInt64 GET(epicsUInt8, epicsUInt64) static long getUcharFloat GET(epicsUInt8, epicsFloat32) static long getUcharDouble GET(epicsUInt8, epicsFloat64) static long getUcharEnum GET(epicsUInt8, epicsEnum16) static long getShortString(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { epicsInt16 *psrc = (epicsInt16 *) paddr->pfield; char *pdst = (char *) pto; if (nRequest==1 && offset==0) { cvtShortToString(*psrc, pdst); return 0; } psrc += offset; while (nRequest--) { cvtShortToString(*psrc, pdst); pdst += MAX_STRING_SIZE; if (++offset == no_elements) psrc = (epicsInt16 *) paddr->pfield; else psrc++; } return 0; } static long getShortChar GET(epicsInt16, char) static long getShortUchar GET(epicsInt16, epicsUInt8) static long getShortShort GET_NOCONVERT(epicsInt16, epicsInt16) static long getShortUshort GET_NOCONVERT(epicsInt16, epicsUInt16) static long getShortLong GET(epicsInt16, epicsInt32) static long getShortUlong GET(epicsInt16, epicsUInt32) static long getShortInt64 GET(epicsInt16, epicsInt64) static long getShortUInt64 GET(epicsInt16, epicsUInt64) static long getShortFloat GET(epicsInt16, epicsFloat32) static long getShortDouble GET(epicsInt16, epicsFloat64) static long getShortEnum GET(epicsInt16, epicsEnum16) static long getUshortString(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { epicsUInt16 *psrc = (epicsUInt16 *) paddr->pfield; char *pdst = (char *) pto; if (nRequest==1 && offset==0) { cvtUshortToString(*psrc, pdst); return 0; } psrc += offset; while (nRequest--) { cvtUshortToString(*psrc, pdst); pdst += MAX_STRING_SIZE; if (++offset == no_elements) psrc = (epicsUInt16 *) paddr->pfield; else psrc++; } return 0; } static long getUshortChar GET(epicsUInt16, char) static long getUshortUchar GET(epicsUInt16, epicsUInt8) static long getUshortShort GET_NOCONVERT(epicsUInt16, epicsInt16) static long getUshortUshort GET_NOCONVERT(epicsUInt16, epicsUInt16) static long getUshortLong GET(epicsUInt16, epicsInt32) static long getUshortUlong GET(epicsUInt16, epicsUInt32) static long getUshortInt64 GET(epicsUInt16, epicsInt64) static long getUshortUInt64 GET(epicsUInt16, epicsUInt64) static long getUshortFloat GET(epicsUInt16, epicsFloat32) static long getUshortDouble GET(epicsUInt16, epicsFloat64) static long getUshortEnum GET(epicsUInt16, epicsEnum16) static long getLongString(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { epicsInt32 *psrc = (epicsInt32 *) paddr->pfield; char *pdst = (char *) pto; if (nRequest==1 && offset==0) { cvtLongToString(*psrc, pdst); return 0; } psrc += offset; while (nRequest--) { cvtLongToString(*psrc, pdst); pdst += MAX_STRING_SIZE; if (++offset == no_elements) psrc = (epicsInt32 *) paddr->pfield; else psrc++; } return 0; } static long getLongChar GET(epicsInt32, char) static long getLongUchar GET(epicsInt32, epicsUInt8) static long getLongShort GET(epicsInt32, epicsInt16) static long getLongUshort GET(epicsInt32, epicsUInt16) static long getLongLong GET_NOCONVERT(epicsInt32, epicsInt32) static long getLongUlong GET_NOCONVERT(epicsInt32, epicsUInt32) static long getLongInt64 GET(epicsInt32, epicsInt64) static long getLongUInt64 GET(epicsInt32, epicsUInt64) static long getLongFloat GET(epicsInt32, epicsFloat32) static long getLongDouble GET(epicsInt32, epicsFloat64) static long getLongEnum GET(epicsInt32, epicsEnum16) static long getUlongString(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { epicsUInt32 *psrc = (epicsUInt32 *) paddr->pfield; char *pdst = (char *) pto; if (nRequest==1 && offset==0) { cvtUlongToString(*psrc, pdst); return 0; } psrc += offset; while (nRequest--) { cvtUlongToString(*psrc, pdst); pdst += MAX_STRING_SIZE; if (++offset == no_elements) psrc = (epicsUInt32 *) paddr->pfield; else psrc++; } return 0; } static long getUlongChar GET(epicsUInt32, char) static long getUlongUchar GET(epicsUInt32, epicsUInt8) static long getUlongShort GET(epicsUInt32, epicsInt16) static long getUlongUshort GET(epicsUInt32, epicsUInt16) static long getUlongLong GET_NOCONVERT(epicsUInt32, epicsInt32) static long getUlongUlong GET_NOCONVERT(epicsUInt32, epicsUInt32) static long getUlongInt64 GET(epicsUInt32, epicsInt64) static long getUlongUInt64 GET(epicsUInt32, epicsUInt64) static long getUlongFloat GET(epicsUInt32, epicsFloat32) static long getUlongDouble GET(epicsUInt32, epicsFloat64) static long getUlongEnum GET(epicsUInt32, epicsEnum16) static long getInt64String(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { epicsInt64 *psrc = (epicsInt64 *) paddr->pfield; char *pdst = (char *) pto; if (nRequest==1 && offset==0) { cvtInt64ToString(*psrc, pdst); return 0; } psrc += offset; while (nRequest--) { cvtInt64ToString(*psrc, pdst); pdst += MAX_STRING_SIZE; if (++offset == no_elements) psrc = (epicsInt64 *) paddr->pfield; else psrc++; } return 0; } static long getInt64Char GET(epicsInt64, char) static long getInt64Uchar GET(epicsInt64, epicsUInt8) static long getInt64Short GET(epicsInt64, epicsInt16) static long getInt64Ushort GET(epicsInt64, epicsUInt16) static long getInt64Long GET(epicsInt64, epicsInt32) static long getInt64Ulong GET(epicsInt64, epicsUInt32) static long getInt64Int64 GET_NOCONVERT(epicsInt64, epicsInt64) static long getInt64UInt64 GET_NOCONVERT(epicsInt64, epicsUInt64) static long getInt64Float GET(epicsInt64, epicsFloat32) static long getInt64Double GET(epicsInt64, epicsFloat64) static long getInt64Enum GET(epicsInt64, epicsEnum16) static long getUInt64String(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { epicsUInt64 *psrc = (epicsUInt64 *) paddr->pfield; char *pdst = (char *) pto; if (nRequest==1 && offset==0) { cvtUInt64ToString(*psrc, pdst); return 0; } psrc += offset; while (nRequest--) { cvtUInt64ToString(*psrc, pdst); pdst += MAX_STRING_SIZE; if (++offset == no_elements) psrc = (epicsUInt64 *) paddr->pfield; else psrc++; } return 0; } static long getUInt64Char GET(epicsUInt64, char) static long getUInt64Uchar GET(epicsUInt64, epicsUInt8) static long getUInt64Short GET(epicsUInt64, epicsInt16) static long getUInt64Ushort GET(epicsUInt64, epicsUInt16) static long getUInt64Long GET(epicsUInt64, epicsInt32) static long getUInt64Ulong GET(epicsUInt64, epicsUInt32) static long getUInt64Int64 GET_NOCONVERT(epicsUInt64, epicsInt64) static long getUInt64UInt64 GET_NOCONVERT(epicsUInt64, epicsUInt64) static long getUInt64Float GET(epicsUInt64, epicsFloat32) static long getUInt64Double GET(epicsUInt64, epicsFloat64) static long getUInt64Enum GET(epicsUInt64, epicsEnum16) static long getFloatString(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { epicsFloat32 *psrc = (epicsFloat32 *) paddr->pfield; char *pdst = (char *) pto; long status = 0; long precision = 6; rset *prset = 0; if (paddr) prset = dbGetRset(paddr); if (prset && prset->get_precision) status = prset->get_precision(paddr, &precision); if (nRequest==1 && offset==0) { cvtFloatToString(*psrc, pdst, precision); return(status); } psrc += offset; while (nRequest--) { cvtFloatToString(*psrc, pdst, precision); pdst += MAX_STRING_SIZE; if (++offset == no_elements) psrc = (epicsFloat32 *) paddr->pfield; else psrc++; } return(status); } static long getFloatChar GET(epicsFloat32, char) static long getFloatUchar GET(epicsFloat32, epicsUInt8) static long getFloatShort GET(epicsFloat32, epicsInt16) static long getFloatUshort GET(epicsFloat32, epicsUInt16) static long getFloatLong GET(epicsFloat32, epicsInt32) static long getFloatUlong GET(epicsFloat32, epicsUInt32) static long getFloatInt64 GET(epicsFloat32, epicsInt64) static long getFloatUInt64 GET(epicsFloat32, epicsUInt64) static long getFloatFloat GET_NOCONVERT(epicsFloat32, epicsFloat32) static long getFloatDouble GET(epicsFloat32, epicsFloat64) static long getFloatEnum GET(epicsFloat32, epicsEnum16) static long getDoubleString(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { epicsFloat64 *psrc = (epicsFloat64 *) paddr->pfield; char *pdst = (char *) pto; long status = 0; long precision = 6; rset *prset = 0; if (paddr) prset = dbGetRset(paddr); if (prset && prset->get_precision) status = prset->get_precision(paddr, &precision); if (nRequest==1 && offset==0) { cvtDoubleToString(*psrc, pdst, precision); return(status); } psrc += offset; while (nRequest--) { cvtDoubleToString(*psrc, pdst, precision); pdst += MAX_STRING_SIZE; if (++offset == no_elements) psrc = (epicsFloat64 *) paddr->pfield; else psrc++; } return(status); } static long getDoubleChar GET(epicsFloat64, char) static long getDoubleUchar GET(epicsFloat64, epicsUInt8) static long getDoubleShort GET(epicsFloat64, epicsInt16) static long getDoubleUshort GET(epicsFloat64, epicsUInt16) static long getDoubleLong GET(epicsFloat64, epicsInt32) static long getDoubleUlong GET(epicsFloat64, epicsUInt32) static long getDoubleInt64 GET(epicsFloat64, epicsInt64) static long getDoubleUInt64 GET(epicsFloat64, epicsUInt64) static long getDoubleFloat(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { epicsFloat64 *psrc = (epicsFloat64 *) paddr->pfield; epicsFloat32 *pdst = (epicsFloat32 *) pto; if (nRequest==1 && offset==0) { *pdst = epicsConvertDoubleToFloat(*psrc); return 0; } psrc += offset; while (nRequest--) { *pdst = epicsConvertDoubleToFloat(*psrc); ++psrc; ++pdst; if (++offset == no_elements) psrc = (epicsFloat64 *) paddr->pfield; } return 0; } static long getDoubleDouble GET_NOCONVERT(epicsFloat64, epicsFloat64) static long getDoubleEnum GET(epicsFloat64, epicsEnum16) static long getEnumString(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *pdst = (char *) pto; rset *prset; long status; prset = dbGetRset(paddr); if (prset && prset->get_enum_str) return prset->get_enum_str(paddr, pdst); status = S_db_noRSET; recGblRecSupError(status, paddr, "dbGet", "get_enum_str"); return S_db_badDbrtype; } static long getEnumChar GET(epicsEnum16, char) static long getEnumUchar GET(epicsEnum16, epicsUInt8) static long getEnumShort GET(epicsEnum16, epicsInt16) static long getEnumUshort GET(epicsEnum16, epicsUInt16) static long getEnumLong GET(epicsEnum16, epicsInt32) static long getEnumUlong GET(epicsEnum16, epicsUInt32) static long getEnumInt64 GET(epicsEnum16, epicsInt64) static long getEnumUInt64 GET(epicsEnum16, epicsUInt64) static long getEnumFloat GET(epicsEnum16, epicsFloat32) static long getEnumDouble GET(epicsEnum16, epicsFloat64) static long getEnumEnum GET_NOCONVERT(epicsEnum16, epicsEnum16) static long getMenuString(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *pdst = (char *) pto; dbFldDes *pdbFldDes = paddr->pfldDes; dbMenu *pdbMenu; char **papChoiceValue; char *pchoice; epicsEnum16 choice_ind= *((epicsEnum16*) paddr->pfield); if (no_elements!=1){ recGblDbaddrError(S_db_onlyOne, paddr, "dbGet(getMenuString)"); return(S_db_onlyOne); } if (!pdbFldDes || !(pdbMenu = (dbMenu *) pdbFldDes->ftPvt) || (choice_ind>=pdbMenu->nChoice) || !(papChoiceValue = pdbMenu->papChoiceValue) || !(pchoice=papChoiceValue[choice_ind])) { recGblDbaddrError(S_db_badChoice, paddr, "dbGet(getMenuString)"); return(S_db_badChoice); } strncpy(pdst, pchoice, MAX_STRING_SIZE); return 0; } static long getDeviceString(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset) { char *pdst = (char *) pto; dbFldDes *pdbFldDes = paddr->pfldDes; dbDeviceMenu *pdbDeviceMenu; char **papChoice; char *pchoice; epicsEnum16 choice_ind= *((epicsEnum16*) paddr->pfield); if (no_elements!=1){ recGblDbaddrError(S_db_onlyOne, paddr, "dbGet(getDeviceString)"); return(S_db_onlyOne); } if (!pdbFldDes || !(pdbDeviceMenu = (dbDeviceMenu *) pdbFldDes->ftPvt) || (choice_ind>=pdbDeviceMenu->nChoice ) || !(papChoice = pdbDeviceMenu->papChoice) || !(pchoice=papChoice[choice_ind])) { recGblDbaddrError(S_db_badChoice, paddr, "dbGet(getDeviceString)"); return(S_db_badChoice); } strncpy(pdst, pchoice, MAX_STRING_SIZE); return 0; } /* dbAccess put conversion support routines */ static long putStringString(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const char *psrc = (const char *) pfrom; char *pdst = paddr->pfield; short size = paddr->field_size; if (nRequest==1 && offset==0) { strncpy(pdst, psrc, size); *(pdst+size-1) = 0; return 0; } pdst+= (size*offset); while (nRequest--) { strncpy(pdst, psrc, size); pdst[size-1] = 0; psrc += MAX_STRING_SIZE; if (++offset == no_elements) pdst = paddr->pfield; else pdst += size; } return 0; } static long putStringChar(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const char *psrc = pfrom; epicsInt8 *pdst = (epicsInt8 *) paddr->pfield + offset; while (nRequest--) { char *end; long status = epicsParseInt8(psrc, pdst++, 10, &end); if (status) return status; psrc += MAX_STRING_SIZE; if (++offset == no_elements) pdst = paddr->pfield; } return 0; } static long putStringUchar(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const char *psrc = pfrom; epicsUInt8 *pdst = (epicsUInt8 *) paddr->pfield + offset; while (nRequest--) { char *end; long status = epicsParseUInt8(psrc, pdst++, 10, &end); if (status) return status; psrc += MAX_STRING_SIZE; if (++offset == no_elements) pdst = paddr->pfield; } return 0; } static long putStringShort(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const char *psrc = pfrom; epicsInt16 *pdst = (epicsInt16 *) paddr->pfield + offset; while (nRequest--) { char *end; long status = epicsParseInt16(psrc, pdst++, 10, &end); if (status) return status; psrc += MAX_STRING_SIZE; if (++offset == no_elements) pdst = paddr->pfield; } return 0; } static long putStringUshort(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const char *psrc = pfrom; epicsUInt16 *pdst = (epicsUInt16 *) paddr->pfield + offset; while (nRequest--) { char *end; long status = epicsParseUInt16(psrc, pdst++, 10, &end); if (status) return status; psrc += MAX_STRING_SIZE; if (++offset == no_elements) pdst = paddr->pfield; } return 0; } static long putStringLong(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const char *psrc = pfrom; epicsInt32 *pdst = (epicsInt32 *) paddr->pfield + offset; while (nRequest--) { char *end; long status = epicsParseInt32(psrc, pdst++, 10, &end); if (status) return status; psrc += MAX_STRING_SIZE; if (++offset == no_elements) pdst = paddr->pfield; } return 0; } static long putStringUlong(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const char *psrc = pfrom; epicsUInt32 *pdst = (epicsUInt32 *) paddr->pfield + offset; while (nRequest--) { char *end; long status = epicsParseUInt32(psrc, pdst, 10, &end); if (status == S_stdlib_noConversion || (!status && (*end == '.' || *end == 'e' || *end == 'E'))) { /* * Convert via double so numbers like 1.0e3 convert properly. * db_access pretends unsigned long is double. */ epicsFloat64 dval; status = epicsParseFloat64(psrc, &dval, &end); if (!status && 0 <= dval && dval <= ULONG_MAX) *pdst = dval; } if (status) return status; psrc += MAX_STRING_SIZE; if (++offset == no_elements) pdst = paddr->pfield; else pdst++; } return 0; } static long putStringInt64(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const char *psrc = pfrom; epicsInt64 *pdst = (epicsInt64 *) paddr->pfield + offset; while (nRequest--) { char *end; long status = epicsParseInt64(psrc, pdst++, 10, &end); if (status) return status; psrc += MAX_STRING_SIZE; if (++offset == no_elements) pdst = paddr->pfield; } return 0; } static long putStringUInt64(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const char *psrc = pfrom; epicsUInt64 *pdst = (epicsUInt64 *) paddr->pfield + offset; while (nRequest--) { char *end; long status = epicsParseUInt64(psrc, pdst, 0, &end); if (status) return status; psrc += MAX_STRING_SIZE; if (++offset == no_elements) pdst = paddr->pfield; else pdst++; } return 0; } static long putStringFloat(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const char *psrc = pfrom; epicsFloat32 *pdst = (epicsFloat32 *) paddr->pfield + offset; while (nRequest--) { char *end; long status = epicsParseFloat32(psrc, pdst++, &end); if (status) return status; psrc += MAX_STRING_SIZE; if (++offset == no_elements) pdst = paddr->pfield; } return 0; } static long putStringDouble(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const char *psrc = pfrom; epicsFloat64 *pdst = (epicsFloat64 *) paddr->pfield + offset; while (nRequest--) { char *end; long status = epicsParseFloat64(psrc, pdst++, &end); if (status) return status; psrc += MAX_STRING_SIZE; if (++offset == no_elements) pdst = paddr->pfield; } return 0; } static long putStringEnum(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { epicsEnum16 *pfield = paddr->pfield; rset *prset = dbGetRset(paddr); long status = S_db_noRSET; struct dbr_enumStrs enumStrs; if (no_elements != 1) { recGblDbaddrError(S_db_onlyOne, paddr, "dbPut(putStringEnum)"); return S_db_onlyOne; } if (!prset || !prset->put_enum_str) { recGblRecSupError(status, paddr, "dbPut(putStringEnum)", "put_enum_str"); return status; } status = prset->put_enum_str(paddr, pfrom); if (!status) return status; if (!prset->get_enum_strs) { recGblRecSupError(status, paddr, "dbPut(putStringEnum)", "get_enum_strs"); return status; } status = prset->get_enum_strs(paddr, &enumStrs); if (!status) { epicsEnum16 val; char *end; status = epicsParseUInt16(pfrom, &val, 10, &end); if (!status && val < enumStrs.no_str) { *pfield = val; return 0; } status = S_db_badChoice; } recGblRecordError(status, paddr->precord, pfrom); return status; } static long putStringMenu(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { dbFldDes *pdbFldDes = paddr->pfldDes; epicsEnum16 *pfield = paddr->pfield; dbMenu *pdbMenu; char **pchoices, *pchoice; if (no_elements != 1) { recGblDbaddrError(S_db_onlyOne, paddr, "dbPut(putStringMenu)"); return S_db_onlyOne; } if (pdbFldDes && (pdbMenu = pdbFldDes->ftPvt) && (pchoices = pdbMenu->papChoiceValue)) { int i, nChoice = pdbMenu->nChoice; epicsEnum16 val; for (i = 0; i < nChoice; i++) { pchoice = pchoices[i]; if (!pchoice) continue; if (strcmp(pchoice, pfrom) == 0) { *pfield = i; return 0; } } if (!epicsParseUInt16(pfrom, &val, 10, NULL) && val < nChoice) { *pfield = val; return 0; } } recGblDbaddrError(S_db_badChoice, paddr, "dbPut(putStringMenu)"); return S_db_badChoice; } static long putStringDevice(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { dbFldDes *pdbFldDes = paddr->pfldDes; dbDeviceMenu *pdbDeviceMenu = pdbFldDes->ftPvt; epicsEnum16 *pfield = paddr->pfield; char **pchoices, *pchoice; if (no_elements != 1) { recGblDbaddrError(S_db_onlyOne, paddr, "dbPut(putStringDevice)"); return S_db_onlyOne; } if (pdbFldDes && (pdbDeviceMenu = pdbFldDes->ftPvt) && (pchoices = pdbDeviceMenu->papChoice)) { int i, nChoice = pdbDeviceMenu->nChoice; epicsEnum16 val; for (i = 0; i < nChoice; i++) { pchoice = pchoices[i]; if (!pchoice) continue; if (strcmp(pchoice, pfrom) == 0) { *pfield = i; return 0; } } if (!epicsParseUInt16(pfrom, &val, 10, NULL) && val < nChoice) { *pfield = val; return 0; } } recGblDbaddrError(S_db_badChoice, paddr, "dbPut(putStringDevice)"); return S_db_badChoice; } static long putCharString(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const char *psrc = (const char *) pfrom; char *pdst = (char *) paddr->pfield; short size = paddr->field_size; if (nRequest==1 && offset==0) { cvtCharToString(*psrc, pdst); return 0; } pdst += (size*offset); while (nRequest--) { cvtCharToString(*psrc, pdst); psrc++; if (++offset == no_elements) pdst = paddr->pfield; else pdst += size; } return 0; } static long putCharChar PUT_NOCONVERT(char, char) static long putCharUchar PUT_NOCONVERT(char, epicsUInt8) static long putCharShort PUT(char, epicsInt16) static long putCharUshort PUT(char, epicsUInt16) static long putCharLong PUT(char, epicsInt32) static long putCharUlong PUT(char, epicsUInt32) static long putCharInt64 PUT(char, epicsInt64) static long putCharUInt64 PUT(char, epicsUInt64) static long putCharFloat PUT(char, epicsFloat32) static long putCharDouble PUT(char, epicsFloat64) static long putCharEnum PUT(char, epicsEnum16) static long putUcharString(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const epicsUInt8 *psrc = (const epicsUInt8 *) pfrom; char *pdst = (char *) paddr->pfield; short size = paddr->field_size; if (nRequest==1 && offset==0) { cvtUcharToString(*psrc, pdst); return 0; } pdst += (size*offset); while (nRequest--) { cvtUcharToString(*psrc, pdst); psrc++; if (++offset == no_elements) pdst = paddr->pfield; else pdst += size; } return 0; } static long putUcharChar PUT_NOCONVERT(epicsUInt8, char) static long putUcharUchar PUT_NOCONVERT(epicsUInt8, epicsUInt8) static long putUcharShort PUT(epicsUInt8, epicsInt16) static long putUcharUshort PUT(epicsUInt8, epicsUInt16) static long putUcharLong PUT(epicsUInt8, epicsInt32) static long putUcharUlong PUT(epicsUInt8, epicsUInt32) static long putUcharInt64 PUT(epicsUInt8, epicsInt64) static long putUcharUInt64 PUT(epicsUInt8, epicsUInt64) static long putUcharFloat PUT(epicsUInt8, epicsFloat32) static long putUcharDouble PUT(epicsUInt8, epicsFloat64) static long putUcharEnum PUT(epicsUInt8, epicsEnum16) static long putShortString(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const epicsInt16 *psrc = (const epicsInt16 *) pfrom; char *pdst = (char *) paddr->pfield; short size = paddr->field_size; if (nRequest==1 && offset==0) { cvtShortToString(*psrc, pdst); return 0; } pdst += (size*offset); while (nRequest--) { cvtShortToString(*psrc, pdst); psrc++; if (++offset == no_elements) pdst = (char *) paddr->pfield; else pdst += size; } return 0; } static long putShortChar PUT(epicsInt16, char) static long putShortUchar PUT(epicsInt16, epicsUInt8) static long putShortShort PUT_NOCONVERT(epicsInt16, epicsInt16) static long putShortUshort PUT_NOCONVERT(epicsInt16, epicsUInt16) static long putShortLong PUT(epicsInt16, epicsInt32) static long putShortUlong PUT(epicsInt16, epicsUInt32) static long putShortInt64 PUT(epicsInt16, epicsInt64) static long putShortUInt64 PUT(epicsInt16, epicsUInt64) static long putShortFloat PUT(epicsInt16, epicsFloat32) static long putShortDouble PUT(epicsInt16, epicsFloat64) static long putShortEnum PUT(epicsInt16, epicsEnum16) static long putUshortString(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const epicsUInt16 *psrc = (const epicsUInt16 *) pfrom; char *pdst = (char *) paddr->pfield; short size = paddr->field_size; if (nRequest==1 && offset==0) { cvtUshortToString(*psrc, pdst); return 0; } pdst += (size*offset); while (nRequest--) { cvtUshortToString(*psrc, pdst); psrc++; if (++offset == no_elements) pdst = (char *) paddr->pfield; else pdst += size; } return 0; } static long putUshortChar PUT(epicsUInt16, char) static long putUshortUchar PUT(epicsUInt16, epicsUInt8) static long putUshortShort PUT_NOCONVERT(epicsUInt16, epicsInt16) static long putUshortUshort PUT_NOCONVERT(epicsUInt16, epicsUInt16) static long putUshortLong PUT(epicsUInt16, epicsInt32) static long putUshortUlong PUT(epicsUInt16, epicsUInt32) static long putUshortInt64 PUT(epicsUInt16, epicsInt64) static long putUshortUInt64 PUT(epicsUInt16, epicsUInt64) static long putUshortFloat PUT(epicsUInt16, epicsFloat32) static long putUshortDouble PUT(epicsUInt16, epicsFloat64) static long putUshortEnum PUT(epicsUInt16, epicsEnum16) static long putLongString(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const epicsInt32 *psrc = (const epicsInt32 *) pfrom; char *pdst = (char *) paddr->pfield; short size = paddr->field_size; if (nRequest==1 && offset==0) { cvtLongToString(*psrc, pdst); return 0; } pdst += (size*offset); while (nRequest--) { cvtLongToString(*psrc, pdst); psrc++; if (++offset == no_elements) pdst = (char *) paddr->pfield; else pdst += size; } return 0; } static long putLongChar PUT(epicsInt32, char) static long putLongUchar PUT(epicsInt32, epicsUInt8) static long putLongShort PUT(epicsInt32, epicsInt16) static long putLongUshort PUT(epicsInt32, epicsUInt16) static long putLongLong PUT_NOCONVERT(epicsInt32, epicsInt32) static long putLongUlong PUT_NOCONVERT(epicsInt32, epicsUInt32) static long putLongInt64 PUT(epicsInt32, epicsInt64) static long putLongUInt64 PUT(epicsInt32, epicsUInt64) static long putLongFloat PUT(epicsInt32, epicsFloat32) static long putLongDouble PUT(epicsInt32, epicsFloat64) static long putLongEnum PUT(epicsInt32, epicsEnum16) static long putUlongString(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const epicsUInt32 *psrc = (const epicsUInt32 *) pfrom; char *pdst = (char *) paddr->pfield; short size = paddr->field_size; if (nRequest==1 && offset==0) { cvtUlongToString(*psrc, pdst); return 0; } pdst += (size*offset); while (nRequest--) { cvtUlongToString(*psrc, pdst); psrc++; if (++offset == no_elements) pdst = (char *) paddr->pfield; else pdst += size; } return 0; } static long putUlongChar PUT(epicsUInt32, char) static long putUlongUchar PUT(epicsUInt32, epicsUInt8) static long putUlongShort PUT(epicsUInt32, epicsInt16) static long putUlongUshort PUT(epicsUInt32, epicsUInt16) static long putUlongLong PUT_NOCONVERT(epicsUInt32, epicsInt32) static long putUlongUlong PUT_NOCONVERT(epicsUInt32, epicsUInt32) static long putUlongInt64 PUT(epicsUInt32, epicsInt64) static long putUlongUInt64 PUT(epicsUInt32, epicsUInt64) static long putUlongFloat PUT(epicsUInt32, epicsFloat32) static long putUlongDouble PUT(epicsUInt32, epicsFloat64) static long putUlongEnum PUT(epicsUInt32, epicsEnum16) static long putInt64String(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const epicsInt64 *psrc = (const epicsInt64 *) pfrom; char *pdst = (char *) paddr->pfield; short size=paddr->field_size; if (nRequest==1 && offset==0) { cvtInt64ToString(*psrc, pdst); return 0; } pdst += (size*offset); while (nRequest--) { cvtInt64ToString(*psrc, pdst); psrc++; if (++offset == no_elements) pdst = (char *) paddr->pfield; else pdst += size; } return 0; } static long putInt64Char PUT(epicsInt64, char) static long putInt64Uchar PUT(epicsInt64, epicsUInt8) static long putInt64Short PUT(epicsInt64, epicsInt16) static long putInt64Ushort PUT(epicsInt64, epicsUInt16) static long putInt64Long PUT(epicsInt64, epicsInt32) static long putInt64Ulong PUT(epicsInt64, epicsUInt32) static long putInt64Int64 PUT_NOCONVERT(epicsInt64, epicsInt64) static long putInt64UInt64 PUT_NOCONVERT(epicsInt64, epicsUInt64) static long putInt64Float PUT(epicsInt64, epicsFloat32) static long putInt64Double PUT(epicsInt64, epicsFloat64) static long putInt64Enum PUT(epicsInt64, epicsEnum16) static long putUInt64String(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const epicsUInt64 *psrc = (const epicsUInt64 *) pfrom; char *pdst = (char *) paddr->pfield; short size=paddr->field_size; if (nRequest==1 && offset==0) { cvtUlongToString(*psrc, pdst); return 0; } pdst += (size*offset); while (nRequest--) { cvtUlongToString(*psrc, pdst); psrc++; if (++offset == no_elements) pdst = (char *) paddr->pfield; else pdst += size; } return 0; } static long putUInt64Char PUT(epicsUInt64, char) static long putUInt64Uchar PUT(epicsUInt64, epicsUInt8) static long putUInt64Short PUT(epicsUInt64, epicsInt16) static long putUInt64Ushort PUT(epicsUInt64, epicsUInt16) static long putUInt64Long PUT(epicsUInt64, epicsInt32) static long putUInt64Ulong PUT(epicsUInt64, epicsUInt32) static long putUInt64Int64 PUT_NOCONVERT(epicsUInt64, epicsInt64) static long putUInt64UInt64 PUT_NOCONVERT(epicsUInt64, epicsUInt64) static long putUInt64Float PUT(epicsUInt64, epicsFloat32) static long putUInt64Double PUT(epicsUInt64, epicsFloat64) static long putUInt64Enum PUT(epicsUInt64, epicsEnum16) static long putFloatString(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const epicsFloat32 *psrc = (const epicsFloat32 *) pfrom; char *pdst = (char *) paddr->pfield; long status = 0; long precision = 6; rset *prset = 0; short size = paddr->field_size; if (paddr) prset = dbGetRset(paddr); if (prset && prset->get_precision) status = prset->get_precision(paddr, &precision); if (nRequest==1 && offset==0) { cvtFloatToString(*psrc, pdst, precision); return(status); } pdst += (size*offset); while (nRequest--) { cvtFloatToString(*psrc, pdst, precision); psrc++; if (++offset == no_elements) pdst = (char *) paddr->pfield; else pdst += size; } return(status); } static long putFloatChar PUT(epicsFloat32, char) static long putFloatUchar PUT(epicsFloat32, epicsUInt8) static long putFloatShort PUT(epicsFloat32, epicsInt16) static long putFloatUshort PUT(epicsFloat32, epicsUInt16) static long putFloatLong PUT(epicsFloat32, epicsInt32) static long putFloatUlong PUT(epicsFloat32, epicsUInt32) static long putFloatInt64 PUT(epicsFloat32, epicsInt64) static long putFloatUInt64 PUT(epicsFloat32, epicsUInt64) static long putFloatFloat PUT_NOCONVERT(epicsFloat32, epicsFloat32) static long putFloatDouble PUT(epicsFloat32, epicsFloat64) static long putFloatEnum PUT(epicsFloat32, epicsEnum16) static long putDoubleString(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const epicsFloat64 *psrc = (const epicsFloat64 *) pfrom; char *pdst = (char *) paddr->pfield; long status = 0; long precision = 6; rset *prset = 0; short size = paddr->field_size; if (paddr) prset = dbGetRset(paddr); if (prset && prset->get_precision) status = prset->get_precision(paddr, &precision); if (nRequest==1 && offset==0) { cvtDoubleToString(*psrc, pdst, precision); return status; } pdst += (size*offset); while (nRequest--) { cvtDoubleToString(*psrc, pdst, precision); psrc++; if (++offset == no_elements) pdst = (char *) paddr->pfield; else pdst += size; } return status; } static long putDoubleChar PUT(epicsFloat64, char) static long putDoubleUchar PUT(epicsFloat64, epicsUInt8) static long putDoubleShort PUT(epicsFloat64, epicsInt16) static long putDoubleUshort PUT(epicsFloat64, epicsUInt16) static long putDoubleLong PUT(epicsFloat64, epicsInt32) static long putDoubleUlong PUT(epicsFloat64, epicsUInt32) static long putDoubleInt64 PUT(epicsFloat64, epicsInt64) static long putDoubleUInt64 PUT(epicsFloat64, epicsUInt64) static long putDoubleFloat(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const epicsFloat64 *psrc = (const epicsFloat64 *) pfrom; epicsFloat32 *pdst = (epicsFloat32 *) paddr->pfield; if (nRequest==1 && offset==0) { *pdst = epicsConvertDoubleToFloat(*psrc); return 0; } pdst += offset; while (nRequest--) { *pdst++ = epicsConvertDoubleToFloat(*psrc++); if (++offset == no_elements) pdst = (epicsFloat32 *) paddr->pfield; } return 0; } static long putDoubleDouble PUT_NOCONVERT(epicsFloat64, epicsFloat64) static long putDoubleEnum PUT(epicsFloat64, epicsEnum16) static long putEnumString(dbAddr *paddr, const void *pfrom, long nRequest, long no_elements, long offset) { const epicsEnum16 *psrc = (const epicsEnum16 *) pfrom; char *pdst = (char *) paddr->pfield; short size = paddr->field_size; if (nRequest==1 && offset==0) { cvtUshortToString(*psrc, pdst); return 0; } pdst += (size*offset); while (nRequest--) { cvtUshortToString(*psrc, pdst); psrc++; if (++offset == no_elements) pdst = (char *) paddr->pfield; else pdst += size; } return 0; } static long putEnumChar PUT(epicsEnum16, char) static long putEnumUchar PUT(epicsEnum16, epicsUInt8) static long putEnumShort PUT(epicsEnum16, epicsInt16) static long putEnumUshort PUT(epicsEnum16, epicsUInt16) static long putEnumLong PUT(epicsEnum16, epicsInt32) static long putEnumUlong PUT(epicsEnum16, epicsUInt32) static long putEnumInt64 PUT(epicsEnum16, epicsInt64) static long putEnumUInt64 PUT(epicsEnum16, epicsUInt64) static long putEnumFloat PUT(epicsEnum16, epicsFloat32) static long putEnumDouble PUT(epicsEnum16, epicsFloat64) static long putEnumEnum PUT_NOCONVERT(epicsEnum16, epicsEnum16) /* This is the table of routines for converting database fields */ /* the rows represent the field type of the database field */ /* the columns represent the types of the buffer in which they are placed */ /* buffer types are******************************************************** DBR_STRING, DBR_CHR, DBR_UCHAR, DBR_SHORT, DBR_USHORT, DBR_LONG, DBR_ULONG, DBR_INT64, DBR_UINT64, DBR_FLOAT, DBR_DOUBLE, DBR_ENUM ***************************************************************************/ epicsShareDef GETCONVERTFUNC dbGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1] = { /* source is a DBF_STRING */ {getStringString, getStringChar, getStringUchar, getStringShort, getStringUshort, getStringLong, getStringUlong, getStringInt64, getStringUInt64, getStringFloat, getStringDouble, getStringUshort}, /* source is a DBF_CHAR */ {getCharString, getCharChar, getCharUchar, getCharShort, getCharUshort, getCharLong, getCharUlong, getCharInt64, getCharUInt64, getCharFloat, getCharDouble, getCharEnum}, /* source is a DBF_UCHAR */ {getUcharString, getUcharChar, getUcharUchar, getUcharShort, getUcharUshort, getUcharLong, getUcharUlong, getUcharInt64, getUcharUInt64, getUcharFloat, getUcharDouble, getUcharEnum}, /* source is a DBF_SHORT */ {getShortString, getShortChar, getShortUchar, getShortShort, getShortUshort, getShortLong, getShortUlong, getShortInt64, getShortUInt64, getShortFloat, getShortDouble, getShortEnum}, /* source is a DBF_USHORT */ {getUshortString, getUshortChar, getUshortUchar, getUshortShort, getUshortUshort, getUshortLong, getUshortUlong, getUshortInt64, getUshortUInt64, getUshortFloat, getUshortDouble, getUshortEnum}, /* source is a DBF_LONG */ {getLongString, getLongChar, getLongUchar, getLongShort, getLongUshort, getLongLong, getLongUlong, getLongInt64, getLongUInt64, getLongFloat, getLongDouble, getLongEnum}, /* source is a DBF_ULONG */ {getUlongString, getUlongChar, getUlongUchar, getUlongShort, getUlongUshort, getUlongLong, getUlongUlong, getUlongInt64, getUlongUInt64, getUlongFloat, getUlongDouble, getUlongEnum}, /* source is a DBF_INT64 */ {getInt64String, getInt64Char, getInt64Uchar, getInt64Short, getInt64Ushort, getInt64Long, getInt64Ulong, getInt64Int64, getInt64UInt64, getInt64Float, getInt64Double, getInt64Enum}, /* source is a DBF_UINT64 */ {getUInt64String, getUInt64Char, getUInt64Uchar, getUInt64Short, getUInt64Ushort, getUInt64Long, getUInt64Ulong, getUInt64Int64, getUInt64UInt64, getUInt64Float, getUInt64Double, getUInt64Enum}, /* source is a DBF_FLOAT */ {getFloatString, getFloatChar, getFloatUchar, getFloatShort, getFloatUshort, getFloatLong, getFloatUlong, getFloatInt64, getFloatUInt64, getFloatFloat, getFloatDouble, getFloatEnum}, /* source is a DBF_DOUBLE */ {getDoubleString, getDoubleChar, getDoubleUchar, getDoubleShort, getDoubleUshort, getDoubleLong, getDoubleUlong, getDoubleInt64, getDoubleUInt64, getDoubleFloat, getDoubleDouble, getDoubleEnum}, /* source is a DBF_ENUM */ {getEnumString, getEnumChar, getEnumUchar, getEnumShort, getEnumUshort, getEnumLong, getEnumUlong, getEnumInt64, getEnumUInt64, getEnumFloat, getEnumDouble, getEnumEnum}, /* source is a DBF_MENU */ {getMenuString, getEnumChar, getEnumUchar, getEnumShort, getEnumUshort, getEnumLong, getEnumUlong, getEnumInt64, getEnumUInt64, getEnumFloat, getEnumDouble, getEnumEnum}, /* source is a DBF_DEVICE */ {getDeviceString, getEnumChar, getEnumUchar, getEnumShort, getEnumUshort, getEnumLong, getEnumUlong, getEnumInt64, getEnumUInt64, getEnumFloat, getEnumDouble, getEnumEnum}, }; /* This is the table of routines for converting database fields */ /* the rows represent the buffer types */ /* the columns represent the field types */ /* field types are******************************************************** DBF_STRING, DBF_CHAR, DBF_UCHAR, DBF_SHORT, DBF_USHORT, DBF_LONG, DBF_ULONG, DBF_INT64, DBF_UINT64, DBF_FLOAT, DBF_DOUBLE, DBF_ENUM DBF_MENU, DBF_DEVICE ***************************************************************************/ epicsShareDef PUTCONVERTFUNC dbPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1] = { /* source is a DBR_STRING */ {putStringString, putStringChar, putStringUchar, putStringShort, putStringUshort, putStringLong, putStringUlong, putStringInt64, putStringUInt64, putStringFloat, putStringDouble, putStringEnum, putStringMenu, putStringDevice}, /* source is a DBR_CHAR */ {putCharString, putCharChar, putCharUchar, putCharShort, putCharUshort, putCharLong, putCharUlong, putCharInt64, putCharUInt64, putCharFloat, putCharDouble, putCharEnum, putCharEnum, putCharEnum}, /* source is a DBR_UCHAR */ {putUcharString, putUcharChar, putUcharUchar, putUcharShort, putUcharUshort, putUcharLong, putUcharUlong, putUcharInt64, putUcharUInt64, putUcharFloat, putUcharDouble, putUcharEnum, putUcharEnum, putUcharEnum}, /* source is a DBR_SHORT */ {putShortString, putShortChar, putShortUchar, putShortShort, putShortUshort, putShortLong, putShortUlong, putShortInt64, putShortUInt64, putShortFloat, putShortDouble, putShortEnum, putShortEnum, putShortEnum}, /* source is a DBR_USHORT */ {putUshortString, putUshortChar, putUshortUchar, putUshortShort, putUshortUshort, putUshortLong, putUshortUlong, putUshortInt64, putUshortUInt64, putUshortFloat, putUshortDouble, putUshortEnum, putUshortEnum, putUshortEnum}, /* source is a DBR_LONG */ {putLongString, putLongChar, putLongUchar, putLongShort, putLongUshort, putLongLong, putLongUlong, putLongInt64, putLongUInt64, putLongFloat, putLongDouble, putLongEnum, putLongEnum, putLongEnum}, /* source is a DBR_ULONG */ {putUlongString, putUlongChar, putUlongUchar, putUlongShort, putUlongUshort, putUlongLong, putUlongUlong, putUlongInt64, putUlongUInt64, putUlongFloat, putUlongDouble, putUlongEnum, putUlongEnum, putUlongEnum}, /* source is a DBR_INT64 */ {putInt64String, putInt64Char, putInt64Uchar, putInt64Short, putInt64Ushort, putInt64Long, putInt64Ulong, putInt64Int64, putInt64UInt64, putInt64Float, putInt64Double, putInt64Enum, putInt64Enum, putInt64Enum}, /* source is a DBR_UINT64 */ {putUInt64String, putUInt64Char, putUInt64Uchar, putUInt64Short, putUInt64Ushort, putUInt64Long, putUInt64Ulong, putUInt64Int64, putUInt64UInt64, putUInt64Float, putUInt64Double, putUInt64Enum, putUInt64Enum, putUInt64Enum}, /* source is a DBR_FLOAT */ {putFloatString, putFloatChar, putFloatUchar, putFloatShort, putFloatUshort, putFloatLong, putFloatUlong, putFloatInt64, putFloatUInt64, putFloatFloat, putFloatDouble, putFloatEnum, putFloatEnum, putFloatEnum}, /* source is a DBR_DOUBLE */ {putDoubleString, putDoubleChar, putDoubleUchar, putDoubleShort, putDoubleUshort, putDoubleLong, putDoubleUlong, putDoubleInt64, putDoubleUInt64, putDoubleFloat, putDoubleDouble, putDoubleEnum, putDoubleEnum, putDoubleEnum}, /* source is a DBR_ENUM */ {putEnumString, putEnumChar, putEnumUchar, putEnumShort, putEnumUshort, putEnumLong, putEnumUlong, putEnumInt64, putEnumUInt64, putEnumFloat, putEnumDouble, putEnumEnum, putEnumEnum, putEnumEnum} }; base-7.0.3.1/modules/database/src/ioc/db/dbConvert.h0000664000577000060420000000213713557101274020657 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbConvert.h */ #ifndef INCdbConverth #define INCdbConverth #include "dbFldTypes.h" #include "dbAddr.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif typedef long (*GETCONVERTFUNC)(const DBADDR *paddr, void *pbuffer, long nRequest, long no_elements, long offset); typedef long (*PUTCONVERTFUNC)(DBADDR *paddr, const void *pbuffer, long nRequest, long no_elements, long offset); epicsShareExtern GETCONVERTFUNC dbGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1]; epicsShareExtern PUTCONVERTFUNC dbPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1]; #ifdef __cplusplus } #endif #endif /*INCdbConverth*/ base-7.0.3.1/modules/database/src/ioc/db/dbConvertFast.h0000664000577000060420000000161713557101274021477 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* dbConvertFast.h */ #ifndef INCdbConvertFasth #define INCdbConvertFasth #include "dbFldTypes.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareExtern long (*dbFastGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1])(); epicsShareExtern long (*dbFastPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1])(); #ifdef __cplusplus } #endif #endif /*INCdbConvertFasth*/ base-7.0.3.1/modules/database/src/ioc/db/dbConvertJSON.c0000664000577000060420000001464313557101274021351 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbConvertJSON.c */ #include #include #include "dbDefs.h" #include "errlog.h" #include "yajl_alloc.h" #include "yajl_parse.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "dbConvertFast.h" #include "dbConvertJSON.h" typedef long (*FASTCONVERT)(); typedef struct parseContext { int depth; short dbrType; short dbrSize; char *pdest; int elems; } parseContext; static int dbcj_null(void *ctx) { return 0; /* Illegal */ } static int dbcj_boolean(void *ctx, int val) { return 0; /* Illegal */ } static int dbcj_integer(void *ctx, long long num) { parseContext *parser = (parseContext *) ctx; epicsInt64 val64 = num; FASTCONVERT conv = dbFastPutConvertRoutine[DBF_INT64][parser->dbrType]; if (parser->elems > 0) { conv(&val64, parser->pdest, NULL); parser->pdest += parser->dbrSize; parser->elems--; } return 1; } static int dblsj_integer(void *ctx, long long num) { return 0; /* Illegal */ } static int dbcj_double(void *ctx, double num) { parseContext *parser = (parseContext *) ctx; FASTCONVERT conv = dbFastPutConvertRoutine[DBF_DOUBLE][parser->dbrType]; if (parser->elems > 0) { conv(&num, parser->pdest, NULL); parser->pdest += parser->dbrSize; parser->elems--; } return 1; } static int dblsj_double(void *ctx, double num) { return 0; /* Illegal */ } static int dbcj_string(void *ctx, const unsigned char *val, size_t len) { parseContext *parser = (parseContext *) ctx; char *pdest = parser->pdest; /* Not attempting to handle char-array fields here, they need more * metadata about the field than we have available at the moment. */ if (parser->dbrType != DBF_STRING) { errlogPrintf("dbConvertJSON: String provided, numeric value(s) expected\n"); return 0; /* Illegal */ } if (parser->elems > 0) { if (len > parser->dbrSize - 1) len = parser->dbrSize - 1; strncpy(pdest, (const char *) val, len); pdest[len] = 0; parser->pdest += parser->dbrSize; parser->elems--; } return 1; } static int dblsj_string(void *ctx, const unsigned char *val, size_t len) { parseContext *parser = (parseContext *) ctx; char *pdest = parser->pdest; if (parser->dbrType != DBF_STRING) { errlogPrintf("dbConvertJSON: dblsj_string dbrType error\n"); return 0; /* Illegal */ } if (parser->elems > 0) { if (len > parser->dbrSize - 1) len = parser->dbrSize - 1; strncpy(pdest, (const char *) val, len); pdest[len] = 0; parser->pdest = pdest + len; parser->elems = 0; } return 1; } static int dbcj_start_map(void *ctx) { errlogPrintf("dbConvertJSON: Map type not supported\n"); return 0; /* Illegal */ } static int dbcj_map_key(void *ctx, const unsigned char *key, size_t len) { return 0; /* Illegal */ } static int dbcj_end_map(void *ctx) { return 0; /* Illegal */ } static int dbcj_start_array(void *ctx) { parseContext *parser = (parseContext *) ctx; if (++parser->depth > 1) errlogPrintf("dbConvertJSON: Embedded arrays not supported\n"); return (parser->depth == 1); } static int dbcj_end_array(void *ctx) { parseContext *parser = (parseContext *) ctx; parser->depth--; return (parser->depth == 0); } static yajl_callbacks dbcj_callbacks = { dbcj_null, dbcj_boolean, dbcj_integer, dbcj_double, NULL, dbcj_string, dbcj_start_map, dbcj_map_key, dbcj_end_map, dbcj_start_array, dbcj_end_array }; long dbPutConvertJSON(const char *json, short dbrType, void *pdest, long *pnRequest) { parseContext context, *parser = &context; yajl_alloc_funcs dbcj_alloc; yajl_handle yh; yajl_status ys; size_t jlen = strlen(json); long status; parser->depth = 0; parser->dbrType = dbrType; parser->dbrSize = dbValueSize(dbrType); parser->pdest = pdest; parser->elems = *pnRequest; yajl_set_default_alloc_funcs(&dbcj_alloc); yh = yajl_alloc(&dbcj_callbacks, &dbcj_alloc, parser); if (!yh) return S_db_noMemory; ys = yajl_parse(yh, (const unsigned char *) json, jlen); if (ys == yajl_status_ok) ys = yajl_complete_parse(yh); switch (ys) { case yajl_status_ok: *pnRequest -= parser->elems; status = 0; break; case yajl_status_error: { unsigned char *err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen); fprintf(stderr, "dbConvertJSON: %s\n", err); yajl_free_error(yh, err); } /* fall through */ default: status = S_db_badField; } yajl_free(yh); return status; } static yajl_callbacks dblsj_callbacks = { dbcj_null, dbcj_boolean, dblsj_integer, dblsj_double, NULL, dblsj_string, dbcj_start_map, dbcj_map_key, dbcj_end_map, dbcj_start_array, dbcj_end_array }; long dbLSConvertJSON(const char *json, char *pdest, epicsUInt32 size, epicsUInt32 *plen) { parseContext context, *parser = &context; yajl_alloc_funcs dbcj_alloc; yajl_handle yh; yajl_status ys; size_t jlen = strlen(json); long status; if (!size) { *plen = 0; return 0; } parser->depth = 0; parser->dbrType = DBF_STRING; parser->dbrSize = size; parser->pdest = pdest; parser->elems = 1; yajl_set_default_alloc_funcs(&dbcj_alloc); yh = yajl_alloc(&dblsj_callbacks, &dbcj_alloc, parser); if (!yh) return S_db_noMemory; ys = yajl_parse(yh, (const unsigned char *) json, jlen); switch (ys) { case yajl_status_ok: *plen = (char *) parser->pdest - pdest + 1; status = 0; break; case yajl_status_error: { unsigned char *err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen); fprintf(stderr, "dbLoadLS_JSON: %s\n", err); yajl_free_error(yh, err); } /* fall through */ default: status = S_db_badField; } yajl_free(yh); return status; } base-7.0.3.1/modules/database/src/ioc/db/dbConvertJSON.h0000664000577000060420000000153013557101274021345 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbConvertJSON.h */ #ifndef INC_dbConvertJSON_H #define INC_dbConvertJSON_H #include #ifdef __cplusplus extern "C" { #endif /* This name should probably be changed to inclue "array" */ epicsShareFunc long dbPutConvertJSON(const char *json, short dbrType, void *pdest, long *psize); epicsShareFunc long dbLSConvertJSON(const char *json, char *pdest, epicsUInt32 size, epicsUInt32 *plen); #ifdef __cplusplus } #endif #endif /* INC_dbConvertJSON_H */ base-7.0.3.1/modules/database/src/ioc/db/dbDbLink.c0000664000577000060420000003300013557101274020366 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbDbLink.c * * Original Authors: Bob Dalesio, Marty Kraimer * Current Author: Andrew Johnson */ /* The PUTF and RPRO fields in dbCommon are flags that indicate when a record * is being processed as a result of an external put (i.e. some server process * calling dbPutField()), ensuring that the record and its successors will * eventually get processed even if they happen to be busy at the time of the * put. From Base-3.16.2 and 7.0.2 the code ensures that all records downstream * from the original are processed even if a busy asynchronous device appears * in the processing chain (this breaks the chain in older versions). * * PUTF - This field is set in dbPutField() prior to it calling dbProcess(). * It is normally cleared at the end of processing in recGblFwdLink(). * It may also be cleared in dbProcess() if DISA==DISV (scan disabled), * or by the processTarget() function below. * * If PUTF is TRUE before a call to dbProcess(prec), then after it returns * either PACT is TRUE, or PUTF will be FALSE. * * RPRO - This field is set by dbPutField() or by the processTarget() function * below when a record to be processed is found to be busy (PACT==1). * It is normally cleared in recGblFwdLink() when the record is queued * for re-processing, or in dbProcess() if DISA==DISV (scan disabled). */ #include #include #include #include #include #include "alarm.h" #include "cantProceed.h" #include "cvtFast.h" #include "dbDefs.h" #include "ellLib.h" #include "epicsTime.h" #include "errlog.h" #include "caeventmask.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" #include "dbBkpt.h" #include "dbCommonPvt.h" #include "dbConvertFast.h" #include "dbConvert.h" #include "db_field_log.h" #include "dbFldTypes.h" #include "dbLink.h" #include "dbLockPvt.h" #include "dbNotify.h" #include "dbScan.h" #include "dbStaticLib.h" #include "dbServer.h" #include "devSup.h" #include "link.h" #include "recGbl.h" #include "recSup.h" #include "special.h" #include "dbDbLink.h" /***************************** Database Links *****************************/ /* Forward definitions */ static lset dbDb_lset; static long processTarget(dbCommon *psrc, dbCommon *pdst); long dbDbInitLink(struct link *plink, short dbfType) { DBADDR dbaddr; long status; DBADDR *pdbAddr; status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr); if (status) return status; plink->lset = &dbDb_lset; plink->type = DB_LINK; pdbAddr = dbCalloc(1, sizeof(struct dbAddr)); *pdbAddr = dbaddr; /* structure copy */ plink->value.pv_link.pvt = pdbAddr; ellAdd(&dbaddr.precord->bklnk, &plink->value.pv_link.backlinknode); /* merging into the same lockset is deferred to the caller. * cf. initPVLinks() */ dbLockSetMerge(NULL, plink->precord, dbaddr.precord); assert(plink->precord->lset->plockSet == dbaddr.precord->lset->plockSet); return 0; } void dbDbAddLink(struct dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget) { plink->lset = &dbDb_lset; plink->type = DB_LINK; plink->value.pv_link.pvt = ptarget; ellAdd(&ptarget->precord->bklnk, &plink->value.pv_link.backlinknode); /* target record is already locked in dbPutFieldLink() */ dbLockSetMerge(locker, plink->precord, ptarget->precord); } static void dbDbRemoveLink(struct dbLocker *locker, struct link *plink) { DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt; plink->type = PV_LINK; /* locker is NULL when an isolated IOC is closing its links */ if (locker) { plink->value.pv_link.pvt = 0; plink->value.pv_link.getCvt = 0; plink->value.pv_link.pvlMask = 0; plink->value.pv_link.lastGetdbrType = 0; ellDelete(&pdbAddr->precord->bklnk, &plink->value.pv_link.backlinknode); dbLockSetSplit(locker, plink->precord, pdbAddr->precord); } free(pdbAddr); } static int dbDbIsConnected(const struct link *plink) { return TRUE; } static int dbDbGetDBFtype(const struct link *plink) { DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; return paddr->field_type; } static long dbDbGetElements(const struct link *plink, long *nelements) { DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; *nelements = paddr->no_elements; return 0; } static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, long *pnRequest) { struct pv_link *ppv_link = &plink->value.pv_link; DBADDR *paddr = ppv_link->pvt; dbCommon *precord = plink->precord; long status; /* scan passive records if link is process passive */ if (ppv_link->pvlMask & pvlOptPP) { status = dbScanPassive(precord, paddr->precord); if (status) return status; } if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) { status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr); } else { unsigned short dbfType = paddr->field_type; if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE) return S_db_badDbrtype; if (paddr->no_elements == 1 && (!pnRequest || *pnRequest == 1) && paddr->special != SPC_DBADDR && paddr->special != SPC_ATTRIBUTE) { ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType]; status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr); } else { ppv_link->getCvt = NULL; status = dbGet(paddr, dbrType, pbuffer, NULL, pnRequest, NULL); } ppv_link->lastGetdbrType = dbrType; } if (!status && precord != paddr->precord) recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode, plink->precord, paddr->precord->stat, paddr->precord->sevr); return status; } static long dbDbGetControlLimits(const struct link *plink, double *low, double *high) { DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; 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); if (status) return status; *low = buffer.lower_ctrl_limit; *high = buffer.upper_ctrl_limit; return 0; } static long dbDbGetGraphicLimits(const struct link *plink, double *low, double *high) { DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; 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); if (status) return status; *low = buffer.lower_disp_limit; *high = buffer.upper_disp_limit; return 0; } static long dbDbGetAlarmLimits(const struct link *plink, double *lolo, double *low, double *high, double *hihi) { DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; 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); if (status) return status; *lolo = buffer.lower_alarm_limit; *low = buffer.lower_warning_limit; *high = buffer.upper_warning_limit; *hihi = buffer.upper_alarm_limit; return 0; } static long dbDbGetPrecision(const struct link *plink, short *precision) { DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; 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); if (status) return status; *precision = (short) buffer.precision.dp; return 0; } static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize) { DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; 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); if (status) return status; strncpy(units, buffer.units, unitsSize); return 0; } static long dbDbGetAlarm(const struct link *plink, epicsEnum16 *status, epicsEnum16 *severity) { DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; if (status) *status = paddr->precord->stat; if (severity) *severity = paddr->precord->sevr; return 0; } static long dbDbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp) { DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; *pstamp = paddr->precord->time; return 0; } static long dbDbPutValue(struct link *plink, short dbrType, const void *pbuffer, long nRequest) { struct pv_link *ppv_link = &plink->value.pv_link; struct dbCommon *psrce = plink->precord; DBADDR *paddr = (DBADDR *) ppv_link->pvt; dbCommon *pdest = paddr->precord; long status = dbPut(paddr, dbrType, pbuffer, nRequest); recGblInheritSevr(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta, psrce->nsev); if (status) return status; if (paddr->pfield == (void *) &pdest->proc || (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) { status = processTarget(psrce, pdest); } return status; } static void dbDbScanFwdLink(struct link *plink) { dbCommon *precord = plink->precord; dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt; dbScanPassive(precord, paddr->precord); } static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) { return rtn(plink, priv); } static lset dbDb_lset = { 0, 0, /* not Constant, not Volatile */ NULL, dbDbRemoveLink, NULL, NULL, NULL, dbDbIsConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue, dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits, dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp, dbDbPutValue, NULL, dbDbScanFwdLink, doLocked }; /* * Process a record if its scan field is passive. */ long dbScanPassive(dbCommon *pfrom, dbCommon *pto) { /* if not passive we're done */ if (pto->scan != 0) return 0; return processTarget(pfrom, pto); } static long processTarget(dbCommon *psrc, dbCommon *pdst) { char context[40] = ""; int trace = dbAccessDebugPUTF && *dbLockSetAddrTrace(psrc); int claim_src = dbRec2Pvt(psrc)->procThread==NULL; int claim_dst = psrc!=pdst && dbRec2Pvt(pdst)->procThread==NULL; long status; epicsUInt8 pact = psrc->pact; epicsThreadId self = epicsThreadGetIdSelf(); psrc->pact = TRUE; if (psrc && psrc->ppn) dbNotifyAdd(psrc, pdst); if (trace && dbServerClient(context, sizeof(context))) { /* No client, use thread name */ strncpy(context, epicsThreadGetNameSelf(), sizeof(context)); context[sizeof(context) - 1] = 0; } if (!pdst->pact) { /* Normal propagation of PUTF from src to dst */ if (trace) printf("%s: '%s' -> '%s' with PUTF=%u\n", context, psrc->name, pdst->name, psrc->putf); pdst->putf = psrc->putf; } else if (psrc->putf && claim_dst) { /* The dst record is busy (awaiting async reprocessing), * not being processed recursively by us, and * we were originally triggered by a call to dbPutField(), * so we mark the dst record for reprocessing once the async * completion is over. */ if (trace) printf("%s: '%s' -> Active '%s', setting RPRO=1\n", context, psrc->name, pdst->name); pdst->putf = FALSE; pdst->rpro = TRUE; } else { /* The dst record is busy, but either is being processed recursively, * or wasn't triggered by a call to dbPutField(). Do nothing. */ if (trace) printf("%s: '%s' -> Active '%s', done\n", context, psrc->name, pdst->name); } if(claim_src) { dbRec2Pvt(psrc)->procThread = self; } if(claim_dst) { dbRec2Pvt(pdst)->procThread = self; } if(dbRec2Pvt(psrc)->procThread!=self || dbRec2Pvt(pdst)->procThread!=self) { errlogPrintf("Logic Error: processTarget 1 from %p, %s(%p) -> %s(%p)\n", self, psrc->name, dbRec2Pvt(psrc), pdst->name, dbRec2Pvt(pdst)); } status = dbProcess(pdst); psrc->pact = pact; if(dbRec2Pvt(psrc)->procThread!=self || dbRec2Pvt(pdst)->procThread!=self) { errlogPrintf("Logic Error: processTarget 2 from %p, %s(%p) -> %s(%p)\n", self, psrc->name, dbRec2Pvt(psrc), pdst->name, dbRec2Pvt(pdst)); } if(claim_src) { dbRec2Pvt(psrc)->procThread = NULL; } if(claim_dst) { dbRec2Pvt(pdst)->procThread = NULL; } return status; } base-7.0.3.1/modules/database/src/ioc/db/dbDbLink.h0000664000577000060420000000170413557101274020401 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbDbLink.h * * Created on: April 3rd, 2016 * Author: Andrew Johnson */ #ifndef INC_dbDbLink_H #define INC_dbDbLink_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif struct link; struct dbLocker; epicsShareFunc long dbDbInitLink(struct link *plink, short dbfType); epicsShareFunc void dbDbAddLink(struct dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget); #ifdef __cplusplus } #endif #endif /* INC_dbDbLink_H */ base-7.0.3.1/modules/database/src/ioc/db/dbEvent.c0000664000577000060420000010071713557101274020316 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbEvent.c */ /* * Author: Jeffrey O. Hill * * Ralph Lange */ #include #include #include #include #include #include #include "cantProceed.h" #include "dbDefs.h" #include "epicsAssert.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "epicsThread.h" #include "errlog.h" #include "freeList.h" #include "taskwd.h" #include "caeventmask.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" #include "dbChannel.h" #include "dbCommon.h" #include "dbEvent.h" #include "db_field_log.h" #include "dbFldTypes.h" #include "dbLock.h" #include "link.h" #include "special.h" /* Queue size based on Ethernet MTU of 1500 bytes. * Assume <=66 bytes of ethernet+IP+TCP overhead * and 40 byte CA messages (DBF_TIME_DOUBLE). * * (1500-66)/40 -> 35 */ #define EVENTSPERQUE 36 #define EVENTENTRIES 4 /* the number of que entries for each event */ #define EVENTQUESIZE (EVENTENTRIES * EVENTSPERQUE) #define EVENTQEMPTY ((struct evSubscrip *)NULL) /* * really a ring buffer */ struct event_que { /* lock writers to the ring buffer only */ /* readers must never slow up writers */ epicsMutexId writelock; db_field_log *valque[EVENTQUESIZE]; struct evSubscrip *evque[EVENTQUESIZE]; struct event_que *nextque; /* in case que quota exceeded */ struct event_user *evUser; /* event user parent struct */ unsigned short putix; unsigned short getix; unsigned short quota; /* the number of assigned entries*/ unsigned short nDuplicates; /* N events duplicated on this q */ unsigned short nCanceled; /* the number of canceled entries */ }; struct event_user { struct event_que firstque; /* the first event que */ epicsMutexId lock; epicsEventId ppendsem; /* Wait while empty */ epicsEventId pflush_sem; /* wait for flush */ EXTRALABORFUNC *extralabor_sub;/* off load to event task */ void *extralabor_arg;/* parameter to above */ epicsThreadId taskid; /* event handler task id */ struct evSubscrip *pSuicideEvent; /* event that is deleteing itself */ unsigned queovr; /* event que overflow count */ unsigned char pendexit; /* exit pend task */ 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; }; /* * Reliable intertask communication requires copying the current value of the * channel for later queing so 3 stepper motor steps of 10 each do not turn * into only 10 or 20 total steps part of the time. */ #define RNGINC(OLD)\ ( (unsigned short) ( (OLD) >= (EVENTQUESIZE-1) ? 0 : (OLD)+1 ) ) #define LOCKEVQUE(EV_QUE) epicsMutexMustLock((EV_QUE)->writelock) #define UNLOCKEVQUE(EV_QUE) epicsMutexUnlock((EV_QUE)->writelock) #define LOCKREC(RECPTR) epicsMutexMustLock((RECPTR)->mlok) #define UNLOCKREC(RECPTR) epicsMutexUnlock((RECPTR)->mlok) static void *dbevEventUserFreeList; static void *dbevEventQueueFreeList; static void *dbevEventSubscriptionFreeList; static void *dbevFieldLogFreeList; static char *EVENT_PEND_NAME = "eventTask"; static struct evSubscrip canceledEvent; static unsigned short ringSpace ( const struct event_que *pevq ) { if ( pevq->evque[pevq->putix] == EVENTQEMPTY ) { if ( pevq->getix > pevq->putix ) { return ( unsigned short ) ( pevq->getix - pevq->putix ); } else { return ( unsigned short ) ( ( EVENTQUESIZE + pevq->getix ) - pevq->putix ); } } return 0; } /* * db_event_list () */ int db_event_list ( const char *pname, unsigned level ) { return dbel ( pname, level ); } /* * dbel () */ int dbel ( const char *pname, unsigned level ) { DBADDR addr; long status; struct evSubscrip *pevent; dbFldDes *pdbFldDes; if ( ! pname ) return DB_EVENT_OK; status = dbNameToAddr ( pname, &addr ); if ( status != 0 ) { errMessage ( status, " dbNameToAddr failed" ); return DB_EVENT_ERROR; } LOCKREC (addr.precord); pevent = (struct evSubscrip *) ellFirst ( &addr.precord->mlis ); if ( ! pevent ) { printf ( "\"%s\": No PV event subscriptions ( monitors ).\n", pname ); UNLOCKREC (addr.precord); return DB_EVENT_OK; } printf ( "%u PV Event Subscriptions ( monitors ).\n", ellCount ( &addr.precord->mlis ) ); while ( pevent ) { pdbFldDes = dbChannelFldDes(pevent->chan); if ( level > 0 ) { printf ( "%4.4s", pdbFldDes->name ); printf ( " { " ); if ( pevent->select & DBE_VALUE ) printf( "VALUE " ); if ( pevent->select & DBE_LOG ) printf( "LOG " ); if ( pevent->select & DBE_ALARM ) printf( "ALARM " ); if ( pevent->select & DBE_PROPERTY ) printf( "PROPERTY " ); printf ( "}" ); if ( pevent->npend ) { printf ( " undelivered=%ld", pevent->npend ); } if ( level > 1 ) { unsigned nEntriesFree; const void * taskId; LOCKEVQUE(pevent->ev_que); nEntriesFree = ringSpace ( pevent->ev_que ); taskId = ( void * ) pevent->ev_que->evUser->taskid; UNLOCKEVQUE(pevent->ev_que); if ( nEntriesFree == 0u ) { printf ( ", thread=%p, queue full", (void *) taskId ); } else if ( nEntriesFree == EVENTQUESIZE ) { printf ( ", thread=%p, queue empty", (void *) taskId ); } else { printf ( ", thread=%p, unused entries=%u", (void *) taskId, nEntriesFree ); } } if ( level > 2 ) { unsigned nDuplicates; unsigned nCanceled; if ( pevent->nreplace ) { printf (", discarded by replacement=%ld", pevent->nreplace); } if ( ! pevent->useValque ) { printf (", queueing disabled" ); } LOCKEVQUE(pevent->ev_que); nDuplicates = pevent->ev_que->nDuplicates; nCanceled = pevent->ev_que->nCanceled; UNLOCKEVQUE(pevent->ev_que); if ( nDuplicates ) { printf (", duplicate count =%u\n", nDuplicates ); } if ( nCanceled ) { printf (", canceled count =%u\n", nCanceled ); } } if ( level > 3 ) { printf ( ", ev %p, ev que %p, ev user %p", ( void * ) pevent, ( void * ) pevent->ev_que, ( void * ) pevent->ev_que->evUser ); } printf( "\n" ); } pevent = (struct evSubscrip *) ellNext ( &pevent->node ); } UNLOCKREC (addr.precord); return DB_EVENT_OK; } /* * DB_INIT_EVENTS() * * * Initialize the event facility for this task. Must be called at least once * by each task which uses the db event facility * * returns: ptr to event user block or NULL if memory can't be allocated */ dbEventCtx db_init_events (void) { struct event_user * evUser; if (!dbevEventUserFreeList) { freeListInitPvt(&dbevEventUserFreeList, sizeof(struct event_user),8); } if (!dbevEventQueueFreeList) { freeListInitPvt(&dbevEventQueueFreeList, sizeof(struct event_que),8); } if (!dbevEventSubscriptionFreeList) { freeListInitPvt(&dbevEventSubscriptionFreeList, sizeof(struct evSubscrip),256); } if (!dbevFieldLogFreeList) { freeListInitPvt(&dbevFieldLogFreeList, sizeof(struct db_field_log),2048); } evUser = (struct event_user *) freeListCalloc(dbevEventUserFreeList); if (!evUser) { return NULL; } evUser->firstque.evUser = evUser; evUser->firstque.writelock = epicsMutexCreate(); if (!evUser->firstque.writelock) goto fail; evUser->ppendsem = epicsEventCreate(epicsEventEmpty); if (!evUser->ppendsem) goto fail; evUser->pflush_sem = epicsEventCreate(epicsEventEmpty); if (!evUser->pflush_sem) goto fail; evUser->lock = epicsMutexCreate(); if (!evUser->lock) goto fail; evUser->flowCtrlMode = FALSE; evUser->extraLaborBusy = FALSE; evUser->pSuicideEvent = NULL; return (dbEventCtx) evUser; fail: if(evUser->lock) epicsMutexDestroy (evUser->lock); if(evUser->firstque.writelock) epicsMutexDestroy (evUser->firstque.writelock); if(evUser->ppendsem) epicsEventDestroy (evUser->ppendsem); if(evUser->pflush_sem) epicsEventDestroy (evUser->pflush_sem); freeListFree(dbevEventUserFreeList,evUser); return NULL; } epicsShareFunc void db_cleanup_events(void) { if(dbevEventUserFreeList) freeListCleanup(dbevEventUserFreeList); dbevEventUserFreeList = NULL; if(dbevEventQueueFreeList) freeListCleanup(dbevEventQueueFreeList); dbevEventQueueFreeList = NULL; if(dbevEventSubscriptionFreeList) freeListCleanup(dbevEventSubscriptionFreeList); dbevEventSubscriptionFreeList = NULL; if(dbevFieldLogFreeList) freeListCleanup(dbevFieldLogFreeList); dbevFieldLogFreeList = NULL; } /* * DB_CLOSE_EVENTS() * * evUser block and additional event queues * deallocated when the event thread terminates * itself * */ void db_close_events (dbEventCtx ctx) { struct event_user * const evUser = (struct event_user *) ctx; /* * Exit not forced on event blocks for now - this is left to channel * access and any other tasks using this facility which can find them * more efficiently. * * NOTE: not deleting events before calling this routine could be * hazardous to the system's health. */ epicsMutexMustLock ( evUser->lock ); evUser->pendexit = TRUE; epicsMutexUnlock ( evUser->lock ); /* notify the waiting task */ epicsEventSignal(evUser->ppendsem); if(evUser->taskid) epicsThreadMustJoin(evUser->taskid); /* evUser has been deleted by the worker */ } /* * create_ev_que() */ static struct event_que * create_ev_que ( struct event_user * const evUser ) { struct event_que * const ev_que = (struct event_que *) freeListCalloc ( dbevEventQueueFreeList ); if ( ! ev_que ) { return NULL; } ev_que->writelock = epicsMutexCreate(); if ( ! ev_que->writelock ) { freeListFree ( dbevEventQueueFreeList, ev_que ); return NULL; } ev_que->evUser = evUser; return ev_que; } /* * DB_ADD_EVENT() */ dbEventSubscription db_add_event ( dbEventCtx ctx, struct dbChannel *chan, EVENTFUNC *user_sub, void *user_arg, unsigned select) { struct event_user * const evUser = (struct event_user *) ctx; struct event_que * ev_que; struct evSubscrip * pevent; /* * Don't add events which will not be triggered */ if ( select==0 || select > UCHAR_MAX ) { return NULL; } pevent = freeListCalloc (dbevEventSubscriptionFreeList); if ( ! pevent ) { return NULL; } /* find an event que block with enough quota */ /* otherwise add a new one to the list */ epicsMutexMustLock ( evUser->lock ); ev_que = & evUser->firstque; while ( TRUE ) { int success = 0; LOCKEVQUE ( ev_que ); success = ( ev_que->quota + ev_que->nCanceled < EVENTQUESIZE - EVENTENTRIES ); if ( success ) { ev_que->quota += EVENTENTRIES; } UNLOCKEVQUE ( ev_que ); if ( success ) { break; } if ( ! ev_que->nextque ) { ev_que->nextque = create_ev_que ( evUser ); if ( ! ev_que->nextque ) { ev_que = NULL; break; } } ev_que = ev_que->nextque; } epicsMutexUnlock ( evUser->lock ); if ( ! ev_que ) { freeListFree ( dbevEventSubscriptionFreeList, pevent ); return NULL; } pevent->npend = 0ul; pevent->nreplace = 0ul; pevent->user_sub = user_sub; pevent->user_arg = user_arg; pevent->chan = chan; pevent->select = (unsigned char) select; pevent->pLastLog = NULL; /* not yet in the queue */ pevent->callBackInProgress = FALSE; pevent->enabled = FALSE; pevent->ev_que = ev_que; /* * Simple types values queued up for reliable interprocess * communication (for other types they get whatever happens to be * there upon wakeup) */ if (dbChannelElements(chan) == 1 && dbChannelSpecial(chan) != SPC_DBADDR && dbChannelFieldSize(chan) <= sizeof(union native_value)) { pevent->useValque = TRUE; } else { pevent->useValque = FALSE; } return pevent; } /* * db_event_enable() */ void db_event_enable (dbEventSubscription event) { struct evSubscrip * const pevent = (struct evSubscrip *) event; struct dbCommon * const precord = dbChannelRecord(pevent->chan); LOCKREC (precord); if ( ! pevent->enabled ) { ellAdd (&precord->mlis, &pevent->node); pevent->enabled = TRUE; } UNLOCKREC (precord); } /* * db_event_disable() */ void db_event_disable (dbEventSubscription event) { struct evSubscrip * const pevent = (struct evSubscrip *) event; struct dbCommon * const precord = dbChannelRecord(pevent->chan); LOCKREC (precord); if ( pevent->enabled ) { ellDelete(&precord->mlis, &pevent->node); pevent->enabled = FALSE; } UNLOCKREC (precord); } /* * event_remove() * event queue lock _must_ be applied * this nulls the entry in the queue, but doesn't delete the db_field_log chunk */ static void event_remove ( struct event_que *ev_que, unsigned short index, struct evSubscrip *placeHolder ) { struct evSubscrip * const pevent = ev_que->evque[index]; ev_que->evque[index] = placeHolder; ev_que->valque[index] = NULL; if ( pevent->npend == 1u ) { pevent->pLastLog = NULL; } else { assert ( pevent->npend > 1u ); assert ( ev_que->nDuplicates >= 1u ); ev_que->nDuplicates--; } pevent->npend--; } /* * DB_CANCEL_EVENT() * * This routine does not prevent two threads from deleting * the same block at the same time. * */ void db_cancel_event (dbEventSubscription event) { struct evSubscrip * const pevent = (struct evSubscrip *) event; unsigned short getix; db_event_disable ( event ); /* * flag the event as canceled by NULLing out the callback handler * * make certain that the event isnt being accessed while * its call back changes */ LOCKEVQUE (pevent->ev_que); pevent->user_sub = NULL; /* * purge this event from the queue * * Its better to take this approach rather than waiting * for the event thread to finish removing this event * from the queue because the event thread will not * process if we are in flow control mode. Since blocking * here will block CA's TCP input queue then a dead lock * would be possible. */ for ( getix = pevent->ev_que->getix; pevent->ev_que->evque[getix] != EVENTQEMPTY; ) { if ( pevent->ev_que->evque[getix] == pevent ) { assert ( pevent->ev_que->nCanceled < USHRT_MAX ); pevent->ev_que->nCanceled++; event_remove ( pevent->ev_que, getix, &canceledEvent ); } getix = RNGINC ( getix ); if ( getix == pevent->ev_que->getix ) { break; } } assert ( pevent->npend == 0u ); if ( pevent->ev_que->evUser->taskid == epicsThreadGetIdSelf() ) { pevent->ev_que->evUser->pSuicideEvent = pevent; } else { while ( pevent->callBackInProgress ) { UNLOCKEVQUE (pevent->ev_que); epicsEventMustWait ( pevent->ev_que->evUser->pflush_sem ); LOCKEVQUE (pevent->ev_que); } } pevent->ev_que->quota -= EVENTENTRIES; UNLOCKEVQUE (pevent->ev_que); freeListFree ( dbevEventSubscriptionFreeList, pevent ); return; } /* * DB_FLUSH_EXTRA_LABOR_EVENT() * * waits for extra labor in progress to finish */ void db_flush_extra_labor_event (dbEventCtx ctx) { struct event_user * const evUser = (struct event_user *) ctx; epicsMutexMustLock ( evUser->lock ); while ( evUser->extraLaborBusy ) { epicsMutexUnlock ( evUser->lock ); epicsThreadSleep(0.1); epicsMutexMustLock ( evUser->lock ); } epicsMutexUnlock ( evUser->lock ); } /* * DB_ADD_EXTRA_LABOR_EVENT() * * Specify a routine to be called * when labor is offloaded to the * event task */ int db_add_extra_labor_event ( dbEventCtx ctx, EXTRALABORFUNC *func, void *arg) { struct event_user * const evUser = (struct event_user *) ctx; epicsMutexMustLock ( evUser->lock ); evUser->extralabor_sub = func; evUser->extralabor_arg = arg; epicsMutexUnlock ( evUser->lock ); return DB_EVENT_OK; } /* * DB_POST_EXTRA_LABOR() */ int db_post_extra_labor (dbEventCtx ctx) { struct event_user * const evUser = (struct event_user *) ctx; int doit; epicsMutexMustLock ( evUser->lock ); if ( ! evUser->extra_labor ) { evUser->extra_labor = TRUE; doit = TRUE; } else { doit = FALSE; } epicsMutexUnlock ( evUser->lock ); if ( doit ) { epicsEventSignal(evUser->ppendsem); } return DB_EVENT_OK; } /* * DB_CREATE_EVENT_LOG() * * NOTE: This assumes that the db scan lock is already applied * (as it copies data from the record) */ db_field_log* db_create_event_log (struct evSubscrip *pevent) { db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList); if (pLog) { struct dbChannel *chan = pevent->chan; struct dbCommon *prec = dbChannelRecord(chan); pLog->ctx = dbfl_context_event; if (pevent->useValque) { pLog->type = dbfl_type_val; pLog->stat = prec->stat; pLog->sevr = prec->sevr; pLog->time = prec->time; pLog->field_type = dbChannelFieldType(chan); pLog->no_elements = dbChannelElements(chan); /* * use memcpy to avoid a bus error on * union copy of char in the db at an odd * address */ memcpy(&pLog->u.v.field, dbChannelField(chan), dbChannelFieldSize(chan)); } else { pLog->type = dbfl_type_rec; } } return pLog; } /* * DB_CREATE_READ_LOG() * */ db_field_log* db_create_read_log (struct dbChannel *chan) { db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList); if (pLog) { pLog->ctx = dbfl_context_read; pLog->type = dbfl_type_rec; } return pLog; } /* * DB_QUEUE_EVENT_LOG() * */ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog) { struct event_que *ev_que; int firstEventFlag; unsigned rngSpace; ev_que = pevent->ev_que; /* * evUser ring buffer must be locked for the multiple * threads writing/reading it */ LOCKEVQUE (ev_que); /* * if we have an event on the queue and both the last * event on the queue and the current event are emtpy * (i.e. of type dbfl_type_rec), simply ignore duplicate * events (saving empty events serves no purpose) */ if (pevent->npend > 0u && (*pevent->pLastLog)->type == dbfl_type_rec && pLog->type == dbfl_type_rec) { db_delete_field_log(pLog); UNLOCKEVQUE (ev_que); return; } /* * add to task local event que */ /* * if an event is on the queue and one of * {flowCtrlMode, not room for one more of each monitor attached} * then replace the last event on the queue (for this monitor) */ rngSpace = ringSpace ( ev_que ); if ( pevent->npend>0u && (ev_que->evUser->flowCtrlMode || rngSpace<=EVENTSPERQUE) ) { /* * replace last event if no space is left */ if (*pevent->pLastLog) { db_delete_field_log(*pevent->pLastLog); *pevent->pLastLog = pLog; } pevent->nreplace++; /* * the event task has already been notified about * this so we dont need to post the semaphore */ firstEventFlag = 0; } /* * Otherwise, the current entry must be available. * Fill it in and advance the ring buffer. */ else { assert ( ev_que->evque[ev_que->putix] == EVENTQEMPTY ); ev_que->evque[ev_que->putix] = pevent; ev_que->valque[ev_que->putix] = pLog; pevent->pLastLog = &ev_que->valque[ev_que->putix]; if (pevent->npend>0u) { ev_que->nDuplicates++; } pevent->npend++; /* * if the ring buffer was empty before * adding this event */ if (rngSpace==EVENTQUESIZE) { firstEventFlag = 1; } else { firstEventFlag = 0; } ev_que->putix = RNGINC ( ev_que->putix ); } UNLOCKEVQUE (ev_que); /* * its more efficent to notify the event handler * only after the event is ready and the lock * is off in case it runs at a higher priority * than the caller here. */ if (firstEventFlag) { /* * notify the event handler */ epicsEventSignal(ev_que->evUser->ppendsem); } } /* * DB_POST_EVENTS() * * NOTE: This assumes that the db scan lock is already applied * */ int db_post_events( void *pRecord, void *pField, unsigned int caEventMask ) { struct dbCommon * const prec = (struct dbCommon *) pRecord; struct evSubscrip *pevent; if (prec->mlis.count == 0) return DB_EVENT_OK; /* no monitors set */ LOCKREC (prec); for (pevent = (struct evSubscrip *) prec->mlis.node.next; pevent; pevent = (struct evSubscrip *) pevent->node.next){ /* * Only send event msg if they are waiting on the field which * changed or pval==NULL, and are waiting on matching event */ if ( (dbChannelField(pevent->chan) == (void *)pField || pField==NULL) && (caEventMask & pevent->select)) { db_field_log *pLog = db_create_event_log(pevent); pLog = dbChannelRunPreChain(pevent->chan, pLog); if (pLog) db_queue_event_log(pevent, pLog); } } UNLOCKREC (prec); return DB_EVENT_OK; } /* * DB_POST_SINGLE_EVENT() */ void db_post_single_event (dbEventSubscription event) { struct evSubscrip * const pevent = (struct evSubscrip *) event; struct dbCommon * const prec = dbChannelRecord(pevent->chan); db_field_log *pLog; dbScanLock (prec); pLog = db_create_event_log(pevent); pLog = dbChannelRunPreChain(pevent->chan, pLog); if(pLog) db_queue_event_log(pevent, pLog); dbScanUnlock (prec); } /* * EVENT_READ() */ static int event_read ( struct event_que *ev_que ) { db_field_log *pfl; void ( *user_sub ) ( void *user_arg, struct dbChannel *chan, int eventsRemaining, db_field_log *pfl ); /* * evUser ring buffer must be locked for the multiple * threads writing/reading it */ LOCKEVQUE (ev_que); /* * if in flow control mode drain duplicates and then * suspend processing events until flow control * mode is over */ if ( ev_que->evUser->flowCtrlMode && ev_que->nDuplicates == 0u ) { UNLOCKEVQUE (ev_que); return DB_EVENT_OK; } while ( ev_que->evque[ev_que->getix] != EVENTQEMPTY ) { struct evSubscrip *pevent = ev_que->evque[ev_que->getix]; pfl = ev_que->valque[ev_que->getix]; if ( pevent == &canceledEvent ) { ev_que->evque[ev_que->getix] = EVENTQEMPTY; if (ev_que->valque[ev_que->getix]) { db_delete_field_log(ev_que->valque[ev_que->getix]); ev_que->valque[ev_que->getix] = NULL; } ev_que->getix = RNGINC ( ev_que->getix ); assert ( ev_que->nCanceled > 0 ); ev_que->nCanceled--; continue; } /* * Simple type values queued up for reliable interprocess * communication. (for other types they get whatever happens * to be there upon wakeup) */ event_remove ( ev_que, ev_que->getix, EVENTQEMPTY ); ev_que->getix = RNGINC ( ev_que->getix ); /* * create a local copy of the call back parameters while * we still have the lock */ user_sub = pevent->user_sub; /* * Next event pointer can be used by event tasks to determine * if more events are waiting in the queue * * Must remove the lock here so that we dont deadlock if * this calls dbGetField() and blocks on the record lock, * dbPutField() is in progress in another task, it has the * record lock, and it is calling db_post_events() waiting * for the event queue lock (which this thread now has). */ if ( user_sub ) { /* * This provides a way to test to see if an event is in use * despite the fact that the event queue does not point to * it. */ pevent->callBackInProgress = TRUE; UNLOCKEVQUE (ev_que); /* Run post-event-queue filter chain */ if (ellCount(&pevent->chan->post_chain)) { pfl = dbChannelRunPostChain(pevent->chan, pfl); } if (pfl) { /* Issue user callback */ ( *user_sub ) ( pevent->user_arg, pevent->chan, ev_que->evque[ev_que->getix] != EVENTQEMPTY, pfl ); } LOCKEVQUE (ev_que); /* * check to see if this event has been canceled each * time that the callBackInProgress flag is set to false * while we have the event queue lock, and post the flush * complete sem if there are no longer any events on the * queue */ if ( ev_que->evUser->pSuicideEvent == pevent ) { ev_que->evUser->pSuicideEvent = NULL; } else { if ( pevent->user_sub==NULL && pevent->npend==0u ) { pevent->callBackInProgress = FALSE; epicsEventSignal ( ev_que->evUser->pflush_sem ); } else { pevent->callBackInProgress = FALSE; } } } db_delete_field_log(pfl); } UNLOCKEVQUE (ev_que); return DB_EVENT_OK; } /* * EVENT_TASK() */ static void event_task (void *pParm) { struct event_user * const evUser = (struct event_user *) pParm; struct event_que * ev_que; unsigned char pendexit; /* init hook */ if (evUser->init_func) { (*evUser->init_func)(evUser->init_func_arg); } taskwdInsert ( epicsThreadGetIdSelf(), NULL, NULL ); do { void (*pExtraLaborSub) (void *); void *pExtraLaborArg; epicsEventMustWait(evUser->ppendsem); /* * check to see if the caller has offloaded * labor to this task */ epicsMutexMustLock ( evUser->lock ); evUser->extraLaborBusy = TRUE; if ( evUser->extra_labor && evUser->extralabor_sub ) { evUser->extra_labor = FALSE; pExtraLaborSub = evUser->extralabor_sub; pExtraLaborArg = evUser->extralabor_arg; } else { pExtraLaborSub = NULL; pExtraLaborArg = NULL; } if ( pExtraLaborSub ) { epicsMutexUnlock ( evUser->lock ); (*pExtraLaborSub)(pExtraLaborArg); epicsMutexMustLock ( evUser->lock ); } evUser->extraLaborBusy = FALSE; for ( ev_que = &evUser->firstque; ev_que; ev_que = ev_que->nextque ) { epicsMutexUnlock ( evUser->lock ); event_read (ev_que); epicsMutexMustLock ( evUser->lock ); } pendexit = evUser->pendexit; epicsMutexUnlock ( evUser->lock ); } while( ! pendexit ); epicsMutexDestroy(evUser->firstque.writelock); { struct event_que *nextque; ev_que = evUser->firstque.nextque; while (ev_que) { nextque = ev_que->nextque; epicsMutexDestroy(ev_que->writelock); freeListFree(dbevEventQueueFreeList, ev_que); ev_que = nextque; } } epicsEventDestroy(evUser->ppendsem); epicsEventDestroy(evUser->pflush_sem); epicsMutexDestroy(evUser->lock); freeListFree(dbevEventUserFreeList, evUser); taskwdRemove(epicsThreadGetIdSelf()); return; } /* * DB_START_EVENTS() */ int db_start_events ( dbEventCtx ctx,const char *taskname, void (*init_func)(void *), void *init_func_arg, unsigned osiPriority ) { struct event_user * const evUser = (struct event_user *) ctx; epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; opts.stackSize = epicsThreadGetStackSize(epicsThreadStackMedium); opts.priority = osiPriority; opts.joinable = 1; epicsMutexMustLock ( evUser->lock ); /* * only one ca_pend_event thread may be * started for each evUser */ if (evUser->taskid) { epicsMutexUnlock ( evUser->lock ); return DB_EVENT_OK; } evUser->init_func = init_func; evUser->init_func_arg = init_func_arg; if (!taskname) { taskname = EVENT_PEND_NAME; } evUser->taskid = epicsThreadCreateOpt ( taskname, event_task, (void *)evUser, &opts); if (!evUser->taskid) { epicsMutexUnlock ( evUser->lock ); return DB_EVENT_ERROR; } epicsMutexUnlock ( evUser->lock ); return DB_EVENT_OK; } /* * db_event_change_priority() */ void db_event_change_priority ( dbEventCtx ctx, unsigned epicsPriority ) { struct event_user * const evUser = ( struct event_user * ) ctx; epicsThreadSetPriority ( evUser->taskid, epicsPriority ); } /* * db_event_flow_ctrl_mode_on() */ void db_event_flow_ctrl_mode_on (dbEventCtx ctx) { struct event_user * const evUser = (struct event_user *) ctx; epicsMutexMustLock ( evUser->lock ); evUser->flowCtrlMode = TRUE; epicsMutexUnlock ( evUser->lock ); /* * notify the event handler task */ epicsEventSignal(evUser->ppendsem); #ifdef DEBUG printf("fc on %lu\n", tickGet()); #endif } /* * db_event_flow_ctrl_mode_off() */ void db_event_flow_ctrl_mode_off (dbEventCtx ctx) { struct event_user * const evUser = (struct event_user *) ctx; epicsMutexMustLock ( evUser->lock ); evUser->flowCtrlMode = FALSE; epicsMutexUnlock ( evUser->lock ); /* * notify the event handler task */ epicsEventSignal (evUser->ppendsem); #ifdef DEBUG printf("fc off %lu\n", tickGet()); #endif } /* * db_delete_field_log() */ void db_delete_field_log (db_field_log *pfl) { if (pfl) { /* Free field if reference type field log and dtor is set */ if (pfl->type == dbfl_type_ref && pfl->u.r.dtor) pfl->u.r.dtor(pfl); /* Free the field log chunk */ freeListFree(dbevFieldLogFreeList, pfl); } } int db_available_logs(void) { return (int) freeListItemsAvail(dbevFieldLogFreeList); } base-7.0.3.1/modules/database/src/ioc/db/dbEvent.h0000664000577000060420000000611413557101274020317 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeffrey O. Hill * * Ralph Lange */ #ifndef INCLdbEventh #define INCLdbEventh #ifdef epicsExportSharedSymbols # undef epicsExportSharedSymbols # define INCLdbEventhExporting #endif #include "epicsThread.h" #ifdef INCLdbEventhExporting # define epicsExportSharedSymbols #endif #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif struct dbChannel; struct db_field_log; struct evSubscrip; epicsShareFunc int db_event_list ( const char *name, unsigned level); epicsShareFunc int dbel ( const char *name, unsigned level); epicsShareFunc int db_post_events ( void *pRecord, void *pField, unsigned caEventMask ); typedef void * dbEventCtx; typedef void EXTRALABORFUNC (void *extralabor_arg); epicsShareFunc dbEventCtx db_init_events (void); epicsShareFunc int db_start_events ( dbEventCtx ctx, const char *taskname, void (*init_func)(void *), void *init_func_arg, unsigned osiPriority ); epicsShareFunc void db_close_events (dbEventCtx ctx); epicsShareFunc void db_event_flow_ctrl_mode_on (dbEventCtx ctx); epicsShareFunc void db_event_flow_ctrl_mode_off (dbEventCtx ctx); epicsShareFunc int db_add_extra_labor_event ( dbEventCtx ctx, EXTRALABORFUNC *func, void *arg); epicsShareFunc void db_flush_extra_labor_event (dbEventCtx); epicsShareFunc int db_post_extra_labor (dbEventCtx ctx); epicsShareFunc void db_event_change_priority ( dbEventCtx ctx, unsigned epicsPriority ); #ifdef EPICS_PRIVATE_API epicsShareFunc void db_cleanup_events(void); #endif typedef void EVENTFUNC (void *user_arg, struct dbChannel *chan, int eventsRemaining, struct db_field_log *pfl); typedef void * dbEventSubscription; epicsShareFunc dbEventSubscription db_add_event ( dbEventCtx ctx, struct dbChannel *chan, EVENTFUNC *user_sub, void *user_arg, unsigned select); epicsShareFunc void db_cancel_event (dbEventSubscription es); epicsShareFunc void db_post_single_event (dbEventSubscription es); epicsShareFunc void db_event_enable (dbEventSubscription es); epicsShareFunc void db_event_disable (dbEventSubscription es); epicsShareFunc struct db_field_log* db_create_event_log (struct evSubscrip *pevent); epicsShareFunc struct db_field_log* db_create_read_log (struct dbChannel *chan); epicsShareFunc void db_delete_field_log (struct db_field_log *pfl); epicsShareFunc int db_available_logs(void); #define DB_EVENT_OK 0 #define DB_EVENT_ERROR (-1) #ifdef __cplusplus } #endif #endif /*INCLdbEventh*/ base-7.0.3.1/modules/database/src/ioc/db/dbExtractArray.c0000664000577000060420000000604513557101274021645 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange * * based on dbConvert.c * written by: Bob Dalesio, Marty Kraimer */ #include #include "epicsTypes.h" #define epicsExportSharedSymbols #include "dbAddr.h" #include "dbExtractArray.h" void dbExtractArrayFromRec(const dbAddr *paddr, void *pto, long nRequest, long no_elements, long offset, long increment) { char *pdst = (char *) pto; char *psrc = (char *) paddr->pfield; long nUpperPart; int i; short srcSize = paddr->field_size; short dstSize = srcSize; char isString = (paddr->field_type == DBF_STRING); if (nRequest > no_elements) nRequest = no_elements; if (isString && srcSize > MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE; if (increment == 1 && dstSize == srcSize) { nUpperPart = nRequest < no_elements - offset ? nRequest : no_elements - offset; memcpy(pdst, &psrc[offset * srcSize], dstSize * nUpperPart); if (nRequest > nUpperPart) memcpy(&pdst[dstSize * nUpperPart], psrc, dstSize * (nRequest - nUpperPart)); if (isString) for (i = 1; i <= nRequest; i++) pdst[dstSize*i-1] = '\0'; } else { for (; nRequest > 0; nRequest--, pdst += dstSize, offset += increment) { offset %= no_elements; memcpy(pdst, &psrc[offset*srcSize], dstSize); if (isString) pdst[dstSize-1] = '\0'; } } } void dbExtractArrayFromBuf(const void *pfrom, void *pto, short field_size, short field_type, long nRequest, long no_elements, long offset, long increment) { char *pdst = (char *) pto; char *psrc = (char *) pfrom; int i; short srcSize = field_size; short dstSize = srcSize; char isString = (field_type == DBF_STRING); if (nRequest > no_elements) nRequest = no_elements; if (offset > no_elements - 1) offset = no_elements - 1; if (isString && dstSize >= MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE - 1; if (increment == 1) { memcpy(pdst, &psrc[offset * srcSize], dstSize * nRequest); if (isString) for (i = 1; i <= nRequest; i++) pdst[dstSize*i] = '\0'; } else { for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) { memcpy(pdst, &psrc[offset*srcSize], dstSize); if (isString) pdst[dstSize] = '\0'; } } } base-7.0.3.1/modules/database/src/ioc/db/dbExtractArray.h0000664000577000060420000000242313557101274021646 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_dbExtractArray_H #define INC_dbExtractArray_H #include "dbFldTypes.h" #include "dbAddr.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void dbExtractArrayFromRec(const DBADDR *paddr, void *pto, long nRequest, long no_elements, long offset, long increment); epicsShareFunc void dbExtractArrayFromBuf(const void *pfrom, void *pto, short field_size, short field_type, long nRequest, long no_elements, long offset, long increment); #ifdef __cplusplus } #endif #endif // INC_dbExtractArray_H base-7.0.3.1/modules/database/src/ioc/db/dbFastLinkConv.c0000664000577000060420000010632513557101274021577 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbFastLinkConv.c */ /* * Author: Matthew Needes * Date: 12-9-93 */ #include #include #include #include #include #include "alarm.h" #include "cvtFast.h" #include "dbDefs.h" #include "epicsConvert.h" #include "epicsStdlib.h" #include "errlog.h" #include "errMdef.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" #include "dbCommon.h" #include "dbConvertFast.h" #include "dbFldTypes.h" #include "dbStaticLib.h" #include "link.h" #include "recGbl.h" #include "recSup.h" #include "special.h" /* * In the following functions: * * cvt_y_z(ARGS) * * converts from type y to type z. If * type y and z are the same, it merely copies. * * where st - string * c - epicsInt8 * uc - epicsUInt8 * s - epicsInt16 * us - epicsUInt16 * l - epicsInt32 * ul - epicsUInt32 * q - epicsInt64 * uq - epicsUInt64 * f - epicsFloat32 * d - epicsFloat64 * e - enum * * These functions are _single_ value functions, * i.e.: do not deal with array types. */ /* * A DB_LINK that is not initialized with recGblInitFastXXXLink() * will have this conversion. */ /* Convert String to String */ static long cvt_st_st( char *from, char *to, const dbAddr *paddr) { size_t size; if (paddr && paddr->field_size < MAX_STRING_SIZE) { size = paddr->field_size - 1; } else { size = MAX_STRING_SIZE - 1; } strncpy(to, from, size); to[size] = 0; return 0; } /* Convert String to Char */ static long cvt_st_c( char *from, epicsInt8 *to, const dbAddr *paddr) { char *end; if (*from == 0) { *to = 0; return 0; } return epicsParseInt8(from, to, 10, &end); } /* Convert String to Unsigned Char */ static long cvt_st_uc( char *from, epicsUInt8 *to, const dbAddr *paddr) { char *end; if (*from == 0) { *to = 0; return 0; } return epicsParseUInt8(from, to, 10, &end); } /* Convert String to Short */ static long cvt_st_s( char *from, epicsInt16 *to, const dbAddr *paddr) { char *end; if (*from == 0) { *to = 0; return 0; } return epicsParseInt16(from, to, 10, &end); } /* Convert String to Unsigned Short */ static long cvt_st_us( char *from, epicsUInt16 *to, const dbAddr *paddr) { char *end; if (*from == 0) { *to = 0; return 0; } return epicsParseUInt16(from, to, 10, &end); } /* Convert String to Long */ static long cvt_st_l( char *from, epicsInt32 *to, const dbAddr *paddr) { char *end; if (*from == 0) { *to = 0; return 0; } return epicsParseInt32(from, to, 10, &end); } /* Convert String to Unsigned Long */ static long cvt_st_ul( char *from, epicsUInt32 *to, const dbAddr *paddr) { char *end; long status; if (*from == 0) { *to = 0; return 0; } status = epicsParseUInt32(from, to, 10, &end); if (status == S_stdlib_noConversion || (!status && (*end == '.' || *end == 'e' || *end == 'E'))) { /* * Convert via double so numbers like 1.0e3 convert properly. * db_access pretends unsigned long is double. */ double dval; status = epicsParseFloat64(from, &dval, &end); if (!status && dval >=0 && dval <= ULONG_MAX) *to = dval; } return status; } /* Convert String to Int64 */ static long cvt_st_q( char *from, epicsInt64 *to, const dbAddr *paddr) { char *end; if (*from == 0) { *to = 0; return 0; } return epicsParseInt64(from, to, 10, &end); } /* Convert String to UInt64 */ static long cvt_st_uq( char *from, epicsUInt64 *to, const dbAddr *paddr) { char *end; if (*from == 0) { *to = 0; return 0; } return epicsParseUInt64(from, to, 0, &end); } /* Convert String to Float */ static long cvt_st_f( char *from, epicsFloat32 *to, const dbAddr *paddr) { char *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) { char *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) { rset *prset = dbGetRset(paddr); long status = S_db_noRSET; struct dbr_enumStrs enumStrs; if (!prset || !prset->put_enum_str) { recGblRecSupError(status, paddr, "dbPutField", "put_enum_str"); return status; } status = prset->put_enum_str(paddr, from); if (!status) return 0; if (!prset->get_enum_strs) { recGblRecSupError(status, paddr, "dbPutField", "get_enum_strs"); return status; } status = prset->get_enum_strs(paddr, &enumStrs); if (!status) { epicsEnum16 val; status = epicsParseUInt16(from, &val, 10, NULL); if (!status && val < enumStrs.no_str) { *to = val; return 0; } status = S_db_badChoice; } recGblRecordError(status, paddr->precord, from); return status; } /* Convert String to Menu */ static long cvt_st_menu( char *from, epicsEnum16 *to, const dbAddr *paddr) { dbFldDes *pdbFldDes = paddr->pfldDes; dbMenu *pdbMenu; char **pchoices; char *pchoice; if (pdbFldDes && (pdbMenu = pdbFldDes->ftPvt) && (pchoices = pdbMenu->papChoiceValue)) { int i, nChoice = pdbMenu->nChoice; epicsEnum16 val; for (i = 0; i < nChoice; i++) { pchoice = pchoices[i]; if (!pchoice) continue; if (strcmp(pchoice, from) == 0) { *to = i; return 0; } } if (!epicsParseUInt16(from, &val, 10, NULL) && val < nChoice) { *to = val; return 0; } } recGblDbaddrError(S_db_badChoice, paddr, "dbFastLinkConv(cvt_st_menu)"); return(S_db_badChoice); } /* Convert String to Device */ static long cvt_st_device( char *from, epicsEnum16 *to, const dbAddr *paddr) { dbFldDes *pdbFldDes = paddr->pfldDes; dbDeviceMenu *pdbDeviceMenu = pdbFldDes->ftPvt; char **pchoices, *pchoice; if (pdbFldDes && (pdbDeviceMenu = pdbFldDes->ftPvt) && (pchoices = pdbDeviceMenu->papChoice)) { int i, nChoice = pdbDeviceMenu->nChoice; epicsEnum16 val; for (i = 0; i < nChoice; i++) { pchoice = pchoices[i]; if (!pchoice) continue; if (strcmp(pchoice, from) == 0) { *to = i; return 0; } } if (!epicsParseUInt16(from, &val, 10, NULL) && val < nChoice) { *to = val; return 0; } } recGblDbaddrError(S_db_badChoice, paddr, "dbFastLinkConv(cvt_st_device)"); return S_db_badChoice; } /* Convert Char to String */ static long cvt_c_st( epicsInt8 *from, char *to, const dbAddr *paddr) { 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); } /* Convert Char to Unsigned Char */ static long cvt_c_uc( epicsInt8 *from, epicsUInt8 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Char to Short */ static long cvt_c_s( epicsInt8 *from, epicsInt16 *to, const dbAddr *paddr) { *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); } /* Convert Char to Long */ static long cvt_c_l( epicsInt8 *from, epicsInt32 *to, const dbAddr *paddr) { *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); } /* Convert Char to Int64 */ static long cvt_c_q( epicsInt8 *from, epicsInt64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Char to UInt64 */ static long cvt_c_uq( epicsInt8 *from, epicsUInt64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Char to Float */ static long cvt_c_f( epicsInt8 *from, epicsFloat32 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Char to Double */ static long cvt_c_d( epicsInt8 *from, epicsFloat64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Char to Enumerated */ static long cvt_c_e( epicsInt8 *from, epicsEnum16 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Char to Char */ static long cvt_uc_c( epicsUInt8 *from, epicsInt8 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Char to Short */ static long cvt_uc_s( epicsUInt8 *from, epicsInt16 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Char to Long */ static long cvt_uc_l( epicsUInt8 *from, epicsInt32 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Char to Int64 */ static long cvt_uc_q( epicsUInt8 *from, epicsInt64 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Char to Float */ static long cvt_uc_f( epicsUInt8 *from, epicsFloat32 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Char to Enumerated */ static long cvt_uc_e( epicsUInt8 *from, epicsEnum16 *to, const dbAddr *paddr) { *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); } /* Convert Short to Char */ static long cvt_s_c( epicsInt16 *from, epicsInt8 *to, const dbAddr *paddr) { *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); } /* Convert Short to Short */ static long cvt_s_s( epicsInt16 *from, epicsInt16 *to, const dbAddr *paddr) { *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); } /* Convert Short to Long */ static long cvt_s_l( epicsInt16 *from, epicsInt32 *to, const dbAddr *paddr) { *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); } /* Convert Short to Int64 */ static long cvt_s_q( epicsInt16 *from, epicsInt64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Short to UInt64 */ static long cvt_s_uq( epicsInt16 *from, epicsUInt64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Short to Float */ static long cvt_s_f( epicsInt16 *from, epicsFloat32 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Short to Double */ static long cvt_s_d( epicsInt16 *from, epicsFloat64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Short to Enumerated */ static long cvt_s_e( epicsInt16 *from, epicsEnum16 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Short to Char */ static long cvt_us_c( epicsUInt16 *from, epicsInt8 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Short to Short */ static long cvt_us_s( epicsUInt16 *from, epicsInt16 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Short to Long */ static long cvt_us_l( epicsUInt16 *from, epicsInt32 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Short to Int64 */ static long cvt_us_q( epicsUInt16 *from, epicsInt64 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Short to Float */ static long cvt_us_f( epicsUInt16 *from, epicsFloat32 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Short to Enumerated */ static long cvt_us_e( epicsUInt16 *from, epicsUInt16 *to, const dbAddr *paddr) { *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); } /* Convert Long to Char */ static long cvt_l_c( epicsInt32 *from, epicsInt8 *to, const dbAddr *paddr) { *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); } /* Convert Long to Short */ static long cvt_l_s( epicsInt32 *from, epicsInt16 *to, const dbAddr *paddr) { *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); } /* Convert Long to Long */ static long cvt_l_l( epicsInt32 *from, epicsInt32 *to, const dbAddr *paddr) { *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); } /* Convert Long to Int64 */ static long cvt_l_q( epicsInt32 *from, epicsInt64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Long to UInt64 */ static long cvt_l_uq( epicsInt32 *from, epicsUInt64 *to, const dbAddr *paddr) { *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); } /* Convert Long to Double */ static long cvt_l_d( epicsInt32 *from, epicsFloat64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Long to Enumerated */ static long cvt_l_e( epicsInt32 *from, epicsEnum16 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Long to Char */ static long cvt_ul_c( epicsUInt32 *from, epicsInt8 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Long to Short */ static long cvt_ul_s( epicsUInt32 *from, epicsInt16 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Long to Long */ static long cvt_ul_l( epicsUInt32 *from, epicsInt32 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Long to Int64 */ static long cvt_ul_q( epicsUInt32 *from, epicsInt64 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Long to Float */ static long cvt_ul_f( epicsUInt32 *from, epicsFloat32 *to, const dbAddr *paddr) { *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); } /* Convert Unsigned Long to Enumerated */ static long cvt_ul_e( epicsUInt32 *from, epicsEnum16 *to, const dbAddr *paddr) { *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); } /* Convert Int64 to Char */ static long cvt_q_c( epicsInt64 *from, epicsInt8 *to, const dbAddr *paddr) { *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); } /* Convert Int64 to Short */ static long cvt_q_s( epicsInt64 *from, epicsInt16 *to, const dbAddr *paddr) { *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); } /* Convert Int64 to Long */ static long cvt_q_l( epicsInt64 *from, epicsInt32 *to, const dbAddr *paddr) { *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); } /* Convert Int64 to Int64 */ static long cvt_q_q( epicsInt64 *from, epicsInt64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Int64 to UInt64 */ static long cvt_q_uq( epicsInt64 *from, epicsUInt64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Int64 to Float */ static long cvt_q_f( epicsInt64 *from, epicsFloat32 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Int64 to Double */ static long cvt_q_d( epicsInt64 *from, epicsFloat64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Int64 to Enumerated */ static long cvt_q_e( epicsInt64 *from, epicsEnum16 *to, const dbAddr *paddr) { *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); } /* Convert UInt64 to Char */ static long cvt_uq_c( epicsUInt64 *from, epicsInt8 *to, const dbAddr *paddr) { *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); } /* Convert UInt64 to Short */ static long cvt_uq_s( epicsUInt64 *from, epicsInt16 *to, const dbAddr *paddr) { *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); } /* Convert UInt64 to Long */ static long cvt_uq_l( epicsUInt64 *from, epicsInt32 *to, const dbAddr *paddr) { *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); } /* Convert UInt64 to Int64 */ static long cvt_uq_q( epicsUInt64 *from, epicsInt64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert UInt64 to UInt64 */ static long cvt_uq_uq( epicsUInt64 *from, epicsUInt64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert UInt64 to Float */ static long cvt_uq_f( epicsUInt64 *from, epicsFloat32 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert UInt64 to Double */ static long cvt_uq_d( epicsUInt64 *from, epicsFloat64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert UInt64 to Enumerated */ static long cvt_uq_e( epicsUInt64 *from, epicsEnum16 *to, const dbAddr *paddr) { *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); } /* Convert Float to Char */ static long cvt_f_c( epicsFloat32 *from, epicsInt8 *to, const dbAddr *paddr) { *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); } /* Convert Float to Short */ static long cvt_f_s( epicsFloat32 *from, epicsInt16 *to, const dbAddr *paddr) { *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); } /* Convert Float to Long */ static long cvt_f_l( epicsFloat32 *from, epicsInt32 *to, const dbAddr *paddr) { *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); } /* Convert Float to Int64 */ static long cvt_f_q( epicsFloat32 *from, epicsInt64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Float to UInt64 */ static long cvt_f_uq( epicsFloat32 *from, epicsUInt64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Float to Float */ static long cvt_f_f( epicsFloat32 *from, epicsFloat32 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Float to Double */ static long cvt_f_d( epicsFloat32 *from, epicsFloat64 *to, const dbAddr *paddr) { *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); } /* 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); } /* Convert Double to Char */ static long cvt_d_c( epicsFloat64 *from, epicsInt8 *to, const dbAddr *paddr) { *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); } /* Convert Double to Short */ static long cvt_d_s( epicsFloat64 *from, epicsInt16 *to, const dbAddr *paddr) { *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); } /* Convert Double to Long */ static long cvt_d_l( epicsFloat64 *from, epicsInt32 *to, const dbAddr *paddr) { *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); } /* Convert Double to Int64 */ static long cvt_d_q( epicsFloat64 *from, epicsInt64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Double to UInt64 */ static long cvt_d_uq( epicsFloat64 *from, epicsUInt64 *to, const dbAddr *paddr) { *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;} /* Convert Double to Double */ static long cvt_d_d( epicsFloat64 *from, epicsFloat64 *to, const dbAddr *paddr) { *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); } /* Convert Enumerated to Char */ static long cvt_e_c( epicsEnum16 *from, epicsInt8 *to, const dbAddr *paddr) { *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); } /* Convert Enumerated to Short */ static long cvt_e_s( epicsEnum16 *from, epicsInt16 *to, const dbAddr *paddr) { *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); } /* Convert Enumerated to Long */ static long cvt_e_l( epicsEnum16 *from, epicsInt32 *to, const dbAddr *paddr) { *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); } /* Convert Enumerated to Int64 */ static long cvt_e_q( epicsEnum16 *from, epicsInt64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Enumerated to UInt64 */ static long cvt_e_uq( epicsEnum16 *from, epicsUInt64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Enumerated to Float */ static long cvt_e_f( epicsEnum16 *from, epicsFloat32 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Enumerated to Double */ static long cvt_e_d( epicsEnum16 *from, epicsFloat64 *to, const dbAddr *paddr) { *to=*from; return(0); } /* Convert Enumerated to Enumerated */ static long cvt_e_e( epicsEnum16 *from, epicsEnum16 *to, const dbAddr *paddr) { *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; if(paddr) prset = dbGetRset(paddr); if (prset && prset->get_enum_str) return (*prset->get_enum_str)(paddr, to); status = S_db_noRSET; recGblRecSupError(status, paddr, "dbGetField", "get_enum_str"); 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); } /* Get Menu to String */ static long cvt_menu_st( epicsEnum16 *from, char *to, const dbAddr *paddr) { dbFldDes *pdbFldDes; dbMenu *pdbMenu; char **papChoiceValue; char *pchoice; if(! paddr || !(pdbFldDes = paddr->pfldDes) || !(pdbMenu = (dbMenu *)pdbFldDes->ftPvt) || *from>=pdbMenu->nChoice || !(papChoiceValue = pdbMenu->papChoiceValue) || !(pchoice=papChoiceValue[*from])) { recGblDbaddrError(S_db_badChoice,paddr,"dbFastLinkConv(cvt_menu_st)"); return(S_db_badChoice); } strncpy(to,pchoice,MAX_STRING_SIZE); return(0); } /* Get Device to String */ static long cvt_device_st( epicsEnum16 *from, char *to, const dbAddr *paddr) { dbFldDes *pdbFldDes; dbDeviceMenu *pdbDeviceMenu; char **papChoice; char *pchoice; if(!paddr || !(pdbFldDes = paddr->pfldDes) || !(pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt) || *from>=pdbDeviceMenu->nChoice || !(papChoice= pdbDeviceMenu->papChoice) || !(pchoice=papChoice[*from])) { recGblDbaddrError(S_db_badChoice,paddr,"dbFastLinkConv(cvt_device_st)"); return(S_db_badChoice); } strncpy(to,pchoice,MAX_STRING_SIZE); return(0); } /* * Get conversion routine lookup table * * Converts type X to ... * * DBR_STRING, DBR_CHR, DBR_UCHAR, DBR_SHORT, DBR_USHORT, * DBR_LONG, DBR_ULONG, DBR_INT64, DBR_UINT64, DBR_FLOAT, DBR_DOUBLE, DBR_ENUM * * NULL implies the conversion is not supported. */ epicsShareDef long (*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 }, /* Convert DBF_CHAR to ... */ { cvt_c_st, cvt_c_c, cvt_c_uc, cvt_c_s, cvt_c_us, cvt_c_l, cvt_c_ul, cvt_c_q, cvt_c_uq, cvt_c_f, cvt_c_d, cvt_c_e }, /* Convert DBF_UCHAR to ... */ { cvt_uc_st, cvt_uc_c, cvt_uc_uc, cvt_uc_s, cvt_uc_us, cvt_uc_l, cvt_uc_ul, cvt_uc_q, cvt_uc_uq, cvt_uc_f, cvt_uc_d, cvt_uc_e }, /* Convert DBF_SHORT to ... */ { cvt_s_st, cvt_s_c, cvt_s_uc, cvt_s_s, cvt_s_us, cvt_s_l, cvt_s_ul, cvt_s_q, cvt_s_uq, cvt_s_f, cvt_s_d, cvt_s_e }, /* Convert DBF_USHORT to ... */ { cvt_us_st, cvt_us_c, cvt_us_uc, cvt_us_s, cvt_us_us, cvt_us_l, cvt_us_ul, cvt_us_q, cvt_us_uq, cvt_us_f, cvt_us_d, cvt_us_e }, /* Convert DBF_LONG to ... */ { cvt_l_st, cvt_l_c, cvt_l_uc, cvt_l_s, cvt_l_us, cvt_l_l, cvt_l_ul, cvt_l_q, cvt_l_uq, cvt_l_f, cvt_l_d, cvt_l_e }, /* Convert DBF_ULONG to ... */ { cvt_ul_st, cvt_ul_c, cvt_ul_uc, cvt_ul_s, cvt_ul_us, cvt_ul_l, cvt_ul_ul, cvt_ul_q, cvt_ul_uq, cvt_ul_f, cvt_ul_d, cvt_ul_e }, /* Convert DBF_INT64 to ... */ { cvt_q_st, cvt_q_c, cvt_q_uc, cvt_q_s, cvt_q_us, cvt_q_l, cvt_q_ul, cvt_q_q, cvt_q_uq, cvt_q_f, cvt_q_d, cvt_q_e }, /* Convert DBF_UINT64 to ... */ { cvt_uq_st, cvt_uq_c, cvt_uq_uc, cvt_uq_s, cvt_uq_us, cvt_uq_l, cvt_uq_ul, cvt_uq_q, cvt_uq_uq, cvt_uq_f, cvt_uq_d, cvt_uq_e }, /* Convert DBF_FLOAT to ... */ { cvt_f_st, cvt_f_c, cvt_f_uc, cvt_f_s, cvt_f_us, cvt_f_l, cvt_f_ul, cvt_f_q, cvt_f_uq, cvt_f_f, cvt_f_d, cvt_f_e }, /* Convert DBF_DOUBLE to ... */ { cvt_d_st, cvt_d_c, cvt_d_uc, cvt_d_s, cvt_d_us, cvt_d_l, cvt_d_ul, cvt_d_q, cvt_d_uq, cvt_d_f, cvt_d_d, cvt_d_e }, /* Convert DBF_ENUM to ... */ { cvt_e_st_get, cvt_e_c, cvt_e_uc, cvt_e_s, cvt_e_us, cvt_e_l, cvt_e_ul, cvt_e_q, cvt_e_uq, cvt_e_f, cvt_e_d, cvt_e_e }, /* Convert DBF_MENU to ... */ { cvt_menu_st, cvt_e_c, cvt_e_uc, cvt_e_s, cvt_e_us, cvt_e_l, cvt_e_ul, cvt_e_q, cvt_e_uq, cvt_e_f, cvt_e_d, cvt_e_e }, /* Convert DBF_DEVICE to ... */ { cvt_device_st, cvt_e_c, cvt_e_uc, cvt_e_s, cvt_e_us, cvt_e_l, cvt_e_ul, cvt_e_q, cvt_e_uq, cvt_e_f, cvt_e_d, cvt_e_e } }; /* * Put conversion routine lookup table * * Converts type X to ... * * DBF_STRING DBF_CHAR DBF_UCHAR DBF_SHORT DBF_USHORT * DBF_LONG DBF_ULONG DBF_INT64 DBF_UINT64 DBF_FLOAT DBF_DOUBLE DBF_ENUM * DBF_MENU DBF_DEVICE * * NULL implies the conversion is not supported. */ epicsShareDef long (*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}, /* Convert DBR_CHAR to ... */ { cvt_c_st, cvt_c_c, cvt_c_uc, cvt_c_s, cvt_c_us, cvt_c_l, cvt_c_ul, cvt_c_q, cvt_c_uq, cvt_c_f, cvt_c_d, cvt_c_e, cvt_c_e, cvt_c_e}, /* Convert DBR_UCHAR to ... */ { cvt_uc_st, cvt_uc_c, cvt_uc_uc, cvt_uc_s, cvt_uc_us, cvt_uc_l, cvt_uc_ul, cvt_uc_q, cvt_uc_uq, cvt_uc_f, cvt_uc_d, cvt_uc_e, cvt_uc_e, cvt_uc_e}, /* Convert DBR_SHORT to ... */ { cvt_s_st, cvt_s_c, cvt_s_uc, cvt_s_s, cvt_s_us, cvt_s_l, cvt_s_ul, cvt_s_q, cvt_s_uq, cvt_s_f, cvt_s_d, cvt_s_e, cvt_s_e, cvt_s_e}, /* Convert DBR_USHORT to ... */ { cvt_us_st, cvt_us_c, cvt_us_uc, cvt_us_s, cvt_us_us, cvt_us_l, cvt_us_ul, cvt_us_q, cvt_us_uq, cvt_us_f, cvt_us_d, cvt_us_e, cvt_us_e, cvt_us_e}, /* Convert DBR_LONG to ... */ { cvt_l_st, cvt_l_c, cvt_l_uc, cvt_l_s, cvt_l_us, cvt_l_l, cvt_l_ul, cvt_l_q, cvt_l_uq, cvt_l_f, cvt_l_d, cvt_l_e, cvt_l_e, cvt_l_e}, /* Convert DBR_ULONG to ... */ { cvt_ul_st, cvt_ul_c, cvt_ul_uc, cvt_ul_s, cvt_ul_us, cvt_ul_l, cvt_ul_ul, cvt_ul_q, cvt_ul_uq, cvt_ul_f, cvt_ul_d, cvt_ul_e, cvt_ul_e, cvt_ul_e}, /* Convert DBR_INT64 to ... */ { cvt_q_st, cvt_q_c, cvt_q_uc, cvt_q_s, cvt_q_us, cvt_q_l, cvt_q_ul, cvt_q_q, cvt_q_uq, cvt_q_f, cvt_q_d, cvt_q_e, cvt_q_e, cvt_q_e}, /* Convert DBR_UINT64 to ... */ { cvt_uq_st, cvt_uq_c, cvt_uq_uc, cvt_uq_s, cvt_uq_us, cvt_uq_l, cvt_uq_ul, cvt_uq_q, cvt_uq_uq, cvt_uq_f, cvt_uq_d, cvt_uq_e, cvt_uq_e, cvt_uq_e}, /* Convert DBR_FLOAT to ... */ { cvt_f_st, cvt_f_c, cvt_f_uc, cvt_f_s, cvt_f_us, cvt_f_l, cvt_f_ul, cvt_f_q, cvt_f_uq, cvt_f_f, cvt_f_d, cvt_f_e, cvt_f_e, cvt_f_e}, /* Convert DBR_DOUBLE to ... */ { cvt_d_st, cvt_d_c, cvt_d_uc, cvt_d_s, cvt_d_us, cvt_d_l, cvt_d_ul, cvt_d_q, cvt_d_uq, cvt_d_f, cvt_d_d, cvt_d_e, cvt_d_e, cvt_d_e}, /* Convert DBR_ENUM to ... */ { cvt_e_st_put, cvt_e_c, cvt_e_uc, cvt_e_s, cvt_e_us, cvt_e_l, cvt_e_ul, cvt_e_q, cvt_e_uq, cvt_e_f, cvt_e_d, cvt_e_e, cvt_e_e, cvt_e_e} }; base-7.0.3.1/modules/database/src/ioc/db/dbIocRegister.c0000664000577000060420000004651713557101274021463 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "iocsh.h" #define epicsExportSharedSymbols #include "callback.h" #include "dbAccess.h" #include "dbBkpt.h" #include "dbCaTest.h" #include "dbEvent.h" #include "dbIocRegister.h" #include "dbJLink.h" #include "dbLock.h" #include "dbNotify.h" #include "dbScan.h" #include "dbServer.h" #include "dbState.h" #include "db_test.h" #include "dbTest.h" epicsShareExtern int callbackParallelThreadsDefault; /* dbLoadDatabase */ static const iocshArg dbLoadDatabaseArg0 = { "file name",iocshArgString}; static const iocshArg dbLoadDatabaseArg1 = { "path",iocshArgString}; static const iocshArg dbLoadDatabaseArg2 = { "substitutions",iocshArgString}; static const iocshArg * const dbLoadDatabaseArgs[3] = { &dbLoadDatabaseArg0,&dbLoadDatabaseArg1,&dbLoadDatabaseArg2 }; static const iocshFuncDef dbLoadDatabaseFuncDef = {"dbLoadDatabase",3,dbLoadDatabaseArgs}; static void dbLoadDatabaseCallFunc(const iocshArgBuf *args) { iocshSetError(dbLoadDatabase(args[0].sval,args[1].sval,args[2].sval)); } /* dbLoadRecords */ static const iocshArg dbLoadRecordsArg0 = { "file name",iocshArgString}; static const iocshArg dbLoadRecordsArg1 = { "substitutions",iocshArgString}; static const iocshArg * const dbLoadRecordsArgs[2] = {&dbLoadRecordsArg0,&dbLoadRecordsArg1}; static const iocshFuncDef dbLoadRecordsFuncDef = {"dbLoadRecords",2,dbLoadRecordsArgs}; static void dbLoadRecordsCallFunc(const iocshArgBuf *args) { iocshSetError(dbLoadRecords(args[0].sval,args[1].sval)); } /* dbb */ static const iocshArg dbbArg0 = { "record name",iocshArgString}; static const iocshArg * const dbbArgs[1] = {&dbbArg0}; static const iocshFuncDef dbbFuncDef = {"dbb",1,dbbArgs}; static void dbbCallFunc(const iocshArgBuf *args) { dbb(args[0].sval);} /* dbd */ static const iocshArg dbdArg0 = { "record name",iocshArgString}; static const iocshArg * const dbdArgs[1] = {&dbdArg0}; static const iocshFuncDef dbdFuncDef = {"dbd",1,dbdArgs}; static void dbdCallFunc(const iocshArgBuf *args) { dbd(args[0].sval);} /* dbc */ static const iocshArg dbcArg0 = { "record name",iocshArgString}; static const iocshArg * const dbcArgs[1] = {&dbcArg0}; static const iocshFuncDef dbcFuncDef = {"dbc",1,dbcArgs}; static void dbcCallFunc(const iocshArgBuf *args) { dbc(args[0].sval);} /* dbs */ static const iocshArg dbsArg0 = { "record name",iocshArgString}; static const iocshArg * const dbsArgs[1] = {&dbsArg0}; static const iocshFuncDef dbsFuncDef = {"dbs",1,dbsArgs}; static void dbsCallFunc(const iocshArgBuf *args) { dbs(args[0].sval);} /* dbstat */ static const iocshFuncDef dbstatFuncDef = {"dbstat",0}; static void dbstatCallFunc(const iocshArgBuf *args) { dbstat();} /* dbp */ static const iocshArg dbpArg0 = { "record name",iocshArgString}; static const iocshArg dbpArg1 = { "interest level",iocshArgInt}; static const iocshArg * const dbpArgs[2] = {&dbpArg0,&dbpArg1}; static const iocshFuncDef dbpFuncDef = {"dbp",2,dbpArgs}; static void dbpCallFunc(const iocshArgBuf *args) { dbp(args[0].sval,args[1].ival);} /* dbap */ static const iocshArg dbapArg0 = { "record name",iocshArgString}; static const iocshArg * const dbapArgs[1] = {&dbapArg0}; static const iocshFuncDef dbapFuncDef = {"dbap",1,dbapArgs}; static void dbapCallFunc(const iocshArgBuf *args) { dbap(args[0].sval);} /* dbsr */ static const iocshArg dbsrArg0 = { "interest level",iocshArgInt}; static const iocshArg * const dbsrArgs[1] = {&dbsrArg0}; static const iocshFuncDef dbsrFuncDef = {"dbsr",1,dbsrArgs}; static void dbsrCallFunc(const iocshArgBuf *args) { dbsr(args[0].ival);} /* dbcar */ static const iocshArg dbcarArg0 = { "record name",iocshArgString}; static const iocshArg dbcarArg1 = { "level",iocshArgInt}; static const iocshArg * const dbcarArgs[2] = {&dbcarArg0,&dbcarArg1}; static const iocshFuncDef dbcarFuncDef = {"dbcar",2,dbcarArgs}; static void dbcarCallFunc(const iocshArgBuf *args) { dbcar(args[0].sval,args[1].ival); } /* dbjlr */ static const iocshArg dbjlrArg0 = { "record name",iocshArgString}; static const iocshArg dbjlrArg1 = { "level",iocshArgInt}; static const iocshArg * const dbjlrArgs[2] = {&dbjlrArg0,&dbjlrArg1}; static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs}; static void dbjlrCallFunc(const iocshArgBuf *args) { dbjlr(args[0].sval,args[1].ival); } /* dbel */ static const iocshArg dbelArg0 = { "record name",iocshArgString}; static const iocshArg dbelArg1 = { "level",iocshArgInt}; static const iocshArg * const dbelArgs[2] = {&dbelArg0,&dbelArg1}; static const iocshFuncDef dbelFuncDef = {"dbel",2,dbelArgs}; static void dbelCallFunc(const iocshArgBuf *args) { dbel(args[0].sval, args[1].ival); } /* dba */ static const iocshArg dbaArg0 = { "record name",iocshArgString}; static const iocshArg * const dbaArgs[1] = {&dbaArg0}; static const iocshFuncDef dbaFuncDef = {"dba",1,dbaArgs}; static void dbaCallFunc(const iocshArgBuf *args) { dba(args[0].sval);} /* dbl */ static const iocshArg dblArg0 = { "record type",iocshArgString}; static const iocshArg dblArg1 = { "fields",iocshArgString}; static const iocshArg * const dblArgs[] = {&dblArg0,&dblArg1}; static const iocshFuncDef dblFuncDef = {"dbl",2,dblArgs}; static void dblCallFunc(const iocshArgBuf *args) { dbl(args[0].sval,args[1].sval); } /* dbnr */ static const iocshArg dbnrArg0 = { "verbose",iocshArgInt}; static const iocshArg * const dbnrArgs[1] = {&dbnrArg0}; static const iocshFuncDef dbnrFuncDef = {"dbnr",1,dbnrArgs}; static void dbnrCallFunc(const iocshArgBuf *args) { dbnr(args[0].ival);} /* dbli */ static const iocshArg dbliArg0 = { "pattern",iocshArgString}; static const iocshArg * const dbliArgs[1] = {&dbliArg0}; static const iocshFuncDef dbliFuncDef = {"dbli",1,dbliArgs}; static void dbliCallFunc(const iocshArgBuf *args) { dbli(args[0].sval);} /* dbla */ static const iocshArg dblaArg0 = { "pattern",iocshArgString}; static const iocshArg * const dblaArgs[1] = {&dblaArg0}; static const iocshFuncDef dblaFuncDef = {"dbla",1,dblaArgs}; static void dblaCallFunc(const iocshArgBuf *args) { dbla(args[0].sval);} /* dbgrep */ static const iocshArg dbgrepArg0 = { "pattern",iocshArgString}; static const iocshArg * const dbgrepArgs[1] = {&dbgrepArg0}; static const iocshFuncDef dbgrepFuncDef = {"dbgrep",1,dbgrepArgs}; static void dbgrepCallFunc(const iocshArgBuf *args) { dbgrep(args[0].sval);} /* dbgf */ static const iocshArg dbgfArg0 = { "record name",iocshArgString}; static const iocshArg * const dbgfArgs[1] = {&dbgfArg0}; static const iocshFuncDef dbgfFuncDef = {"dbgf",1,dbgfArgs}; static void dbgfCallFunc(const iocshArgBuf *args) { dbgf(args[0].sval);} /* dbpf */ static const iocshArg dbpfArg0 = { "record name",iocshArgString}; static const iocshArg dbpfArg1 = { "value",iocshArgString}; static const iocshArg * const dbpfArgs[2] = {&dbpfArg0,&dbpfArg1}; static const iocshFuncDef dbpfFuncDef = {"dbpf",2,dbpfArgs}; static void dbpfCallFunc(const iocshArgBuf *args) { dbpf(args[0].sval,args[1].sval);} /* dbpr */ static const iocshArg dbprArg0 = { "record name",iocshArgString}; static const iocshArg dbprArg1 = { "interest level",iocshArgInt}; static const iocshArg * const dbprArgs[2] = {&dbprArg0,&dbprArg1}; static const iocshFuncDef dbprFuncDef = {"dbpr",2,dbprArgs}; static void dbprCallFunc(const iocshArgBuf *args) { dbpr(args[0].sval,args[1].ival);} /* dbtr */ static const iocshArg dbtrArg0 = { "record name",iocshArgString}; static const iocshArg * const dbtrArgs[1] = {&dbtrArg0}; static const iocshFuncDef dbtrFuncDef = {"dbtr",1,dbtrArgs}; static void dbtrCallFunc(const iocshArgBuf *args) { dbtr(args[0].sval);} /* dbtgf */ static const iocshArg dbtgfArg0 = { "record name",iocshArgString}; static const iocshArg * const dbtgfArgs[1] = {&dbtgfArg0}; static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs}; static void dbtgfCallFunc(const iocshArgBuf *args) { dbtgf(args[0].sval);} /* dbtpf */ static const iocshArg dbtpfArg0 = { "record name",iocshArgString}; static const iocshArg dbtpfArg1 = { "value",iocshArgString}; static const iocshArg * const dbtpfArgs[2] = {&dbtpfArg0,&dbtpfArg1}; static const iocshFuncDef dbtpfFuncDef = {"dbtpf",2,dbtpfArgs}; static void dbtpfCallFunc(const iocshArgBuf *args) { dbtpf(args[0].sval,args[1].sval);} /* dbior */ static const iocshArg dbiorArg0 = { "driver name",iocshArgString}; static const iocshArg dbiorArg1 = { "interest level",iocshArgInt}; static const iocshArg * const dbiorArgs[] = {&dbiorArg0,&dbiorArg1}; static const iocshFuncDef dbiorFuncDef = {"dbior",2,dbiorArgs}; static void dbiorCallFunc(const iocshArgBuf *args) { dbior(args[0].sval,args[1].ival);} /* dbhcr */ static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0}; static void dbhcrCallFunc(const iocshArgBuf *args) { dbhcr();} /* gft */ static const iocshArg gftArg0 = { "record name",iocshArgString}; static const iocshArg * const gftArgs[1] = {&gftArg0}; static const iocshFuncDef gftFuncDef = {"gft",1,gftArgs}; static void gftCallFunc(const iocshArgBuf *args) { gft(args[0].sval);} /* pft */ static const iocshArg pftArg0 = { "record name",iocshArgString}; static const iocshArg pftArg1 = { "value",iocshArgString}; static const iocshArg * const pftArgs[2] = {&pftArg0,&pftArg1}; static const iocshFuncDef pftFuncDef = {"pft",2,pftArgs}; static void pftCallFunc(const iocshArgBuf *args) { pft(args[0].sval,args[1].sval);} /* dbtpn */ static const iocshArg dbtpnArg0 = { "record name",iocshArgString}; static const iocshArg dbtpnArg1 = { "value",iocshArgString}; static const iocshArg * const dbtpnArgs[2] = {&dbtpnArg0,&dbtpnArg1}; static const iocshFuncDef dbtpnFuncDef = {"dbtpn",2,dbtpnArgs}; static void dbtpnCallFunc(const iocshArgBuf *args) { dbtpn(args[0].sval,args[1].sval);} /* dbNotifyDump */ static const iocshFuncDef dbNotifyDumpFuncDef = {"dbNotifyDump",0,0}; static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { dbNotifyDump();} /* dbPutAttribute */ static const iocshArg dbPutAttrArg0 = { "record type",iocshArgString}; static const iocshArg dbPutAttrArg1 = { "attribute name",iocshArgString}; static const iocshArg dbPutAttrArg2 = { "value",iocshArgString}; static const iocshArg * const dbPutAttrArgs[] = {&dbPutAttrArg0, &dbPutAttrArg1, &dbPutAttrArg2}; static const iocshFuncDef dbPutAttrFuncDef = {"dbPutAttribute",3,dbPutAttrArgs}; static void dbPutAttrCallFunc(const iocshArgBuf *args) { dbPutAttribute(args[0].sval,args[1].sval,args[2].sval);} /* tpn */ static const iocshArg tpnArg0 = { "record name",iocshArgString}; static const iocshArg tpnArg1 = { "value",iocshArgString}; static const iocshArg * const tpnArgs[2] = {&tpnArg0,&tpnArg1}; static const iocshFuncDef tpnFuncDef = {"tpn",2,tpnArgs}; static void tpnCallFunc(const iocshArgBuf *args) { tpn(args[0].sval,args[1].sval);} /* dblsr */ static const iocshArg dblsrArg0 = { "record name",iocshArgString}; static const iocshArg dblsrArg1 = { "interest level",iocshArgInt}; static const iocshArg * const dblsrArgs[2] = {&dblsrArg0,&dblsrArg1}; static const iocshFuncDef dblsrFuncDef = {"dblsr",2,dblsrArgs}; static void dblsrCallFunc(const iocshArgBuf *args) { dblsr(args[0].sval,args[1].ival);} /* dbLockShowLocked */ static const iocshArg dbLockShowLockedArg0 = { "interest level",iocshArgInt}; static const iocshArg * const dbLockShowLockedArgs[1] = {&dbLockShowLockedArg0}; static const iocshFuncDef dbLockShowLockedFuncDef = {"dbLockShowLocked",1,dbLockShowLockedArgs}; static void dbLockShowLockedCallFunc(const iocshArgBuf *args) { dbLockShowLocked(args[0].ival);} /* scanOnceSetQueueSize */ static const iocshArg scanOnceSetQueueSizeArg0 = { "size",iocshArgInt}; static const iocshArg * const scanOnceSetQueueSizeArgs[1] = {&scanOnceSetQueueSizeArg0}; static const iocshFuncDef scanOnceSetQueueSizeFuncDef = {"scanOnceSetQueueSize",1,scanOnceSetQueueSizeArgs}; static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args) { scanOnceSetQueueSize(args[0].ival); } /* scanOnceQueueShow */ static const iocshArg scanOnceQueueShowArg0 = { "reset",iocshArgInt}; static const iocshArg * const scanOnceQueueShowArgs[1] = {&scanOnceQueueShowArg0}; static const iocshFuncDef scanOnceQueueShowFuncDef = {"scanOnceQueueShow",1,scanOnceQueueShowArgs}; static void scanOnceQueueShowCallFunc(const iocshArgBuf *args) { scanOnceQueueShow(args[0].ival); } /* scanppl */ static const iocshArg scanpplArg0 = { "rate",iocshArgDouble}; static const iocshArg * const scanpplArgs[1] = {&scanpplArg0}; static const iocshFuncDef scanpplFuncDef = {"scanppl",1,scanpplArgs}; static void scanpplCallFunc(const iocshArgBuf *args) { scanppl(args[0].dval);} /* scanpel */ static const iocshArg scanpelArg0 = { "event name",iocshArgString}; static const iocshArg * const scanpelArgs[1] = {&scanpelArg0}; static const iocshFuncDef scanpelFuncDef = {"scanpel",1,scanpelArgs}; static void scanpelCallFunc(const iocshArgBuf *args) { scanpel(args[0].sval);} /* postEvent */ static const iocshArg postEventArg0 = { "event name",iocshArgString}; static const iocshArg * const postEventArgs[1] = {&postEventArg0}; static const iocshFuncDef postEventFuncDef = {"postEvent",1,postEventArgs}; static void postEventCallFunc(const iocshArgBuf *args) { EVENTPVT pel = eventNameToHandle(args[0].sval); postEvent(pel); } /* scanpiol */ static const iocshFuncDef scanpiolFuncDef = {"scanpiol",0}; static void scanpiolCallFunc(const iocshArgBuf *args) { scanpiol();} /* callbackSetQueueSize */ static const iocshArg callbackSetQueueSizeArg0 = { "bufsize",iocshArgInt}; static const iocshArg * const callbackSetQueueSizeArgs[1] = {&callbackSetQueueSizeArg0}; static const iocshFuncDef callbackSetQueueSizeFuncDef = {"callbackSetQueueSize",1,callbackSetQueueSizeArgs}; static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args) { callbackSetQueueSize(args[0].ival); } /* callbackQueueShow */ static const iocshArg callbackQueueShowArg0 = { "reset", iocshArgInt}; static const iocshArg * const callbackQueueShowArgs[1] = {&callbackQueueShowArg0}; static const iocshFuncDef callbackQueueShowFuncDef = {"callbackQueueShow",1,callbackQueueShowArgs}; static void callbackQueueShowCallFunc(const iocshArgBuf *args) { callbackQueueShow(args[0].ival); } /* callbackParallelThreads */ static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgInt}; static const iocshArg callbackParallelThreadsArg1 = { "priority", iocshArgString}; static const iocshArg * const callbackParallelThreadsArgs[2] = {&callbackParallelThreadsArg0,&callbackParallelThreadsArg1}; static const iocshFuncDef callbackParallelThreadsFuncDef = {"callbackParallelThreads",2,callbackParallelThreadsArgs}; static void callbackParallelThreadsCallFunc(const iocshArgBuf *args) { callbackParallelThreads(args[0].ival, args[1].sval); } /* dbStateCreate */ static const iocshArg dbStateArgName = { "name", iocshArgString }; static const iocshArg * const dbStateCreateArgs[] = { &dbStateArgName }; static const iocshFuncDef dbStateCreateFuncDef = { "dbStateCreate", 1, dbStateCreateArgs }; static void dbStateCreateCallFunc (const iocshArgBuf *args) { dbStateCreate(args[0].sval); } /* dbStateSet */ static const iocshArg * const dbStateSetArgs[] = { &dbStateArgName }; static const iocshFuncDef dbStateSetFuncDef = { "dbStateSet", 1, dbStateSetArgs }; static void dbStateSetCallFunc (const iocshArgBuf *args) { dbStateId sid = dbStateFind(args[0].sval); if (sid) dbStateSet(sid); } /* dbStateClear */ static const iocshArg * const dbStateClearArgs[] = { &dbStateArgName }; static const iocshFuncDef dbStateClearFuncDef = { "dbStateClear", 1, dbStateClearArgs }; static void dbStateClearCallFunc (const iocshArgBuf *args) { dbStateId sid = dbStateFind(args[0].sval); if (sid) dbStateClear(sid); } /* dbStateShow */ static const iocshArg dbStateShowArg1 = { "level", iocshArgInt }; static const iocshArg * const dbStateShowArgs[] = { &dbStateArgName, &dbStateShowArg1 }; static const iocshFuncDef dbStateShowFuncDef = { "dbStateShow", 2, dbStateShowArgs }; static void dbStateShowCallFunc (const iocshArgBuf *args) { dbStateId sid = dbStateFind(args[0].sval); if (sid) dbStateShow(sid, args[1].ival); } /* dbStateShowAll */ static const iocshArg dbStateShowAllArg0 = { "level", iocshArgInt }; static const iocshArg * const dbStateShowAllArgs[] = { &dbStateShowAllArg0 }; static const iocshFuncDef dbStateShowAllFuncDef = { "dbStateShowAll", 1, dbStateShowAllArgs }; static void dbStateShowAllCallFunc (const iocshArgBuf *args) { dbStateShowAll(args[0].ival); } void dbIocRegister(void) { iocshRegister(&dbbFuncDef,dbbCallFunc); iocshRegister(&dbdFuncDef,dbdCallFunc); iocshRegister(&dbcFuncDef,dbcCallFunc); iocshRegister(&dbsFuncDef,dbsCallFunc); iocshRegister(&dbstatFuncDef,dbstatCallFunc); iocshRegister(&dbpFuncDef,dbpCallFunc); iocshRegister(&dbapFuncDef,dbapCallFunc); iocshRegister(&dbsrFuncDef,dbsrCallFunc); iocshRegister(&dbcarFuncDef,dbcarCallFunc); iocshRegister(&dbelFuncDef,dbelCallFunc); iocshRegister(&dbjlrFuncDef,dbjlrCallFunc); iocshRegister(&dbLoadDatabaseFuncDef,dbLoadDatabaseCallFunc); iocshRegister(&dbLoadRecordsFuncDef,dbLoadRecordsCallFunc); iocshRegister(&dbaFuncDef,dbaCallFunc); iocshRegister(&dblFuncDef,dblCallFunc); iocshRegister(&dbnrFuncDef,dbnrCallFunc); iocshRegister(&dblaFuncDef,dblaCallFunc); iocshRegister(&dbliFuncDef,dbliCallFunc); iocshRegister(&dbgrepFuncDef,dbgrepCallFunc); iocshRegister(&dbgfFuncDef,dbgfCallFunc); iocshRegister(&dbpfFuncDef,dbpfCallFunc); iocshRegister(&dbprFuncDef,dbprCallFunc); iocshRegister(&dbtrFuncDef,dbtrCallFunc); iocshRegister(&dbtgfFuncDef,dbtgfCallFunc); iocshRegister(&dbtpfFuncDef,dbtpfCallFunc); iocshRegister(&dbiorFuncDef,dbiorCallFunc); iocshRegister(&dbhcrFuncDef,dbhcrCallFunc); iocshRegister(&gftFuncDef,gftCallFunc); iocshRegister(&pftFuncDef,pftCallFunc); iocshRegister(&dbtpnFuncDef,dbtpnCallFunc); iocshRegister(&dbNotifyDumpFuncDef,dbNotifyDumpCallFunc); iocshRegister(&dbPutAttrFuncDef,dbPutAttrCallFunc); iocshRegister(&tpnFuncDef,tpnCallFunc); iocshRegister(&dblsrFuncDef,dblsrCallFunc); iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc); iocshRegister(&scanOnceSetQueueSizeFuncDef,scanOnceSetQueueSizeCallFunc); iocshRegister(&scanOnceQueueShowFuncDef,scanOnceQueueShowCallFunc); iocshRegister(&scanpplFuncDef,scanpplCallFunc); iocshRegister(&scanpelFuncDef,scanpelCallFunc); iocshRegister(&postEventFuncDef,postEventCallFunc); iocshRegister(&scanpiolFuncDef,scanpiolCallFunc); iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc); iocshRegister(&callbackQueueShowFuncDef,callbackQueueShowCallFunc); iocshRegister(&callbackParallelThreadsFuncDef,callbackParallelThreadsCallFunc); /* Needed before callback system is initialized */ callbackParallelThreadsDefault = epicsThreadGetCPUs(); iocshRegister(&dbStateCreateFuncDef, dbStateCreateCallFunc); iocshRegister(&dbStateSetFuncDef, dbStateSetCallFunc); iocshRegister(&dbStateClearFuncDef, dbStateClearCallFunc); iocshRegister(&dbStateShowFuncDef, dbStateShowCallFunc); iocshRegister(&dbStateShowAllFuncDef, dbStateShowAllCallFunc); } base-7.0.3.1/modules/database/src/ioc/db/dbIocRegister.h0000664000577000060420000000132313557101274021452 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_dbIocRegister_H #define INC_dbIocRegister_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void dbIocRegister(void); #ifdef __cplusplus } #endif #endif /* INC_dbIocRegister_H */ base-7.0.3.1/modules/database/src/ioc/db/dbJLink.c0000664000577000060420000004044413557101274020244 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbJLink.c */ #include #include #include "epicsAssert.h" #include "dbmf.h" #include "errlog.h" #include "yajl_alloc.h" #include "yajl_parse.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "dbCommon.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "dbLink.h" #include "dbJLink.h" #include "dbLock.h" #include "dbStaticLib.h" #include "link.h" #include "epicsExport.h" epicsShareDef int dbJLinkDebug = 0; epicsExportAddress(int, dbJLinkDebug); #define IFDEBUG(n) if (dbJLinkDebug >= (n)) typedef struct parseContext { jlink *pjlink; jlink *product; short dbfType; short jsonDepth; } parseContext; epicsShareDef const char *jlif_result_name[2] = { "jlif_stop", "jlif_continue", }; epicsShareDef const char *jlif_key_result_name[5] = { "jlif_key_stop", "jlif_key_continue", "jlif_key_child_inlink", "jlif_key_child_outlink", "jlif_key_child_fwdlink" }; #define CALL_OR_STOP(routine) !(routine) ? jlif_stop : (routine) static int dbjl_return(parseContext *parser, jlif_result result) { jlink *pjlink = parser->pjlink; IFDEBUG(10) { printf("dbjl_return(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result); printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } if (result == jlif_stop && pjlink) { jlink *parent; while ((parent = pjlink->parent)) { pjlink->pif->free_jlink(pjlink); pjlink = parent; } pjlink->pif->free_jlink(pjlink); } IFDEBUG(10) printf(" returning %d %s\n", result, result == jlif_stop ? "*** STOP ***" : "Continue"); return result; } static int dbjl_value(parseContext *parser, jlif_result result) { jlink *pjlink = parser->pjlink; jlink *parent; IFDEBUG(10) { printf("dbjl_value(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result); printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } if (result == jlif_stop || pjlink->parseDepth > 0) return dbjl_return(parser, result); parent = pjlink->parent; if (!parent) { parser->product = pjlink; } else if (parent->pif->end_child) { parent->pif->end_child(parent, pjlink); } parser->pjlink = parent; IFDEBUG(8) printf("dbjl_value: product = %p\n", pjlink); return jlif_continue; } static int dbjl_null(void *ctx) { parseContext *parser = (parseContext *) ctx; jlink *pjlink = parser->pjlink; IFDEBUG(10) printf("dbjl_null(%s@%p)\n", pjlink ? pjlink->pif->name : "", pjlink); assert(pjlink); return dbjl_value(parser, CALL_OR_STOP(pjlink->pif->parse_null)(pjlink)); } static int dbjl_boolean(void *ctx, int val) { parseContext *parser = (parseContext *) ctx; jlink *pjlink = parser->pjlink; assert(pjlink); return dbjl_value(parser, CALL_OR_STOP(pjlink->pif->parse_boolean)(pjlink, val)); } static int dbjl_integer(void *ctx, long long num) { parseContext *parser = (parseContext *) ctx; jlink *pjlink = parser->pjlink; IFDEBUG(10) printf("dbjl_integer(%s@%p, %lld)\n", pjlink->pif->name, pjlink, num); assert(pjlink); return dbjl_value(parser, CALL_OR_STOP(pjlink->pif->parse_integer)(pjlink, num)); } static int dbjl_double(void *ctx, double num) { parseContext *parser = (parseContext *) ctx; jlink *pjlink = parser->pjlink; IFDEBUG(10) printf("dbjl_double(%s@%p, %g)\n", pjlink->pif->name, pjlink, num); assert(pjlink); return dbjl_value(parser, CALL_OR_STOP(pjlink->pif->parse_double)(pjlink, num)); } static int dbjl_string(void *ctx, const unsigned char *val, size_t len) { parseContext *parser = (parseContext *) ctx; jlink *pjlink = parser->pjlink; IFDEBUG(10) printf("dbjl_string(%s@%p, \"%.*s\")\n", pjlink->pif->name, pjlink, (int) len, val); assert(pjlink); return dbjl_value(parser, CALL_OR_STOP(pjlink->pif->parse_string)(pjlink, (const char *) val, len)); } static int dbjl_start_map(void *ctx) { parseContext *parser = (parseContext *) ctx; jlink *pjlink = parser->pjlink; int result; if (!pjlink) { IFDEBUG(10) { printf("dbjl_start_map(NULL)\t"); printf(" jsonDepth=%d, parseDepth=00, dbfType=%d\n", parser->jsonDepth, parser->dbfType); } assert(parser->jsonDepth == 0); parser->jsonDepth++; return jlif_continue; /* Opening '{' */ } IFDEBUG(10) { printf("dbjl_start_map(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink); printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } pjlink->parseDepth++; parser->jsonDepth++; result = CALL_OR_STOP(pjlink->pif->parse_start_map)(pjlink); switch (result) { case jlif_key_child_inlink: parser->dbfType = DBF_INLINK; result = jlif_continue; break; case jlif_key_child_outlink: parser->dbfType = DBF_OUTLINK; result = jlif_continue; break; case jlif_key_child_fwdlink: parser->dbfType = DBF_FWDLINK; result = jlif_continue; break; case jlif_key_stop: case jlif_key_continue: break; default: errlogPrintf("dbJLinkInit: Bad return %d from '%s'::parse_start_map()\n", result, pjlink->pif->name); result = jlif_stop; break; } IFDEBUG(10) printf("dbjl_start_map -> %d\n", result); return dbjl_return(parser, result); } static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) { parseContext *parser = (parseContext *) ctx; jlink *pjlink = parser->pjlink; char *link_name; linkSup *linkSup; jlif *pjlif; jlink *child; if (parser->dbfType == 0) { if (!pjlink) { errlogPrintf("dbJLinkInit: Illegal second link key '%.*s'\n", (int) len, key); return dbjl_return(parser, jlif_stop); } IFDEBUG(10) { printf("dbjl_map_key(%s@%p, \"%.*s\")\t", pjlink->pif->name, pjlink, (int) len, key); printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } assert(pjlink->parseDepth > 0); return dbjl_return(parser, CALL_OR_STOP(pjlink->pif->parse_map_key)(pjlink, (const char *) key, len)); } IFDEBUG(10) { printf("dbjl_map_key(NULL, \"%.*s\")\t", (int) len, key); printf(" jsonDepth=%d, parseDepth=00, dbfType=%d\n", parser->jsonDepth, parser->dbfType); } link_name = dbmfStrndup((const char *) key, len); linkSup = dbFindLinkSup(pdbbase, link_name); if (!linkSup) { errlogPrintf("dbJLinkInit: Link type '%s' not found\n", link_name); dbmfFree(link_name); return dbjl_return(parser, jlif_stop); } pjlif = linkSup->pjlif; if (!pjlif) { errlogPrintf("dbJLinkInit: Support for Link type '%s' not loaded\n", link_name); dbmfFree(link_name); return dbjl_return(parser, jlif_stop); } child = pjlif->alloc_jlink(parser->dbfType); if (!child) { errlogPrintf("dbJLinkInit: Link type '%s' allocation failed. \n", link_name); dbmfFree(link_name); return dbjl_return(parser, jlif_stop); } child->pif = pjlif; child->parseDepth = 0; child->debug = 0; if (parser->pjlink) { /* We're starting a child link, save its parent */ child->parent = pjlink; if (pjlink->pif->start_child) pjlink->pif->start_child(pjlink, child); } else child->parent = NULL; parser->pjlink = child; parser->dbfType = 0; dbmfFree(link_name); IFDEBUG(8) printf("dbjl_map_key: New %s@%p\n", child ? child->pif->name : "", child); return jlif_continue; } static int dbjl_end_map(void *ctx) { parseContext *parser = (parseContext *) ctx; jlink *pjlink = parser->pjlink; jlif_result result; IFDEBUG(10) { printf("dbjl_end_map(%s@%p)\t", pjlink ? pjlink->pif->name : "NULL", pjlink); printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } parser->jsonDepth--; if (pjlink) { pjlink->parseDepth--; result = dbjl_value(parser, CALL_OR_STOP(pjlink->pif->parse_end_map)(pjlink)); } else { result = jlif_continue; } return result; } static int dbjl_start_array(void *ctx) { parseContext *parser = (parseContext *) ctx; jlink *pjlink = parser->pjlink; IFDEBUG(10) { printf("dbjl_start_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink); printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } assert(pjlink); pjlink->parseDepth++; parser->jsonDepth++; return dbjl_return(parser, CALL_OR_STOP(pjlink->pif->parse_start_array)(pjlink)); } static int dbjl_end_array(void *ctx) { parseContext *parser = (parseContext *) ctx; jlink *pjlink = parser->pjlink; IFDEBUG(10) { printf("dbjl_end_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink); printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } assert(pjlink); pjlink->parseDepth--; parser->jsonDepth--; return dbjl_value(parser, CALL_OR_STOP(pjlink->pif->parse_end_array)(pjlink)); } static yajl_callbacks dbjl_callbacks = { dbjl_null, dbjl_boolean, dbjl_integer, dbjl_double, NULL, dbjl_string, dbjl_start_map, dbjl_map_key, dbjl_end_map, dbjl_start_array, dbjl_end_array }; long dbJLinkParse(const char *json, size_t jlen, short dbfType, jlink **ppjlink) { parseContext context, *parser = &context; yajl_alloc_funcs dbjl_allocs; yajl_handle yh; yajl_status ys; long status; parser->pjlink = NULL; parser->product = NULL; parser->dbfType = dbfType; parser->jsonDepth = 0; IFDEBUG(10) printf("dbJLinkInit(\"%.*s\", %d, %p)\n", (int) jlen, json, dbfType, ppjlink); IFDEBUG(10) printf("dbJLinkInit: jsonDepth=%d, dbfType=%d\n", parser->jsonDepth, parser->dbfType); yajl_set_default_alloc_funcs(&dbjl_allocs); yh = yajl_alloc(&dbjl_callbacks, &dbjl_allocs, parser); if (!yh) return S_db_noMemory; ys = yajl_parse(yh, (const unsigned char *) json, jlen); IFDEBUG(10) printf("dbJLinkInit: yajl_parse() returned %d\n", ys); if (ys == yajl_status_ok) { ys = yajl_complete_parse(yh); IFDEBUG(10) printf("dbJLinkInit: yajl_complete_parse() returned %d\n", ys); } switch (ys) { unsigned char *err; case yajl_status_ok: assert(parser->jsonDepth == 0); *ppjlink = parser->product; status = 0; break; case yajl_status_error: IFDEBUG(10) printf(" jsonDepth=%d, product=%p, pjlink=%p\n", parser->jsonDepth, parser->product, parser->pjlink); err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen); errlogPrintf("dbJLinkInit: %s\n", err); yajl_free_error(yh, err); dbJLinkFree(parser->pjlink); dbJLinkFree(parser->product); /* fall through */ default: status = S_db_badField; } yajl_free(yh); IFDEBUG(10) printf("dbJLinkInit: returning status=0x%lx\n\n", status); return status; } long dbJLinkInit(struct link *plink) { assert(plink); if (plink->type == JSON_LINK) { jlink *pjlink = plink->value.json.jlink; if (pjlink) plink->lset = pjlink->pif->get_lset(pjlink); } dbLinkOpen(plink); return 0; } void dbJLinkFree(jlink *pjlink) { if (pjlink) pjlink->pif->free_jlink(pjlink); } void dbJLinkReport(jlink *pjlink, int level, int indent) { if (pjlink && pjlink->pif->report) pjlink->pif->report(pjlink, level, indent); } long dbJLinkMapChildren(struct link *plink, jlink_map_fn rtn, void *ctx) { jlink *pjlink; long status; if (!plink || plink->type != JSON_LINK) return 0; pjlink = plink->value.json.jlink; if (!pjlink) return 0; status = rtn(pjlink, ctx); if (!status && pjlink->pif->map_children) status = pjlink->pif->map_children(pjlink, rtn, ctx); return status; } long dbjlr(const char *recname, int level) { DBENTRY dbentry; DBENTRY * const pdbentry = &dbentry; long status; if (!recname || recname[0] == '\0' || !strcmp(recname, "*")) { recname = NULL; printf("JSON links in all records\n\n"); } else printf("JSON links in record '%s'\n\n", recname); dbInitEntry(pdbbase, pdbentry); for (status = dbFirstRecordType(pdbentry); status == 0; status = dbNextRecordType(pdbentry)) { for (status = dbFirstRecord(pdbentry); status == 0; status = dbNextRecord(pdbentry)) { dbRecordType *pdbRecordType = pdbentry->precordType; dbCommon *precord = pdbentry->precnode->precord; char *prec = (char *) precord; int i; if (recname && strcmp(recname, dbGetRecordName(pdbentry))) continue; if (dbIsAlias(pdbentry)) continue; printf(" %s record '%s':\n", pdbRecordType->name, precord->name); dbScanLock(precord); for (i = 0; i < pdbRecordType->no_links; i++) { int idx = pdbRecordType->link_ind[i]; dbFldDes *pdbFldDes = pdbRecordType->papFldDes[idx]; DBLINK *plink = (DBLINK *) (prec + pdbFldDes->offset); if (plink->type != JSON_LINK) continue; if (!dbLinkIsDefined(plink)) continue; printf(" Link field '%s':\n", pdbFldDes->name); dbJLinkReport(plink->value.json.jlink, level, 6); } dbScanUnlock(precord); if (recname) goto done; } } done: return 0; } long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx) { DBENTRY dbentry; DBENTRY * const pdbentry = &dbentry; long status; if (recname && (recname[0] = '\0' || !strcmp(recname, "*"))) recname = NULL; dbInitEntry(pdbbase, pdbentry); for (status = dbFirstRecordType(pdbentry); status == 0; status = dbNextRecordType(pdbentry)) { for (status = dbFirstRecord(pdbentry); status == 0; status = dbNextRecord(pdbentry)) { dbRecordType *pdbRecordType = pdbentry->precordType; dbCommon *precord = pdbentry->precnode->precord; char *prec = (char *) precord; int i; if (recname && strcmp(recname, dbGetRecordName(pdbentry))) continue; if (dbIsAlias(pdbentry)) continue; dbScanLock(precord); for (i = 0; i < pdbRecordType->no_links; i++) { int idx = pdbRecordType->link_ind[i]; dbFldDes *pdbFldDes = pdbRecordType->papFldDes[idx]; DBLINK *plink = (DBLINK *) (prec + pdbFldDes->offset); status = dbJLinkMapChildren(plink, rtn, ctx); if (status) goto unlock; } unlock: dbScanUnlock(precord); if (status || recname) goto done; } } done: return status; } base-7.0.3.1/modules/database/src/ioc/db/dbJLink.h0000664000577000060420000001135713557101274020252 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbJLink.h */ #ifndef INC_dbJLink_H #define INC_dbJLink_H #include #include #ifdef __cplusplus extern "C" { #endif typedef enum { jlif_stop = 0, jlif_continue = 1 } jlif_result; epicsShareExtern const char *jlif_result_name[2]; typedef enum { jlif_key_stop = jlif_stop, jlif_key_continue = jlif_continue, jlif_key_child_inlink, jlif_key_child_outlink, jlif_key_child_fwdlink } jlif_key_result; epicsShareExtern const char *jlif_key_result_name[5]; struct link; struct lset; struct jlif; typedef struct jlink { struct jlif *pif; /* Link methods */ struct jlink *parent; /* NULL for top-level links */ int parseDepth; /* Used by parser, unused afterwards */ unsigned debug:1; /* Set to request debug output to console */ /* Link types extend or embed this structure for private storage */ } jlink; typedef long (*jlink_map_fn)(jlink *, void *ctx); typedef struct jlif { /* Optional parser methods below given as NULL are equivalent to * providing a routine that always returns jlif_stop, meaning that * this JSON construct is not allowed at this point in the parse. */ const char *name; /* Name for the link type, used in link value */ jlink* (*alloc_jlink)(short dbfType); /* Required, allocate new link structure */ void (*free_jlink)(jlink *); /* Required, release all resources allocated for link */ jlif_result (*parse_null)(jlink *); /* Optional, parser saw a null value */ jlif_result (*parse_boolean)(jlink *, int val); /* Optional, parser saw a boolean value */ jlif_result (*parse_integer)(jlink *, long long num); /* Optional, parser saw an integer value */ jlif_result (*parse_double)(jlink *, double num); /* Optional, parser saw a double value */ jlif_result (*parse_string)(jlink *, const char *val, size_t len); /* Optional, parser saw a string value */ jlif_key_result (*parse_start_map)(jlink *); /* Optional, parser saw an open-brace '{'. Return jlif_key_child_inlink, * jlif_key_child_outlink, or jlif_key_child_fwdlink to expect a child * link next (extra key/value pairs may follow) */ jlif_result (*parse_map_key)(jlink *, const char *key, size_t len); /* Optional, parser saw a map key */ jlif_result (*parse_end_map)(jlink *); /* Optional, parser saw a close-brace '}' */ jlif_result (*parse_start_array)(jlink *); /* Optional, parser saw an open-bracket */ jlif_result (*parse_end_array)(jlink *); /* Optional, parser saw a close-bracket */ void (*end_child)(jlink *parent, jlink *child); /* Optional, called with pointer to the new child link after * the child link has finished parsing successfully */ struct lset* (*get_lset)(const jlink *); /* Required, return lset for this link instance */ void (*report)(const jlink *, int level, int indent); /* Optional, print status information about this link instance, then * if (level > 0) print a link identifier (at indent+2) and call * dbJLinkReport(child, level-1, indent+4) * for each child. */ long (*map_children)(jlink *, jlink_map_fn rtn, void *ctx); /* Optional, call dbJLinkMapChildren() on all embedded links. * Stop immediately and return status if non-zero. */ void (*start_child)(jlink *parent, jlink *child); /* Optional, called with pointer to the new child link after * parse_start_map() returned a jlif_key_child_link value and * the child link has been allocated (but not parsed yet) */ /* Link types must NOT extend this table with their own routines, * this space is reserved for extensions to the jlink interface. */ } jlif; epicsShareFunc long dbJLinkParse(const char *json, size_t len, short dbfType, jlink **ppjlink); epicsShareFunc long dbJLinkInit(struct link *plink); epicsShareFunc void dbJLinkFree(jlink *); epicsShareFunc void dbJLinkReport(jlink *, int level, int indent); epicsShareFunc long dbJLinkMapChildren(struct link *, jlink_map_fn rtn, void *ctx); epicsShareFunc long dbjlr(const char *recname, int level); epicsShareFunc long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx); #ifdef __cplusplus } #endif #endif /* INC_dbJLink_H */ base-7.0.3.1/modules/database/src/ioc/db/dbLink.c0000664000577000060420000003046013557101274020127 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbLink.c */ /* * Original Authors: Bob Dalesio, Marty Kraimer * Current Author: Andrew Johnson */ #include #include #include #include #include #include "alarm.h" #include "cvtFast.h" #include "dbDefs.h" #include "ellLib.h" #include "epicsTime.h" #include "errlog.h" #include "caeventmask.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" #include "dbCa.h" #include "dbCommon.h" #include "dbConstLink.h" #include "dbDbLink.h" #include "db_field_log.h" #include "dbFldTypes.h" #include "dbJLink.h" #include "dbLink.h" #include "dbLock.h" #include "dbScan.h" #include "dbStaticLib.h" #include "devSup.h" #include "link.h" #include "recGbl.h" #include "recSup.h" #include "special.h" /* How to identify links in error messages */ const char * dbLinkFieldName(const struct link *plink) { const struct dbCommon *precord = plink->precord; const dbRecordType *pdbRecordType = precord->rdes; dbFldDes * const *papFldDes = pdbRecordType->papFldDes; const short *link_ind = pdbRecordType->link_ind; int i; for (i = 0; i < pdbRecordType->no_links; i++) { const dbFldDes *pdbFldDes = papFldDes[link_ind[i]]; if (plink == (DBLINK *)((char *)precord + pdbFldDes->offset)) return pdbFldDes->name; } return "????"; } /* Special TSEL handler for PV links */ /* FIXME: Generalize for new link types... */ static void TSEL_modified(struct link *plink) { struct pv_link *ppv_link; char *pfieldname; if (plink->type != PV_LINK) { errlogPrintf("dbLink::TSEL_modified called for non PV_LINK\n"); return; } /* If pvname contains .TIME truncate it to point to VAL instead */ ppv_link = &plink->value.pv_link; pfieldname = strstr(ppv_link->pvname, ".TIME"); if (pfieldname) { *pfieldname = 0; plink->flags |= DBLINK_FLAG_TSELisTIME; } } /***************************** Generic Link API *****************************/ void dbInitLink(struct link *plink, short dbfType) { struct dbCommon *precord = plink->precord; /* Only initialize link once */ if (plink->flags & DBLINK_FLAG_INITIALIZED) return; else plink->flags |= DBLINK_FLAG_INITIALIZED; if (plink->type == CONSTANT) { dbConstInitLink(plink); return; } if (plink->type == JSON_LINK) { dbJLinkInit(plink); return; } if (plink->type != PV_LINK) return; if (plink == &precord->tsel) TSEL_modified(plink); if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) { /* Make it a DB link if possible */ if (!dbDbInitLink(plink, dbfType)) return; } /* Make it a CA link */ if (dbfType == DBF_INLINK) plink->value.pv_link.pvlMask |= pvlOptInpNative; dbCaAddLink(NULL, plink, dbfType); if (dbfType == DBF_FWDLINK) { char *pperiod = strrchr(plink->value.pv_link.pvname, '.'); if (pperiod && strstr(pperiod, "PROC")) { plink->value.pv_link.pvlMask |= pvlOptFWD; } else { errlogPrintf("Forward-link uses Channel Access " "without pointing to PROC field\n" " %s.%s => %s\n", precord->name, dbLinkFieldName(plink), plink->value.pv_link.pvname); } } } void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget) { struct dbCommon *precord = plink->precord; /* Clear old TSELisTIME flag */ plink->flags &= ~DBLINK_FLAG_TSELisTIME; if (plink->type == CONSTANT) { dbConstAddLink(plink); return; } if (plink->type == JSON_LINK) { /* * FIXME: Can't create DB links as dbJLink types yet, * dbLock.c doesn't have any way to find/track them. */ dbJLinkInit(plink); return; } if (plink->type != PV_LINK) return; if (plink == &precord->tsel) TSEL_modified(plink); if (ptarget) { /* It's a DB link */ dbDbAddLink(locker, plink, dbfType, ptarget); return; } /* Make it a CA link */ if (dbfType == DBF_INLINK) plink->value.pv_link.pvlMask |= pvlOptInpNative; dbCaAddLink(locker, plink, dbfType); if (dbfType == DBF_FWDLINK) { char *pperiod = strrchr(plink->value.pv_link.pvname, '.'); if (pperiod && strstr(pperiod, "PROC")) plink->value.pv_link.pvlMask |= pvlOptFWD; } } void dbLinkOpen(struct link *plink) { lset *plset = plink->lset; if (plset && plset->openLink) plset->openLink(plink); } void dbRemoveLink(struct dbLocker *locker, struct link *plink) { lset *plset = plink->lset; if (plset) { if (plset->removeLink) plset->removeLink(locker, plink); plink->lset = NULL; } if (plink->type == JSON_LINK) plink->value.json.jlink = NULL; } int dbLinkIsDefined(const struct link *plink) { return (plink->lset != 0); } int dbLinkIsConstant(const struct link *plink) { lset *plset = plink->lset; return !plset || plset->isConstant; } int dbLinkIsVolatile(const struct link *plink) { lset *plset = plink->lset; return plset && plset->isVolatile; } long dbLoadLink(struct link *plink, short dbrType, void *pbuffer) { lset *plset = plink->lset; if (plset && plset->loadScalar) return plset->loadScalar(plink, dbrType, pbuffer); return S_db_noLSET; } long dbLoadLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size, epicsUInt32 *plen) { lset *plset = plink->lset; if (plset && plset->loadLS) return plset->loadLS(plink, pbuffer, size, plen); return S_db_noLSET; } long dbLoadLinkArray(struct link *plink, short dbrType, void *pbuffer, long *pnRequest) { lset *plset = plink->lset; if (plset && plset->loadArray) return plset->loadArray(plink, dbrType, pbuffer, pnRequest); return S_db_noLSET; } int dbIsLinkConnected(const struct link *plink) { lset *plset = plink->lset; if (!plset) return FALSE; if (!plset->isVolatile) return TRUE; if (!plset->isConnected) { struct dbCommon *precord = plink->precord; errlogPrintf("dbLink: Link type for '%s.%s' is volatile but has no" " lset::isConnected() method\n", precord->name, dbLinkFieldName(plink)); return FALSE; } return plset->isConnected(plink); } int dbGetLinkDBFtype(const struct link *plink) { lset *plset = plink->lset; if (!plset || !plset->getDBFtype) return -1; return plset->getDBFtype(plink); } long dbGetNelements(const struct link *plink, long *nelements) { lset *plset = plink->lset; if (!plset || !plset->getElements) return S_db_noLSET; return plset->getElements(plink, nelements); } long dbTryGetLink(struct link *plink, short dbrType, void *pbuffer, long *pnRequest) { lset *plset = plink->lset; if (!plset || !plset->getValue) return S_db_noLSET; return plset->getValue(plink, dbrType, pbuffer, pnRequest); } long dbGetLink(struct link *plink, short dbrType, void *pbuffer, long *poptions, long *pnRequest) { struct dbCommon *precord = plink->precord; long status; if (poptions && *poptions) { printf("dbGetLink: Use of poptions no longer supported\n"); *poptions = 0; } status = dbTryGetLink(plink, dbrType, pbuffer, pnRequest); if (status == S_db_noLSET) return -1; if (status) recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); return status; } long dbGetControlLimits(const struct link *plink, double *low, double *high) { lset *plset = plink->lset; if (!plset || !plset->getControlLimits) return S_db_noLSET; return plset->getControlLimits(plink, low, high); } long dbGetGraphicLimits(const struct link *plink, double *low, double *high) { lset *plset = plink->lset; if (!plset || !plset->getGraphicLimits) return S_db_noLSET; return plset->getGraphicLimits(plink, low, high); } long dbGetAlarmLimits(const struct link *plink, double *lolo, double *low, double *high, double *hihi) { lset *plset = plink->lset; if (!plset || !plset->getAlarmLimits) return S_db_noLSET; return plset->getAlarmLimits(plink, lolo, low, high, hihi); } long dbGetPrecision(const struct link *plink, short *precision) { lset *plset = plink->lset; if (!plset || !plset->getPrecision) return S_db_noLSET; return plset->getPrecision(plink, precision); } long dbGetUnits(const struct link *plink, char *units, int unitsSize) { lset *plset = plink->lset; if (!plset || !plset->getUnits) return S_db_noLSET; return plset->getUnits(plink, units, unitsSize); } long dbGetAlarm(const struct link *plink, epicsEnum16 *status, epicsEnum16 *severity) { lset *plset = plink->lset; if (!plset || !plset->getAlarm) return S_db_noLSET; return plset->getAlarm(plink, status, severity); } long dbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp) { lset *plset = plink->lset; if (!plset || !plset->getTimeStamp) return S_db_noLSET; return plset->getTimeStamp(plink, pstamp); } long dbPutLink(struct link *plink, short dbrType, const void *pbuffer, long nRequest) { lset *plset = plink->lset; long status; if (!plset || !plset->putValue) return S_db_noLSET; status = plset->putValue(plink, dbrType, pbuffer, nRequest); if (status) { struct dbCommon *precord = plink->precord; recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); } return status; } void dbLinkAsyncComplete(struct link *plink) { dbCommon *pdbCommon = plink->precord; dbScanLock(pdbCommon); pdbCommon->rset->process(pdbCommon); dbScanUnlock(pdbCommon); } long dbPutLinkAsync(struct link *plink, short dbrType, const void *pbuffer, long nRequest) { lset *plset = plink->lset; long status; if (!plset || !plset->putAsync) return S_db_noLSET; status = plset->putAsync(plink, dbrType, pbuffer, nRequest); if (status) { struct dbCommon *precord = plink->precord; recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); } return status; } void dbScanFwdLink(struct link *plink) { lset *plset = plink->lset; if (plset && plset->scanForward) plset->scanForward(plink); } long dbLinkDoLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) { lset *plset = plink->lset; if (!rtn || !plset || !plset->doLocked) return S_db_noLSET; return plset->doLocked(plink, rtn, priv); } /* Helper functions for long string support */ long dbGetLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size, epicsUInt32 *plen) { int dtyp = dbGetLinkDBFtype(plink); long len = size; long status; if (dtyp < 0) /* Not connected */ return 0; if (dtyp == DBR_CHAR || dtyp == DBF_UCHAR) { status = dbGetLink(plink, dtyp, pbuffer, 0, &len); } else if (size >= MAX_STRING_SIZE) status = dbGetLink(plink, DBR_STRING, pbuffer, 0, 0); else { /* pbuffer is too small to fetch using DBR_STRING */ char tmp[MAX_STRING_SIZE]; status = dbGetLink(plink, DBR_STRING, tmp, 0, 0); if (!status) strncpy(pbuffer, tmp, len - 1); } if (!status) { pbuffer[--len] = 0; *plen = (epicsUInt32) strlen(pbuffer) + 1; } return status; } long dbPutLinkLS(struct link *plink, char *pbuffer, epicsUInt32 len) { int dtyp = dbGetLinkDBFtype(plink); if (dtyp < 0) return 0; /* Not connected */ if (dtyp == DBR_CHAR || dtyp == DBF_UCHAR) return dbPutLink(plink, dtyp, pbuffer, len); return dbPutLink(plink, DBR_STRING, pbuffer, 1); } base-7.0.3.1/modules/database/src/ioc/db/dbLink.h0000664000577000060420000004121713557101274020136 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 The UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbLink.h * * Created on: Mar 21, 2010 * Author: Andrew Johnson */ #ifndef INC_dbLink_H #define INC_dbLink_H #include "link.h" #include "shareLib.h" #include "epicsTypes.h" #include "epicsTime.h" #include "dbAddr.h" #ifdef __cplusplus extern "C" { #endif struct dbLocker; /** @file dbLink.h * @brief Link Support API * * Link support run-time API, all link types provide an lset which is used by * the IOC database to control and operate the link. This file also declares the * dbLink routines that IOC, record and device code can call to perform link * operations. */ /** @brief callback routine for locked link operations * * Called by the lset::doLocked method to permit multiple link operations * while the link instance is locked. * * @param plink the link * @param priv context for the callback routine */ typedef long (*dbLinkUserCallback)(struct link *plink, void *priv); /** @brief Link Support Entry Table * * This structure provides information about and methods for an individual link * type. A pointer to this structure is included in every link's lset field, and * is used to perform operations on the link. For JSON links the pointer is * obtained by calling pjlink->pif->get_lset() at link initialization time, * immediately before calling dbLinkOpen() to activate the link. */ typedef struct lset { /* Characteristics of the link type */ /** @brief link constancy * * 1 means this is a constant link type whose value doesn't change. * The link's value will be obtained using one of the methods loadScalar, * loadLS or loadArray. */ const unsigned isConstant:1; /** @brief link volatility * * 0 means the link is always connected. */ const unsigned isVolatile:1; /** @brief activate link * * Optional, called whenever a JSON link is initialized or added at runtime. * * @param plink the link */ void (*openLink)(struct link *plink); /** @brief deactivate link * * Optional, called whenever a link address is changed at runtime, or the * IOC is shutting down. * * @param locker * @param plink the link */ void (*removeLink)(struct dbLocker *locker, struct link *plink); /* Constant link initialization and data type hinting */ /** @brief load constant scalar from link type * * Usually called during IOC initialization, constant link types must copy a * scalar value of the indicated data type to the buffer provided and return * 0. A non-constant link type can use this method call as an early hint * that subsequent calls to dbGetLink() will request scalar data of the * indicated type, although the type might change. * * @param plink the link * @param dbrType data type code * @param pbuffer where to put the value * @returns 0 if a value was loaded, non-zero otherwise */ long (*loadScalar)(struct link *plink, short dbrType, void *pbuffer); /** @brief load constant long string from link type * * Usually called during IOC initialization, constant link types must copy a * nil-terminated string up to size characters long to the buffer provided, * and write the length of that string to the plen location. A non-constant * link type can use this as an early hint that subsequent calls to * dbGetLink() will request long string data, although this might change. * * @param plink the link * @param pbuffer where to put the string * @param size length of pbuffer in chars * @param plen set to number of chars written * @returns status value */ long (*loadLS)(struct link *plink, char *pbuffer, epicsUInt32 size, epicsUInt32 *plen); /** @brief load constant array from link type * * Usually called during IOC initialization, constant link types must copy * an array value of the indicated data type to the buffer provided, update * the pnRequest location to indicate how many elements were loaded, and * return 0. A non-constant link type can use this method call as an early * hint that subsequent calls to dbGetLink() will request array data of the * indicated type and max size, although the request might change. * * @param plink the link * @param dbrType data type code * @param pbuffer where to put the value * @param pnRequest Max elements on entry, actual on exit * @returns 0 if elements were loaded, non-zero otherwise */ long (*loadArray)(struct link *plink, short dbrType, void *pbuffer, long *pnRequest); /* Metadata */ /** @brief return link connection status * * Return an indication whether this link is connected or not. This routine * is polled by the calcout and some external record types. Not required for * non-volatile link types, which are by definition always connected. * * @param plink the link * @returns 1 if connected, 0 if disconnected */ int (*isConnected)(const struct link *plink); /** @brief get data type of link destination * * Called on both input and output links by long string support code to * decide whether to use DBR_CHAR/DBR_UCHAR or DBR_STRING for a subsequent * dbPutLink() or dbGetLink() call. Optional, but if not provided long * strings cannot be transported over this link type, and no warning or * error will appear to explain why. Not required for constant link types. * * @param plink the link * @returns DBF_* type code, or -1 on error/disconnected link */ int (*getDBFtype)(const struct link *plink); /* Get data */ /** @brief get array size of an input link * * Called on input links by the compress record type for memory allocation * purposes, before using the dbGetLink() routine to fetch the actual * array data. * * @param plink the link * @param pnElements where to put the answer * @returns status value */ long (*getElements)(const struct link *plink, long *pnElements); /** @brief get value from an input link * * Called to fetch data from the link, which must be converted into the * given data type and placed in the buffer indicated. The actual number of * elements retrieved should be updated in the pnRequest location. If this * method returns an error status value, the link's record will be placed * into an Invalid severity / Link Alarm state by the dbGetLink() routine * that calls this method. * * @param plink the link * @param dbrType data type code * @param pbuffer where to put the value * @param pnRequest max elements on entry, actual on exit * @returns status value */ long (*getValue)(struct link *plink, short dbrType, void *pbuffer, long *pnRequest); /** @brief get the control range for an output link * * Called to fetch the control range for the link target, as a pair of * double values for the lowest and highest values that the target will * accept. This method is not used at all by the IOC or built-in record * types, although external record types may require it. * * @param plink the link * @param lo lowest accepted value * @param hi highest accepted value * @returns status value */ long (*getControlLimits)(const struct link *plink, double *lo, double *hi); /** @brief get the display range from an input link * * Called to fetch the display range for an input link target, as a pair of * double values for the lowest and highest values that the PV expects to * return. This method is used by several built-in record types to obtain * the display range for their generic input links. * * @param plink the link * @param lo lowest accepted value * @param hi highest accepted value * @returns status value */ long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi); /** @brief get the alarm limits from an input link * * Called to fetch the alarm limits for an input link target, as four * double values for the warning and alarm levels that the PV checks its * value against. This method is used by several built-in record types to * obtain the alarm limits for their generic input links. * * @param plink the link * @param lolo low alarm value * @param lo low warning value * @param hi high warning value * @param hihi high alarm value * @returns status value */ long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo, double *hi, double *hihi); /** @brief get the precision from an input link * * Called to fetch the precision for an input link target. This method is * used by several built-in record types to obtain the precision for their * generic input links. * * @param plink the link * @param precision where to put the answer * @returns status value */ long (*getPrecision)(const struct link *plink, short *precision); /** @brief get the units string from an input link * * Called to fetch the units string for an input link target. This method is * used by several built-in record types to obtain the units string for * their generic input links. * * @param plink the link * @param units where to put the answer * @param unitsSize buffer size for the answer * @returns status value */ long (*getUnits)(const struct link *plink, char *units, int unitsSize); /** @brief get the alarm condition from an input link * * Called to fetch the alarm status and severity for an input link target. * Either status or severity pointers may be NULL when that value is not * needed by the calling code. This method is used by several built-in * record types to obtain the alarm condition for their generic input links. * * @param plink the link * @param status where to put the alarm status (or NULL) * @param severity where to put the severity (or NULL) * @returns status value */ long (*getAlarm)(const struct link *plink, epicsEnum16 *status, epicsEnum16 *severity); /** @brief get the time-stamp from an input link * * Called to fetch the time-stamp for an input link target. This method is * used by many built-in device supports to obtain the precision for their * generic input links. * * @param plink the link * @param pstamp where to put the answer * @returns status value */ long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp); /* Put data */ /** @brief put a value to an output link * * Called to send nRequest elements of type dbrType found at pbuffer to an * output link target. * * @param plink the link * @param dbrType data type code * @param pbuffer where to put the value * @param nRequest number of elements to send * @returns status value */ long (*putValue)(struct link *plink, short dbrType, const void *pbuffer, long nRequest); /** @brief put a value to an output link with asynchronous completion * * Called to send nRequest elements of type dbrType found at pbuffer to an * output link target. If the return status is zero, the link type will * later indicate the put has completed by calling dbLinkAsyncComplete() * from a background thread, which will be used to continue the record * process operation from where it left off. * * @param plink the link * @param dbrType data type code * @param pbuffer where to put the value * @param nRequest number of elements to send * @returns status value */ long (*putAsync)(struct link *plink, short dbrType, const void *pbuffer, long nRequest); /* Process */ /** @brief trigger processing of a forward link * * Called to trigger processing of the record pointed to by a forward link. * This routine is optional, but if not provided no warning message will be * shown when called by dbScanFwdLink(). JSON link types that do not support * this operation should return NULL from their jlif::alloc_jlink() method * if it gets called with a dbfType of DBF_FWDLINK. * * @param plink the link */ void (*scanForward)(struct link *plink); /* Atomicity */ /** @brief execute a callback routine with link locked * * Called on an input link when multiple link attributes need to be fetched * in an atomic fashion. The link type must call the callback routine and * prevent any background I/O from updating any cached link data until that * routine returns. This method is used by most input device support to * fetch the timestamp along with the value when the record's TSE field is * set to epicsTimeEventDeviceTime. * * @param plink the link * @param rtn routine to execute * @returns status value */ long (*doLocked)(struct link *plink, dbLinkUserCallback rtn, void *priv); } lset; #define dbGetSevr(link, sevr) \ dbGetAlarm(link, NULL, sevr) epicsShareFunc const char * dbLinkFieldName(const struct link *plink); epicsShareFunc void dbInitLink(struct link *plink, short dbfType); epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget); epicsShareFunc void dbLinkOpen(struct link *plink); epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink); epicsShareFunc int dbLinkIsDefined(const struct link *plink); /* 0 or 1 */ epicsShareFunc int dbLinkIsConstant(const struct link *plink); /* 0 or 1 */ epicsShareFunc int dbLinkIsVolatile(const struct link *plink); /* 0 or 1 */ epicsShareFunc long dbLoadLink(struct link *plink, short dbrType, void *pbuffer); epicsShareFunc long dbLoadLinkArray(struct link *, short dbrType, void *pbuffer, long *pnRequest); epicsShareFunc long dbGetNelements(const struct link *plink, long *pnElements); epicsShareFunc int dbIsLinkConnected(const struct link *plink); /* 0 or 1 */ epicsShareFunc int dbGetLinkDBFtype(const struct link *plink); epicsShareFunc long dbTryGetLink(struct link *, short dbrType, void *pbuffer, long *nRequest); epicsShareFunc long dbGetLink(struct link *, short dbrType, void *pbuffer, long *options, long *nRequest); epicsShareFunc long dbGetControlLimits(const struct link *plink, double *low, double *high); epicsShareFunc long dbGetGraphicLimits(const struct link *plink, double *low, double *high); epicsShareFunc long dbGetAlarmLimits(const struct link *plink, double *lolo, double *low, double *high, double *hihi); epicsShareFunc long dbGetPrecision(const struct link *plink, short *precision); epicsShareFunc long dbGetUnits(const struct link *plink, char *units, int unitsSize); epicsShareFunc long dbGetAlarm(const struct link *plink, epicsEnum16 *status, epicsEnum16 *severity); epicsShareFunc long dbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp); epicsShareFunc long dbPutLink(struct link *plink, short dbrType, const void *pbuffer, long nRequest); epicsShareFunc void dbLinkAsyncComplete(struct link *plink); epicsShareFunc long dbPutLinkAsync(struct link *plink, short dbrType, const void *pbuffer, long nRequest); epicsShareFunc void dbScanFwdLink(struct link *plink); epicsShareFunc long dbLinkDoLocked(struct link *plink, dbLinkUserCallback rtn, void *priv); epicsShareFunc long dbLoadLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size, epicsUInt32 *plen); epicsShareFunc long dbGetLinkLS(struct link *plink, char *pbuffer, epicsUInt32 buffer_size, epicsUInt32 *plen); epicsShareFunc long dbPutLinkLS(struct link *plink, char *pbuffer, epicsUInt32 len); #ifdef __cplusplus } #endif #endif /* INC_dbLink_H */ base-7.0.3.1/modules/database/src/ioc/db/dbLock.c0000664000577000060420000006751713557101274020137 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include #include #include #include #include "cantProceed.h" #include "dbDefs.h" #include "ellLib.h" #include "epicsAssert.h" #include "epicsAtomic.h" #include "epicsMutex.h" #include "epicsPrint.h" #include "epicsSpin.h" #include "epicsStdio.h" #include "epicsThread.h" #include "errMdef.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" #include "dbLink.h" #include "dbCommon.h" #include "dbFldTypes.h" #include "dbLockPvt.h" #include "dbStaticLib.h" #include "link.h" typedef struct dbScanLockNode dbScanLockNode; static epicsThreadOnceId dbLockOnceInit = EPICS_THREAD_ONCE_INIT; static ELLLIST lockSetsActive; /* in use */ #ifndef LOCKSET_NOFREE static ELLLIST lockSetsFree; /* free list */ #endif /* Guard the global list */ static epicsMutexId lockSetsGuard; #ifndef LOCKSET_NOCNT /* Counter which we increment whenever * any lockRecord::plockSet is changed. * An optimization to avoid checking lockSet * associations when no links have changed. */ static size_t recomputeCnt; #endif /*private routines */ static void dbLockOnce(void* ignore) { lockSetsGuard = epicsMutexMustCreate(); } /* global ID number assigned to each lockSet on creation. * When the free-list is in use will never exceed * the number of records +1. * Without the free-list it can roll over, potentially * leading to duplicate IDs. */ static size_t next_id = 1; static lockSet* makeSet(void) { lockSet *ls; int iref; epicsMutexMustLock(lockSetsGuard); #ifndef LOCKSET_NOFREE ls = (lockSet*)ellGet(&lockSetsFree); if(!ls) { epicsMutexUnlock(lockSetsGuard); #endif ls=dbCalloc(1,sizeof(*ls)); ellInit(&ls->lockRecordList); ls->lock = epicsMutexMustCreate(); ls->id = epicsAtomicIncrSizeT(&next_id); #ifndef LOCKSET_NOFREE epicsMutexMustLock(lockSetsGuard); } #endif /* the initial reference for the first lockRecord */ iref = epicsAtomicIncrIntT(&ls->refcount); ellAdd(&lockSetsActive, &ls->node); epicsMutexUnlock(lockSetsGuard); assert(ls->id>0); assert(iref>0); assert(ellCount(&ls->lockRecordList)==0); return ls; } unsigned long dbLockGetRefs(struct dbCommon* prec) { return (unsigned long)epicsAtomicGetIntT(&prec->lset->plockSet->refcount); } unsigned long dbLockCountSets(void) { unsigned long count; epicsMutexMustLock(lockSetsGuard); count = (unsigned long)ellCount(&lockSetsActive); epicsMutexUnlock(lockSetsGuard); return count; } /* caller must lock accessLock.*/ void dbLockIncRef(lockSet* ls) { int cnt = epicsAtomicIncrIntT(&ls->refcount); if(cnt<=1) { errlogPrintf("dbLockIncRef(%p) on dead lockSet. refs: %d\n", ls, cnt); cantProceed(NULL); } } /* caller must lock accessLock. * lockSet must *not* be locked */ void dbLockDecRef(lockSet *ls) { int cnt = epicsAtomicDecrIntT(&ls->refcount); assert(cnt>=0); if(cnt) return; /* lockSet is unused and will be free'd */ /* not necessary as no one else (should) hold a reference, * but lock anyway to quiet valgrind */ epicsMutexMustLock(ls->lock); if(ellCount(&ls->lockRecordList)!=0) { errlogPrintf("dbLockDecRef(%p) would free lockSet with %d records\n", ls, ellCount(&ls->lockRecordList)); cantProceed(NULL); } epicsMutexUnlock(ls->lock); epicsMutexMustLock(lockSetsGuard); ellDelete(&lockSetsActive, &ls->node); #ifndef LOCKSET_NOFREE ellAdd(&lockSetsFree, &ls->node); #else epicsMutexDestroy(ls->lock); memset(ls, 0, sizeof(*ls)); /* paranoia */ free(ls); #endif epicsMutexUnlock(lockSetsGuard); } lockSet* dbLockGetRef(lockRecord *lr) { lockSet *ls; epicsSpinLock(lr->spin); ls = lr->plockSet; dbLockIncRef(ls); epicsSpinUnlock(lr->spin); return ls; } unsigned long dbLockGetLockId(dbCommon *precord) { unsigned long id=0; epicsSpinLock(precord->lset->spin); id = precord->lset->plockSet->id; epicsSpinUnlock(precord->lset->spin); return id; } void dbScanLock(dbCommon *precord) { int cnt; lockRecord * const lr = precord->lset; lockSet *ls; assert(lr); ls = dbLockGetRef(lr); assert(epicsAtomicGetIntT(&ls->refcount)>0); retry: epicsMutexMustLock(ls->lock); epicsSpinLock(lr->spin); if(ls!=lr->plockSet) { /* oops, collided with recompute. * take a reference to the new lockSet. */ lockSet *ls2 = lr->plockSet; int newcnt = epicsAtomicIncrIntT(&ls2->refcount); assert(newcnt>=2); /* at least lockRecord and us */ epicsSpinUnlock(lr->spin); epicsMutexUnlock(ls->lock); dbLockDecRef(ls); ls = ls2; goto retry; } epicsSpinUnlock(lr->spin); /* Release reference taken within this * function. The count will *never* fall to zero * as the lockRecords can't be changed while * we hold the lock. */ cnt = epicsAtomicDecrIntT(&ls->refcount); assert(cnt>0); #ifdef LOCKSET_DEBUG if(ls->owner) { assert(ls->owner==epicsThreadGetIdSelf()); assert(ls->ownercount>=1); ls->ownercount++; } else { assert(ls->ownercount==0); ls->owner = epicsThreadGetIdSelf(); ls->ownercount = 1; } #endif } void dbScanUnlock(dbCommon *precord) { lockSet *ls = precord->lset->plockSet; dbLockIncRef(ls); #ifdef LOCKSET_DEBUG assert(ls->owner==epicsThreadGetIdSelf()); assert(ls->ownercount>=1); ls->ownercount--; if(ls->ownercount==0) ls->owner = NULL; #endif epicsMutexUnlock(ls->lock); dbLockDecRef(ls); } static int lrrcompare(const void *rawA, const void *rawB) { const lockRecordRef *refA=rawA, *refB=rawB; const lockSet *A=refA->plockSet, *B=refB->plockSet; if(!A && !B) return 0; /* NULL == NULL */ else if(!A) return 1; /* NULL > !NULL */ else if(!B) return -1; /* !NULL < NULL */ else if(A < B) return -1; else if(A > B) return 1; else return 0; } /* Call w/ update=1 before locking to update cached lockSet entries. * Call w/ update=0 after locking to verify that lockRecord weren't updated */ static int dbLockUpdateRefs(dbLocker *locker, int update) { int changed = 0; size_t i, nlock = locker->maxrefs; #ifndef LOCKSET_NOCNT const size_t recomp = epicsAtomicGetSizeT(&recomputeCnt); if(locker->recomp!=recomp) { #endif /* some lockset recompute happened. * must re-check our references. */ for(i=0; irefs[i]; lockSet *oldref = NULL; if(!ref->plr) { /* this lockRecord slot not used */ assert(!ref->plockSet); continue; } epicsSpinLock(ref->plr->spin); if(ref->plockSet!=ref->plr->plockSet) { changed = 1; if(update) { /* exchange saved lockSet reference */ oldref = ref->plockSet; /* will be NULL on first iteration */ ref->plockSet = ref->plr->plockSet; dbLockIncRef(ref->plockSet); } } epicsSpinUnlock(ref->plr->spin); if(oldref) dbLockDecRef(oldref); if(!update && changed) return changed; } #ifndef LOCKSET_NOCNT /* Use the value captured before we started. * If it has changed in the intrim we will catch this later * during the update==0 pass (which triggers a re-try) */ if(update) locker->recomp = recomp; } #endif if(changed && update) { qsort(locker->refs, nlock, sizeof(lockRecordRef), &lrrcompare); } return changed; } void dbLockerPrepare(struct dbLocker *locker, struct dbCommon * const *precs, size_t nrecs) { size_t i; locker->maxrefs = nrecs; /* intentionally spoil the recomp count to ensure that * references will be updated this first time */ #ifndef LOCKSET_NOCNT locker->recomp = epicsAtomicGetSizeT(&recomputeCnt)-1; #endif for(i=0; irefs[i].plr = precs[i] ? precs[i]->lset : NULL; } /* acquire a reference to all lockRecords */ dbLockUpdateRefs(locker, 1); } dbLocker *dbLockerAlloc(dbCommon * const *precs, size_t nrecs, unsigned int flags) { size_t Nextra = nrecs>DBLOCKER_NALLOC ? nrecs-DBLOCKER_NALLOC : 0; dbLocker *locker = calloc(1, sizeof(*locker)+Nextra*sizeof(lockRecordRef)); if(locker) dbLockerPrepare(locker, precs, nrecs); return locker; } void dbLockerFinalize(dbLocker *locker) { size_t i; assert(ellCount(&locker->locked)==0); /* release references taken in dbLockUpdateRefs() */ for(i=0; imaxrefs; i++) { if(locker->refs[i].plockSet) dbLockDecRef(locker->refs[i].plockSet); } } void dbLockerFree(dbLocker *locker) { dbLockerFinalize(locker); free(locker); } /* Lock the given list of records. * This function modifies its arguments. */ void dbScanLockMany(dbLocker* locker) { size_t i, nlock = locker->maxrefs; lockSet *plock; #ifdef LOCKSET_DEBUG const epicsThreadId myself = epicsThreadGetIdSelf(); #endif if(ellCount(&locker->locked)!=0) { cantProceed("dbScanLockMany(%p) already locked. Recursive locking not allowed", locker); return; } retry: assert(ellCount(&locker->locked)==0); dbLockUpdateRefs(locker, 1); for(i=0, plock=NULL; irefs[i]; /* skip duplicates (same lockSet * referenced by more than one lockRecord). * Sorting will group these together. */ if(!ref->plr || (plock && plock==ref->plockSet)) continue; plock = ref->plockSet; epicsMutexMustLock(plock->lock); assert(plock->ownerlocker==NULL); plock->ownerlocker = locker; ellAdd(&locker->locked, &plock->lockernode); /* An extra ref for the locked list */ dbLockIncRef(plock); #ifdef LOCKSET_DEBUG if(plock->owner) { if(plock->owner!=myself || plock->ownercount<1) { errlogPrintf("dbScanLockMany(%p) ownership violation %p (%p) %u\n", locker, plock->owner, myself, plock->ownercount); cantProceed(NULL); } plock->ownercount++; } else { assert(plock->ownercount==0); plock->owner = myself; plock->ownercount = 1; } #endif } if(dbLockUpdateRefs(locker, 0)) { /* oops, collided with recompute */ dbScanUnlockMany(locker); goto retry; } if(nlock!=0 && ellCount(&locker->locked)<=0) { /* if we have at least one lockRecord, then we will always lock * at least its present lockSet */ errlogPrintf("dbScanLockMany(%p) didn't lock anything\n", locker); cantProceed(NULL); } } void dbScanUnlockMany(dbLocker* locker) { ELLNODE *cur; #ifdef LOCKSET_DEBUG const epicsThreadId myself = epicsThreadGetIdSelf(); #endif while((cur=ellGet(&locker->locked))!=NULL) { lockSet *plock = CONTAINER(cur, lockSet, lockernode); assert(plock->ownerlocker==locker); plock->ownerlocker = NULL; #ifdef LOCKSET_DEBUG assert(plock->owner==myself); assert(plock->ownercount>=1); plock->ownercount--; if(plock->ownercount==0) plock->owner = NULL; #endif epicsMutexUnlock(plock->lock); /* release ref for locked list */ dbLockDecRef(plock); } } typedef int (*reciter)(void*, DBENTRY*); static int forEachRecord(void *priv, dbBase *pdbbase, reciter fn) { long status; int ret = 0; DBENTRY dbentry; dbInitEntry(pdbbase,&dbentry); status = dbFirstRecordType(&dbentry); while(!status) { status = dbFirstRecord(&dbentry); while(!status) { /* skip alias names */ if(!dbentry.precnode->recordname[0] || dbentry.precnode->flags & DBRN_FLAGS_ISALIAS) { /* skip */ } else { ret = fn(priv, &dbentry); if(ret) goto done; } status = dbNextRecord(&dbentry); } status = dbNextRecordType(&dbentry); } done: dbFinishEntry(&dbentry); return ret; } static int createLockRecord(void* junk, DBENTRY* pdbentry) { dbCommon *prec = pdbentry->precnode->precord; lockRecord *lrec; assert(!prec->lset); /* TODO: one allocation for all records? */ lrec = callocMustSucceed(1, sizeof(*lrec), "lockRecord"); lrec->spin = epicsSpinCreate(); if(!lrec->spin) cantProceed("no memory for spinlock in lockRecord"); lrec->precord = prec; prec->lset = lrec; prec->lset->plockSet = makeSet(); ellAdd(&prec->lset->plockSet->lockRecordList, &prec->lset->node); return 0; } void dbLockInitRecords(dbBase *pdbbase) { epicsThreadOnce(&dbLockOnceInit, &dbLockOnce, NULL); /* create all lockRecords and lockSets */ forEachRecord(NULL, pdbbase, &createLockRecord); } static int freeLockRecord(void* junk, DBENTRY* pdbentry) { dbCommon *prec = pdbentry->precnode->precord; lockRecord *lr = prec->lset; lockSet *ls = lr->plockSet; prec->lset = NULL; lr->precord = NULL; assert(ls->refcount>0); assert(ellCount(&ls->lockRecordList)>0); ellDelete(&ls->lockRecordList, &lr->node); dbLockDecRef(ls); epicsSpinDestroy(lr->spin); free(lr); return 0; } void dbLockCleanupRecords(dbBase *pdbbase) { #ifndef LOCKSET_NOFREE ELLNODE *cur; #endif epicsThreadOnce(&dbLockOnceInit, &dbLockOnce, NULL); forEachRecord(NULL, pdbbase, &freeLockRecord); if(ellCount(&lockSetsActive)) { printf("Warning: dbLockCleanupRecords() leaking lockSets\n"); dblsr(NULL,2); } #ifndef LOCKSET_NOFREE while((cur=ellGet(&lockSetsFree))!=NULL) { lockSet *ls = (lockSet*)cur; assert(ls->refcount==0); assert(ellCount(&ls->lockRecordList)==0); epicsMutexDestroy(ls->lock); free(ls); } #endif } /* Called in two modes. * During dbLockInitRecords w/ locker==NULL, then no mutex are locked. * After dbLockInitRecords w/ locker!=NULL, then * the caller must lock both pfirst and psecond. * * Assumes that pfirst has been modified * to link to psecond. */ void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) { ELLNODE *cur; lockSet *A=pfirst->lset->plockSet, *B=psecond->lset->plockSet; int Nb; #ifdef LOCKSET_DEBUG const epicsThreadId myself = epicsThreadGetIdSelf(); #endif assert(A && B); #ifdef LOCKSET_DEBUG if(locker && (A->owner!=myself || B->owner!=myself)) { errlogPrintf("dbLockSetMerge(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n", locker, pfirst->name, psecond->name, A->owner, B->owner, myself); cantProceed(NULL); } #endif if(locker && (A->ownerlocker!=locker || B->ownerlocker!=locker)) { errlogPrintf("dbLockSetMerge(%p,\"%s\",\"%s\") locker ownership violation %p %p (%p)\n", locker, pfirst->name, psecond->name, A->ownerlocker, B->ownerlocker, locker); cantProceed(NULL); } if(A==B) return; /* already in the same lockSet */ Nb = ellCount(&B->lockRecordList); assert(Nb>0); /* move all records from B to A */ while((cur=ellGet(&B->lockRecordList))!=NULL) { lockRecord *lr = CONTAINER(cur, lockRecord, node); assert(lr->plockSet==B); ellAdd(&A->lockRecordList, cur); epicsSpinLock(lr->spin); lr->plockSet = A; #ifndef LOCKSET_NOCNT epicsAtomicIncrSizeT(&recomputeCnt); #endif epicsSpinUnlock(lr->spin); } /* there are at minimum, 1 ref for each lockRecord, * and one for the locker's locked list * (and perhaps another for its refs cache) */ assert(epicsAtomicGetIntT(&B->refcount)>=Nb+(locker?1:0)); /* update ref counters. for lockRecords */ epicsAtomicAddIntT(&A->refcount, Nb); epicsAtomicAddIntT(&B->refcount, -Nb+1); /* drop all but one ref, see below */ if(locker) { /* at least two refs, possibly three, remain. * # One ref from above * # locker->locked list, which is released now. * # locker->refs array, assuming it is directly referenced, * and not added as the result of a dbLockSetSplit, * which will be cleaned when the locker is free'd (not here). */ #ifdef LOCKSET_DEBUG B->owner = NULL; B->ownercount = 0; #endif assert(B->ownerlocker==locker); ellDelete(&locker->locked, &B->lockernode); B->ownerlocker = NULL; epicsAtomicDecrIntT(&B->refcount); epicsMutexUnlock(B->lock); } dbLockDecRef(B); /* last ref we hold */ assert(A==psecond->lset->plockSet); } /* recompute assuming a link from pfirst to psecond * may have been removed. * pfirst and psecond must currently be in the same lockset, * which the caller must lock before calling this function. * If a new lockset is created, then it is locked * when this function returns. */ void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) { lockSet *ls = pfirst->lset->plockSet; ELLLIST toInspect, newLS; #ifdef LOCKSET_DEBUG const epicsThreadId myself = epicsThreadGetIdSelf(); #endif #ifdef LOCKSET_DEBUG if(ls->owner!=myself || psecond->lset->plockSet->owner!=myself) { errlogPrintf("dbLockSetSplit(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n", locker, pfirst->name, psecond->name, ls->owner, psecond->lset->plockSet->owner, myself); cantProceed(NULL); } #endif /* lockset consistency violation */ if(ls!=psecond->lset->plockSet) { errlogPrintf("dbLockSetSplit(%p,\"%s\",\"%s\") consistency violation %p %p\n", locker, pfirst->name, psecond->name, pfirst->lset->plockSet, psecond->lset->plockSet); cantProceed(NULL); } if(pfirst==psecond) return; /* at least 1 ref for each lockRecord, * and one for the locker */ assert(epicsAtomicGetIntT(&ls->refcount)>=ellCount(&ls->lockRecordList)+1); ellInit(&toInspect); ellInit(&newLS); /* strategy is to start with psecond and do * a breadth first traversal until all records are * visited. If we encounter pfirst, then there * is no need to create a new lockset so we abort * early. */ ellAdd(&toInspect, &psecond->lset->compnode); psecond->lset->compflag = 1; { lockSet *splitset; ELLNODE *cur; while((cur=ellGet(&toInspect))!=NULL) { lockRecord *lr=CONTAINER(cur,lockRecord,compnode); dbCommon *prec=lr->precord; dbRecordType *rtype = prec->rdes; size_t i; ELLNODE *bcur; ellAdd(&newLS, cur); prec->lset->compflag = 2; /* Visit all the links originating from prec */ for(i=0; ino_links; i++) { dbFldDes *pdesc = rtype->papFldDes[rtype->link_ind[i]]; DBLINK *plink = (DBLINK*)((char*)prec + pdesc->offset); DBADDR *ptarget; lockRecord *lr; if(plink->type!=DB_LINK) continue; ptarget = plink->value.pv_link.pvt; lr = ptarget->precord->lset; assert(lr); if(lr->precord==pfirst) { /* so pfirst is still reachable from psecond, * no new lock set should be created. */ goto nosplit; } /* have we already visited this record? */ if(lr->compflag) continue; ellAdd(&toInspect, &lr->compnode); lr->compflag = 1; } /* Visit all links terminating at prec */ for(bcur=ellFirst(&prec->bklnk); bcur; bcur=ellNext(bcur)) { struct pv_link *plink1 = CONTAINER(bcur, struct pv_link, backlinknode); union value *plink2 = CONTAINER(plink1, union value, pv_link); DBLINK *plink = CONTAINER(plink2, DBLINK, value); lockRecord *lr = plink->precord->lset; /* plink->type==DB_LINK is implied. Only DB_LINKs are tracked from BKLNK */ if(lr->precord==pfirst) { goto nosplit; } if(lr->compflag) continue; ellAdd(&toInspect, &lr->compnode); lr->compflag = 1; } } /* All links involving psecond were traversed without finding * pfirst. So we must create a new lockset. * newLS contains the nodes which will * make up this new lockset. */ /* newLS will have at least psecond in it */ assert(ellCount(&newLS) > 0); /* If we didn't find pfirst, then it must be in the * original lockset, and not the new one */ assert(ellCount(&newLS) < ellCount(&ls->lockRecordList)); assert(ellCount(&newLS) < ls->refcount); splitset = makeSet(); /* reference for locker->locked */ epicsMutexMustLock(splitset->lock); assert(splitset->ownerlocker==NULL); ellAdd(&locker->locked, &splitset->lockernode); splitset->ownerlocker = locker; assert(splitset->refcount==1); #ifdef LOCKSET_DEBUG splitset->owner = ls->owner; splitset->ownercount = 1; assert(ls->ownercount==1); #endif while((cur=ellGet(&newLS))!=NULL) { lockRecord *lr=CONTAINER(cur,lockRecord,compnode); lr->compflag = 0; /* reset for next time */ assert(lr->plockSet == ls); ellDelete(&ls->lockRecordList, &lr->node); ellAdd(&splitset->lockRecordList, &lr->node); epicsSpinLock(lr->spin); lr->plockSet = splitset; #ifndef LOCKSET_NOCNT epicsAtomicIncrSizeT(&recomputeCnt); #endif epicsSpinUnlock(lr->spin); /* new lockSet is "live" at this point * as other threads may find it. */ } /* refcount of ls can't go to zero as the locker * holds at least one reference (its locked list) */ epicsAtomicAddIntT(&ls->refcount, -ellCount(&splitset->lockRecordList)); assert(ls->refcount>0); epicsAtomicAddIntT(&splitset->refcount, ellCount(&splitset->lockRecordList)); assert(splitset->refcount>=ellCount(&splitset->lockRecordList)+1); assert(psecond->lset->plockSet==splitset); /* must have refs from pfirst lockRecord, * and the locked list. */ assert(epicsAtomicGetIntT(&ls->refcount)>=2); return; } nosplit: { /* reset compflag for all nodes visited * during the aborted search */ ELLNODE *cur; while((cur=ellGet(&toInspect))!=NULL) { lockRecord *lr=CONTAINER(cur,lockRecord,compnode); lr->compflag = 0; } while((cur=ellGet(&newLS))!=NULL) { lockRecord *lr=CONTAINER(cur,lockRecord,compnode); lr->compflag = 0; } return; } } static char *msstring[4]={"NMS","MS","MSI","MSS"}; long dblsr(char *recordname,int level) { int link; DBENTRY dbentry; DBENTRY *pdbentry=&dbentry; long status; dbCommon *precord; lockSet *plockSet; lockRecord *plockRecord; dbRecordType *pdbRecordType; dbFldDes *pdbFldDes; DBLINK *plink; if (recordname && ((*recordname == '\0') || !strcmp(recordname,"*"))) recordname = NULL; if(recordname) { dbInitEntry(pdbbase,pdbentry); status = dbFindRecord(pdbentry,recordname); if(status) { printf("Record not found\n"); dbFinishEntry(pdbentry); return 0; } precord = pdbentry->precnode->precord; dbFinishEntry(pdbentry); plockRecord = precord->lset; if (!plockRecord) return 0; /* before iocInit */ plockSet = plockRecord->plockSet; } else { plockSet = (lockSet *)ellFirst(&lockSetsActive); } for( ; plockSet; plockSet = (lockSet *)ellNext(&plockSet->node)) { printf("Lock Set %lu %d members %d refs epicsMutexId %p\n", plockSet->id,ellCount(&plockSet->lockRecordList),plockSet->refcount,plockSet->lock); if(level==0) { if(recordname) break; continue; } for(plockRecord = (lockRecord *)ellFirst(&plockSet->lockRecordList); plockRecord; plockRecord = (lockRecord *)ellNext(&plockRecord->node)) { precord = plockRecord->precord; pdbRecordType = precord->rdes; printf("%s\n",precord->name); if(level<=1) continue; for(link=0; (linkno_links) ; link++) { DBADDR *pdbAddr; 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); printf("\t%s",pdbFldDes->name); if(pdbFldDes->field_type==DBF_INLINK) { printf("\t INLINK"); } else if(pdbFldDes->field_type==DBF_OUTLINK) { printf("\tOUTLINK"); } else if(pdbFldDes->field_type==DBF_FWDLINK) { printf("\tFWDLINK"); } printf(" %s %s", ((plink->value.pv_link.pvlMask&pvlOptPP)?" PP":"NPP"), msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]); printf(" %s\n",pdbAddr->precord->name); } } if(recordname) break; } return 0; } long dbLockShowLocked(int level) { int indListType; lockSet *plockSet; printf("Active lockSets: %d\n", ellCount(&lockSetsActive)); #ifndef LOCKSET_NOFREE printf("Free lockSets: %d\n", ellCount(&lockSetsFree)); #endif /*Even if failure on lockSetModifyLock will continue */ for(indListType=0; indListType <= 1; ++indListType) { plockSet = (lockSet *)ellFirst(&lockSetsActive); if(plockSet) { if (indListType==0) printf("listTypeScanLock\n"); else printf("listTypeRecordLock\n"); } while(plockSet) { epicsMutexLockStatus status; status = epicsMutexTryLock(plockSet->lock); if(status==epicsMutexLockOK) epicsMutexUnlock(plockSet->lock); if(status!=epicsMutexLockOK || indListType==1) { epicsMutexShow(plockSet->lock,level); } plockSet = (lockSet *)ellNext(&plockSet->node); } } return 0; } int * dbLockSetAddrTrace(dbCommon *precord) { lockRecord *plockRecord = precord->lset; lockSet *plockSet = plockRecord->plockSet; return(&plockSet->trace); } base-7.0.3.1/modules/database/src/ioc/db/dbLock.h0000664000577000060420000000366613557101274020137 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* dbLock.h */ /* Author: Marty Kraimer Date: 12MAR96 */ #ifndef INCdbLockh #define INCdbLockh #include #include "ellLib.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif struct dbCommon; struct dbBase; typedef struct dbLocker dbLocker; epicsShareFunc void dbScanLock(struct dbCommon *precord); epicsShareFunc void dbScanUnlock(struct dbCommon *precord); epicsShareFunc dbLocker *dbLockerAlloc(struct dbCommon * const *precs, size_t nrecs, unsigned int flags); epicsShareFunc void dbLockerFree(dbLocker *); epicsShareFunc void dbScanLockMany(dbLocker*); epicsShareFunc void dbScanUnlockMany(dbLocker*); epicsShareFunc unsigned long dbLockGetLockId( struct dbCommon *precord); epicsShareFunc void dbLockInitRecords(struct dbBase *pdbbase); epicsShareFunc void dbLockCleanupRecords(struct dbBase *pdbbase); /* Lock Set Report */ epicsShareFunc long dblsr(char *recordname,int level); /* If recordname NULL then all records*/ /* level = (0,1,2) (lock set state, + recordname, +DB links) */ epicsShareFunc long dbLockShowLocked(int level); /*KLUDGE to support field TPRO*/ epicsShareFunc int * dbLockSetAddrTrace(struct dbCommon *precord); /* debugging */ epicsShareFunc unsigned long dbLockGetRefs(struct dbCommon*); epicsShareFunc unsigned long dbLockCountSets(void); #ifdef __cplusplus } #endif #endif /*INCdbLockh*/ base-7.0.3.1/modules/database/src/ioc/db/dbLockPvt.h0000664000577000060420000000644213557101274020624 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Assoc., as Operator of Brookhaven * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef DBLOCKPVT_H #define DBLOCKPVT_H #include "dbLock.h" #include "epicsSpin.h" /* Define to enable additional error checking */ #undef LOCKSET_DEBUG /* Define to disable the free list for lockSets */ #undef LOCKSET_NOFREE /* Define to disable use of recomputeCnt optimization */ #undef LOCKSET_NOCNT /* except for refcount (and lock), all members of dbLockSet * are guarded by its lock. */ typedef struct dbLockSet { ELLNODE node; ELLLIST lockRecordList; /* holds lockRecord::node */ epicsMutexId lock; unsigned long id; int refcount; #ifdef LOCKSET_DEBUG int ownercount; epicsThreadId owner; #endif dbLocker *ownerlocker; ELLNODE lockernode; int trace; /*For field TPRO*/ } lockSet; struct lockRecord; /* dbCommon.LSET is a plockRecord. * Except for spin and plockSet, all members of lockRecord are guarded * by the present lockset lock. * plockSet is guarded by spin. */ typedef struct lockRecord { ELLNODE node; /* in lockSet::lockRecordList */ /* The association between lockRecord and lockSet * can only be changed while the lockSet is held, * and the lockRecord's spinlock is held. * It may be read which either lock is held. */ lockSet *plockSet; /* the association between lockRecord and dbCommon never changes */ dbCommon *precord; epicsSpinId spin; /* temp used during lockset split. * lockSet must be locked for access */ ELLNODE compnode; unsigned int compflag; } lockRecord; typedef struct { lockRecord *plr; /* the last lock found associated with the ref. * not stable unless lock is locked, or ref spin * is locked. */ lockSet *plockSet; } lockRecordRef; #define DBLOCKER_NALLOC 2 /* a dbLocker can only be used by a single thread. */ struct dbLocker { ELLLIST locked; #ifndef LOCKSET_NOCNT size_t recomp; /* snapshot of recomputeCnt when refs[] cache updated */ #endif size_t maxrefs; lockRecordRef refs[DBLOCKER_NALLOC]; /* actual length is maxrefs */ }; /* These are exported for testing only */ epicsShareFunc lockSet* dbLockGetRef(lockRecord *lr); /* lookup lockset and increment ref count */ epicsShareFunc void dbLockIncRef(lockSet* ls); epicsShareFunc void dbLockDecRef(lockSet *ls); /* Calling dbLockerPrepare directly is an internal * optimization used when dbLocker on the stack. * nrecs must be <=DBLOCKER_NALLOC. */ void dbLockerPrepare(struct dbLocker *locker, struct dbCommon * const *precs, size_t nrecs); void dbLockerFinalize(dbLocker *); void dbLockSetMerge(struct dbLocker *locker, struct dbCommon *pfirst, struct dbCommon *psecond); void dbLockSetSplit(struct dbLocker *locker, struct dbCommon *psource, struct dbCommon *psecond); #endif /* DBLOCKPVT_H */ base-7.0.3.1/modules/database/src/ioc/db/dbNotify.c0000664000577000060420000005233513557101274020507 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbNotify.c */ /* * Author: Marty Kraimer * Andrew Johnson * * Extracted from dbLink.c */ #include #include #include #include #include #include "cantProceed.h" #include "dbDefs.h" #include "ellLib.h" #include "epicsAssert.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "epicsString.h" #include "epicsThread.h" #include "epicsTime.h" #include "errlog.h" #include "errMdef.h" #define epicsExportSharedSymbols #include "callback.h" #include "dbAccessDefs.h" #include "dbBase.h" #include "dbChannel.h" #include "dbCommon.h" #include "dbFldTypes.h" #include "dbLock.h" #include "dbNotify.h" #include "dbScan.h" #include "dbStaticLib.h" #include "link.h" #include "recGbl.h" /*notify state values */ typedef enum { notifyNotActive, notifyWaitForRestart, notifyRestartCallbackRequested, notifyRestartInProgress, notifyProcessInProgress, notifyUserCallbackRequested, notifyUserCallbackActive } notifyState; /*structure attached to ppnr field of each record*/ typedef struct processNotifyRecord { ellCheckNode waitNode; ELLLIST restartList; /*list of processNotifys to restart*/ dbCommon *precord; } processNotifyRecord; #define MAGIC 0xfedc0123 typedef struct notifyPvt { ELLNODE node; /*For free list*/ long magic; short state; epicsCallback callback; ELLLIST waitList; /*list of records for current processNotify*/ short cancelWait; short userCallbackWait; epicsEventId cancelEvent; epicsEventId userCallbackEvent; } notifyPvt; /* processNotify groups can span locksets if links are dynamically modified*/ /* Thus a global lock is taken while processNotify fields are accessed */ typedef struct notifyGlobal { epicsMutexId lock; ELLLIST freeList; } notifyGlobal; static notifyGlobal *pnotifyGlobal = 0; static void notifyCallback(epicsCallback *pcallback); #define ellSafeAdd(list,listnode) \ { \ assert((listnode)->isOnList==0); \ ellAdd((list),&((listnode)->node)); \ (listnode)->isOnList=1; \ } #define ellSafeDelete(list,listnode) \ { \ assert((listnode)->isOnList); \ ellDelete((list),&((listnode)->node)); \ (listnode)->isOnList=0; \ } static void notifyFree(void *raw) { notifyPvt *pnotifyPvt = raw; assert(pnotifyPvt->magic==MAGIC); epicsEventDestroy(pnotifyPvt->cancelEvent); epicsEventDestroy(pnotifyPvt->userCallbackEvent); free(pnotifyPvt); } static void notifyInit(processNotify *ppn) { notifyPvt *pnotifyPvt; pnotifyPvt = (notifyPvt *) ellFirst(&pnotifyGlobal->freeList); if (pnotifyPvt) { ellDelete(&pnotifyGlobal->freeList, &pnotifyPvt->node); } else { pnotifyPvt = dbCalloc(1,sizeof(notifyPvt)); pnotifyPvt->cancelEvent = epicsEventCreate(epicsEventEmpty); pnotifyPvt->userCallbackEvent = epicsEventCreate(epicsEventEmpty); pnotifyPvt->magic = MAGIC; pnotifyPvt->state = notifyNotActive; } pnotifyPvt->state = notifyNotActive; callbackSetCallback(notifyCallback,&pnotifyPvt->callback); callbackSetUser(ppn,&pnotifyPvt->callback); callbackSetPriority(priorityLow,&pnotifyPvt->callback); ellInit(&pnotifyPvt->waitList); ppn->status = notifyOK; ppn->wasProcessed = 0; pnotifyPvt->state = notifyNotActive; pnotifyPvt->cancelWait = pnotifyPvt->userCallbackWait = 0; ppn->pnotifyPvt = pnotifyPvt; } static void notifyCleanup(processNotify *ppn) { notifyPvt *pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; pnotifyPvt->state = notifyNotActive; ellAdd(&pnotifyGlobal->freeList, &pnotifyPvt->node); ppn->pnotifyPvt = 0; } static void restartCheck(processNotifyRecord *ppnr) { dbCommon *precord = ppnr->precord; processNotify *pfirst; notifyPvt *pnotifyPvt; assert(precord->ppn); pfirst = (processNotify *) ellFirst(&ppnr->restartList); if (!pfirst) { precord->ppn = 0; return; } pnotifyPvt = (notifyPvt *) pfirst->pnotifyPvt; assert(pnotifyPvt->state == notifyWaitForRestart); /* remove pfirst from restartList */ ellSafeDelete(&ppnr->restartList, &pfirst->restartNode); /*make pfirst owner of the record*/ precord->ppn = pfirst; /* request callback for pfirst */ pnotifyPvt->state = notifyRestartCallbackRequested; callbackRequest(&pnotifyPvt->callback); } static void callDone(dbCommon *precord, processNotify *ppn) { notifyPvt *pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; epicsMutexUnlock(pnotifyGlobal->lock); if (ppn->requestType == processGetRequest || ppn->requestType == putProcessGetRequest) { ppn->getCallback(ppn, getFieldType); } dbScanUnlock(precord); ppn->doneCallback(ppn); epicsMutexMustLock(pnotifyGlobal->lock); if (pnotifyPvt->cancelWait && pnotifyPvt->userCallbackWait) { errlogPrintf("%s processNotify: both cancelWait and userCallbackWait true." "This is illegal\n", precord->name); pnotifyPvt->cancelWait = pnotifyPvt->userCallbackWait = 0; } if (!pnotifyPvt->cancelWait && !pnotifyPvt->userCallbackWait) { notifyCleanup(ppn); epicsMutexUnlock(pnotifyGlobal->lock); return; } if (pnotifyPvt->cancelWait) { pnotifyPvt->cancelWait = 0; epicsEventSignal(pnotifyPvt->cancelEvent); epicsMutexUnlock(pnotifyGlobal->lock); return; } assert(pnotifyPvt->userCallbackWait); pnotifyPvt->userCallbackWait = 0; epicsEventSignal(pnotifyPvt->userCallbackEvent); epicsMutexUnlock(pnotifyGlobal->lock); return; } static void processNotifyCommon(processNotify *ppn, dbCommon *precord, int first) { notifyPvt *pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; int didPut = 0; int doProcess = 0; if (precord->ppn && pnotifyPvt->state != notifyRestartCallbackRequested) { /* Another processNotify owns the record */ pnotifyPvt->state = notifyWaitForRestart; ellSafeAdd(&precord->ppnr->restartList, &ppn->restartNode); epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); return; } else if (precord->ppn) { assert(precord->ppn == ppn); assert(pnotifyPvt->state == notifyRestartCallbackRequested); } if (precord->pact) { precord->ppn = ppn; ellSafeAdd(&pnotifyPvt->waitList, &precord->ppnr->waitNode); pnotifyPvt->state = notifyRestartInProgress; epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); return; } if (ppn->requestType == putProcessRequest || ppn->requestType == putProcessGetRequest) { /* Check if puts disabled */ if (precord->disp && (dbChannelField(ppn->chan) != (void *) &precord->disp)) { ppn->putCallback(ppn, putDisabledType); } else { didPut = ppn->putCallback(ppn, putType); } } /* Check if dbProcess should be called */ if (didPut && ((dbChannelField(ppn->chan) == (void *) &precord->proc) || (dbChannelFldDes(ppn->chan)->process_passive && precord->scan == 0))) doProcess = 1; else if (ppn->requestType == processGetRequest && precord->scan == 0) doProcess = 1; if (doProcess) { if (first) { precord->putf = TRUE; } ppn->wasProcessed = 1; precord->ppn = ppn; ellSafeAdd(&pnotifyPvt->waitList, &precord->ppnr->waitNode); pnotifyPvt->state = notifyProcessInProgress; epicsMutexUnlock(pnotifyGlobal->lock); dbProcess(precord); dbScanUnlock(precord); return; } if (pnotifyPvt->state == notifyRestartCallbackRequested) { restartCheck(precord->ppnr); } pnotifyPvt->state = notifyUserCallbackActive; assert(precord->ppn!=ppn); callDone(precord, ppn); } static void notifyCallback(epicsCallback *pcallback) { processNotify *ppn = NULL; dbCommon *precord; notifyPvt *pnotifyPvt; callbackGetUser(ppn,pcallback); pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; precord = dbChannelRecord(ppn->chan); dbScanLock(precord); epicsMutexMustLock(pnotifyGlobal->lock); assert(precord->ppnr); assert(pnotifyPvt->state == notifyRestartCallbackRequested || pnotifyPvt->state == notifyUserCallbackRequested); assert(ellCount(&pnotifyPvt->waitList) == 0); if (pnotifyPvt->cancelWait) { if (pnotifyPvt->state == notifyRestartCallbackRequested) { restartCheck(precord->ppnr); } epicsEventSignal(pnotifyPvt->cancelEvent); epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); return; } if(pnotifyPvt->state == notifyRestartCallbackRequested) { processNotifyCommon(ppn, precord, 0); return; } /* All done. Clean up and call userCallback */ pnotifyPvt->state = notifyUserCallbackActive; assert(precord->ppn!=ppn); callDone(precord, ppn); } void dbProcessNotifyExit(void) { ellFree2(&pnotifyGlobal->freeList, ¬ifyFree); epicsMutexDestroy(pnotifyGlobal->lock); free(pnotifyGlobal); pnotifyGlobal = NULL; } void dbProcessNotifyInit(void) { if (pnotifyGlobal) return; pnotifyGlobal = dbCalloc(1,sizeof(notifyGlobal)); pnotifyGlobal->lock = epicsMutexMustCreate(); ellInit(&pnotifyGlobal->freeList); } void dbProcessNotify(processNotify *ppn) { struct dbChannel *chan = ppn->chan; dbCommon *precord = dbChannelRecord(chan); short dbfType = dbChannelFieldType(chan); notifyPvt *pnotifyPvt; /* Must handle DBF_XXXLINKs as special case. * Only dbPutField will change link fields. * Also the record is not processed as a result */ ppn->status = notifyOK; ppn->wasProcessed = 0; if (dbfType>=DBF_INLINK && dbfType<=DBF_FWDLINK) { if (ppn->requestType == putProcessRequest || ppn->requestType == putProcessGetRequest) { /* Check if puts disabled */ if (precord->disp && (dbChannelField(ppn->chan) != (void *) &precord->disp)) { ppn->putCallback(ppn, putDisabledType); } else { ppn->putCallback(ppn, putFieldType); } } if (ppn->requestType == processGetRequest || ppn->requestType == putProcessGetRequest) { ppn->getCallback(ppn, getFieldType); } ppn->doneCallback(ppn); return; } dbScanLock(precord); epicsMutexMustLock(pnotifyGlobal->lock); pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; if (pnotifyPvt && (pnotifyPvt->magic != MAGIC)) { printf("dbPutNotify:pnotifyPvt was not initialized\n"); pnotifyPvt = 0; } if (pnotifyPvt) { assert(pnotifyPvt->state == notifyUserCallbackActive); pnotifyPvt->userCallbackWait = 1; epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); epicsEventWait(pnotifyPvt->userCallbackEvent); dbScanLock(precord); epicsMutexMustLock(pnotifyGlobal->lock); notifyCleanup(ppn); } pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; assert(!pnotifyPvt); notifyInit(ppn); pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; if (!precord->ppnr) { /* make sure record has a processNotifyRecord*/ precord->ppnr = dbCalloc(1, sizeof(processNotifyRecord)); precord->ppnr->precord = precord; ellInit(&precord->ppnr->restartList); } processNotifyCommon(ppn, precord, 1); } void dbNotifyCancel(processNotify *ppn) { dbCommon *precord = dbChannelRecord(ppn->chan); notifyState state; notifyPvt *pnotifyPvt; dbScanLock(precord); epicsMutexMustLock(pnotifyGlobal->lock); ppn->status = notifyCanceled; pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; if (!pnotifyPvt || pnotifyPvt->state == notifyNotActive) { epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); return; } state = pnotifyPvt->state; switch (state) { case notifyUserCallbackRequested: case notifyRestartCallbackRequested: case notifyUserCallbackActive: /* Callback is scheduled or active, wait for it to complete */ pnotifyPvt->cancelWait = 1; epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); epicsEventWait(pnotifyPvt->cancelEvent); epicsMutexMustLock(pnotifyGlobal->lock); notifyCleanup(ppn); epicsMutexUnlock(pnotifyGlobal->lock); return; case notifyNotActive: break; case notifyWaitForRestart: assert(precord->ppn); assert(precord->ppn!=ppn); ellSafeDelete(&precord->ppnr->restartList,&ppn->restartNode); break; case notifyRestartInProgress: case notifyProcessInProgress: { /*Take all records out of wait list */ processNotifyRecord *ppnrWait; while ((ppnrWait = (processNotifyRecord *) ellFirst(&pnotifyPvt->waitList))) { ellSafeDelete(&pnotifyPvt->waitList, &ppnrWait->waitNode); restartCheck(ppnrWait); } } if (precord->ppn == ppn) restartCheck(precord->ppnr); break; default: printf("dbNotify: illegal state for notifyCallback\n"); } pnotifyPvt->state = notifyNotActive; notifyCleanup(ppn); epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); } void dbNotifyCompletion(dbCommon *precord) { processNotify *ppn = precord->ppn; notifyPvt *pnotifyPvt; epicsMutexMustLock(pnotifyGlobal->lock); assert(ppn); assert(precord->ppnr); pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; if (pnotifyPvt->state != notifyRestartInProgress && pnotifyPvt->state != notifyProcessInProgress) { epicsMutexUnlock(pnotifyGlobal->lock); return; } ellSafeDelete(&pnotifyPvt->waitList, &precord->ppnr->waitNode); if ((ellCount(&pnotifyPvt->waitList) != 0)) { restartCheck(precord->ppnr); } else if (pnotifyPvt->state == notifyProcessInProgress) { pnotifyPvt->state = notifyUserCallbackRequested; restartCheck(precord->ppnr); callbackRequest(&pnotifyPvt->callback); } else if(pnotifyPvt->state == notifyRestartInProgress) { pnotifyPvt->state = notifyRestartCallbackRequested; callbackRequest(&pnotifyPvt->callback); } else { cantProceed("dbNotifyCompletion illegal state"); } epicsMutexUnlock(pnotifyGlobal->lock); } void dbNotifyAdd(dbCommon *pfrom, dbCommon *pto) { processNotify *ppn = pfrom->ppn; notifyPvt *pnotifyPvt; if (pto->pact) return; /*if active it will not be processed*/ epicsMutexMustLock(pnotifyGlobal->lock); if (!pto->ppnr) {/* make sure record has a processNotifyRecord*/ pto->ppnr = dbCalloc(1, sizeof(processNotifyRecord)); pto->ppnr->precord = pto; ellInit(&pto->ppnr->restartList); } assert(ppn); pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; if (!pto->ppn && (pnotifyPvt->state == notifyProcessInProgress) && (pto != dbChannelRecord(ppn->chan))) { notifyPvt *pnotifyPvt; pto->ppn = pfrom->ppn; pnotifyPvt = (notifyPvt *) pfrom->ppn->pnotifyPvt; ellSafeAdd(&pnotifyPvt->waitList, &pto->ppnr->waitNode); } epicsMutexUnlock(pnotifyGlobal->lock); } typedef struct tpnInfo { epicsEventId callbackDone; processNotify *ppn; char buffer[80]; } tpnInfo; static int putCallback(processNotify *ppn, notifyPutType type) { tpnInfo *ptpnInfo = (tpnInfo *) ppn->usrPvt; int status = 0; if (ppn->status == notifyCanceled) return 0; ppn->status = notifyOK; switch (type) { case putDisabledType: ppn->status = notifyError; return 0; case putFieldType: status = dbChannelPutField(ppn->chan, DBR_STRING, ptpnInfo->buffer, 1); break; case putType: status = dbChannelPut(ppn->chan, DBR_STRING, ptpnInfo->buffer, 1); break; } if (status) ppn->status = notifyError; return 1; } static void getCallback(processNotify *ppn,notifyGetType type) { tpnInfo *ptpnInfo = (tpnInfo *)ppn->usrPvt; int status = 0; long no_elements = 1; long options = 0; if(ppn->status==notifyCanceled) { printf("dbtpn:getCallback notifyCanceled\n"); return; } switch(type) { case getFieldType: status = dbChannelGetField(ppn->chan, DBR_STRING, ptpnInfo->buffer, &options, &no_elements, 0); break; case getType: status = dbChannelGet(ppn->chan, DBR_STRING, ptpnInfo->buffer, &options, &no_elements, 0); break; } if (status) { ppn->status = notifyError; printf("dbtpn:getCallback error\n"); } else { printf("dbtpn:getCallback value %s\n", ptpnInfo->buffer); } } static void doneCallback(processNotify *ppn) { notifyStatus status = ppn->status; tpnInfo *ptpnInfo = (tpnInfo *) ppn->usrPvt; const char *pname = dbChannelRecord(ppn->chan)->name; if (status == 0) printf("dbtpnCallback: success record=%s\n", pname); else printf("%s dbtpnCallback processNotify.status %d\n", pname, (int) status); epicsEventSignal(ptpnInfo->callbackDone); } static void tpnThread(void *pvt) { tpnInfo *ptpnInfo = (tpnInfo *) pvt; processNotify *ppn = (processNotify *) ptpnInfo->ppn; dbProcessNotify(ppn); epicsEventMustWait(ptpnInfo->callbackDone); dbNotifyCancel(ppn); epicsEventDestroy(ptpnInfo->callbackDone); dbChannelDelete(ppn->chan); free(ppn); free(ptpnInfo); } long dbtpn(char *pname, char *pvalue) { struct dbChannel *chan; tpnInfo *ptpnInfo; processNotify *ppn=NULL; if (!pname) { printf("Usage: dbtpn \"name\", \"value\"\n"); return -1; } chan = dbChannelCreate(pname); if (!chan) { printf("dbtpn: No such channel\n"); return -1; } ppn = dbCalloc(1, sizeof(processNotify)); ppn->requestType = pvalue ? putProcessRequest : processGetRequest; ppn->chan = chan; ppn->putCallback = putCallback; ppn->getCallback = getCallback; ppn->doneCallback = doneCallback; ptpnInfo = dbCalloc(1, sizeof(tpnInfo)); ptpnInfo->ppn = ppn; ptpnInfo->callbackDone = epicsEventCreate(epicsEventEmpty); strncpy(ptpnInfo->buffer, pvalue, 80); ptpnInfo->buffer[79] = 0; ppn->usrPvt = ptpnInfo; epicsThreadCreate("dbtpn", epicsThreadPriorityHigh, epicsThreadGetStackSize(epicsThreadStackMedium), tpnThread, ptpnInfo); return 0; } int dbNotifyDump(void) { epicsMutexLockStatus lockStatus; dbRecordType *pdbRecordType; processNotify *ppnRestart; processNotifyRecord *ppnr; int itry; for (itry = 0; itry < 100; itry++) { lockStatus = epicsMutexTryLock(pnotifyGlobal->lock); if (lockStatus == epicsMutexLockOK) break; epicsThreadSleep(.05); } for (pdbRecordType = (dbRecordType *) ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *) ellNext(&pdbRecordType->node)) { dbRecordNode *pdbRecordNode; for (pdbRecordNode = (dbRecordNode *) ellFirst(&pdbRecordType->recList); pdbRecordNode; pdbRecordNode = (dbRecordNode *) ellNext(&pdbRecordNode->node)) { dbCommon *precord = pdbRecordNode->precord; processNotify *ppn; notifyPvt *pnotifyPvt; if (!precord->name[0] || pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) continue; ppn = precord->ppn; if (!ppn || !precord->ppnr) continue; if (dbChannelRecord(precord->ppn->chan) != precord) continue; pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; printf("%s state %d ppn %p\n waitList\n", precord->name, pnotifyPvt->state, (void*) ppn); ppnr = (processNotifyRecord *) ellFirst(&pnotifyPvt->waitList); while (ppnr) { printf(" %s pact %d\n", ppnr->precord->name, ppnr->precord->pact); ppnr = (processNotifyRecord *) ellNext(&ppnr->waitNode.node); } ppnr = precord->ppnr; if (ppnr) { ppnRestart = (processNotify *)ellFirst( &precord->ppnr->restartList); if (ppnRestart) printf("%s restartList\n", precord->name); while (ppnRestart) { printf(" %s\n", dbChannelRecord(ppnRestart->chan)->name); ppnRestart = (processNotify *) ellNext( &ppnRestart->restartNode.node); } } } } if (lockStatus == epicsMutexLockOK) epicsMutexUnlock(pnotifyGlobal->lock); return 0; } base-7.0.3.1/modules/database/src/ioc/db/dbNotify.h0000664000577000060420000001371213557101274020510 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbNotify.h */ #ifndef INCdbNotifyh #define INCdbNotifyh #include "shareLib.h" #include "ellLib.h" #ifdef __cplusplus extern "C" { #endif struct dbCommon; struct processNotify; typedef struct ellCheckNode{ ELLNODE node; int isOnList; } ellCheckNode; typedef enum { processRequest, putProcessRequest, processGetRequest, putProcessGetRequest } notifyRequestType; typedef enum { putDisabledType, putFieldType, putType } notifyPutType; typedef enum { getFieldType, getType /* FIXME: Never used? */ } notifyGetType; typedef enum { notifyOK, notifyCanceled, notifyError, notifyPutDisabled } notifyStatus; typedef struct processNotify { /* following fields are for private use by dbNotify implementation */ ellCheckNode restartNode; void *pnotifyPvt; /* The following fields are set by dbNotify. */ notifyStatus status; int wasProcessed; /* (0,1) => (no,yes) */ /*The following members are set by user*/ notifyRequestType requestType; struct dbChannel *chan; /*dbChannel*/ int (*putCallback)(struct processNotify *,notifyPutType type); void (*getCallback)(struct processNotify *,notifyGetType type); void (*doneCallback)(struct processNotify *); void *usrPvt; /*for private use of user*/ } processNotify; /* dbProcessNotify and dbNotifyCancel are called by user*/ epicsShareFunc void dbProcessNotify(processNotify *pprocessNotify); epicsShareFunc void dbNotifyCancel(processNotify *pprocessNotify); /* dbProcessNotifyInit called by iocInit */ epicsShareFunc void dbProcessNotifyInit(void); epicsShareFunc void dbProcessNotifyExit(void); /*dbNotifyAdd called by dbScanPassive and dbScanLink*/ epicsShareFunc void dbNotifyAdd( struct dbCommon *pfrom,struct dbCommon *pto); /*dbNotifyCompletion called by recGblFwdLink or dbAccess*/ epicsShareFunc void dbNotifyCompletion(struct dbCommon *precord); /* db_put_process defined here since it requires dbNotify. * src_type is the old DBR type * This is called by a dbNotify putCallback that uses oldDbr types */ epicsShareFunc int db_put_process( processNotify *processNotify,notifyPutType type, int src_type,const void *psrc, int no_elements); /* dbtpn is test routine for dbNotify putProcessRequest */ epicsShareFunc long dbtpn(char *recordname,char *value); /* dbNotifyDump is an INVASIVE debug utility. Don't use this needlessly*/ epicsShareFunc int dbNotifyDump(void); /* This module provides code to handle process notify. * client code semantics are: * 1) The client code allocates storage for a processNotify structure. * This structure can be used for multiple calls to dbProcessNotify. * The client is responsible for setting the following fields : * requestType - The type of request. * chan - This is typically set via a call to dbChannelCreate. * putCallback - If requestType is putProcessRequest or putProcessGetRequest * getCallback - If request is processGetRequest or putProcessGetRequest * doneCallback - Must be set * usrPvt - For exclusive use of client. dbNotify does not access this field * 2) The client calls dbProcessNotify. * 3) putCallback is called after dbNotify has claimed the record instance * but before a potential process is requested. * The putCallback MUST issue the correct put request * specified by notifyPutType * 4) getCallback is called after a possible process is complete * (including asynchronous completion) but before dbNotify has * released the record. * The getCallback MUST issue the correct get request * specified by notifyGetType * 5) doneCallback is called when dbNotify has released the record. * The client can issue a new dbProcessNotify request from * doneCallback or anytime after doneCallback returns. * 6) The client can call dbNotifyCancel at any time. * If a dbProcessNotify is active, dbNotifyCancel will not return until * the dbNotifyRequest is actually canceled. The client must be prepared * for a callback to be called while dbNotifyCancel is active. * * dbProcessNotify handles the semantics of record locking and deciding * if a process request is issued and also calls the client callbacks. * * A process request is issued if any of the following is true. * 1) The requester has issued a processs request and record is passive. * 2) The requester is doing a put, the record is passive, and either * a) The field description is process passive. * b) The field is PROC. * 3) The requester has requested processGet and the record is passive. * * iocInit calls processNotifyInit. * * The other global routines (dbNotifyAdd and dbNotifyCompletion) are called by: * * dbAccess.c * dbScanPassive and dbScanLink * call dbNotifyAdd just before calling dbProcess * dbProcess * Calls dbNotifyCompletion if dbProcess does not call process * Unless pact is already true. * recGbl * recGblFwdLink calls dbNotifyCompletion * * Two fields in dbCommon are used for put notify. * ppn pointer to processNotify * If a record is part of a put notify group, * This field is the address of the associated processNotify. * As soon as a record completes processing the field is set NULL * ppnr pointer to processNotifyRecord, which is a private structure * owned by dbNotify. * dbNotify is reponsible for this structure. * */ #ifdef __cplusplus } #endif #endif /*INCdbNotifyh*/ base-7.0.3.1/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp0000664000577000060420000001574213557101274023043 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: * Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include #include #include #include "epicsMutex.h" #include "epicsEvent.h" #include "epicsTime.h" #include "tsFreeList.h" #include "errMdef.h" #include "errlog.h" #include "caerr.h" // this needs to be eliminated #include "db_access.h" // this needs to be eliminated #define epicsExportSharedSymbols #include "dbCAC.h" #include "dbChannelIO.h" #include "dbPutNotifyBlocker.h" dbPutNotifyBlocker::dbPutNotifyBlocker ( epicsMutex & mutexIn ) : mutex ( mutexIn ), pNotify ( 0 ), maxValueSize ( sizeof ( this->dbrScalarValue ) ) { memset ( & this->pn, '\0', sizeof ( this->pn ) ); memset ( & this->dbrScalarValue, '\0', sizeof ( this->dbrScalarValue ) ); this->pbuffer = & this->dbrScalarValue; } dbPutNotifyBlocker::~dbPutNotifyBlocker () { } void dbPutNotifyBlocker::destructor ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->cancel ( cbGuard, guard ); if ( this->maxValueSize > sizeof ( this->dbrScalarValue ) ) { char * pBuf = static_cast < char * > ( this->pbuffer ); delete [] pBuf; } this->~dbPutNotifyBlocker (); } void dbPutNotifyBlocker::cancel ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( this->pNotify ) { epicsGuardRelease < epicsMutex > unguard ( guard ); dbNotifyCancel ( &this->pn ); } this->pNotify = 0; this->block.signal (); } void dbPutNotifyBlocker::expandValueBuf ( epicsGuard < epicsMutex > & guard, unsigned long newSize ) { guard.assertIdenticalMutex ( this->mutex ); if ( this->maxValueSize < newSize ) { if ( this->maxValueSize > sizeof ( this->dbrScalarValue ) ) { char * pBuf = static_cast < char * > ( this->pbuffer ); delete [] pBuf; this->maxValueSize = sizeof ( this->dbrScalarValue ); this->pbuffer = & this->dbrScalarValue; } this->pbuffer = new char [newSize]; this->maxValueSize = newSize; } } extern "C" int putNotifyPut ( processNotify *ppn, notifyPutType type ) { if(ppn->status==notifyCanceled) return 0; /* * No locking in this method because only a dbNotifyCancel could interrupt * and it does not return until cancel is done. */ dbPutNotifyBlocker * pBlocker = static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt ); return db_put_process(ppn,type, pBlocker->dbrType,pBlocker->pbuffer,pBlocker->nRequest); } extern "C" void putNotifyCompletion ( processNotify *ppn ) { dbPutNotifyBlocker * const pBlocker = static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt ); epicsGuard < epicsMutex > guard ( pBlocker->mutex ); cacWriteNotify * const pNtfy = pBlocker->pNotify; if ( pNtfy ) { pBlocker->pNotify = 0; // Its necessary to signal the initiators now before we call // the user callback. This is less efficent, and potentially // causes more thread context switching, but its probably // unavoidable because its possible that the use callback // might destroy this object. pBlocker->block.signal (); if ( pBlocker->pn.status != notifyOK ) { pNtfy->exception ( guard, ECA_PUTFAIL, "put notify unsuccessful", static_cast < unsigned > (pBlocker->dbrType), static_cast < unsigned > (pBlocker->nRequest) ); } else { pNtfy->completion ( guard ); } } else { errlogPrintf ( "put notify completion with nill pNotify?\n" ); } } void dbPutNotifyBlocker::initiatePutNotify ( epicsGuard < epicsMutex > & guard, cacWriteNotify & notify, struct dbChannel * dbch, unsigned type, unsigned long count, const void * pValue ) { guard. assertIdenticalMutex ( this->mutex ); epicsTime begin; bool beginTimeInit = false; while ( true ) { if ( this->pNotify == 0 ) { this->pNotify = & notify; break; } if ( beginTimeInit ) { if ( epicsTime::getMonotonic () - begin > 30.0 ) { throw cacChannel::requestTimedOut (); } } else { begin = epicsTime::getMonotonic (); beginTimeInit = true; } { epicsGuardRelease < epicsMutex > autoRelease ( guard ); this->block.wait ( 1.0 ); } } if ( count > LONG_MAX ) { throw cacChannel::outOfBounds(); } if ( type > SHRT_MAX ) { throw cacChannel::badType(); } this->dbrType = type; this->nRequest = static_cast < unsigned > ( count ); this->pn.requestType = putProcessRequest; this->pn.chan = dbch; this->pn.putCallback = putNotifyPut; this->pn.doneCallback = putNotifyCompletion; this->pn.usrPvt = this; unsigned long size = dbr_size_n ( type, count ); this->expandValueBuf ( guard, size ); memcpy ( this->pbuffer, pValue, size ); { epicsGuardRelease < epicsMutex > autoRelease ( guard ); ::dbProcessNotify ( &this->pn ); } } void dbPutNotifyBlocker::show ( unsigned level ) const { epicsGuard < epicsMutex > guard ( this->mutex ); this->show ( guard, level ); } void dbPutNotifyBlocker::show ( epicsGuard < epicsMutex > &, unsigned level ) const { printf ( "put notify blocker at %p\n", static_cast ( this ) ); if ( level > 0u ) { this->block.show ( level - 1u ); } } dbSubscriptionIO * dbPutNotifyBlocker::isSubscription () { return 0; } void * dbPutNotifyBlocker::operator new ( size_t size, tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE void dbPutNotifyBlocker::operator delete ( void *pCadaver, tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif void dbPutNotifyBlocker::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } base-7.0.3.1/modules/database/src/ioc/db/dbPutNotifyBlocker.h0000664000577000060420000000550613557101274022505 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef dbPutNotifyBlockerh #define dbPutNotifyBlockerh #ifdef epicsExportSharedSymbols #define dbPutNotifyBlockerh_restore_epicsExportSharedSymbols #undef epicsExportSharedSymbols #endif #include "tsFreeList.h" #include "compilerDependencies.h" #ifdef dbPutNotifyBlockerh_restore_epicsExportSharedSymbols #define epicsExportSharedSymbols #endif class dbPutNotifyBlocker : public dbBaseIO { public: dbPutNotifyBlocker ( epicsMutex & ); void destructor ( CallbackGuard &, epicsGuard < epicsMutex > & ); void initiatePutNotify ( epicsGuard < epicsMutex > &, cacWriteNotify &, struct dbChannel *, unsigned type, unsigned long count, const void * pValue ); void cancel ( CallbackGuard &, epicsGuard < epicsMutex > & ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; void show ( unsigned level ) const; void * operator new ( size_t size, tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & )) private: processNotify pn; // // Include a union of all scalar types // including fixed length strings so // that in many cases we can avoid // allocating another buffer // union { dbr_string_t strval; dbr_short_t shrtval; dbr_short_t intval; dbr_float_t fltval; dbr_enum_t enmval; dbr_char_t charval; dbr_long_t longval; dbr_double_t doubleval; } dbrScalarValue; epicsEvent block; epicsMutex & mutex; cacWriteNotify * pNotify; unsigned long maxValueSize; // arguments for db_put_field void *pbuffer; long nRequest; short dbrType; // end arguments for db_put_field dbSubscriptionIO * isSubscription (); void expandValueBuf ( epicsGuard < epicsMutex > &, unsigned long newSize ); friend void putNotifyCompletion ( processNotify * ppn ); friend int putNotifyPut ( processNotify *ppn, notifyPutType type ); dbPutNotifyBlocker ( const dbPutNotifyBlocker & ); dbPutNotifyBlocker & operator = ( const dbPutNotifyBlocker & ); virtual ~dbPutNotifyBlocker (); void operator delete ( void * ); }; #endif // ifndef dbPutNotifyBlockerh base-7.0.3.1/modules/database/src/ioc/db/dbScan.c0000664000577000060420000007603713557101274020130 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * Copyright (c) 2013 Helmholtz-Zentrum Berlin * für Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbScan.c */ /* tasks and subroutines to scan the database */ /* * Original Authors: Bob Dalesio & Marty Kraimer */ #include #include #include #include #include #include #include "cantProceed.h" #include "dbDefs.h" #include "ellLib.h" #include "epicsAtomic.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "epicsPrint.h" #include "epicsRingBytes.h" #include "epicsStdio.h" #include "epicsStdlib.h" #include "epicsString.h" #include "epicsThread.h" #include "epicsTime.h" #include "taskwd.h" #define epicsExportSharedSymbols #include "callback.h" #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" #include "dbCommon.h" #include "dbFldTypes.h" #include "dbLock.h" #include "dbScan.h" #include "dbStaticLib.h" #include "devSup.h" #include "link.h" #include "recGbl.h" /* Task Control */ enum ctl {ctlInit, ctlRun, ctlPause, ctlExit}; /* Task Startup/Shutdown Synchronization */ static epicsEventId startStopEvent; static volatile enum ctl scanCtl; /* SCAN ONCE */ static int onceQueueSize = 1000; static epicsEventId onceSem; static epicsRingBytesId onceQ; static int onceQOverruns = 0; static epicsThreadId onceTaskId; static void *exitOnce; /* All other scan types */ typedef struct scan_list{ epicsMutexId lock; ELLLIST list; short modified;/*has list been modified?*/ } scan_list; /*scan_elements are allocated and the address stored in dbCommon.spvt*/ typedef struct scan_element{ ELLNODE node; scan_list *pscan_list; struct dbCommon *precord; } scan_element; /* PERIODIC */ #define OVERRUN_REPORT_DELAY 10.0 /* Time between initial reports */ #define OVERRUN_REPORT_MAX 3600.0 /* Maximum time between reports */ typedef struct periodic_scan_list { scan_list scan_list; double period; const char *name; unsigned long overruns; volatile enum ctl scanCtl; epicsEventId loopEvent; } periodic_scan_list; static int nPeriodic = 0; static periodic_scan_list **papPeriodic; /* pointer to array of pointers */ static epicsThreadId *periodicTaskId; /* array of thread ids */ static char *priorityName[NUM_CALLBACK_PRIORITIES] = { "Low", "Medium", "High" }; /* EVENT */ typedef struct event_list { epicsCallback callback[NUM_CALLBACK_PRIORITIES]; scan_list scan_list[NUM_CALLBACK_PRIORITIES]; struct event_list *next; char eventname[1]; /* actually arbitrary size */ } event_list; static event_list * volatile pevent_list[256]; static epicsMutexId event_lock; /* IO_EVENT*/ typedef struct io_scan_list { epicsCallback callback; scan_list scan_list; } io_scan_list; typedef struct ioscan_head { struct ioscan_head *next; struct io_scan_list iosl[NUM_CALLBACK_PRIORITIES]; io_scan_complete cb; void *arg; } ioscan_head; static ioscan_head *pioscan_list = NULL; static epicsMutexId ioscan_lock; /* Private routines */ static void onceTask(void *); static void initOnce(void); static void periodicTask(void *arg); static void initPeriodic(void); static void deletePeriodic(void); static void spawnPeriodic(int ind); static void eventCallback(epicsCallback *pcallback); static void ioscanInit(void); static void ioscanCallback(epicsCallback *pcallback); static void ioscanDestroy(void); static void printList(scan_list *psl, char *message); static void scanList(scan_list *psl); static void buildScanLists(void); static void addToList(struct dbCommon *precord, scan_list *psl); static void deleteFromList(struct dbCommon *precord, scan_list *psl); void scanStop(void) { int i; if (scanCtl == ctlExit) return; scanCtl = ctlExit; interruptAccept = FALSE; for (i = 0; i < nPeriodic; i++) { periodic_scan_list *ppsl = papPeriodic[i]; if (!ppsl) continue; ppsl->scanCtl = ctlExit; epicsEventSignal(ppsl->loopEvent); epicsEventWait(startStopEvent); } scanOnce((dbCommon *)&exitOnce); epicsEventWait(startStopEvent); } void scanCleanup(void) { deletePeriodic(); ioscanDestroy(); epicsRingBytesDelete(onceQ); free(periodicTaskId); papPeriodic = NULL; periodicTaskId = NULL; } long scanInit(void) { int i; if(!startStopEvent) startStopEvent = epicsEventMustCreate(epicsEventEmpty); scanCtl = ctlPause; initPeriodic(); initOnce(); buildScanLists(); for (i = 0; i < nPeriodic; i++) spawnPeriodic(i); return 0; } void scanRun(void) { int i; interruptAccept = TRUE; scanCtl = ctlRun; for (i = 0; i < nPeriodic; i++) { periodic_scan_list *ppsl = papPeriodic[i]; if (!ppsl) continue; ppsl->scanCtl = ctlRun; } } void scanPause(void) { int i; for (i = nPeriodic - 1; i >= 0; --i) { periodic_scan_list *ppsl = papPeriodic[i]; if (!ppsl) continue; ppsl->scanCtl = ctlPause; } scanCtl = ctlPause; interruptAccept = FALSE; } void scanAdd(struct dbCommon *precord) { int scan; /* get the list on which this record belongs */ scan = precord->scan; if (scan == menuScanPassive) return; if (scan < 0 || scan >= nPeriodic + SCAN_1ST_PERIODIC) { recGblRecordError(-1, (void *)precord, "scanAdd detected illegal SCAN value"); } else if (scan == menuScanEvent) { char* eventname; int prio; event_list *pel; eventname = precord->evnt; prio = precord->prio; if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) { recGblRecordError(-1, (void *)precord, "scanAdd: illegal prio field"); return; } pel = eventNameToHandle(eventname); if (pel) addToList(precord, &pel->scan_list[prio]); } else if (scan == menuScanI_O_Intr) { ioscan_head *piosh = NULL; int prio; DEVSUPFUN get_ioint_info; if (precord->dset == NULL){ recGblRecordError(-1, (void *)precord, "scanAdd: I/O Intr not valid (no DSET) "); precord->scan = menuScanPassive; return; } get_ioint_info = precord->dset->get_ioint_info; if (get_ioint_info == NULL) { recGblRecordError(-1, (void *)precord, "scanAdd: I/O Intr not valid (no get_ioint_info)"); precord->scan = menuScanPassive; return; } if (get_ioint_info(0, precord, &piosh)) { precord->scan = menuScanPassive; return; } if (piosh == NULL) { recGblRecordError(-1, (void *)precord, "scanAdd: I/O Intr not valid"); precord->scan = menuScanPassive; return; } prio = precord->prio; if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) { recGblRecordError(-1, (void *)precord, "scanAdd: illegal prio field"); precord->scan = menuScanPassive; return; } addToList(precord, &piosh->iosl[prio].scan_list); } else if (scan >= SCAN_1ST_PERIODIC) { periodic_scan_list *ppsl = papPeriodic[scan - SCAN_1ST_PERIODIC]; if (ppsl) addToList(precord, &ppsl->scan_list); } } void scanDelete(struct dbCommon *precord) { short scan; /* get the list on which this record belongs */ scan = precord->scan; if (scan == menuScanPassive) return; if (scan < 0 || scan >= nPeriodic + SCAN_1ST_PERIODIC) { recGblRecordError(-1, (void *)precord, "scanDelete detected illegal SCAN value"); } else if (scan == menuScanEvent) { int prio; event_list *pel; scan_list *psl = 0; prio = precord->prio; if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) { recGblRecordError(-1, (void *)precord, "scanDelete detected illegal PRIO field"); return; } pel = eventNameToHandle(precord->evnt); if (pel && (psl = &pel->scan_list[prio])) deleteFromList(precord, psl); } else if (scan == menuScanI_O_Intr) { ioscan_head *piosh = NULL; int prio; DEVSUPFUN get_ioint_info; if (precord->dset==NULL) { recGblRecordError(-1, (void *)precord, "scanDelete: I/O Intr not valid (no DSET)"); return; } get_ioint_info=precord->dset->get_ioint_info; if (get_ioint_info == NULL) { recGblRecordError(-1, (void *)precord, "scanDelete: I/O Intr not valid (no get_ioint_info)"); return; } if (get_ioint_info(1, precord, &piosh)) return; if (piosh == NULL) { recGblRecordError(-1, (void *)precord, "scanDelete: I/O Intr not valid"); return; } prio = precord->prio; if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) { recGblRecordError(-1, (void *)precord, "scanDelete: get_ioint_info returned illegal priority"); return; } deleteFromList(precord, &piosh->iosl[prio].scan_list); } else if (scan >= SCAN_1ST_PERIODIC) { periodic_scan_list *ppsl = papPeriodic[scan - SCAN_1ST_PERIODIC]; if (ppsl) deleteFromList(precord, &ppsl->scan_list); } } double scanPeriod(int scan) { periodic_scan_list *ppsl; scan -= SCAN_1ST_PERIODIC; if (scan < 0 || scan >= nPeriodic) return 0.0; ppsl = papPeriodic[scan]; return ppsl ? ppsl->period : 0.0; } int scanppl(double period) /* print periodic scan list(s) */ { dbMenu *pmenu = dbFindMenu(pdbbase, "menuScan"); char message[80]; int i; if (!pmenu || !papPeriodic) { printf("scanppl: dbScan subsystem not initialized\n"); return -1; } for (i = 0; i < nPeriodic; i++) { periodic_scan_list *ppsl = papPeriodic[i]; if (!ppsl) { const char *choice = pmenu->papChoiceValue[i + SCAN_1ST_PERIODIC]; printf("Periodic scan list for SCAN = '%s' not initialized\n", choice); continue; } if (period > 0.0 && (fabs(period - ppsl->period) > 0.05)) continue; sprintf(message, "Records with SCAN = '%s' (%lu over-runs):", ppsl->name, ppsl->overruns); printList(&ppsl->scan_list, message); } return 0; } int scanpel(const char* eventname) /* print event list */ { char message[80]; int prio; event_list *pel; for (pel = pevent_list[0]; pel; pel = pel->next) { if (!eventname || epicsStrGlobMatch(pel->eventname, eventname)) { printf("Event \"%s\"\n", pel->eventname); for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { if (ellCount(&pel->scan_list[prio].list) == 0) continue; sprintf(message, " Priority %s", priorityName[prio]); printList(&pel->scan_list[prio], message); } } } return 0; } int scanpiol(void) /* print pioscan_list */ { ioscan_head *piosh; ioscanInit(); epicsMutexMustLock(ioscan_lock); piosh = pioscan_list; while (piosh) { int prio; for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { io_scan_list *piosl = &piosh->iosl[prio]; char message[80]; sprintf(message, "IO Event %p: Priority %s", piosh, priorityName[prio]); printList(&piosl->scan_list, message); } piosh = piosh->next; } epicsMutexUnlock(ioscan_lock); return 0; } static void eventCallback(epicsCallback *pcallback) { scan_list *psl; callbackGetUser(psl, pcallback); scanList(psl); } static void eventOnce(void *arg) { event_lock = epicsMutexMustCreate(); } event_list *eventNameToHandle(const char *eventname) { int prio; event_list *pel; static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; double eventnumber = 0; size_t namelength; if (!eventname) return NULL; while (isspace((int) eventname[0])) eventname++; if (!eventname[0]) return NULL; namelength = strlen(eventname); while (isspace((int) eventname[namelength-1])) namelength--; /* Backward compatibility with numeric events: Treat any string that represents a double with an integer part between 0 and 255 the same as the integer because it is most probably a conversion from double like from a calc record. */ if (epicsParseDouble(eventname, &eventnumber, NULL) == 0) { if (eventnumber >= 0 && eventnumber < 256) { if (eventnumber < 1) return NULL; /* 0 is no event */ if ((pel = pevent_list[(int)eventnumber]) != NULL) return pel; } else eventnumber = 0; /* not a numeric event between 1 and 255 */ } epicsThreadOnce(&onceId, eventOnce, NULL); epicsMutexMustLock(event_lock); for (pel = pevent_list[0]; pel; pel=pel->next) { if (strncmp(pel->eventname, eventname, namelength) == 0 && pel->eventname[namelength] == 0) break; } if (pel == NULL) { pel = calloc(1, sizeof(event_list) + namelength); if (!pel) goto done; if (eventnumber > 0) { /* backward compatibility: make all numeric events look like integers */ sprintf(pel->eventname, "%i", (int)eventnumber); pevent_list[(int)eventnumber] = pel; } else strncpy(pel->eventname, eventname, namelength); for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { callbackSetUser(&pel->scan_list[prio], &pel->callback[prio]); callbackSetPriority(prio, &pel->callback[prio]); callbackSetCallback(eventCallback, &pel->callback[prio]); pel->scan_list[prio].lock = epicsMutexMustCreate(); ellInit(&pel->scan_list[prio].list); } pel->next=pevent_list[0]; pevent_list[0]=pel; } done: epicsMutexUnlock(event_lock); return pel; } void postEvent(event_list *pel) { int prio; if (scanCtl != ctlRun) return; if (!pel) return; for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { if (ellCount(&pel->scan_list[prio].list) >0) callbackRequest(&pel->callback[prio]); } } /* backward compatibility */ void post_event(int event) { if (event <= 0 || event > 255) return; postEvent(pevent_list[event]); } static void ioscanOnce(void *arg) { ioscan_lock = epicsMutexMustCreate(); } static void ioscanInit(void) { static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; epicsThreadOnce(&onceId, ioscanOnce, NULL); } static void ioscanDestroy(void) { ioscan_head *piosh; ioscanInit(); epicsMutexMustLock(ioscan_lock); piosh = pioscan_list; pioscan_list = NULL; epicsMutexUnlock(ioscan_lock); while (piosh) { ioscan_head *pnext = piosh->next; int prio; for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { epicsMutexDestroy(piosh->iosl[prio].scan_list.lock); ellFree(&piosh->iosl[prio].scan_list.list); } free(piosh); piosh = pnext; } } void scanIoInit(IOSCANPVT *pioscanpvt) { ioscan_head *piosh = dbCalloc(1, sizeof(ioscan_head)); int prio; ioscanInit(); for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { io_scan_list *piosl = &piosh->iosl[prio]; callbackSetCallback(ioscanCallback, &piosl->callback); callbackSetPriority(prio, &piosl->callback); callbackSetUser(piosh, &piosl->callback); ellInit(&piosl->scan_list.list); piosl->scan_list.lock = epicsMutexMustCreate(); } epicsMutexMustLock(ioscan_lock); piosh->next = pioscan_list; pioscan_list = piosh; epicsMutexUnlock(ioscan_lock); *pioscanpvt = piosh; } /* Return a bit mask indicating each priority level * in which a callback request was successfully queued. */ unsigned int scanIoRequest(IOSCANPVT piosh) { int prio; unsigned int queued = 0; if (scanCtl != ctlRun) return 0; for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { io_scan_list *piosl = &piosh->iosl[prio]; if (ellCount(&piosl->scan_list.list) > 0) if (!callbackRequest(&piosl->callback)) queued |= 1 << prio; } return queued; } unsigned int scanIoImmediate(IOSCANPVT piosh, int prio) { io_scan_list *piosl; if (prio<0 || prio>=NUM_CALLBACK_PRIORITIES) return S_db_errArg; else if (scanCtl != ctlRun) return 0; piosl = &piosh->iosl[prio]; if (ellCount(&piosl->scan_list.list) == 0) return 0; scanList(&piosl->scan_list); if (piosh->cb) piosh->cb(piosh->arg, piosh, prio); return 1 << prio; } /* May not be called while a scan request is queued or running */ void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg) { piosh->cb = cb; piosh->arg = arg; } int scanOnce(struct dbCommon *precord) { return scanOnceCallback(precord, NULL, NULL); } typedef struct { struct dbCommon *prec; once_complete cb; void *usr; } onceEntry; int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr) { static int newOverflow = TRUE; onceEntry ent; int pushOK; ent.prec = precord; ent.cb = cb; ent.usr = usr; pushOK = epicsRingBytesPut(onceQ, (void*)&ent, sizeof(ent)); if (!pushOK) { if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n"); newOverflow = FALSE; epicsAtomicIncrIntT(&onceQOverruns); } else { newOverflow = TRUE; } epicsEventSignal(onceSem); return !pushOK; } static void onceTask(void *arg) { taskwdInsert(0, NULL, NULL); epicsEventSignal(startStopEvent); while (TRUE) { epicsEventMustWait(onceSem); while(1) { onceEntry ent; int bytes = epicsRingBytesGet(onceQ, (void*)&ent, sizeof(ent)); if(bytes==0) break; if(bytes!=sizeof(ent)) { errlogPrintf("onceTask: received incomplete %d of %u\n", bytes, (unsigned)sizeof(ent)); continue; /* what to do? */ } else if (ent.prec == (void*)&exitOnce) goto shutdown; dbScanLock(ent.prec); dbProcess(ent.prec); dbScanUnlock(ent.prec); if(ent.cb) ent.cb(ent.usr, ent.prec); } } shutdown: taskwdRemove(0); epicsEventSignal(startStopEvent); } int scanOnceSetQueueSize(int size) { onceQueueSize = size; return 0; } int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result) { int ret; if (!onceQ) return -1; if (result) { result->size = epicsRingBytesSize(onceQ) / sizeof(onceEntry); result->numUsed = epicsRingBytesUsedBytes(onceQ) / sizeof(onceEntry); result->maxUsed = epicsRingBytesHighWaterMark(onceQ) / sizeof(onceEntry); result->numOverflow = epicsAtomicGetIntT(&onceQOverruns); ret = 0; } else { ret = -2; } if (reset) { epicsRingBytesResetHighWaterMark(onceQ); } return ret; } void scanOnceQueueShow(const int reset) { scanOnceQueueStats stats; if (scanOnceQueueStatus(reset, &stats) == -1) { fprintf(stderr, "scanOnce system not initialized, yet. Please run " "iocInit before using this command.\n"); } else { double qusage = 100.0 * stats.numUsed / stats.size; printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n"); printf("%8s %15d %10d %6d %6.1f %11d\n", "scanOnce", stats.maxUsed, stats.numUsed, stats.size, qusage, epicsAtomicGetIntT(&onceQOverruns)); } } static void initOnce(void) { if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) { cantProceed("initOnce: Ring buffer create failed\n"); } if(!onceSem) onceSem = epicsEventMustCreate(epicsEventEmpty); onceTaskId = epicsThreadCreate("scanOnce", epicsThreadPriorityScanLow + nPeriodic, epicsThreadGetStackSize(epicsThreadStackBig), onceTask, 0); epicsEventWait(startStopEvent); } static void periodicTask(void *arg) { periodic_scan_list *ppsl = (periodic_scan_list *)arg; epicsTimeStamp next, reported; unsigned int overruns = 0; double report_delay = OVERRUN_REPORT_DELAY; double overtime = 0.0; double over_min = 0.0; double over_max = 0.0; const double penalty = (ppsl->period >= 2) ? 1 : (ppsl->period / 2); taskwdInsert(0, NULL, NULL); epicsEventSignal(startStopEvent); epicsTimeGetMonotonic(&next); reported = next; while (ppsl->scanCtl != ctlExit) { double delay; epicsTimeStamp now; if (ppsl->scanCtl == ctlRun) scanList(&ppsl->scan_list); epicsTimeAddSeconds(&next, ppsl->period); epicsTimeGetMonotonic(&now); delay = epicsTimeDiffInSeconds(&next, &now); if (delay <= 0.0) { if (overtime == 0.0) { overtime = over_min = over_max = -delay; } else { overtime -= delay; if (over_min + delay > 0) over_min = -delay; if (over_max + delay < 0) over_max = -delay; } delay = penalty; ppsl->overruns++; next = now; epicsTimeAddSeconds(&next, delay); if (++overruns >= 10 && epicsTimeDiffInSeconds(&now, &reported) > report_delay) { errlogPrintf("\ndbScan warning from '%s' scan thread:\n" "\tScan processing averages %.3f seconds (%.3f .. %.3f).\n" "\tOver-runs have now happened %u times in a row.\n" "\tTo fix this, move some records to a slower scan rate.\n", ppsl->name, ppsl->period + overtime / overruns, ppsl->period + over_min, ppsl->period + over_max, overruns); reported = now; if (report_delay < (OVERRUN_REPORT_MAX / 2)) report_delay *= 2; else report_delay = OVERRUN_REPORT_MAX; } } else { overruns = 0; report_delay = OVERRUN_REPORT_DELAY; overtime = 0.0; } epicsEventWaitWithTimeout(ppsl->loopEvent, delay); } taskwdRemove(0); epicsEventSignal(startStopEvent); } static void initPeriodic(void) { dbMenu *pmenu = dbFindMenu(pdbbase, "menuScan"); double quantum = epicsThreadSleepQuantum(); int i; if (!pmenu) { errlogPrintf("initPeriodic: menuScan not present\n"); return; } nPeriodic = pmenu->nChoice - SCAN_1ST_PERIODIC; papPeriodic = dbCalloc(nPeriodic, sizeof(periodic_scan_list*)); periodicTaskId = dbCalloc(nPeriodic, sizeof(void *)); for (i = 0; i < nPeriodic; i++) { periodic_scan_list *ppsl = dbCalloc(1, sizeof(periodic_scan_list)); const char *choice = pmenu->papChoiceValue[i + SCAN_1ST_PERIODIC]; double number; char *unit; int status = epicsParseDouble(choice, &number, &unit); if (status || number <= 0) { errlogPrintf("initPeriodic: Bad menuScan choice '%s'\n", choice); } else if (!*unit || !epicsStrCaseCmp(unit, "second") || !epicsStrCaseCmp(unit, "seconds")) { ppsl->period = number; } else if (!epicsStrCaseCmp(unit, "minute") || !epicsStrCaseCmp(unit, "minutes")) { ppsl->period = number * 60; } else if (!epicsStrCaseCmp(unit, "hour") || !epicsStrCaseCmp(unit, "hours")) { ppsl->period = number * 60 * 60; } else if (!epicsStrCaseCmp(unit, "Hz") || !epicsStrCaseCmp(unit, "Hertz")) { ppsl->period = 1 / number; } else { errlogPrintf("initPeriodic: Bad menuScan choice '%s'\n", choice); } if (ppsl->period == 0) { free(ppsl); continue; } ppsl->scan_list.lock = epicsMutexMustCreate(); ellInit(&ppsl->scan_list.list); ppsl->name = choice; ppsl->scanCtl = ctlPause; ppsl->loopEvent = epicsEventMustCreate(epicsEventEmpty); number = ppsl->period / quantum; if ((ppsl->period < 2 * quantum) || (number / floor(number) > 1.1)) { errlogPrintf("initPeriodic: Scan rate '%s' is not achievable.\n", choice); } papPeriodic[i] = ppsl; } } static void deletePeriodic(void) { int i; for (i = 0; i < nPeriodic; i++) { periodic_scan_list *ppsl = papPeriodic[i]; if (!ppsl) continue; ellFree(&ppsl->scan_list.list); epicsEventDestroy(ppsl->loopEvent); epicsMutexDestroy(ppsl->scan_list.lock); free(ppsl); } free(papPeriodic); papPeriodic = NULL; } static void spawnPeriodic(int ind) { periodic_scan_list *ppsl = papPeriodic[ind]; char taskName[20]; if (!ppsl) return; sprintf(taskName, "scan-%g", ppsl->period); periodicTaskId[ind] = epicsThreadCreate( taskName, epicsThreadPriorityScanLow + ind, epicsThreadGetStackSize(epicsThreadStackBig), periodicTask, (void *)ppsl); epicsEventWait(startStopEvent); } static void ioscanCallback(epicsCallback *pcallback) { ioscan_head *piosh; int prio; callbackGetUser(piosh, pcallback); callbackGetPriority(prio, pcallback); scanList(&piosh->iosl[prio].scan_list); if (piosh->cb) piosh->cb(piosh->arg, piosh, prio); } static void printList(scan_list *psl, char *message) { scan_element *pse; epicsMutexMustLock(psl->lock); pse = (scan_element *)ellFirst(&psl->list); epicsMutexUnlock(psl->lock); if (!pse) return; printf("%s\n", message); while (pse) { printf(" %-28s\n", pse->precord->name); epicsMutexMustLock(psl->lock); if (pse->pscan_list != psl) { epicsMutexUnlock(psl->lock); printf(" Scan list changed while printing, try again.\n"); return; } pse = (scan_element *)ellNext(&pse->node); epicsMutexUnlock(psl->lock); } } static void scanList(scan_list *psl) { /* When reading this code remember that the call to dbProcess can result * in the SCAN field being changed in an arbitrary number of records. */ scan_element *pse; scan_element *prev = NULL; scan_element *next = NULL; epicsMutexMustLock(psl->lock); psl->modified = FALSE; pse = (scan_element *)ellFirst(&psl->list); if (pse) next = (scan_element *)ellNext(&pse->node); epicsMutexUnlock(psl->lock); while (pse) { struct dbCommon *precord = pse->precord; dbScanLock(precord); dbProcess(precord); dbScanUnlock(precord); epicsMutexMustLock(psl->lock); if (!psl->modified) { prev = pse; pse = (scan_element *)ellNext(&pse->node); if (pse) next = (scan_element *)ellNext(&pse->node); } else if (pse->pscan_list == psl) { /*This scan element is still in same scan list*/ prev = pse; pse = (scan_element *)ellNext(&pse->node); if (pse) next = (scan_element *)ellNext(&pse->node); psl->modified = FALSE; } else if (prev && prev->pscan_list == psl) { /*Previous scan element is still in same scan list*/ pse = (scan_element *)ellNext(&prev->node); if (pse) { prev = (scan_element *)ellPrevious(&pse->node); next = (scan_element *)ellNext(&pse->node); } psl->modified = FALSE; } else if (next && next->pscan_list == psl) { /*Next scan element is still in same scan list*/ pse = next; prev = (scan_element *)ellPrevious(&pse->node); next = (scan_element *)ellNext(&pse->node); psl->modified = FALSE; } else { /*Too many changes. Just wait till next period*/ epicsMutexUnlock(psl->lock); return; } epicsMutexUnlock(psl->lock); } } static void buildScanLists(void) { dbRecordType *pdbRecordType; for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { dbRecordNode *pdbRecordNode; for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList); pdbRecordNode; pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { dbCommon *precord = pdbRecordNode->precord; if (!precord->name[0] || pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) continue; scanAdd(precord); } } } static void addToList(struct dbCommon *precord, scan_list *psl) { scan_element *pse, *ptemp; epicsMutexMustLock(psl->lock); pse = precord->spvt; if (pse == NULL) { pse = dbCalloc(1, sizeof(scan_element)); precord->spvt = pse; pse->precord = precord; } pse->pscan_list = psl; ptemp = (scan_element *)ellLast(&psl->list); while (ptemp) { if (ptemp->precord->phas <= precord->phas) { ellInsert(&psl->list, &ptemp->node, &pse->node); break; } ptemp = (scan_element *)ellPrevious(&ptemp->node); } if (ptemp == NULL) ellAdd(&psl->list, (void *)pse); psl->modified = TRUE; epicsMutexUnlock(psl->lock); } static void deleteFromList(struct dbCommon *precord, scan_list *psl) { scan_element *pse; epicsMutexMustLock(psl->lock); pse = precord->spvt; if (pse == NULL) { epicsMutexUnlock(psl->lock); errlogPrintf("dbScan: Tried to delete record from wrong scan list!\n" "\t%s.SPVT = NULL, but psl = %p\n", precord->name, (void *)psl); return; } if (pse->pscan_list != psl) { epicsMutexUnlock(psl->lock); errlogPrintf("dbScan: Tried to delete record from wrong scan list!\n" "\t%s.SPVT->pscan_list = %p but psl = %p\n", precord->name, (void *)pse, (void *)psl); return; } pse->pscan_list = NULL; ellDelete(&psl->list, (void *)pse); psl->modified = TRUE; epicsMutexUnlock(psl->lock); } base-7.0.3.1/modules/database/src/ioc/db/dbScan.h0000664000577000060420000000525313557101274020125 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Marty Kraimer * Date: 07-17-91 */ #ifndef INCdbScanH #define INCdbScanH #include #include "menuScan.h" #include "shareLib.h" #include "compilerDependencies.h" #include "devSup.h" #ifdef __cplusplus extern "C" { #endif #define SCAN_PASSIVE menuScanPassive #define SCAN_EVENT menuScanEvent #define SCAN_IO_EVENT menuScanI_O_Intr #define SCAN_1ST_PERIODIC (menuScanI_O_Intr + 1) #define MAX_PHASE SHRT_MAX #define MIN_PHASE SHRT_MIN /*definitions for I/O Interrupt Scanning */ /* IOSCANPVT now defined in devSup.h */ typedef struct event_list *EVENTPVT; struct dbCommon; typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio); typedef void (*once_complete)(void *usr, struct dbCommon*); typedef struct scanOnceQueueStats { int size; int numUsed; int maxUsed; int numOverflow; } scanOnceQueueStats; epicsShareFunc long scanInit(void); epicsShareFunc void scanRun(void); epicsShareFunc void scanPause(void); epicsShareFunc void scanStop(void); epicsShareFunc void scanCleanup(void); epicsShareFunc EVENTPVT eventNameToHandle(const char* event); epicsShareFunc void postEvent(EVENTPVT epvt); epicsShareFunc void post_event(int event); epicsShareFunc void scanAdd(struct dbCommon *); epicsShareFunc void scanDelete(struct dbCommon *); epicsShareFunc double scanPeriod(int scan); epicsShareFunc int scanOnce(struct dbCommon *); epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr); epicsShareFunc int scanOnceSetQueueSize(int size); epicsShareFunc int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result); epicsShareFunc void scanOnceQueueShow(const int reset); /*print periodic lists*/ epicsShareFunc int scanppl(double rate); /*print event lists*/ epicsShareFunc int scanpel(const char *event_name); /*print io_event list*/ epicsShareFunc int scanpiol(void); epicsShareFunc void scanIoInit(IOSCANPVT *ppios); epicsShareFunc unsigned int scanIoRequest(IOSCANPVT pios); epicsShareFunc unsigned int scanIoImmediate(IOSCANPVT pios, int prio); epicsShareFunc void scanIoSetComplete(IOSCANPVT, io_scan_complete, void *usr); #ifdef __cplusplus } #endif #endif base-7.0.3.1/modules/database/src/ioc/db/dbServer.c0000664000577000060420000000777513557101274020515 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Andrew Johnson */ #include #include #include "dbDefs.h" #include "ellLib.h" #include "envDefs.h" #include "epicsStdio.h" #define epicsExportSharedSymbols #include "dbServer.h" static ELLLIST serverList = ELLLIST_INIT; static enum { registering, initialized, running, paused, stopped } state = registering; static char *stateNames[] = { "registering", "initialized", "running", "paused", "stopped" }; int dbRegisterServer(dbServer *psrv) { const char * ignore = envGetConfigParamPtr(&EPICS_IOC_IGNORE_SERVERS); ELLNODE *node; if (!psrv || !psrv->name || state != registering) return -1; if (strchr(psrv->name, ' ')) { fprintf(stderr, "dbRegisterServer: Bad server name '%s'\n", psrv->name); return -1; } if (ignore) { size_t len = strlen(psrv->name); const char *found; while ((found = strstr(ignore, psrv->name))) { /* Make sure the name isn't just a substring */ if ((found == ignore || (found > ignore && found[-1] == ' ')) && (found[len] == 0 || found[len] == ' ')) { fprintf(stderr, "dbRegisterServer: Ignoring '%s', per environment\n", psrv->name); return 0; } /* It was, try again further down */ ignore = found + len; } } for(node=ellFirst(&serverList); node; node = ellNext(node)) { dbServer *cur = CONTAINER(node, dbServer, node); if(cur==psrv) { return 0; /* already registered identically. */ } else if(strcmp(cur->name, psrv->name)==0) { fprintf(stderr, "dbRegisterServer: Can't redefine '%s'.\n", cur->name); return -1; } } ellAdd(&serverList, &psrv->node); return 0; } int dbUnregisterServer(dbServer *psrv) { if (state != registering && state != stopped) { fprintf(stderr, "dbUnregisterServer: Servers still active!\n"); return -1; } if (ellFind(&serverList, &psrv->node) < 0) { fprintf(stderr, "dbUnregisterServer: '%s' not registered.\n", psrv->name); return -1; } if (state == stopped && psrv->stop == NULL) { fprintf(stderr, "dbUnregisterServer: '%s' has no stop() method.\n", psrv->name); return -1; } ellDelete(&serverList, &psrv->node); return 0; } void dbsr(unsigned level) { dbServer *psrv = (dbServer *)ellFirst(&serverList); if (!psrv) { printf("No server layers registered with IOC\n"); return; } printf("Server state: %s\n", stateNames[state]); while (psrv) { printf("Server '%s'\n", psrv->name); if (state == running && psrv->report) psrv->report(level); psrv = (dbServer *)ellNext(&psrv->node); } } int dbServerClient(char *pBuf, size_t bufSize) { dbServer *psrv = (dbServer *)ellFirst(&serverList); if (state != running) return -1; while (psrv) { if (psrv->client && psrv->client(pBuf, bufSize) == 0) return 0; psrv = (dbServer *)ellNext(&psrv->node); } return -1; } #define STARTSTOP(routine, method, newState) \ void routine(void) \ { \ dbServer *psrv = (dbServer *)ellFirst(&serverList); \ \ while (psrv) { \ if (psrv->method) \ psrv->method(); \ psrv = (dbServer *)ellNext(&psrv->node); \ } \ state = newState; \ } STARTSTOP(dbInitServers, init, initialized) STARTSTOP(dbRunServers, run, running) STARTSTOP(dbPauseServers, pause, paused) STARTSTOP(dbStopServers, stop, stopped) base-7.0.3.1/modules/database/src/ioc/db/dbServer.h0000664000577000060420000001237613557101274020513 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /** * @file dbServer.h * @author Andrew Johnson * * @brief The IOC's interface to the server layers that publish its PVs. * * All server layers which publish IOC record data should initialize a * dbServer structure and register it with the IOC. The methods that * the dbServer interface provides allow the IOC to start, pause and stop * the servers together, and to provide status and debugging information * to the IOC user/developer through a common set of commands. * * @todo No API is provided yet for calling stats() methods. * Nothing in the IOC calls dbStopServers(), not sure where it should go. */ #ifndef INC_dbServer_H #define INC_dbServer_H #include #include "ellLib.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /** @brief Server information structure. * * Every server layer should initialize and register an instance of this * structure with the IOC by passing it to the dbRegisterServer() routine. * * All methods in this struct are optional; use @c NULL if a server is * unable to support a particular operation (or if it hasn't been * implemented yet). */ typedef struct dbServer { /** @brief Linked list node; initialize to @c ELLNODE_INIT */ ELLNODE node; /** @brief A short server identifier; printable, with no spaces */ const char *name; /** @brief Print level-dependent status report to stdout. * * @param level Interest level, specifies how much detail to print. */ void (* report) (unsigned level); /** @brief Get number of channels and clients currently connected. * * @param channels NULL or pointer for returning channel count. * @param clients NULL or pointer for returning client count. */ void (* stats) (unsigned *channels, unsigned *clients); /** @brief Get identity of client initiating the calling thread. * * Must fill in the buffer with the client's identity when called from a * thread that belongs to this server layer. For other threads, the * method should do nothing, just return -1. * @param pBuf Buffer for client identity string. * @param bufSize Number of chars available in pBuf. * @return -1 means calling thread is not owned by this server. * 0 means the thread was recognized and pBuf has been filled in. */ int (* client) (char *pBuf, size_t bufSize); /** @name Control Methods * These control methods for the server will be called by routines * related to iocInit for all registered servers in turn when the IOC * is being initialized, run, paused and stopped respectively. * * @{ */ /** @brief Server init method. * * Called for all registered servers by dbInitServers(). */ void (* init) (void); /** @brief Server run method. * * Called for all registered servers by dbRunServers(). */ void (* run) (void); /** @brief Server pause method. * * Called for all registered servers by dbPauseServers(). */ void (* pause) (void); /** @brief Server stop method. * * Called for all registered servers by dbStopServers(). */ void (* stop) (void); /** @} */ } dbServer; /** @brief Register a server layer with the IOC * * This should only be called once for each server layer. * @param psrv Server information structure for the server */ epicsShareFunc int dbRegisterServer(dbServer *psrv); /** @brief Unregister a server layer * * This should only be called when the servers are inactive. * @param psrv Server information structure for the server */ epicsShareFunc int dbUnregisterServer(dbServer *psrv); /** @brief Print dbServer Reports. * * Calls the report methods of all registered servers. * This routine is provided as an IOC Shell command. * @param level Interest level, specifies how much detail to print. */ epicsShareFunc void dbsr(unsigned level); /** @brief Query servers for client's identity. * * This routine is called by code that wants to identify who (or what) * is responsible for the thread which is currently running. Setting * the @c TPRO field of a record is one way to trigger this; the identity * of the calling thread is printed along with the record name whenever * the record is subsequently processed. */ epicsShareFunc int dbServerClient(char *pBuf, size_t bufSize); /** @brief Initialize all registered servers. * * Calls all dbServer::init() methods. */ epicsShareFunc void dbInitServers(void); /** @brief Run all registered servers. * * Calls all dbServer::run() methods. */ epicsShareFunc void dbRunServers(void); /** @brief Pause all registered servers. * * Calls all dbServer::pause() methods. */ epicsShareFunc void dbPauseServers(void); /** @brief Stop all registered servers. * * Calls all dbServer::stop() methods. */ epicsShareFunc void dbStopServers(void); #ifdef __cplusplus } #endif #endif /* INC_dbServer_H */ base-7.0.3.1/modules/database/src/ioc/db/dbState.c0000664000577000060420000000465113557101274020315 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #include #include "ellLib.h" #include "epicsMutex.h" #include "epicsString.h" #include "iocsh.h" #define epicsExportSharedSymbols #include "dbDefs.h" #include "dbState.h" #include "dbStaticLib.h" static ELLLIST states = ELLLIST_INIT; typedef struct dbState { ELLNODE node; int status; char *name; epicsMutexId lock; /* FIXME: Use atomic operations instead */ } dbState; dbStateId dbStateFind(const char *name) { ELLNODE *node; dbStateId id; if (!name) return NULL; for (node = ellFirst(&states); node; node = ellNext(node)) { id = CONTAINER(node, dbState, node); if (strcmp(id->name, name) == 0) return id; } return NULL; } dbStateId dbStateCreate(const char *name) { dbStateId id; if (!name) return NULL; if ((id = dbStateFind(name))) return id; id = callocMustSucceed(1, sizeof(dbState), "createDbState"); id->name = epicsStrDup(name); id->lock = epicsMutexMustCreate(); ellAdd(&states, &id->node); return id; } void dbStateSet(dbStateId id) { if (!id) return; epicsMutexMustLock(id->lock); id->status = 1; epicsMutexUnlock(id->lock); } void dbStateClear(dbStateId id) { if (!id) return; epicsMutexMustLock(id->lock); id->status = 0; epicsMutexUnlock(id->lock); } int dbStateGet(dbStateId id) { int status; if (!id) return 0; epicsMutexMustLock(id->lock); status = id->status; epicsMutexUnlock(id->lock); return status; } void dbStateShow(dbStateId id, unsigned int level) { if (level >=1) printf("id %p '%s' : ", id, id->name); printf("%s\n", dbStateGet(id) ? "TRUE" : "FALSE"); } void dbStateShowAll(unsigned int level) { ELLNODE *node; dbStateId id; for (node = ellFirst(&states); node; node = ellNext(node)) { id = CONTAINER(node, dbState, node); dbStateShow(id, level+1); } } base-7.0.3.1/modules/database/src/ioc/db/dbState.h0000664000577000060420000000474113557101274020322 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #ifndef INCdbStateH #define INCdbStateH #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /** @file dbState.h * @brief Generic IOC state facility * * This library provides a simple global flag facility that can be used to * synchronize e.g. plugins with IOC-wide states, that may be derived from * events (either soft events or hard events coming from specialized timing * and event hardware). * * A subset of this API is provided as IOC Shell commands to allow * command line debugging. * */ typedef struct dbState *dbStateId; /** @brief Create db state. * * Creates a new db state with the specified 'name', returning the new id. * If state with that name already exists, the existing state's id is returned. * * Also provided as an IOC Shell command. * * @param name Db state name. * @return Id of db state, NULL for failure. */ epicsShareFunc dbStateId dbStateCreate(const char *name); /** @brief Find db state. * * @param name Db state name. * @return Id of db state, NULL if not found. */ epicsShareFunc dbStateId dbStateFind(const char *name); /** @brief Set db state to TRUE. * * Also provided as an IOC Shell command. * * @param id Db state id. */ epicsShareFunc void dbStateSet(dbStateId id); /** @brief Set db state to FALSE. * * Also provided as an IOC Shell command. * * @param id Db state id. */ epicsShareFunc void dbStateClear(dbStateId id); /** @brief Get db state. * * @param id Db state id. * @return Current db state (0|1). */ epicsShareFunc int dbStateGet(dbStateId id); /** @brief Print info about db state. * * Also provided as an IOC Shell command. * * @param id Db state id. * @param level Interest level. */ epicsShareFunc void dbStateShow(dbStateId id, unsigned int level); /** @brief Print info about all db states. * * Also provided as an IOC Shell command. * * @param level Interest level. */ epicsShareFunc void dbStateShowAll(unsigned int level); #ifdef __cplusplus } #endif #endif // INCdbStateH base-7.0.3.1/modules/database/src/ioc/db/dbSubscriptionIO.cpp0000664000577000060420000001123413557101274022504 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include #include #include "epicsMutex.h" #include "epicsEvent.h" #include "tsFreeList.h" #include "db_access.h" // need to eliminate this #include "cadef.h" // this can be eliminated when the callbacks use the new interface #include "errlog.h" #define epicsExportSharedSymbols #include "dbCAC.h" #include "dbChannelIO.h" #include "db_access_routines.h" dbSubscriptionIO::dbSubscriptionIO ( epicsGuard < epicsMutex > & guard, epicsMutex & mutexIn, dbContext &, dbChannelIO & chanIO, dbChannel * dbch, cacStateNotify & notifyIn, unsigned typeIn, unsigned long countIn, unsigned maskIn, dbEventCtx ctx ) : mutex ( mutexIn ), count ( countIn ), notify ( notifyIn ), chan ( chanIO ), es ( 0 ), type ( typeIn ), id ( 0u ) { guard.assertIdenticalMutex ( this->mutex ); { epicsGuardRelease < epicsMutex > unguard ( guard ); this->es = db_add_event ( ctx, dbch, dbSubscriptionEventCallback, (void *) this, maskIn ); if ( this->es == 0 ) { throw std::bad_alloc(); } db_post_single_event ( this->es ); db_event_enable ( this->es ); } } dbSubscriptionIO::~dbSubscriptionIO () { } void dbSubscriptionIO::destructor ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->~dbSubscriptionIO (); } void dbSubscriptionIO::unsubscribe ( CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); if ( this->es ) { dbEventSubscription tmp = this->es; this->es = 0; { epicsGuardRelease < epicsMutex > unguard ( guard ); db_cancel_event ( tmp ); } } } void dbSubscriptionIO::channelDeleteException ( CallbackGuard &, epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->notify.exception ( guard, ECA_CHANDESTROY, this->chan.pName(guard), this->type, this->count ); } void dbSubscriptionIO::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } void * dbSubscriptionIO::operator new ( size_t size, tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE void dbSubscriptionIO::operator delete ( void * pCadaver, tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbChannel * /* dbch */, int /* eventsRemaining */, struct db_field_log *pfl ) { dbSubscriptionIO * pIO = static_cast < dbSubscriptionIO * > ( pPrivate ); pIO->chan.callStateNotify ( pIO->type, pIO->count, pfl, pIO->notify ); } void dbSubscriptionIO::show ( unsigned level ) const { epicsGuard < epicsMutex > guard ( this->mutex ); this->show ( guard, level ); } void dbSubscriptionIO::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( this->mutex ); printf ( "Data base subscription IO at %p\n", static_cast ( this ) ); if ( level > 0u ) { short tmpType; if ( this->type < SHRT_MAX ) { tmpType = static_cast < short > ( this->type ); printf ( "\ttype %s, count %lu, channel at %p\n", dbf_type_to_text ( tmpType ), this->count, static_cast ( &this->chan ) ); } else { printf ( "strange type !, count %lu, channel at %p\n", this->count, static_cast ( &this->chan ) ); } } } dbSubscriptionIO * dbSubscriptionIO::isSubscription () { return this; } base-7.0.3.1/modules/database/src/ioc/db/dbTest.c0000664000577000060420000011605013557101274020151 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* database access test subroutines */ #include #include #include #include "cvtFast.h" #include "dbDefs.h" #include "ellLib.h" #include "epicsMutex.h" #include "epicsStdio.h" #include "epicsStdlib.h" #include "epicsString.h" #include "errlog.h" #define epicsExportSharedSymbols #include "callback.h" #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" #include "dbCommon.h" #include "dbEvent.h" #include "db_field_log.h" #include "dbFldTypes.h" #include "dbLock.h" #include "dbStaticLib.h" #include "dbTest.h" #include "devSup.h" #include "drvSup.h" #include "link.h" #include "recGbl.h" #include "recSup.h" #include "special.h" #define MAXLINE 80 #define MAXMESS 128 struct msgBuff { /* line output structure */ char out_buff[MAXLINE + 1]; char *pNext; char *pLast; char *pNexTab; char message[MAXMESS]; }; typedef struct msgBuff TAB_BUFFER; #ifndef MIN # define MIN(x,y) (((x) < (y)) ? (x) : (y)) #endif #ifndef MAX # define MAX(x,y) (((x) > (y)) ? (x) : (y)) #endif /* Local Routines */ static long nameToAddr(const char *pname, DBADDR *paddr); static void printDbAddr(DBADDR *paddr); static void printBuffer(long status, short dbr_type, void *pbuffer, long reqOptions, long retOptions, long no_elements, TAB_BUFFER *pMsgBuff, int tab_size); static int dbpr_report(const char *pname, DBADDR *paddr, int interest_level, TAB_BUFFER *pMsgBuff, int tab_size); static void dbpr_msgOut(TAB_BUFFER *pMsgBuff,int tab_size); static void dbpr_init_msg(TAB_BUFFER *pMsgBuff,int tab_size); static void dbpr_insert_msg(TAB_BUFFER *pMsgBuff,size_t len,int tab_size); static void dbpr_msg_flush(TAB_BUFFER *pMsgBuff,int tab_size); static char *dbf[DBF_NTYPES] = { "STRING","CHAR","UCHAR","SHORT","USHORT","LONG","ULONG", "INT64","UINT64","FLOAT","DOUBLE","ENUM","MENU","DEVICE", "INLINK","OUTLINK","FWDLINK","NOACCESS" }; static char *dbr[DBR_ENUM+2] = { "STRING","CHAR","UCHAR","SHORT","USHORT","LONG","ULONG", "INT64","UINT64","FLOAT","DOUBLE","ENUM","NOACCESS" }; long dba(const char*pname) { DBADDR addr; if (!pname || !*pname) { printf("Usage: dba \"pv name\"\n"); return 1; } if (nameToAddr(pname, &addr)) return -1; printDbAddr(&addr); return 0; } long dbl(const char *precordTypename, const char *fields) { DBENTRY dbentry; DBENTRY *pdbentry=&dbentry; long status; int nfields = 0; int ifield; char *fieldnames = 0; char **papfields = 0; if (!pdbbase) { printf("No database loaded\n"); return 0; } if (precordTypename && ((*precordTypename == '\0') || !strcmp(precordTypename,"*"))) precordTypename = NULL; if (fields && (*fields == '\0')) fields = NULL; if (fields) { char *pnext; fieldnames = epicsStrDup(fields); nfields = 1; pnext = fieldnames; while (*pnext && (pnext = strchr(pnext,' '))) { nfields++; while (*pnext == ' ') pnext++; } papfields = dbCalloc(nfields,sizeof(char *)); pnext = fieldnames; for (ifield = 0; ifield < nfields; ifield++) { papfields[ifield] = pnext; if (ifield < nfields - 1) { pnext = strchr(pnext, ' '); *pnext++ = 0; while (*pnext == ' ') pnext++; } } } dbInitEntry(pdbbase, pdbentry); if (!precordTypename) status = dbFirstRecordType(pdbentry); else status = dbFindRecordType(pdbentry,precordTypename); if (status) { printf("No record type\n"); } while (!status) { status = dbFirstRecord(pdbentry); while (!status) { printf("%s", dbGetRecordName(pdbentry)); for (ifield = 0; ifield < nfields; ifield++) { char *pvalue; status = dbFindField(pdbentry, papfields[ifield]); if (status) { if (!strcmp(papfields[ifield], "recordType")) { pvalue = dbGetRecordTypeName(pdbentry); } else { printf(", "); continue; } } else { pvalue = dbGetString(pdbentry); } printf(", \"%s\"", pvalue ? pvalue : ""); } printf("\n"); status = dbNextRecord(pdbentry); } if (precordTypename) break; status = dbNextRecordType(pdbentry); } if (nfields > 0) { free((void *)papfields); free((void *)fieldnames); } dbFinishEntry(pdbentry); return 0; } long dbnr(int verbose) { DBENTRY dbentry; DBENTRY *pdbentry=&dbentry; long status; int nrecords; int naliases; int trecords = 0; int taliases = 0; if (!pdbbase) { printf("No database loaded\n"); return 0; } dbInitEntry(pdbbase, pdbentry); status = dbFirstRecordType(pdbentry); if (status) { printf("No record types loaded\n"); return 0; } printf("Records Aliases Record Type\n"); while (!status) { naliases = dbGetNAliases(pdbentry); taliases += naliases; nrecords = dbGetNRecords(pdbentry) - naliases; trecords += nrecords; if (verbose || nrecords) printf(" %5d %5d %s\n", nrecords, naliases, dbGetRecordTypeName(pdbentry)); status = dbNextRecordType(pdbentry); } dbFinishEntry(pdbentry); printf("Total %d records, %d aliases\n", trecords, taliases); return 0; } long dbla(const char *pmask) { DBENTRY dbentry; DBENTRY *pdbentry = &dbentry; long status; if (!pdbbase) { printf("No database loaded\n"); return 0; } dbInitEntry(pdbbase, pdbentry); status = dbFirstRecordType(pdbentry); while (!status) { for (status = dbFirstRecord(pdbentry); !status; status = dbNextRecord(pdbentry)) { char *palias; if (!dbIsAlias(pdbentry)) continue; palias = dbGetRecordName(pdbentry); if (pmask && *pmask && !epicsStrGlobMatch(palias, pmask)) continue; dbFindField(pdbentry, "NAME"); printf("%s -> %s\n", palias, dbGetString(pdbentry)); } status = dbNextRecordType(pdbentry); } dbFinishEntry(pdbentry); return 0; } long dbli(const char *pattern) { DBENTRY dbentry; void* ptr; if (!pdbbase) { printf("No database loaded\n"); return 0; } dbInitEntry(pdbbase, &dbentry); while (dbNextMatchingInfo(&dbentry, pattern) == 0) { printf("%s info(%s, \"%s\"", dbGetRecordName(&dbentry), dbGetInfoName(&dbentry), dbGetInfoString(&dbentry)); if ((ptr = dbGetInfoPointer(&dbentry)) != NULL) printf(", %p", ptr); printf(")\n"); } dbFinishEntry(&dbentry); return 0; } long dbgrep(const char *pmask) { DBENTRY dbentry; DBENTRY *pdbentry = &dbentry; long status; if (!pmask || !*pmask) { printf("Usage: dbgrep \"pattern\"\n"); return 1; } if (!pdbbase) { printf("No database loaded\n"); return 0; } dbInitEntry(pdbbase, pdbentry); status = dbFirstRecordType(pdbentry); while (!status) { status = dbFirstRecord(pdbentry); while (!status) { char *pname = dbGetRecordName(pdbentry); if (epicsStrGlobMatch(pname, pmask)) puts(pname); status = dbNextRecord(pdbentry); } status = dbNextRecordType(pdbentry); } dbFinishEntry(pdbentry); return 0; } long dbgf(const char *pname) { /* declare buffer long just to ensure correct alignment */ long buffer[100]; long *pbuffer=&buffer[0]; DBADDR addr; long options = 0; long no_elements; static TAB_BUFFER msg_Buff; if (!pname || !*pname) { printf("Usage: dbgf \"pv name\"\n"); return 1; } if (nameToAddr(pname, &addr)) return -1; if (addr.precord->lset == NULL) { printf("dbgf only works after iocInit\n"); return -1; } no_elements = MIN(addr.no_elements, sizeof(buffer)/addr.field_size); if (addr.dbr_field_type == DBR_ENUM) { long status = dbGetField(&addr, DBR_STRING, pbuffer, &options, &no_elements, NULL); printBuffer(status, DBR_STRING, pbuffer, 0L, 0L, no_elements, &msg_Buff, 10); } else { long status = dbGetField(&addr, addr.dbr_field_type, pbuffer, &options, &no_elements, NULL); printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L, no_elements, &msg_Buff, 10); } msg_Buff.message[0] = '\0'; dbpr_msgOut(&msg_Buff, 10); return 0; } long dbpf(const char *pname,const char *pvalue) { DBADDR addr; long status; short dbrType; size_t n = 1; if (!pname || !*pname || !pvalue) { printf("Usage: dbpf \"pv name\", \"value\"\n"); return 1; } if (nameToAddr(pname, &addr)) return -1; if (addr.precord->lset == NULL) { printf("dbpf only works after iocInit\n"); return -1; } if (addr.no_elements > 1 && (addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR)) { dbrType = addr.dbr_field_type; n = strlen(pvalue) + 1; } else { dbrType = DBR_STRING; } status = dbPutField(&addr, dbrType, pvalue, (long) n); dbgf(pname); return status; } long dbpr(const char *pname,int interest_level) { static TAB_BUFFER msg_Buff; TAB_BUFFER *pMsgBuff = &msg_Buff; DBADDR addr; char *pmsg; int tab_size = 20; if (!pname || !*pname) { printf("Usage: dbpr \"pv name\", level\n"); return 1; } if (nameToAddr(pname, &addr)) return -1; pmsg = pMsgBuff->message; if (dbpr_report(pname, &addr, interest_level, pMsgBuff, tab_size)) return 1; pmsg[0] = '\0'; dbpr_msgOut(pMsgBuff, tab_size); return 0; } long dbtr(const char *pname) { DBADDR addr; long status; struct dbCommon *precord; if (!pname || !*pname) { printf("Usage: dbtr \"pv name\"\n"); return 1; } if (nameToAddr(pname, &addr)) return -1; if (addr.precord->lset == NULL) { printf("dbtr only works after iocInit\n"); return -1; } precord = (struct dbCommon*)addr.precord; if (precord->pact) { printf("record active\n"); return 1; } dbScanLock(precord); status = dbProcess(precord); dbScanUnlock(precord); if (status) recGblRecordError(status, precord, "dbtr(dbProcess)"); dbpr(pname, 3); return 0; } long dbtgf(const char *pname) { /* declare buffer long just to ensure correct alignment */ long buffer[400]; long *pbuffer = &buffer[0]; DBADDR addr; long status; long req_options, ret_options, no_elements; short dbr_type; static TAB_BUFFER msg_Buff; TAB_BUFFER *pMsgBuff = &msg_Buff; char *pmsg = pMsgBuff->message; int tab_size = 10; if (pname==0 || *pname==0) { printf("Usage: dbtgf \"pv name\"\n"); return 1; } if (nameToAddr(pname, &addr)) return -1; if (addr.precord->lset == NULL) { printf("dbtgf only works after iocInit\n"); return -1; } /* try all options first */ req_options = 0xffffffff; ret_options = req_options; no_elements = 0; status = dbGetField(&addr, addr.dbr_field_type, pbuffer, &ret_options, &no_elements, NULL); printBuffer(status, addr.dbr_field_type, pbuffer, req_options, ret_options, no_elements, pMsgBuff, tab_size); /* Now try all request types */ ret_options=0; dbr_type = DBR_STRING; no_elements = MIN(addr.no_elements,((sizeof(buffer))/MAX_STRING_SIZE)); status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); dbr_type = DBR_CHAR; no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt8))); status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); dbr_type = DBR_UCHAR; no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt8))); status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); dbr_type = DBR_SHORT; no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt16))); status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); dbr_type = DBR_USHORT; no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt16))); status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); dbr_type = DBR_LONG; no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt32))); status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); dbr_type = DBR_ULONG; no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt32))); status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); dbr_type = DBR_INT64; no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt64))); status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); dbr_type = DBR_UINT64; no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt64))); status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); dbr_type = DBR_FLOAT; no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat32))); status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); dbr_type = DBR_DOUBLE; no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat64))); status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); dbr_type = DBR_ENUM; no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsEnum16))); status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); pmsg[0] = '\0'; dbpr_msgOut(pMsgBuff, tab_size); return(0); } long dbtpf(const char *pname, const char *pvalue) { /* declare buffer long just to ensure correct alignment */ long buffer[100]; long *pbuffer = buffer; DBADDR addr; int put_type; static TAB_BUFFER msg_Buff; TAB_BUFFER *pMsgBuff = &msg_Buff; char *pmsg = pMsgBuff->message; int tab_size = 10; if (!pname || !*pname || !pvalue) { printf("Usage: dbtpf \"pv name\", \"value\"\n"); return 1; } if (nameToAddr(pname, &addr)) return -1; if (addr.precord->lset == NULL) { printf("dbtpf only works after iocInit\n"); return -1; } for (put_type = DBR_STRING; put_type <= DBF_ENUM; put_type++) { union { epicsInt8 i8; epicsUInt8 u8; epicsInt16 i16; epicsUInt16 u16; epicsInt32 i32; epicsUInt32 u32; epicsInt64 i64; epicsUInt64 u64; epicsFloat32 f32; epicsFloat64 f64; epicsEnum16 e16; } val; const void *pval = &val; int valid = 1; switch (put_type) { case DBR_STRING: pval = pvalue; break; case DBR_CHAR: valid = !epicsParseInt8(pvalue, &val.i8, 10, NULL); break; case DBR_UCHAR: valid = !epicsParseUInt8(pvalue, &val.u8, 10, NULL); break; case DBR_SHORT: valid = !epicsParseInt16(pvalue, &val.i16, 10, NULL); break; case DBR_USHORT: valid = !epicsParseUInt16(pvalue, &val.u16, 10, NULL); break; case DBR_LONG: valid = !epicsParseInt32(pvalue, &val.i32, 10, NULL); break; case DBR_ULONG: valid = !epicsParseUInt32(pvalue, &val.u32, 10, NULL); break; case DBR_INT64: valid = !epicsParseInt64(pvalue, &val.i64, 10, NULL); break; case DBR_UINT64: valid = !epicsParseUInt64(pvalue, &val.u64, 10, NULL); break; case DBR_FLOAT: valid = !epicsParseFloat32(pvalue, &val.f32, NULL); break; case DBR_DOUBLE: valid = !epicsParseFloat64(pvalue, &val.f64, NULL); break; case DBR_ENUM: valid = !epicsParseUInt16(pvalue, &val.e16, 10, NULL); break; } if (valid) { long status = dbPutField(&addr, put_type, pval, 1); if (status) { printf("Put as DBR_%-6s Failed.\n", dbr[put_type]); } else { long options = 0; long no_elements = MIN(addr.no_elements, ((sizeof(buffer))/addr.field_size)); printf("Put as DBR_%-6s Ok, result as ", dbr[put_type]); status = dbGetField(&addr, addr.dbr_field_type, pbuffer, &options, &no_elements, NULL); printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L, no_elements, pMsgBuff, tab_size); } } else { printf("Cvt to DBR_%s failed.\n", dbr[put_type]); } } pmsg[0] = '\0'; dbpr_msgOut(pMsgBuff, tab_size); return 0; } long dbior(const char *pdrvName,int interest_level) { drvSup *pdrvSup; struct drvet *pdrvet; dbRecordType *pdbRecordType; if (!pdbbase) { printf("No database loaded\n"); return 0; } if (pdrvName && ((*pdrvName == '\0') || !strcmp(pdrvName,"*"))) pdrvName = NULL; for (pdrvSup = (drvSup *)ellFirst(&pdbbase->drvList); pdrvSup; pdrvSup = (drvSup *)ellNext(&pdrvSup->node)) { const char *pname = pdrvSup->name; if (pdrvName!=NULL && *pdrvName!='\0' && (strcmp(pdrvName,pname)!=0)) continue; pdrvet = pdrvSup->pdrvet ; if (pdrvet == NULL) { printf("No driver entry table is present for %s\n", pname); continue; } if (pdrvet->report == NULL) printf("Driver: %s No report available\n", pname); else { printf("Driver: %s\n", pname); pdrvet->report(interest_level); } } /* now check devSup reports */ for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { devSup *pdevSup; for (pdevSup = (devSup *)ellFirst(&pdbRecordType->devList); pdevSup; pdevSup = (devSup *)ellNext(&pdevSup->node)) { struct dset *pdset = pdevSup->pdset; const char *pname = pdevSup->name; if (!pdset || !pname) continue; if (pdrvName != NULL && *pdrvName != '\0' && (strcmp(pdrvName, pname) != 0)) continue; if (pdset->report != NULL) { printf("Device Support: %s\n", pname); pdset->report(interest_level); } } } return 0; } int dbhcr(void) { if (!pdbbase) { printf("No database loaded\n"); return 0; } dbReportDeviceConfig(pdbbase, stdout); return 0; } static long nameToAddr(const char *pname, DBADDR *paddr) { long status = dbNameToAddr(pname, paddr); if (status) { printf("PV '%s' not found\n", pname); } return status; } static void printDbAddr(DBADDR *paddr) { dbFldDes *pdbFldDes = paddr->pfldDes; short field_type = paddr->field_type; short dbr_field_type = paddr->dbr_field_type; printf("Record Address: %p", paddr->precord); printf(" Field Address: %p", paddr->pfield); printf(" Field Description: %p\n", pdbFldDes); printf(" No Elements: %ld\n", paddr->no_elements); printf(" Record Type: %s\n", pdbFldDes->pdbRecordType->name); printf(" Field Type: %d = DBF_%s\n", field_type, (field_type < 0 || field_type > DBR_NOACCESS) ? "????" : dbf[field_type]); printf(" Field Size: %d\n", paddr->field_size); printf(" Special: %d\n", paddr->special); if (dbr_field_type == DBR_NOACCESS) dbr_field_type = DBR_ENUM + 1; printf("DBR Field Type: %d = DBR_%s\n", paddr->dbr_field_type, (dbr_field_type < 0 || dbr_field_type > (DBR_ENUM+1)) ? "????" : dbr[dbr_field_type]); } static void printBuffer( long status, short dbr_type, void *pbuffer, long reqOptions, long retOptions, long no_elements, TAB_BUFFER *pMsgBuff, int tab_size) { char *pmsg = pMsgBuff->message; int i; if (reqOptions & DBR_STATUS) { if (retOptions & DBR_STATUS) { struct dbr_status *pdbr_status = (void *)pbuffer; printf("status = %u, severity = %u\n", pdbr_status->status, pdbr_status->severity); } else { printf("status and severity not returned\n"); } pbuffer = (char *)pbuffer + dbr_status_size; } if (reqOptions & DBR_UNITS) { if (retOptions & DBR_UNITS) { struct dbr_units *pdbr_units = (void *)pbuffer; printf("units = \"%s\"\n", pdbr_units->units); } else { printf("units not returned\n"); } pbuffer = (char *)pbuffer + dbr_units_size; } if (reqOptions & DBR_PRECISION) { if (retOptions & DBR_PRECISION){ struct dbr_precision *pdbr_precision = (void *)pbuffer; printf("precision = %ld\n", pdbr_precision->precision.dp); } else { printf("precision not returned\n"); } pbuffer = (char *)pbuffer + dbr_precision_size; } if (reqOptions & DBR_TIME) { if (retOptions & DBR_TIME) { struct dbr_time *pdbr_time = (void *)pbuffer; char time_buf[40]; epicsTimeToStrftime(time_buf, 40, "%Y-%m-%d %H:%M:%S.%09f", &pdbr_time->time); printf("time = %s\n", time_buf); } else { printf("time not returned\n"); } pbuffer = (char *)pbuffer + dbr_time_size; } if (reqOptions & DBR_ENUM_STRS) { if (retOptions & DBR_ENUM_STRS) { struct dbr_enumStrs *pdbr_enumStrs = (void *)pbuffer; printf("no_strs = %u:\n", pdbr_enumStrs->no_str); for (i = 0; i < pdbr_enumStrs->no_str; i++) printf("\t\"%s\"\n", pdbr_enumStrs->strs[i]); } else { printf("enum strings not returned\n"); } pbuffer = (char *)pbuffer + dbr_enumStrs_size; } if (reqOptions & DBR_GR_LONG) { if (retOptions & DBR_GR_LONG) { struct dbr_grLong *pdbr_grLong = (void *)pbuffer; printf("grLong: %d .. %d\n", pdbr_grLong->lower_disp_limit, pdbr_grLong->upper_disp_limit); } else { printf("DBRgrLong not returned\n"); } pbuffer = (char *)pbuffer + dbr_grLong_size; } if (reqOptions & DBR_GR_DOUBLE) { if (retOptions & DBR_GR_DOUBLE) { struct dbr_grDouble *pdbr_grDouble = (void *)pbuffer; printf("grDouble: %g .. %g\n", pdbr_grDouble->lower_disp_limit, pdbr_grDouble->upper_disp_limit); } else { printf("DBRgrDouble not returned\n"); } pbuffer = (char *)pbuffer + dbr_grDouble_size; } if (reqOptions & DBR_CTRL_LONG) { if (retOptions & DBR_CTRL_LONG){ struct dbr_ctrlLong *pdbr_ctrlLong = (void *)pbuffer; printf("ctrlLong: %d .. %d\n", pdbr_ctrlLong->lower_ctrl_limit, pdbr_ctrlLong->upper_ctrl_limit); } else { printf("DBRctrlLong not returned\n"); } pbuffer = (char *)pbuffer + dbr_ctrlLong_size; } if (reqOptions & DBR_CTRL_DOUBLE) { if (retOptions & DBR_CTRL_DOUBLE) { struct dbr_ctrlDouble *pdbr_ctrlDouble = (void *)pbuffer; printf("ctrlDouble: %g .. %g\n", pdbr_ctrlDouble->lower_ctrl_limit, pdbr_ctrlDouble->upper_ctrl_limit); } else { printf("DBRctrlDouble not returned\n"); } pbuffer = (char *)pbuffer + dbr_ctrlDouble_size; } if (reqOptions & DBR_AL_LONG) { if (retOptions & DBR_AL_LONG) { struct dbr_alLong *pdbr_alLong = (void *)pbuffer; printf("alLong: %d < %d .. %d < %d\n", pdbr_alLong->lower_alarm_limit, pdbr_alLong->lower_warning_limit, pdbr_alLong->upper_warning_limit, pdbr_alLong->upper_alarm_limit); } else { printf("DBRalLong not returned\n"); } pbuffer = (char *)pbuffer + dbr_alLong_size; } if (reqOptions & DBR_AL_DOUBLE) { if (retOptions & DBR_AL_DOUBLE) { struct dbr_alDouble *pdbr_alDouble = (void *)pbuffer; printf("alDouble: %g < %g .. %g < %g\n", pdbr_alDouble->lower_alarm_limit, pdbr_alDouble->lower_warning_limit, pdbr_alDouble->upper_warning_limit, pdbr_alDouble->upper_alarm_limit); } else { printf("DBRalDouble not returned\n"); } pbuffer = (char *)pbuffer + dbr_alDouble_size; } /* Now print values */ if (no_elements == 0) return; if (no_elements == 1) sprintf(pmsg, "DBF_%s: ", dbr[dbr_type]); else sprintf(pmsg, "DBF_%s[%ld]: ", dbr[dbr_type], no_elements); dbpr_msgOut(pMsgBuff, tab_size); if (status != 0) { strcpy(pmsg, "failed."); dbpr_msgOut(pMsgBuff, tab_size); } else { switch (dbr_type) { case DBR_STRING: for(i=0; i 0) { int chunk = (len > MAXLINE - 5) ? MAXLINE - 5 : len; sprintf(pmsg, "\"%.*s\"", chunk, (char *)pbuffer + i); len -= chunk; i += chunk; if (len > 0) strcat(pmsg, " +"); dbpr_msgOut(pMsgBuff, tab_size); } } break; case DBR_UCHAR: for (i = 0; i < no_elements; i++) { epicsUInt32 val = *(epicsUInt8 *) pbuffer; sprintf(pmsg, "%u = 0x%x", val, val); dbpr_msgOut(pMsgBuff, tab_size); pbuffer = (char *)pbuffer + sizeof(epicsUInt8); } break; case DBR_SHORT: for (i = 0; i < no_elements; i++) { epicsInt16 val = *(epicsInt16 *) pbuffer; sprintf(pmsg, "%hd = 0x%hx", val, val); dbpr_msgOut(pMsgBuff, tab_size); pbuffer = (char *)pbuffer + sizeof(epicsInt16); } break; case DBR_USHORT: for (i = 0; i < no_elements; i++) { epicsUInt16 val = *(epicsUInt16 *) pbuffer; sprintf(pmsg, "%hu = 0x%hx", val, val); dbpr_msgOut(pMsgBuff, tab_size); pbuffer = (char *)pbuffer + sizeof(epicsUInt16); } break; case DBR_LONG: for (i = 0; i < no_elements; i++) { epicsInt32 val = *(epicsInt32 *) pbuffer; sprintf(pmsg, "%d = 0x%x", val, val); dbpr_msgOut(pMsgBuff, tab_size); pbuffer = (char *)pbuffer + sizeof(epicsInt32); } break; case DBR_ULONG: for (i = 0; i < no_elements; i++) { epicsUInt32 val = *(epicsUInt32 *) pbuffer; sprintf(pmsg, "%u = 0x%x", val, val); dbpr_msgOut(pMsgBuff, tab_size); pbuffer = (char *)pbuffer + sizeof(epicsUInt32); } break; case DBR_INT64: for (i = 0; i < no_elements; i++) { epicsInt64 val = *(epicsInt64 *) pbuffer; pmsg += cvtInt64ToString(val, pmsg); strcpy(pmsg, " = "); pmsg += 3; cvtInt64ToHexString(val, pmsg); dbpr_msgOut(pMsgBuff, tab_size); pmsg = pMsgBuff->message; pbuffer = (char *)pbuffer + sizeof(epicsInt64); } break; case DBR_UINT64: for (i = 0; i < no_elements; i++) { epicsUInt64 val = *(epicsUInt64 *) pbuffer; pmsg += cvtUInt64ToString(val, pmsg); strcpy(pmsg, " = "); pmsg += 3; cvtUInt64ToHexString(val, pmsg); dbpr_msgOut(pMsgBuff, tab_size); pmsg = pMsgBuff->message; pbuffer = (char *)pbuffer + sizeof(epicsUInt64); } break; case DBR_FLOAT: for (i = 0; i < no_elements; i++) { sprintf(pmsg, "%.6g", *((epicsFloat32 *) pbuffer)); dbpr_msgOut(pMsgBuff, tab_size); pbuffer = (char *)pbuffer + sizeof(epicsFloat32); } break; case DBR_DOUBLE: for (i = 0; i < no_elements; i++) { sprintf(pmsg, "%.12g", *((epicsFloat64 *) pbuffer)); dbpr_msgOut(pMsgBuff, tab_size); pbuffer = (char *)pbuffer + sizeof(epicsFloat64); } break; case DBR_ENUM: for (i = 0; i < no_elements; i++) { sprintf(pmsg, "%u", *((epicsEnum16 *) pbuffer)); dbpr_msgOut(pMsgBuff, tab_size); pbuffer = (char *)pbuffer + sizeof(epicsEnum16); } break; default: sprintf(pmsg, "Bad DBR type %d", dbr_type); dbpr_msgOut(pMsgBuff, tab_size); break; } } dbpr_msg_flush(pMsgBuff, tab_size); } static int dbpr_report( const char *pname, DBADDR *paddr, int interest_level, TAB_BUFFER *pMsgBuff, int tab_size) { char *pmsg; dbFldDes *pdbFldDes = paddr->pfldDes; dbRecordType *pdbRecordType = pdbFldDes->pdbRecordType; short n2; void *pfield; char *pfield_name; char *pfield_value; DBENTRY dbentry; DBENTRY *pdbentry = &dbentry; long status; dbInitEntry(pdbbase,pdbentry); status = dbFindRecord(pdbentry,pname); if (status) { errMessage(status,pname); return -1; } pmsg = pMsgBuff->message; for (n2 = 0; n2 <= pdbRecordType->no_fields - 1; n2++) { pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->sortFldInd[n2]]; pfield_name = pdbFldDes->name; pfield = ((char *)paddr->precord) + pdbFldDes->offset; if (pdbFldDes->interest > interest_level ) continue; switch (pdbFldDes->field_type) { case DBF_STRING: case DBF_USHORT: case DBF_ENUM: case DBF_FLOAT: case DBF_CHAR: case DBF_UCHAR: case DBF_SHORT: case DBF_LONG: case DBF_ULONG: case DBF_INT64: case DBF_UINT64: case DBF_DOUBLE: case DBF_MENU: case DBF_DEVICE: status = dbFindField(pdbentry,pfield_name); pfield_value = dbGetString(pdbentry); sprintf(pmsg, "%-4s: %s", pfield_name, (pfield_value ? pfield_value : "")); dbpr_msgOut(pMsgBuff, tab_size); break; case DBF_INLINK: case DBF_OUTLINK: case DBF_FWDLINK: { DBLINK *plink = (DBLINK *)pfield; int ind; const char *type = "LINK"; status = dbFindField(pdbentry,pfield_name); if (!plink->text) for (ind=0; indtype) { type = pamaplinkType[ind].strvalue; break; } } epicsSnprintf(pmsg, MAXMESS, "%-4s: %s %s", pfield_name, type, dbGetString(pdbentry)); dbpr_msgOut(pMsgBuff, tab_size); } break; case DBF_NOACCESS: if (pfield == (void *)&paddr->precord->time) { /* Special for the TIME field, make it human-readable */ char time_buf[40]; epicsTimeToStrftime(time_buf, 40, "%Y-%m-%d %H:%M:%S.%09f", &paddr->precord->time); sprintf(pmsg, "%-4s: %s", pfield_name, time_buf); dbpr_msgOut(pMsgBuff, tab_size); } else if (pdbFldDes->size == sizeof(void *) && strchr(pdbFldDes->extra, '*')) { /* Special for pointers */ sprintf(pmsg, "%-4s: PTR %p", pfield_name, *(void **)pfield); dbpr_msgOut(pMsgBuff, tab_size); } else if (pdbFldDes->size == sizeof(ELLLIST) && !strncmp(pdbFldDes->extra, "ELLLIST", 7)) { /* Special for linked lists */ ELLLIST *plist = (ELLLIST *)pfield; sprintf(pmsg, "%-4s: ELL %d [%p .. %p]", pfield_name, ellCount(plist), ellFirst(plist), ellLast(plist)); dbpr_msgOut(pMsgBuff, tab_size); } else { /* just print field as hex bytes */ unsigned char *pchar = (unsigned char *)pfield; char temp_buf[61]; char *ptemp_buf = &temp_buf[0]; short n = pdbFldDes->size; short i; unsigned int value; if (n > sizeof(temp_buf)/3) n = sizeof(temp_buf)/3; for (i=0; imessage; static int last_tabsize; if (!((tab_size == 10) || (tab_size == 20))) { printf("tab_size not 10 or 20 - dbpr_msgOut()\n"); return; } /* init if first time */ if (!(pMsgBuff->pNext)) dbpr_init_msg(pMsgBuff, tab_size); if (tab_size != last_tabsize) pMsgBuff->pNexTab = pMsgBuff->out_buff + tab_size; last_tabsize = tab_size; /* flush output if NULL string command */ if (*pmsg == 0) { dbpr_msg_flush(pMsgBuff, tab_size); return; } /* truncate if too long */ len = strlen(pmsg); if (len > MAXLINE) { err = 1; len = MAXLINE; } dbpr_insert_msg(pMsgBuff, len, tab_size); /* warn if msg gt 80 */ if (err == 1) { len = strlen(pmsg); sprintf(pmsg, "dbpr_msgOut: ERROR - msg length=%d limit=%d ", (int)len, MAXLINE); dbpr_insert_msg(pMsgBuff, len, tab_size); } } static void dbpr_init_msg(TAB_BUFFER *pMsgBuff,int tab_size) { pMsgBuff->pNext = pMsgBuff->out_buff; pMsgBuff->pLast = pMsgBuff->out_buff + MAXLINE; pMsgBuff->pNexTab = pMsgBuff->out_buff + tab_size; } static void dbpr_insert_msg(TAB_BUFFER *pMsgBuff,size_t len,int tab_size) { size_t current_len; size_t n; size_t tot_line; char *pmsg = pMsgBuff->message; current_len = strlen(pMsgBuff->out_buff); tot_line = current_len + len; /* flush buffer if overflow would occor */ if (tot_line > MAXLINE) dbpr_msg_flush(pMsgBuff, tab_size); /* append message to buffer */ n = 0; while ((*pmsg) && (n < len)) { *pMsgBuff->pNext++ = *pmsg++; /* position to next tab stop */ if (*(pMsgBuff->pNexTab - 1) != '\0') pMsgBuff->pNexTab = pMsgBuff->pNexTab + tab_size; n++; } /* fill spaces to next tab stop */ while (*(pMsgBuff->pNexTab - 1) != ' ' && pMsgBuff->pNext < pMsgBuff->pLast) { *pMsgBuff->pNext++ = ' '; } } static void dbpr_msg_flush(TAB_BUFFER *pMsgBuff,int tab_size) { /* skip print if buffer empty */ if (pMsgBuff->pNext != pMsgBuff->out_buff) printf("%s\n", pMsgBuff->out_buff); memset(pMsgBuff->out_buff,'\0', (int) MAXLINE + 1); pMsgBuff->pNext = pMsgBuff->out_buff; pMsgBuff->pNexTab = pMsgBuff->out_buff + tab_size; } base-7.0.3.1/modules/database/src/ioc/db/dbTest.h0000664000577000060420000000323413557101274020155 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_dbTest_H #define INC_dbTest_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /*dbAddr info */ epicsShareFunc long dba(const char *pname); /*list records*/ epicsShareFunc long dbl( const char *precordTypename,const char *fields); /*list number of records of each type*/ epicsShareFunc long dbnr(int verbose); /* list aliases */ epicsShareFunc long dbla(const char *pmask); /* list infos */ epicsShareFunc long dbli(const char *patern); /*list records with mask*/ epicsShareFunc long dbgrep(const char *pmask); /*get field value*/ epicsShareFunc long dbgf(const char *pname); /*put field value*/ epicsShareFunc long dbpf(const char *pname,const char *pvalue); /*print record*/ epicsShareFunc long dbpr(const char *pname,int interest_level); /*test record*/ epicsShareFunc long dbtr(const char *pname); /*test get field*/ epicsShareFunc long dbtgf(const char *pname); /*test put field*/ epicsShareFunc long dbtpf(const char *pname,const char *pvalue); /*I/O report */ epicsShareFunc long dbior( const char *pdrvName,int interest_level); /*Hardware Configuration Report*/ epicsShareFunc int dbhcr(void); #ifdef __cplusplus } #endif #endif /* INC_dbTest_H */ base-7.0.3.1/modules/database/src/ioc/db/dbUnitTest.c0000664000577000060420000003002113557101274021002 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 Brookhaven National Laboratory. * Copyright (c) 2013 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver * Ralph Lange */ #include #define EPICS_PRIVATE_API #include "dbmf.h" #include "epicsUnitTest.h" #include "osiFileName.h" #include "osiUnistd.h" #include "registry.h" #include "epicsEvent.h" #include "epicsThread.h" #define epicsExportSharedSymbols #include "dbAccess.h" #include "dbBase.h" #include "dbChannel.h" #include "dbEvent.h" #include "dbStaticLib.h" #include "dbUnitTest.h" #include "initHooks.h" #include "iocInit.h" #include "errSymTbl.h" static dbEventCtx testEvtCtx; static epicsMutexId testEvtLock; static ELLLIST testEvtList; /* holds testMonitor::node */ struct testMonitor { ELLNODE node; dbEventSubscription sub; dbChannel *chan; epicsEventId event; unsigned count; }; void testdbPrepare(void) { if(!testEvtLock) testEvtLock = epicsMutexMustCreate(); } void testdbReadDatabase(const char* file, const char* path, const char* substitutions) { if(!path) path = "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common"; if(dbReadDatabase(&pdbbase, file, path, substitutions)) { char buf[100]; const char *cwd = getcwd(buf, sizeof(buf)); if(!cwd) cwd = ""; testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)\n from: \"%s\"", file, path, substitutions, cwd); } } void testIocInitOk(void) { if(iocBuildIsolated() || iocRun()) testAbort("Failed to start up test database"); if(!(testEvtCtx=db_init_events())) testAbort("Failed to initialize test dbEvent context"); if(DB_EVENT_OK!=db_start_events(testEvtCtx, "CAS-test", NULL, NULL, epicsThreadPriorityCAServerLow)) testAbort("Failed to start test dbEvent context"); } void testIocShutdownOk(void) { epicsMutexMustLock(testEvtLock); if(ellCount(&testEvtList)) testDiag("Warning, testing monitors still active at testIocShutdownOk()"); epicsMutexUnlock(testEvtLock); db_close_events(testEvtCtx); testEvtCtx = NULL; if(iocShutdown()) testAbort("Failed to shutdown test database"); } void testdbCleanup(void) { dbFreeBase(pdbbase); db_cleanup_events(); initHookFree(); registryFree(); pdbbase = NULL; dbmfFreeChunks(); } union anybuf { epicsAny val; char valStr[MAX_STRING_SIZE]; char bytes[sizeof(epicsAny)]; }; long testdbVPutField(const char* pv, short dbrType, va_list ap) { DBADDR addr; union anybuf pod; if (dbNameToAddr(pv, &addr)) { testFail("Missing PV \"%s\"", pv); return S_dbLib_recNotFound; } switch(dbrType) { case DBR_STRING: { const char *uarg = va_arg(ap,char*); strncpy(pod.valStr, uarg, sizeof(pod.valStr)); pod.valStr[sizeof(pod.valStr)-1] = '\0'; return dbPutField(&addr, dbrType, pod.valStr, 1); } /* The Type parameter takes into consideration * the C language rules for promotion of argument types * in variadic functions. */ #define OP(DBR,Type,mem) case DBR: {pod.val.mem = va_arg(ap,Type); break;} OP(DBR_CHAR, int, int8); OP(DBR_UCHAR, int, uInt8); OP(DBR_SHORT, int, int16); OP(DBR_USHORT, int, uInt16); OP(DBR_LONG, int, int32); OP(DBR_ULONG, unsigned int, uInt32); OP(DBR_INT64, long long, int64); OP(DBR_UINT64, unsigned long long, uInt64); OP(DBR_FLOAT, double, float32); OP(DBR_DOUBLE, double, float64); OP(DBR_ENUM, int, enum16); #undef OP default: testFail("invalid DBR: dbPutField(\"%s\", %d, ...)", addr.precord->name, dbrType); return S_db_badDbrtype; } return dbPutField(&addr, dbrType, pod.bytes, 1); } void testdbPutFieldOk(const char* pv, int dbrType, ...) { long ret; va_list ap; va_start(ap, dbrType); ret = testdbVPutField(pv, dbrType, ap); va_end(ap); testOk(ret==0, "dbPutField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, ret, errSymMsg(ret)); } void testdbPutFieldFail(long status, const char* pv, int dbrType, ...) { long ret; va_list ap; va_start(ap, dbrType); ret = testdbVPutField(pv, dbrType, ap); va_end(ap); testOk(ret==status, "dbPutField(\"%s\", %d, ...) -> %#lx (%s) == %#lx (%s)", pv, dbrType, status, errSymMsg(status), ret, errSymMsg(ret)); } void testdbGetFieldEqual(const char* pv, int dbrType, ...) { va_list ap; va_start(ap, dbrType); testdbVGetFieldEqual(pv, dbrType, ap); va_end(ap); } void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) { DBADDR addr; long nReq = 1; union anybuf pod; long status; if(dbNameToAddr(pv, &addr)) { testFail("Missing PV \"%s\"", pv); return; } status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL); if (status) { testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status)); return; } else if(nReq==0) { testFail("dbGetField(\"%s\", %d, ...) -> zero length", pv, dbrType); return; } switch(dbrType) { case DBR_STRING: { const char *expect = va_arg(ap, char*); testOk(strcmp(expect, pod.valStr)==0, "dbGetField(\"%s\", %d) -> \"%s\" == \"%s\"", pv, dbrType, expect, pod.valStr); break; } #define OP(DBR,Type,mem,pat) case DBR: {Type expect = va_arg(ap,Type); \ testOk(expect==pod.val.mem, "dbGetField(\"%s\", %d) -> " pat " == " pat, \ pv, dbrType, expect, (Type)pod.val.mem); break;} OP(DBR_CHAR, int, int8, "%d"); OP(DBR_UCHAR, int, uInt8, "%d"); OP(DBR_SHORT, int, int16, "%d"); OP(DBR_USHORT, int, uInt16, "%d"); OP(DBR_LONG, int, int32, "%d"); OP(DBR_ULONG, unsigned int, uInt32, "%u"); OP(DBR_INT64, long long, int64, "%lld"); OP(DBR_UINT64, unsigned long long, uInt64, "%llu"); OP(DBR_FLOAT, double, float32, "%e"); OP(DBR_DOUBLE, double, float64, "%e"); OP(DBR_ENUM, int, enum16, "%d"); #undef OP default: testFail("dbGetField(\"%s\", %d) -> unsupported dbf", pv, dbrType); } } void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long count, const void *pbuf) { DBADDR addr; long status; if (dbNameToAddr(pv, &addr)) { testFail("Missing PV \"%s\"", pv); return; } status = dbPutField(&addr, dbrType, pbuf, count); testOk(status==0, "dbPutField(\"%s\", dbr=%d, count=%lu, ...) -> %ld", pv, dbrType, count, status); } void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsigned long cnt, const void *pbufraw) { DBADDR addr; const long vSize = dbValueSize(dbfType); const long nStore = vSize * nRequest; long status; char *gbuf, *gstore; const char *pbuf = pbufraw; if(dbNameToAddr(pv, &addr)) { testFail("Missing PV \"%s\"", pv); return; } gbuf = gstore = malloc(nStore); if(!gbuf && nStore!=0) { /* note that malloc(0) is allowed to return NULL on success */ testFail("Allocation failed esize=%ld total=%ld", vSize, nStore); return; } status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest, NULL); if (status) { testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status); } else { unsigned match = nRequest==cnt; long n, N = nRequest < cnt ? nRequest : cnt; if(!match) testDiag("Length mis-match. expected=%lu actual=%lu", cnt, nRequest); for(n=0; ncount++; epicsMutexUnlock(testEvtLock); epicsEventMustTrigger(mon->event); } testMonitor* testMonitorCreate(const char* pvname, unsigned mask, unsigned opt) { long status; testMonitor *mon; dbChannel *chan; assert(testEvtCtx); mon = callocMustSucceed(1, sizeof(*mon), "testMonitorCreate"); mon->event = epicsEventMustCreate(epicsEventEmpty); chan = mon->chan = dbChannelCreate(pvname); if(!chan) testAbort("testMonitorCreate - dbChannelCreate(\"%s\") fails", pvname); if(!!(status=dbChannelOpen(chan))) testAbort("testMonitorCreate - dbChannelOpen(\"%s\") fails w/ %ld", pvname, status); mon->sub = db_add_event(testEvtCtx, chan, &testmonupdate, mon, mask); if(!mon->sub) testAbort("testMonitorCreate - db_add_event(\"%s\") fails", pvname); db_event_enable(mon->sub); epicsMutexMustLock(testEvtLock); ellAdd(&testEvtList, &mon->node); epicsMutexUnlock(testEvtLock); return mon; } void testMonitorDestroy(testMonitor *mon) { if(!mon) return; db_event_disable(mon->sub); epicsMutexMustLock(testEvtLock); ellDelete(&testEvtList, &mon->node); epicsMutexUnlock(testEvtLock); db_cancel_event(mon->sub); dbChannelDelete(mon->chan); epicsEventDestroy(mon->event); free(mon); } void testMonitorWait(testMonitor *mon) { static const double delay = 60.0; switch(epicsEventWaitWithTimeout(mon->event, delay)) { case epicsEventOK: return; case epicsEventWaitTimeout: default: testAbort("testMonitorWait() exceeded %g second timeout", delay); } } unsigned testMonitorCount(testMonitor *mon, unsigned reset) { unsigned count; epicsMutexMustLock(testEvtLock); count = mon->count; if(reset) { mon->count = 0; epicsEventWaitWithTimeout(mon->event, 0); /* clear the event */ } epicsMutexUnlock(testEvtLock); return count; } static epicsMutexId test_global; static epicsThreadOnceId test_global_once = EPICS_THREAD_ONCE_INIT; static void test_global_init(void* ignored) { test_global = epicsMutexMustCreate(); } void testGlobalLock(void) { epicsThreadOnce(&test_global_once, &test_global_init, NULL); epicsMutexMustLock(test_global); } void testGlobalUnlock(void) { epicsMutexUnlock(test_global); } base-7.0.3.1/modules/database/src/ioc/db/dbUnitTest.h0000664000577000060420000001241613557101274021017 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 Brookhaven National Laboratory. * Copyright (c) 2013 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver * Ralph Lange */ #ifndef EPICSUNITTESTDB_H #define EPICSUNITTESTDB_H #include #include "epicsUnitTest.h" #include "dbAddr.h" #include "dbCommon.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void testdbPrepare(void); epicsShareFunc void testdbReadDatabase(const char* file, const char* path, const char* substitutions); epicsShareFunc void testIocInitOk(void); epicsShareFunc void testIocShutdownOk(void); epicsShareFunc void testdbCleanup(void); /* Correct argument types must be used with this var-arg function! * Doing otherwise will result in corruption of argument values! * * int for DBR_UCHAR, DBR_CHAR, DBR_USHORT, DBR_SHORT, DBR_LONG * unsigned int for DBR_ULONG * long long for DBF_INT64 * unsigned long long for DBF_UINT64 * double for DBR_FLOAT and DBR_DOUBLE * const char* for DBR_STRING * * eg. * testdbPutFieldOk("pvname", DBF_ULONG, (unsigned int)5); * testdbPutFieldOk("pvname", DBF_FLOAT, (double)4.1); * testdbPutFieldOk("pvname", DBF_STRING, "hello world"); */ epicsShareFunc void testdbPutFieldOk(const char* pv, int dbrType, ...); /* Tests for put failure */ epicsShareFunc void testdbPutFieldFail(long status, const char* pv, int dbrType, ...); epicsShareFunc long testdbVPutField(const char* pv, short dbrType, va_list ap); epicsShareFunc void testdbGetFieldEqual(const char* pv, int dbrType, ...); epicsShareFunc void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap); epicsShareFunc void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long count, const void *pbuf); /** * @param pv PV name string * @param dbfType One of the DBF_* macros from dbAccess.h * @param nRequest Number of elements to request from pv * @param pbufcnt Number of elements pointed to be pbuf * @param pbuf Expected value buffer * * Execute dbGet() of nRequest elements and compare the result with * pbuf (pbufcnt is an element count). * Element size is derived from dbfType. * * nRequest > pbufcnt will detect truncation. * nRequest < pbufcnt always fails. * nRequest ==pbufcnt checks prefix (actual may be longer than expected) */ epicsShareFunc void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsigned long pbufcnt, const void *pbuf); epicsShareFunc dbCommon* testdbRecordPtr(const char* pv); typedef struct testMonitor testMonitor; /* Begin monitoring the named PV for changes */ epicsShareFunc testMonitor* testMonitorCreate(const char* pvname, unsigned dbe_mask, unsigned opt); /* End monitoring */ epicsShareFunc void testMonitorDestroy(testMonitor*); /* Return immediately if it has been updated since create, last wait, * or reset (count w/ reset=1). * Otherwise, block until the value of the target PV is updated. */ epicsShareFunc void testMonitorWait(testMonitor*); /* Return the number of monitor events which have occured since create, * or a pervious reset (called reset=1). * Calling w/ reset=0 only returns the count. * Calling w/ reset=1 resets the count to zero and ensures that the next * wait will block unless subsequent events occur. Returns the previous * count. */ epicsShareFunc unsigned testMonitorCount(testMonitor*, unsigned reset); /** Synchronize the shared callback queues. * * Block until all callback queue jobs which were queued, or running, * have completed. */ epicsShareFunc void testSyncCallback(void); /** Global mutex for use by test code. * * This utility mutex is intended to be used to avoid races in situations * where some other syncronization primitive is being destroyed (epicsEvent, * epicsMutex, ...). * * For example. The following has a subtle race where the event may be * destroyed (free()'d) before the call to epicsEventMustSignal() has * returned. On some targets this leads to a use after free() error. * @code epicsEventId evt; void thread1() { evt = epicsEventMustCreate(...); // spawn thread2() epicsEventMustWait(evt); epicsEventDestroy(evt); } // ... void thread2() { epicsEventMustSignal(evt); } @endcode * * One way to avoid this race is to use a global mutex to ensure * that epicsEventMustSignal() has returned before destroying * the event. * @code epicsEventId evt; void thread1() { evt = epicsEventMustCreate(...); // spawn thread2() epicsEventMustWait(evt); testGlobalLock(); // <-- added epicsEventDestroy(evt); testGlobalUnlock(); // <-- added } // ... void thread2() { testGlobalLock(); // <-- added epicsEventMustSignal(evt); testGlobalUnlock(); // <-- added } @endcode * * This must be a global mutex to avoid simply shifting the race * from the event to a locally allocated mutex. */ epicsShareFunc void testGlobalLock(void); epicsShareFunc void testGlobalUnlock(void); #ifdef __cplusplus } #endif #endif // EPICSUNITTESTDB_H base-7.0.3.1/modules/database/src/ioc/db/db_access.c0000664000577000060420000011120613557101274020630 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Interface between old database access and new * * Author: Marty Kraimer * Andrew Johnson */ #include #include #include #include #include #include #include "alarm.h" #include "dbDefs.h" #include "ellLib.h" #include "epicsConvert.h" #include "epicsTime.h" #include "errlog.h" #include "errMdef.h" #define db_accessHFORdb_accessC #include "db_access.h" #undef db_accessHFORdb_accessC #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "db_access_routines.h" #include "dbBase.h" #include "dbChannel.h" #include "dbCommon.h" #include "dbEvent.h" #include "dbLock.h" #include "dbNotify.h" #include "dbStaticLib.h" #include "recSup.h" #define oldDBF_STRING 0 #define oldDBF_INT 1 #define oldDBF_SHORT 1 #define oldDBF_FLOAT 2 #define oldDBF_ENUM 3 #define oldDBF_CHAR 4 #define oldDBF_LONG 5 #define oldDBF_DOUBLE 6 /* data request buffer types */ #define oldDBR_STRING oldDBF_STRING #define oldDBR_INT oldDBF_INT #define oldDBR_SHORT oldDBF_INT #define oldDBR_FLOAT oldDBF_FLOAT #define oldDBR_ENUM oldDBF_ENUM #define oldDBR_CHAR oldDBF_CHAR #define oldDBR_LONG oldDBF_LONG #define oldDBR_DOUBLE oldDBF_DOUBLE #define oldDBR_STS_STRING 7 #define oldDBR_STS_INT 8 #define oldDBR_STS_SHORT 8 #define oldDBR_STS_FLOAT 9 #define oldDBR_STS_ENUM 10 #define oldDBR_STS_CHAR 11 #define oldDBR_STS_LONG 12 #define oldDBR_STS_DOUBLE 13 #define oldDBR_TIME_STRING 14 #define oldDBR_TIME_INT 15 #define oldDBR_TIME_SHORT 15 #define oldDBR_TIME_FLOAT 16 #define oldDBR_TIME_ENUM 17 #define oldDBR_TIME_CHAR 18 #define oldDBR_TIME_LONG 19 #define oldDBR_TIME_DOUBLE 20 #define oldDBR_GR_STRING 21 #define oldDBR_GR_INT 22 #define oldDBR_GR_SHORT 22 #define oldDBR_GR_FLOAT 23 #define oldDBR_GR_ENUM 24 #define oldDBR_GR_CHAR 25 #define oldDBR_GR_LONG 26 #define oldDBR_GR_DOUBLE 27 #define oldDBR_CTRL_STRING 28 #define oldDBR_CTRL_INT 29 #define oldDBR_CTRL_SHORT 29 #define oldDBR_CTRL_FLOAT 30 #define oldDBR_CTRL_ENUM 31 #define oldDBR_CTRL_CHAR 32 #define oldDBR_CTRL_LONG 33 #define oldDBR_CTRL_DOUBLE 34 #define oldDBR_PUT_ACKT oldDBR_CTRL_DOUBLE + 1 #define oldDBR_PUT_ACKS oldDBR_PUT_ACKT + 1 #define oldDBR_STSACK_STRING oldDBR_PUT_ACKS + 1 #define oldDBR_CLASS_NAME oldDBR_STSACK_STRING + 1 typedef char DBSTRING[MAX_STRING_SIZE]; struct dbChannel * dbChannel_create(const char *pname) { dbChannel *chan = dbChannelCreate(pname); if (!chan) return NULL; if (INVALID_DB_REQ(dbChannelExportType(chan)) || dbChannelOpen(chan)) { dbChannelDelete(chan); return NULL; } return chan; } int dbChannel_get(struct dbChannel *chan, int buffer_type, void *pbuffer, long no_elements, void *pfl) { long nRequest = no_elements; int result = dbChannel_get_count( chan, buffer_type, pbuffer, &nRequest, pfl); if (nRequest < no_elements) { /* The database request returned fewer elements than requested, so * fill the remainder of the buffer with zeros. */ int val_size = dbr_value_size[buffer_type]; int offset = dbr_size[buffer_type] + (nRequest - 1) * val_size; int nbytes = (no_elements - nRequest) * val_size; memset((char *)pbuffer + offset, 0, nbytes); } return result; } /* Performs the work of the public db_get_field API, but also returns the number * of elements actually copied to the buffer. The caller is responsible for * zeroing the remaining part of the buffer. */ int dbChannel_get_count( struct dbChannel *chan, int buffer_type, void *pbuffer, long *nRequest, void *pfl) { long status; long options; long i; long zero = 0; /* The order of the DBR* elements in the "newSt" structures below is * very important and must correspond to the order of processing * in the dbAccess.c dbGet() and getOptions() routines. */ dbScanLock(dbChannelRecord(chan)); switch(buffer_type) { case(oldDBR_STRING): status = dbChannelGet(chan, DBR_STRING, pbuffer, &zero, nRequest, pfl); break; /* case(oldDBR_INT): */ case(oldDBR_SHORT): status = dbChannelGet(chan, DBR_SHORT, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_FLOAT): status = dbChannelGet(chan, DBR_FLOAT, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_ENUM): status = dbChannelGet(chan, DBR_ENUM, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_CHAR): status = dbChannelGet(chan, DBR_CHAR, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_LONG): status = dbChannelGet(chan, DBR_LONG, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_DOUBLE): status = dbChannelGet(chan, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_STS_STRING): case(oldDBR_GR_STRING): case(oldDBR_CTRL_STRING): { struct dbr_sts_string *pold = (struct dbr_sts_string *)pbuffer; struct { DBRstatus } newSt; options = DBR_STATUS; status = dbChannelGet(chan, DBR_STRING, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; status = dbChannelGet(chan, DBR_STRING, pold->value, &zero, nRequest, pfl); } break; /* case(oldDBR_STS_INT): */ case(oldDBR_STS_SHORT): { struct dbr_sts_int *pold = (struct dbr_sts_int *)pbuffer; struct { DBRstatus } newSt; options = DBR_STATUS; status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; status = dbChannelGet(chan, DBR_SHORT, &pold->value, &zero, nRequest, pfl); } break; case(oldDBR_STS_FLOAT): { struct dbr_sts_float *pold = (struct dbr_sts_float *)pbuffer; struct { DBRstatus } newSt; options = DBR_STATUS; status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &zero, nRequest, pfl); } break; case(oldDBR_STS_ENUM): { struct dbr_sts_enum *pold = (struct dbr_sts_enum *)pbuffer; struct { DBRstatus } newSt; options = DBR_STATUS; status = dbChannelGet(chan, DBR_ENUM, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; status = dbChannelGet(chan, DBR_ENUM, &pold->value, &zero, nRequest, pfl); } break; case(oldDBR_STS_CHAR): { struct dbr_sts_char *pold = (struct dbr_sts_char *)pbuffer; struct { DBRstatus } newSt; options = DBR_STATUS; status = dbChannelGet(chan, DBR_UCHAR, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; status = dbChannelGet(chan, DBR_UCHAR, &pold->value, &zero, nRequest, pfl); } break; case(oldDBR_STS_LONG): { struct dbr_sts_long *pold = (struct dbr_sts_long *)pbuffer; struct { DBRstatus } newSt; options = DBR_STATUS; status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; status = dbChannelGet(chan, DBR_LONG, &pold->value, &zero, nRequest, pfl); } break; case(oldDBR_STS_DOUBLE): { struct dbr_sts_double *pold = (struct dbr_sts_double *)pbuffer; struct { DBRstatus } newSt; options = DBR_STATUS; status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; options = 0; status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_TIME_STRING): { struct dbr_time_string *pold = (struct dbr_time_string *)pbuffer; struct { DBRstatus DBRtime } newSt; options = DBR_STATUS | DBR_TIME; status = dbChannelGet(chan, DBR_STRING, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; status = dbChannelGet(chan, DBR_STRING, pold->value, &options, nRequest, pfl); } break; /* case(oldDBR_TIME_INT): */ case(oldDBR_TIME_SHORT): { struct dbr_time_short *pold = (struct dbr_time_short *)pbuffer; struct { DBRstatus DBRtime } newSt; options = DBR_STATUS | DBR_TIME; status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; status = dbChannelGet(chan, DBR_SHORT, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_TIME_FLOAT): { struct dbr_time_float *pold = (struct dbr_time_float *)pbuffer; struct { DBRstatus DBRtime } newSt; options = DBR_STATUS | DBR_TIME; status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_TIME_ENUM): { struct dbr_time_enum *pold = (struct dbr_time_enum *)pbuffer; struct { DBRstatus DBRtime } newSt; options = DBR_STATUS | DBR_TIME; status = dbChannelGet(chan, DBR_ENUM, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; status = dbChannelGet(chan, DBR_ENUM, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_TIME_CHAR): { struct dbr_time_char *pold = (struct dbr_time_char *)pbuffer; struct { DBRstatus DBRtime } newSt; options = DBR_STATUS | DBR_TIME; status = dbChannelGet(chan, DBR_CHAR, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; status = dbChannelGet(chan, DBR_CHAR, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_TIME_LONG): { struct dbr_time_long *pold = (struct dbr_time_long *)pbuffer; struct { DBRstatus DBRtime } newSt; options = DBR_STATUS | DBR_TIME; status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; status = dbChannelGet(chan, DBR_LONG, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_TIME_DOUBLE): { struct dbr_time_double *pold = (struct dbr_time_double *)pbuffer; struct { DBRstatus DBRtime } newSt; options = DBR_STATUS | DBR_TIME; status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options, nRequest, pfl); } break; /* case(oldDBR_GR_STRING): NOT IMPLEMENTED - use DBR_STS_STRING */ /* case(oldDBR_GR_INT): */ case(oldDBR_GR_SHORT): { struct dbr_gr_int *pold = (struct dbr_gr_int *)pbuffer; struct { DBRstatus DBRunits DBRgrLong DBRalLong } newSt; options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG; status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); pold->units[MAX_UNITS_SIZE-1] = '\0'; pold->upper_disp_limit = newSt.upper_disp_limit; pold->lower_disp_limit = newSt.lower_disp_limit; pold->upper_alarm_limit = newSt.upper_alarm_limit; pold->upper_warning_limit = newSt.upper_warning_limit; pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; options = 0; status = dbChannelGet(chan, DBR_SHORT, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_GR_FLOAT): { struct dbr_gr_float *pold = (struct dbr_gr_float *)pbuffer; struct { DBRstatus DBRunits DBRprecision DBRgrDouble DBRalDouble } newSt; options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | DBR_AL_DOUBLE; status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->precision = (dbr_short_t) newSt.precision.dp; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); pold->units[MAX_UNITS_SIZE-1] = '\0'; pold->upper_disp_limit = epicsConvertDoubleToFloat(newSt.upper_disp_limit); pold->lower_disp_limit = epicsConvertDoubleToFloat(newSt.lower_disp_limit); pold->upper_alarm_limit = epicsConvertDoubleToFloat(newSt.upper_alarm_limit); pold->lower_alarm_limit = epicsConvertDoubleToFloat(newSt.lower_alarm_limit); pold->upper_warning_limit = epicsConvertDoubleToFloat(newSt.upper_warning_limit); pold->lower_warning_limit = epicsConvertDoubleToFloat(newSt.lower_warning_limit); options = 0; status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &options, nRequest, pfl); } break; /* case(oldDBR_GR_ENUM): see oldDBR_CTRL_ENUM */ case(oldDBR_GR_CHAR): { struct dbr_gr_char *pold = (struct dbr_gr_char *)pbuffer; struct { DBRstatus DBRunits DBRgrLong DBRalLong } newSt; options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG; status = dbChannelGet(chan, DBR_UCHAR, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); pold->units[MAX_UNITS_SIZE-1] = '\0'; pold->upper_disp_limit = newSt.upper_disp_limit; pold->lower_disp_limit = newSt.lower_disp_limit; pold->upper_alarm_limit = newSt.upper_alarm_limit; pold->upper_warning_limit = newSt.upper_warning_limit; pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; options = 0; status = dbChannelGet(chan, DBR_UCHAR, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_GR_LONG): { struct dbr_gr_long *pold = (struct dbr_gr_long *)pbuffer; struct { DBRstatus DBRunits DBRgrLong DBRalLong } newSt; options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG; status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); pold->units[MAX_UNITS_SIZE-1] = '\0'; pold->upper_disp_limit = newSt.upper_disp_limit; pold->lower_disp_limit = newSt.lower_disp_limit; pold->upper_alarm_limit = newSt.upper_alarm_limit; pold->upper_warning_limit = newSt.upper_warning_limit; pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; options = 0; status = dbChannelGet(chan, DBR_LONG, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_GR_DOUBLE): { struct dbr_gr_double *pold = (struct dbr_gr_double *)pbuffer; struct { DBRstatus DBRunits DBRprecision DBRgrDouble DBRalDouble } newSt; options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | DBR_AL_DOUBLE; status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->precision = (dbr_short_t) newSt.precision.dp; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); pold->units[MAX_UNITS_SIZE-1] = '\0'; pold->upper_disp_limit = newSt.upper_disp_limit; pold->lower_disp_limit = newSt.lower_disp_limit; pold->upper_alarm_limit = newSt.upper_alarm_limit; pold->upper_warning_limit = newSt.upper_warning_limit; pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; options = 0; status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options, nRequest, pfl); } break; /* case(oldDBR_CTRL_INT): */ case(oldDBR_CTRL_SHORT): { struct dbr_ctrl_int *pold = (struct dbr_ctrl_int *)pbuffer; struct { DBRstatus DBRunits DBRgrLong DBRctrlLong DBRalLong } newSt; options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG | DBR_AL_LONG; status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); pold->units[MAX_UNITS_SIZE-1] = '\0'; pold->upper_disp_limit = newSt.upper_disp_limit; pold->lower_disp_limit = newSt.lower_disp_limit; pold->upper_alarm_limit = newSt.upper_alarm_limit; pold->upper_warning_limit = newSt.upper_warning_limit; pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; pold->upper_ctrl_limit = newSt.upper_ctrl_limit; pold->lower_ctrl_limit = newSt.lower_ctrl_limit; options = 0; status = dbChannelGet(chan, DBR_SHORT, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_CTRL_FLOAT): { struct dbr_ctrl_float *pold = (struct dbr_ctrl_float *)pbuffer; struct { DBRstatus DBRunits DBRprecision DBRgrDouble DBRctrlDouble DBRalDouble } newSt; options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | DBR_CTRL_DOUBLE | DBR_AL_DOUBLE; status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->precision = (dbr_short_t) newSt.precision.dp; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); pold->units[MAX_UNITS_SIZE-1] = '\0'; pold->upper_disp_limit = epicsConvertDoubleToFloat(newSt.upper_disp_limit); pold->lower_disp_limit = epicsConvertDoubleToFloat(newSt.lower_disp_limit); pold->upper_alarm_limit = epicsConvertDoubleToFloat(newSt.upper_alarm_limit); pold->lower_alarm_limit = epicsConvertDoubleToFloat(newSt.lower_alarm_limit); pold->upper_warning_limit = epicsConvertDoubleToFloat(newSt.upper_warning_limit); pold->lower_warning_limit = epicsConvertDoubleToFloat(newSt.lower_warning_limit); pold->upper_ctrl_limit = epicsConvertDoubleToFloat(newSt.upper_ctrl_limit); pold->lower_ctrl_limit = epicsConvertDoubleToFloat(newSt.lower_ctrl_limit); options = 0; status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_GR_ENUM): case(oldDBR_CTRL_ENUM): { struct dbr_ctrl_enum *pold = (struct dbr_ctrl_enum *)pbuffer; struct { DBRstatus DBRenumStrs } newSt; short no_str; memset(pold, '\0', sizeof(struct dbr_ctrl_enum)); /* first get status and severity */ options = DBR_STATUS | DBR_ENUM_STRS; status = dbChannelGet(chan, DBR_ENUM, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; no_str = newSt.no_str; if (no_str>16) no_str=16; pold->no_str = no_str; for (i = 0; i < no_str; i++) strncpy(pold->strs[i], newSt.strs[i], sizeof(pold->strs[i])); /*now get values*/ options = 0; status = dbChannelGet(chan, DBR_ENUM, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_CTRL_CHAR): { struct dbr_ctrl_char *pold = (struct dbr_ctrl_char *)pbuffer; struct { DBRstatus DBRunits DBRgrLong DBRctrlLong DBRalLong } newSt; options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG | DBR_AL_LONG; status = dbChannelGet(chan, DBR_UCHAR, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); pold->units[MAX_UNITS_SIZE-1] = '\0'; pold->upper_disp_limit = newSt.upper_disp_limit; pold->lower_disp_limit = newSt.lower_disp_limit; pold->upper_alarm_limit = newSt.upper_alarm_limit; pold->upper_warning_limit = newSt.upper_warning_limit; pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; pold->upper_ctrl_limit = newSt.upper_ctrl_limit; pold->lower_ctrl_limit = newSt.lower_ctrl_limit; options = 0; status = dbChannelGet(chan, DBR_UCHAR, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_CTRL_LONG): { struct dbr_ctrl_long *pold = (struct dbr_ctrl_long *)pbuffer; struct { DBRstatus DBRunits DBRgrLong DBRctrlLong DBRalLong } newSt; options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG | DBR_AL_LONG; status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); pold->units[MAX_UNITS_SIZE-1] = '\0'; pold->upper_disp_limit = newSt.upper_disp_limit; pold->lower_disp_limit = newSt.lower_disp_limit; pold->upper_alarm_limit = newSt.upper_alarm_limit; pold->upper_warning_limit = newSt.upper_warning_limit; pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; pold->upper_ctrl_limit = newSt.upper_ctrl_limit; pold->lower_ctrl_limit = newSt.lower_ctrl_limit; options = 0; status = dbChannelGet(chan, DBR_LONG, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_CTRL_DOUBLE): { struct dbr_ctrl_double *pold = (struct dbr_ctrl_double *)pbuffer; struct { DBRstatus DBRunits DBRprecision DBRgrDouble DBRctrlDouble DBRalDouble } newSt; options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | DBR_CTRL_DOUBLE | DBR_AL_DOUBLE; status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->precision = (dbr_short_t) newSt.precision.dp; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); pold->units[MAX_UNITS_SIZE-1] = '\0'; pold->upper_disp_limit = newSt.upper_disp_limit; pold->lower_disp_limit = newSt.lower_disp_limit; pold->upper_alarm_limit = newSt.upper_alarm_limit; pold->upper_warning_limit = newSt.upper_warning_limit; pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; pold->upper_ctrl_limit = newSt.upper_ctrl_limit; pold->lower_ctrl_limit = newSt.lower_ctrl_limit; options = 0; status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options, nRequest, pfl); } break; case(oldDBR_STSACK_STRING): { struct dbr_stsack_string *pold = (struct dbr_stsack_string *)pbuffer; struct { DBRstatus } newSt; options = DBR_STATUS; status = dbChannelGet(chan, DBR_STRING, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->ackt = newSt.ackt; pold->acks = newSt.acks; options = 0; status = dbChannelGet(chan, DBR_STRING, pold->value, &options, nRequest, pfl); } break; case(oldDBR_CLASS_NAME): { DBENTRY dbEntry; char *name = 0; char *pto = (char *)pbuffer; if (!pdbbase) { status = S_db_notFound; break; } dbInitEntry(pdbbase, &dbEntry); status = dbFindRecord(&dbEntry, dbChannelRecord(chan)->name); if (!status) name = dbGetRecordTypeName(&dbEntry); dbFinishEntry(&dbEntry); if (status) break; if (!name) { status = S_dbLib_recordTypeNotFound; break; } pto[MAX_STRING_SIZE-1] = 0; strncpy(pto, name, MAX_STRING_SIZE-1); } break; default: status = -1; break; } dbScanUnlock(dbChannelRecord(chan)); if (status) return -1; return 0; } int dbChannel_put(struct dbChannel *chan, int src_type, const void *psrc, long no_elements) { long status; switch (src_type) { case(oldDBR_STRING): status = dbChannelPutField(chan, DBR_STRING, psrc, no_elements); break; /* case(oldDBR_INT): */ case(oldDBR_SHORT): status = dbChannelPutField(chan, DBR_SHORT, psrc, no_elements); break; case(oldDBR_FLOAT): status = dbChannelPutField(chan, DBR_FLOAT, psrc, no_elements); break; case(oldDBR_ENUM): status = dbChannelPutField(chan, DBR_ENUM, psrc, no_elements); break; case(oldDBR_CHAR): status = dbChannelPutField(chan, DBR_UCHAR, psrc, no_elements); break; case(oldDBR_LONG): status = dbChannelPutField(chan, DBR_LONG, psrc, no_elements); break; case(oldDBR_DOUBLE): status = dbChannelPutField(chan, DBR_DOUBLE, psrc, no_elements); break; case(oldDBR_STS_STRING): status = dbChannelPutField(chan, DBR_STRING, ((const struct dbr_sts_string *)psrc)->value, no_elements); break; /* case(oldDBR_STS_INT): */ case(oldDBR_STS_SHORT): status = dbChannelPutField(chan, DBR_SHORT, &((const struct dbr_sts_short *)psrc)->value, no_elements); break; case(oldDBR_STS_FLOAT): status = dbChannelPutField(chan, DBR_FLOAT, &((const struct dbr_sts_float *)psrc)->value, no_elements); break; case(oldDBR_STS_ENUM): status = dbChannelPutField(chan, DBR_ENUM, &((const struct dbr_sts_enum *)psrc)->value, no_elements); break; case(oldDBR_STS_CHAR): status = dbChannelPutField(chan, DBR_UCHAR, &((const struct dbr_sts_char *)psrc)->value, no_elements); break; case(oldDBR_STS_LONG): status = dbChannelPutField(chan, DBR_LONG, &((const struct dbr_sts_long *)psrc)->value, no_elements); break; case(oldDBR_STS_DOUBLE): status = dbChannelPutField(chan, DBR_DOUBLE, &((const struct dbr_sts_double *)psrc)->value, no_elements); break; case(oldDBR_TIME_STRING): status = dbChannelPutField(chan, DBR_TIME, ((const struct dbr_time_string *)psrc)->value, no_elements); break; /* case(oldDBR_TIME_INT): */ case(oldDBR_TIME_SHORT): status = dbChannelPutField(chan, DBR_SHORT, &((const struct dbr_time_short *)psrc)->value, no_elements); break; case(oldDBR_TIME_FLOAT): status = dbChannelPutField(chan, DBR_FLOAT, &((const struct dbr_time_float *)psrc)->value, no_elements); break; case(oldDBR_TIME_ENUM): status = dbChannelPutField(chan, DBR_ENUM, &((const struct dbr_time_enum *)psrc)->value, no_elements); break; case(oldDBR_TIME_CHAR): status = dbChannelPutField(chan, DBR_UCHAR, &((const struct dbr_time_char *)psrc)->value, no_elements); break; case(oldDBR_TIME_LONG): status = dbChannelPutField(chan, DBR_LONG, &((const struct dbr_time_long *)psrc)->value, no_elements); break; case(oldDBR_TIME_DOUBLE): status = dbChannelPutField(chan, DBR_DOUBLE, &((const struct dbr_time_double *)psrc)->value, no_elements); break; case(oldDBR_GR_STRING): /* no struct dbr_gr_string - use dbr_sts_string instead */ status = dbChannelPutField(chan, DBR_STRING, ((const struct dbr_sts_string *)psrc)->value, no_elements); break; /* case(oldDBR_GR_INT): */ case(oldDBR_GR_SHORT): status = dbChannelPutField(chan, DBR_SHORT, &((const struct dbr_gr_short *)psrc)->value, no_elements); break; case(oldDBR_GR_FLOAT): status = dbChannelPutField(chan, DBR_FLOAT, &((const struct dbr_gr_float *)psrc)->value, no_elements); break; case(oldDBR_GR_ENUM): status = dbChannelPutField(chan, DBR_ENUM, &((const struct dbr_gr_enum *)psrc)->value, no_elements); break; case(oldDBR_GR_CHAR): status = dbChannelPutField(chan, DBR_UCHAR, &((const struct dbr_gr_char *)psrc)->value, no_elements); break; case(oldDBR_GR_LONG): status = dbChannelPutField(chan, DBR_LONG, &((const struct dbr_gr_long *)psrc)->value, no_elements); break; case(oldDBR_GR_DOUBLE): status = dbChannelPutField(chan, DBR_DOUBLE, &((const struct dbr_gr_double *)psrc)->value, no_elements); break; case(oldDBR_CTRL_STRING): /* no struct dbr_ctrl_string - use dbr_sts_string instead */ status = dbChannelPutField(chan, DBR_STRING, ((const struct dbr_sts_string *)psrc)->value, no_elements); break; /* case(oldDBR_CTRL_INT): */ case(oldDBR_CTRL_SHORT): status = dbChannelPutField(chan, DBR_SHORT, &((const struct dbr_ctrl_short *)psrc)->value, no_elements); break; case(oldDBR_CTRL_FLOAT): status = dbChannelPutField(chan, DBR_FLOAT, &((const struct dbr_ctrl_float *)psrc)->value, no_elements); break; case(oldDBR_CTRL_ENUM): status = dbChannelPutField(chan, DBR_ENUM, &((const struct dbr_ctrl_enum *)psrc)->value, no_elements); break; case(oldDBR_CTRL_CHAR): status = dbChannelPutField(chan, DBR_UCHAR, &((const struct dbr_ctrl_char *)psrc)->value, no_elements); break; case(oldDBR_CTRL_LONG): status = dbChannelPutField(chan, DBR_LONG, &((const struct dbr_ctrl_long *)psrc)->value, no_elements); break; case(oldDBR_CTRL_DOUBLE): status = dbChannelPutField(chan, DBR_DOUBLE, &((const struct dbr_ctrl_double *)psrc)->value, no_elements); break; case(oldDBR_PUT_ACKT): status = dbChannelPutField(chan, DBR_PUT_ACKT, psrc, no_elements); break; case(oldDBR_PUT_ACKS): status = dbChannelPutField(chan, DBR_PUT_ACKS, psrc, no_elements); break; default: return -1; } if (status) return -1; return 0; } static int mapOldType (short oldtype) { int dbrType = -1; switch (oldtype) { case oldDBR_STRING: dbrType = DBR_STRING; break; /* case oldDBR_INT: */ case oldDBR_SHORT: dbrType = DBR_SHORT; break; case oldDBR_FLOAT: dbrType = DBR_FLOAT; break; case oldDBR_ENUM: dbrType = DBR_ENUM; break; case oldDBR_CHAR: dbrType = DBR_UCHAR; break; case oldDBR_LONG: dbrType = DBR_LONG; break; case oldDBR_DOUBLE: dbrType = DBR_DOUBLE; break; case oldDBR_PUT_ACKT: dbrType = DBR_PUT_ACKT; break; case oldDBR_PUT_ACKS: dbrType = DBR_PUT_ACKS; break; default: return -1; } return dbrType; } int db_put_process(processNotify *ppn, notifyPutType type, int src_type, const void *psrc, int no_elements) { int status = 0; int dbrType = mapOldType(src_type); switch(type) { case putDisabledType: ppn->status = notifyError; return 0; case putFieldType: status = dbChannelPutField(ppn->chan, dbrType, psrc, no_elements); break; case putType: status = dbChannelPut(ppn->chan, dbrType, psrc, no_elements); break; } if (status) ppn->status = notifyError; return 1; } base-7.0.3.1/modules/database/src/ioc/db/db_access_routines.h0000664000577000060420000000266413557101274022574 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* This defined routines for old database access. These were broken out of db_access.h so that ca can be build independent of db. src/ca contains db_access, which contains that data definitions */ #ifndef INCLdb_access_routinesh #define INCLdb_access_routinesh #ifdef __cplusplus extern "C" { #endif #include "shareLib.h" epicsShareExtern struct dbBase *pdbbase; epicsShareExtern volatile int interruptAccept; /* * Adaptors for db_access users */ epicsShareFunc struct dbChannel * dbChannel_create(const char *pname); epicsShareFunc int dbChannel_get(struct dbChannel *chan, int buffer_type, void *pbuffer, long no_elements, void *pfl); epicsShareFunc int dbChannel_put(struct dbChannel *chan, int src_type, const void *psrc, long no_elements); epicsShareFunc int dbChannel_get_count(struct dbChannel *chan, int buffer_type, void *pbuffer, long *nRequest, void *pfl); #ifdef __cplusplus } #endif #endif /* INCLdb_access_routinesh */ base-7.0.3.1/modules/database/src/ioc/db/db_convert.h0000664000577000060420000000474513557101274021065 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* db_convert.h */ #ifndef INCLdb_converth #define INCLdb_converth #ifdef __cplusplus extern "C" { #endif #include "shareLib.h" #include "dbAddr.h" epicsShareExtern struct dbBase *pdbbase; epicsShareExtern volatile int interruptAccept; /*Definitions that allow old database access to use new conversion routines*/ #define newDBF_DEVICE 13 #define newDBR_ENUM 11 epicsShareExtern long (*dbGetConvertRoutine[newDBF_DEVICE+1][newDBR_ENUM+1]) (struct dbAddr *paddr, void *pbuffer,long nRequest, long no_elements, long offset); epicsShareExtern long (*dbPutConvertRoutine[newDBR_ENUM+1][newDBF_DEVICE+1]) (struct dbAddr *paddr, const void *pbuffer,long nRequest, long no_elements, long offset); epicsShareExtern long (*dbFastGetConvertRoutine[newDBF_DEVICE+1][newDBR_ENUM+1]) (const void *from, void *to, dbAddr *paddr); epicsShareExtern long (*dbFastPutConvertRoutine[newDBR_ENUM+1][newDBF_DEVICE+1]) (const void *from, void *to, dbAddr *paddr); /*Conversion between old and new DBR types*/ epicsShareExtern unsigned short dbDBRoldToDBFnew[DBR_DOUBLE+1]; epicsShareExtern unsigned short dbDBRnewToDBRold[newDBR_ENUM+1]; #ifdef DB_CONVERT_GBLSOURCE epicsShareDef unsigned short dbDBRoldToDBFnew[DBR_DOUBLE+1] = { 0, /*DBR_STRING to DBF_STRING*/ 3, /*DBR_INT to DBF_SHORT*/ 9, /*DBR_FLOAT to DBF_FLOAT*/ 11, /*DBR_ENUM to DBF_ENUM*/ 1, /*DBR_CHAR to DBF_CHAR*/ 5, /*DBR_LONG to DBF_LONG*/ 10 /*DBR_DOUBLE to DBF_DOUBLE*/ }; epicsShareDef unsigned short dbDBRnewToDBRold[newDBR_ENUM+1] = { 0, /*DBR_STRING to DBR_STRING*/ 4, /*DBR_CHAR to DBR_CHAR*/ 4, /*DBR_UCHAR to DBR_CHAR*/ 1, /*DBR_SHORT to DBR_SHORT*/ 5, /*DBR_USHORT to DBR_LONG*/ 5, /*DBR_LONG to DBR_LONG*/ 6, /*DBR_ULONG to DBR_DOUBLE*/ 6, /*DBR_INT64 to DBR_DOUBLE*/ 6, /*DBR_UINT64 to DBR_DOUBLE*/ 2, /*DBR_FLOAT to DBR_FLOAT*/ 6, /*DBR_DOUBLE to DBR_DOUBLE*/ 3, /*DBR_ENUM to DBR_ENUM*/ }; #endif /*DB_CONVERT_GBLSOURCE*/ #ifdef __cplusplus } #endif #endif /* INCLdb_converth */ base-7.0.3.1/modules/database/src/ioc/db/db_field_log.h0000664000577000060420000001074713557101274021330 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeffrey O. Hill * * Ralph Lange */ #ifndef INCLdb_field_logh #define INCLdb_field_logh #include #include #ifdef __cplusplus extern "C" { #endif /* * Simple native types (anything which is not a string or an array for * now) logged by db_post_events() for reliable interprocess communication. * (for other types they get whatever happens to be there when the lower * priority task pending on the event queue wakes up). Strings would slow down * events for more reasonable size values. DB fields of native type string * will most likely change infrequently. * * Strings can be added to the set of types for which updates will be queued * by defining the macro DB_EVENT_LOG_STRINGS. The code in db_add_event() * will adjust automatically, it just compares field sizes. */ union native_value { epicsInt8 dbf_char; epicsInt16 dbf_short; epicsEnum16 dbf_enum; epicsInt32 dbf_long; epicsFloat32 dbf_float; epicsFloat64 dbf_double; #ifdef DB_EVENT_LOG_STRINGS char dbf_string[MAX_STRING_SIZE]; #endif }; /* * structure to log the state of a data base field at the time * an event is triggered. */ struct db_field_log; typedef void (dbfl_freeFunc)(struct db_field_log *pfl); /* Types of db_field_log: rec = use record, val = val inside, ref = reference inside */ typedef enum dbfl_type { dbfl_type_rec = 0, dbfl_type_val, dbfl_type_ref } dbfl_type; /* Context of db_field_log: event = subscription update, read = read reply */ typedef enum dbfl_context { dbfl_context_read = 0, dbfl_context_event } dbfl_context; #define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref") struct dbfl_val { union native_value field; /* Field value */ }; /* External data reference. * If dtor is provided then it should be called when the referenced * data is no longer needed. This is done automatically by * db_delete_field_log(). Any code which changes a dbfl_type_ref * field log to another type, or to reference different data, * must explicitly call the dtor function. */ struct dbfl_ref { dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */ void *pvt; /* Private pointer */ void *field; /* Field value */ }; typedef struct db_field_log { unsigned int type:2; /* type (union) selector */ /* ctx is used for all types */ unsigned int ctx:1; /* context (operation type) */ /* the following are used for value and reference types */ epicsTimeStamp time; /* Time stamp */ unsigned short stat; /* Alarm Status */ unsigned short sevr; /* Alarm Severity */ short field_type; /* DBF type of data */ short field_size; /* Data size */ long no_elements; /* No of array elements */ union { struct dbfl_val v; struct dbfl_ref r; } u; } db_field_log; /* * A db_field_log will in one of three types: * * dbfl_type_rec - Reference to record * The field log stores no data itself. Data must instead be taken * via the dbChannel* which must always be provided when along * with the field log. * For this type only the 'type' and 'ctx' members are used. * * dbfl_type_ref - Reference to outside value * Used for variable size (array) data types. Meta-data * is stored in the field log, but value data is stored externally * (see struct dbfl_ref). * For this type all meta-data members are used. The dbfl_ref side of the * data union is used. * * dbfl_type_val - Internal value * Used to store small scalar data. Meta-data and value are * present in this structure and no external references are used. * For this type all meta-data members are used. The dbfl_val side of the * data union is used. */ #ifdef __cplusplus } #endif #endif /*INCLdb_field_logh*/ base-7.0.3.1/modules/database/src/ioc/db/db_test.c0000664000577000060420000001757113557101274020360 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* database access subroutines */ /* * Author: Bob Dalesio * Andrew Johnson */ #include #include #include #include "cadef.h" #include "dbDefs.h" #include "epicsStdio.h" #include "epicsStdlib.h" #include "epicsString.h" #include "errlog.h" #include "epicsEvent.h" #define epicsExportSharedSymbols #include "db_access_routines.h" #include "dbChannel.h" #include "dbCommon.h" #include "dbNotify.h" #include "db_test.h" #define MAX_ELEMS 10 int gft(const char *pname) { char tgf_buffer[MAX_ELEMS*MAX_STRING_SIZE + sizeof(struct dbr_ctrl_double)]; struct dbChannel *chan; struct dbCommon *precord; long elements; short type; int i; if (!pname) { printf("Usage: gft \"pv_name\"\n"); return -1; } chan = dbChannel_create(pname); if (!chan) { printf("Channel couldn't be created\n"); return 1; } precord = dbChannelRecord(chan); elements = dbChannelElements(chan); type = dbChannelExportCAType(chan); printf(" Record Name: %s\n", precord->name); printf("Record Address: 0x%p\n", precord); printf(" Export Type: %d\n", type); printf(" Field Address: 0x%p\n", dbChannelField(chan)); printf(" Field Size: %d\n", dbChannelFieldSize(chan)); printf(" No Elements: %ld\n", elements); if (elements > MAX_ELEMS) elements = MAX_ELEMS; for (i = 0; i <= LAST_BUFFER_TYPE; i++) { if (type == 0) { if ((i != DBR_STRING) && (i != DBR_STS_STRING) && (i != DBR_TIME_STRING) && (i != DBR_GR_STRING) && (i != DBR_CTRL_STRING)) continue; } if (dbChannel_get(chan, i, tgf_buffer, elements, NULL) < 0) printf("\t%s Failed\n", dbr_text[i]); else ca_dump_dbr(i, elements, tgf_buffer); } dbChannelDelete(chan); return 0; } /* * TPF * Test put field */ int pft(const char *pname, const char *pvalue) { struct dbChannel *chan; struct dbCommon *precord; long elements; short type; char buffer[500]; short shortvalue; long longvalue; float floatvalue; unsigned char charvalue; double doublevalue; if (!pname || !pvalue) { printf("Usage: pft \"pv_name\", \"value\"\n"); return -1; } chan = dbChannel_create(pname); if (!chan) { printf("Channel couldn't be created\n"); return 1; } precord = dbChannelRecord(chan); elements = dbChannelElements(chan); type = dbChannelExportCAType(chan); printf(" Record Name: %s\n", precord->name); printf("Record Address: 0x%p\n", precord); printf(" Export Type: %d\n", type); printf(" Field Address: 0x%p\n", dbChannelField(chan)); printf(" Field Size: %d\n", dbChannelFieldSize(chan)); printf(" No Elements: %ld\n", elements); if (dbChannel_put(chan, DBR_STRING,pvalue, 1) < 0) printf("\n\t failed "); if (dbChannel_get(chan, DBR_STRING,buffer, 1, NULL) < 0) printf("\n\tfailed"); else ca_dump_dbr(DBR_STRING,1, buffer); if (type <= DBF_STRING || type == DBF_ENUM) return 0; if (sscanf(pvalue, "%hd", &shortvalue) == 1) { if (dbChannel_put(chan, DBR_SHORT,&shortvalue, 1) < 0) printf("\n\t SHORT failed "); if (dbChannel_get(chan, DBR_SHORT,buffer, 1, NULL) < 0) printf("\n\t SHORT GET failed"); else ca_dump_dbr(DBR_SHORT,1, buffer); } if (sscanf(pvalue, "%ld", &longvalue) == 1) { if (dbChannel_put(chan, DBR_LONG,&longvalue, 1) < 0) printf("\n\t LONG failed "); if (dbChannel_get(chan, DBR_LONG,buffer, 1, NULL) < 0) printf("\n\t LONG GET failed"); else ca_dump_dbr(DBR_LONG,1, buffer); } if (epicsScanFloat(pvalue, &floatvalue) == 1) { if (dbChannel_put(chan, DBR_FLOAT,&floatvalue, 1) < 0) printf("\n\t FLOAT failed "); if (dbChannel_get(chan, DBR_FLOAT,buffer, 1, NULL) < 0) printf("\n\t FLOAT GET failed"); else ca_dump_dbr(DBR_FLOAT,1, buffer); } if (epicsScanFloat(pvalue, &floatvalue) == 1) { doublevalue = floatvalue; if (dbChannel_put(chan, DBR_DOUBLE,&doublevalue, 1) < 0) printf("\n\t DOUBLE failed "); if (dbChannel_get(chan, DBR_DOUBLE,buffer, 1, NULL) < 0) printf("\n\t DOUBLE GET failed"); else ca_dump_dbr(DBR_DOUBLE,1, buffer); } if (sscanf(pvalue, "%hd", &shortvalue) == 1) { charvalue = (unsigned char) shortvalue; if (dbChannel_put(chan, DBR_CHAR,&charvalue, 1) < 0) printf("\n\t CHAR failed "); if (dbChannel_get(chan, DBR_CHAR,buffer, 1, NULL) < 0) printf("\n\t CHAR GET failed"); else ca_dump_dbr(DBR_CHAR,1, buffer); } if (sscanf(pvalue, "%hd", &shortvalue) == 1) { if (dbChannel_put(chan, DBR_ENUM,&shortvalue, 1) < 0) printf("\n\t ENUM failed "); if (dbChannel_get(chan, DBR_ENUM,buffer, 1, NULL) < 0) printf("\n\t ENUM GET failed"); else ca_dump_dbr(DBR_ENUM,1, buffer); } printf("\n"); dbChannelDelete(chan); return (0); } typedef struct tpnInfo { epicsEventId callbackDone; processNotify *ppn; char buffer[80]; } tpnInfo; static int putCallback(processNotify *ppn,notifyPutType type) { tpnInfo *ptpnInfo = (tpnInfo *)ppn->usrPvt; return db_put_process(ppn, type, DBR_STRING, ptpnInfo->buffer, 1); } static void doneCallback(processNotify *ppn) { tpnInfo *ptpnInfo = (tpnInfo *) ppn->usrPvt; notifyStatus status = ppn->status; const char *pname = dbChannelRecord(ppn->chan)->name; if (status == 0) printf("tpnCallback '%s': Success\n", pname); else printf("tpnCallback '%s': Notify status %d\n", pname, (int)status); epicsEventSignal(ptpnInfo->callbackDone); } static void tpnThread(void *pvt) { tpnInfo *ptpnInfo = (tpnInfo *) pvt; processNotify *ppn = (processNotify *) ptpnInfo->ppn; dbProcessNotify(ppn); epicsEventWait(ptpnInfo->callbackDone); dbNotifyCancel(ppn); epicsEventDestroy(ptpnInfo->callbackDone); dbChannelDelete(ppn->chan); free(ppn); free(ptpnInfo); } int tpn(const char *pname, const char *pvalue) { struct dbChannel *chan; tpnInfo *ptpnInfo; processNotify *ppn = NULL; if (!pname || !pvalue) { printf("Usage: tpn \"pv_name\", \"value\"\n"); return -1; } chan = dbChannel_create(pname); if (!chan) { printf("Channel couldn't be created\n"); return 1; } ppn = calloc(1, sizeof(processNotify)); if (!ppn) { printf("calloc failed\n"); dbChannelDelete(chan); return -1; } ppn->requestType = putProcessRequest; ppn->chan = chan; ppn->putCallback = putCallback; ppn->doneCallback = doneCallback; ptpnInfo = calloc(1, sizeof(tpnInfo)); if (!ptpnInfo) { printf("calloc failed\n"); free(ppn); dbChannelDelete(chan); return -1; } ptpnInfo->ppn = ppn; ptpnInfo->callbackDone = epicsEventCreate(epicsEventEmpty); strncpy(ptpnInfo->buffer, pvalue, 80); ptpnInfo->buffer[79] = 0; ppn->usrPvt = ptpnInfo; epicsThreadCreate("tpn", epicsThreadPriorityHigh, epicsThreadGetStackSize(epicsThreadStackMedium), tpnThread, ptpnInfo); return 0; } base-7.0.3.1/modules/database/src/ioc/db/db_test.h0000664000577000060420000000147513557101274020361 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INCLdb_testh #define INCLdb_testh #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc int gft(const char *pname); epicsShareFunc int pft(const char *pname, const char *pvalue); epicsShareFunc int tpn(const char *pname, const char *pvalue); #ifdef __cplusplus } #endif #endif /* INCLdb_testh */ base-7.0.3.1/modules/database/src/ioc/db/menuAlarmSevr.dbd.pod0000664000577000060420000000162413557101274022575 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =head1 Menu menuAlarmSevr This menu defines the four possible alarm severities that EPICS records can exhibit. Note that it is not possible to add or remove severities just by changing the choices defined here. =menu menuAlarmSevr =cut menu(menuAlarmSevr) { choice(menuAlarmSevrNO_ALARM,"NO_ALARM") choice(menuAlarmSevrMINOR,"MINOR") choice(menuAlarmSevrMAJOR,"MAJOR") choice(menuAlarmSevrINVALID,"INVALID") } base-7.0.3.1/modules/database/src/ioc/db/menuAlarmStat.dbd.pod0000664000577000060420000000310513557101274022565 0ustar anjaesctl#************************************************************************* # 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. # 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. #************************************************************************* =head1 Menu menuAlarmStat This menu defines the possible alarm statuses that EPICS records can exhibit which is used for C and C fields of all record types. See L for more information. =menu menuAlarmStat =cut menu(menuAlarmStat) { choice(menuAlarmStatNO_ALARM,"NO_ALARM") choice(menuAlarmStatREAD,"READ") choice(menuAlarmStatWRITE,"WRITE") choice(menuAlarmStatHIHI,"HIHI") choice(menuAlarmStatHIGH,"HIGH") choice(menuAlarmStatLOLO,"LOLO") choice(menuAlarmStatLOW,"LOW") choice(menuAlarmStatSTATE,"STATE") choice(menuAlarmStatCOS,"COS") choice(menuAlarmStatCOMM,"COMM") choice(menuAlarmStatTIMEOUT,"TIMEOUT") choice(menuAlarmStatHWLIMIT,"HWLIMIT") choice(menuAlarmStatCALC,"CALC") choice(menuAlarmStatSCAN,"SCAN") choice(menuAlarmStatLINK,"LINK") choice(menuAlarmStatSOFT,"SOFT") choice(menuAlarmStatBAD_SUB,"BAD_SUB") choice(menuAlarmStatUDF,"UDF") choice(menuAlarmStatDISABLE,"DISABLE") choice(menuAlarmStatSIMM,"SIMM") choice(menuAlarmStatREAD_ACCESS,"READ_ACCESS") choice(menuAlarmStatWRITE_ACCESS,"WRITE_ACCESS") } base-7.0.3.1/modules/database/src/ioc/db/menuFtype.dbd.pod0000664000577000060420000000200113557101274021756 0ustar anjaesctl#************************************************************************* # Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =head1 Menu menuFtype This menu is used for the C and similar fields of many record types. =menu menuFtype =cut menu(menuFtype) { choice(menuFtypeSTRING,"STRING") choice(menuFtypeCHAR,"CHAR") choice(menuFtypeUCHAR,"UCHAR") choice(menuFtypeSHORT,"SHORT") choice(menuFtypeUSHORT,"USHORT") choice(menuFtypeLONG,"LONG") choice(menuFtypeULONG,"ULONG") choice(menuFtypeINT64,"INT64") choice(menuFtypeUINT64,"UINT64") choice(menuFtypeFLOAT,"FLOAT") choice(menuFtypeDOUBLE,"DOUBLE") choice(menuFtypeENUM,"ENUM") } base-7.0.3.1/modules/database/src/ioc/db/menuIvoa.dbd.pod0000664000577000060420000000155113557101274021576 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =head1 Menu menuIvoa This menu specifies the possibile actions to take when the INVALID alarm is triggered. See individual record types for more information. =menu menuIvoa =cut menu(menuIvoa) { choice(menuIvoaContinue_normally,"Continue normally") choice(menuIvoaDon_t_drive_outputs,"Don't drive outputs") choice(menuIvoaSet_output_to_IVOV,"Set output to IVOV") } base-7.0.3.1/modules/database/src/ioc/db/menuOmsl.dbd.pod0000664000577000060420000000173713557101274021620 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =head1 Menu menuOmsl This menu is used for the C field of many output record types. It controls whether the record will fetch an input value from its C input link when processed, which is useful when it is part of a closed loop control algorithm. The C state means the input link will not be used, C enables the input link. =menu menuOmsl =cut menu(menuOmsl) { choice(menuOmslsupervisory,"supervisory") choice(menuOmslclosed_loop,"closed_loop") } base-7.0.3.1/modules/database/src/ioc/db/menuPini.dbd0000664000577000060420000000126513557101274021020 0ustar anjaesctl#************************************************************************* # Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* menu(menuPini) { choice(menuPiniNO,"NO") choice(menuPiniYES,"YES") choice(menuPiniRUN,"RUN") choice(menuPiniRUNNING,"RUNNING") choice(menuPiniPAUSE,"PAUSE") choice(menuPiniPAUSED,"PAUSED") } base-7.0.3.1/modules/database/src/ioc/db/menuPost.dbd0000664000577000060420000000073513557101274021047 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* menu(menuPost) { choice(menuPost_OnChange, "On Change") choice(menuPost_Always, "Always") } base-7.0.3.1/modules/database/src/ioc/db/menuPriority.dbd0000664000577000060420000000121713557101274021737 0ustar anjaesctl#************************************************************************* # 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. # 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. #************************************************************************* menu(menuPriority) { choice(menuPriorityLOW,"LOW") choice(menuPriorityMEDIUM,"MEDIUM") choice(menuPriorityHIGH,"HIGH") } base-7.0.3.1/modules/database/src/ioc/db/menuScan.dbd.pod0000664000577000060420000000332213557101274021562 0ustar anjaesctl#************************************************************************* # Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =head1 Menu menuScan This menu is used for the C field of all record types. The set of periodic scan rates may be modified for an individual IOC by copying the F file from Base into the IOC's source directory and changing it to contain the desired scan rates. The scan periods are extracted from the choice strings at runtime, which must be expressed as a number with any of the following units appended: =over 4 second seconds minute minutes hour hours Hertz Hz =back At IOC start-up a separate scan thread will be created for each period, with thread priority increasing further down the list, so faster periods should appear after slower ones. Scan rates that cannot be achieved will generate a warning message from the C command. =menu menuScan =cut menu(menuScan) { choice(menuScanPassive,"Passive") choice(menuScanEvent,"Event") choice(menuScanI_O_Intr,"I/O Intr") # Periodic scans follow, ordered from slowest to fastest choice(menuScan10_second,"10 second") choice(menuScan5_second,"5 second") choice(menuScan2_second,"2 second") choice(menuScan1_second,"1 second") choice(menuScan_5_second,".5 second") choice(menuScan_2_second,".2 second") choice(menuScan_1_second,".1 second") } base-7.0.3.1/modules/database/src/ioc/db/menuSimm.dbd.pod0000664000577000060420000000142313557101274021603 0ustar anjaesctl#************************************************************************* # Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =head1 Menu menuSimm This menu is used for Simulation Mode (SIMM) fields of input record types that can fetch either raw or engineering values through their SIOL link. =menu menuSimm =cut menu(menuSimm) { choice(menuSimmNO,"NO") choice(menuSimmYES,"YES") choice(menuSimmRAW,"RAW") } base-7.0.3.1/modules/database/src/ioc/db/menuYesNo.dbd.pod0000664000577000060420000000220413557101274021731 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =head1 Menu menuYesNo This menu is used by many record types to specify simple C or C options for record-specific purposes. Note that no other values for a field that uses menuYesNo are possible, e.g. 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 . =menu menuYesNo =cut menu(menuYesNo) { choice(menuYesNoNO,"NO") choice(menuYesNoYES,"YES") } base-7.0.3.1/modules/database/src/ioc/db/recGbl.c0000664000577000060420000002564313557101274020131 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* recGbl.c */ /* * Author: Marty Kraimer * Andrew Johnson */ #include #include #include #include #include "alarm.h" #include "dbDefs.h" #include "alarm.h" #include "epicsMath.h" #include "epicsPrint.h" #include "epicsStdlib.h" #include "epicsTime.h" #include "errlog.h" #include "caeventmask.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "dbStaticLib.h" #include "dbAddr.h" #include "dbBase.h" #include "dbCommon.h" #include "menuSimm.h" #include "dbEvent.h" #include "db_field_log.h" #include "dbFldTypes.h" #include "dbLink.h" #include "dbNotify.h" #include "dbScan.h" #include "devSup.h" #include "link.h" #include "recGbl.h" /* Hook Routines */ epicsShareDef RECGBL_ALARM_HOOK_ROUTINE recGblAlarmHook = NULL; /* local routines */ static void getMaxRangeValues(short field_type, double *pupper_limit, double *plower_limit); void recGblDbaddrError(long status, const struct dbAddr *paddr, const char *pmessage) { dbCommon *precord = 0; dbFldDes *pdbFldDes = 0; if(paddr) { pdbFldDes = paddr->pfldDes; precord = paddr->precord; } errPrintf(status,0,0, "PV: %s.%s " "error detected in routine: %s\n", (paddr ? precord->name : "Unknown"), (pdbFldDes ? pdbFldDes->name : ""), (pmessage ? pmessage : "Unknown")); return; } void recGblRecordError(long status, void *pdbc, const char *pmessage) { dbCommon *precord = pdbc; errPrintf(status,0,0, "PV: %s %s\n", (precord ? precord->name : "Unknown"), (pmessage ? pmessage : "")); return; } void recGblRecSupError(long status, const struct dbAddr *paddr, const char *pmessage, const char *psupport_name) { dbCommon *precord = 0; dbFldDes *pdbFldDes = 0; dbRecordType *pdbRecordType = 0; if(paddr) { precord = paddr->precord; pdbFldDes = paddr->pfldDes; if(pdbFldDes) pdbRecordType = pdbFldDes->pdbRecordType; } errPrintf(status,0,0, "Record Support Routine (%s) " "Record Type %s " "PV %s.%s " " %s\n", (psupport_name ? psupport_name : "Unknown"), (pdbRecordType ? pdbRecordType->name : "Unknown"), (paddr ? precord->name : "Unknown"), (pdbFldDes ? pdbFldDes->name : ""), (pmessage ? pmessage : "")); return; } void recGblGetPrec(const struct dbAddr *paddr, long *precision) { dbFldDes *pdbFldDes = paddr->pfldDes; switch (pdbFldDes->field_type) { case DBF_CHAR: case DBF_UCHAR: case DBF_SHORT: case DBF_USHORT: case DBF_LONG: case DBF_ULONG: case DBF_INT64: case DBF_UINT64: *precision = 0; break; case DBF_FLOAT: case DBF_DOUBLE: if (*precision < 0 || *precision > 15) *precision = 15; break; default: break; } } void recGblGetGraphicDouble(const struct dbAddr *paddr, struct dbr_grDouble *pgd) { dbFldDes *pdbFldDes = paddr->pfldDes; getMaxRangeValues(pdbFldDes->field_type, &pgd->upper_disp_limit, &pgd->lower_disp_limit); } void recGblGetAlarmDouble(const struct dbAddr *paddr, struct dbr_alDouble *pad) { pad->upper_alarm_limit = epicsNAN; pad->upper_warning_limit = epicsNAN; pad->lower_warning_limit = epicsNAN; pad->lower_alarm_limit = epicsNAN; } void recGblGetControlDouble(const struct dbAddr *paddr, struct dbr_ctrlDouble *pcd) { dbFldDes *pdbFldDes = paddr->pfldDes; getMaxRangeValues(pdbFldDes->field_type, &pcd->upper_ctrl_limit, &pcd->lower_ctrl_limit); } int recGblInitConstantLink(struct link *plink, short dbftype, void *pdest) { return !dbLoadLink(plink, dbftype, pdest); } unsigned short recGblResetAlarms(void *precord) { dbCommon *pdbc = precord; epicsEnum16 prev_stat = pdbc->stat; epicsEnum16 prev_sevr = pdbc->sevr; epicsEnum16 new_stat = pdbc->nsta; epicsEnum16 new_sevr = pdbc->nsev; epicsEnum16 val_mask = 0; epicsEnum16 stat_mask = 0; if (new_sevr > INVALID_ALARM) new_sevr = INVALID_ALARM; pdbc->stat = new_stat; pdbc->sevr = new_sevr; pdbc->nsta = 0; pdbc->nsev = 0; if (prev_sevr != new_sevr) { stat_mask = DBE_ALARM; db_post_events(pdbc, &pdbc->sevr, DBE_VALUE); } if (prev_stat != new_stat) { stat_mask |= DBE_VALUE; } if (stat_mask) { db_post_events(pdbc, &pdbc->stat, stat_mask); val_mask = DBE_ALARM; if (!pdbc->ackt || new_sevr >= pdbc->acks) { pdbc->acks = new_sevr; db_post_events(pdbc, &pdbc->acks, DBE_VALUE); } if (recGblAlarmHook) { (*recGblAlarmHook)(pdbc, prev_sevr, prev_stat); } } return val_mask; } int recGblSetSevr(void *precord, epicsEnum16 new_stat, epicsEnum16 new_sevr) { struct dbCommon *prec = precord; if (prec->nsev < new_sevr) { prec->nsta = new_stat; prec->nsev = new_sevr; return TRUE; } return FALSE; } void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat, epicsEnum16 sevr) { switch (msMode) { case pvlOptNMS: break; case pvlOptMSI: if (sevr < INVALID_ALARM) break; /* Fall through */ case pvlOptMS: recGblSetSevr(precord, LINK_ALARM, sevr); break; case pvlOptMSS: recGblSetSevr(precord, stat, sevr); break; } } void recGblFwdLink(void *precord) { dbCommon *pdbc = precord; dbScanFwdLink(&pdbc->flnk); /*Handle dbPutFieldNotify record completions*/ if(pdbc->ppn) dbNotifyCompletion(pdbc); if(pdbc->rpro) { /*If anyone requested reprocessing do it*/ pdbc->rpro = FALSE; scanOnce(pdbc); } /*In case putField caused put we are all done */ pdbc->putf = FALSE; } void recGblGetTimeStamp(void *pvoid) { recGblGetTimeStampSimm(pvoid, menuSimmNO, 0); } void recGblGetTimeStampSimm(void *pvoid, const epicsEnum16 simm, struct link *siol) { dbCommon *prec = (dbCommon *)pvoid; struct link *plink = &prec->tsel; if (!dbLinkIsConstant(plink)) { if (plink->flags & DBLINK_FLAG_TSELisTIME) { if (dbGetTimeStamp(plink, &prec->time)) errlogPrintf("recGblGetTimeStamp: dbGetTimeStamp failed for %s.TSEL", prec->name); return; } dbGetLink(plink, DBR_SHORT, &prec->tse, 0, 0); } if (prec->tse != epicsTimeEventDeviceTime) { if (epicsTimeGetEvent(&prec->time, prec->tse)) errlogPrintf("recGblGetTimeStampSimm: epicsTimeGetEvent failed, %s.TSE = %d\n", prec->name, prec->tse); } else { if (simm != menuSimmNO) { if (siol && !dbLinkIsConstant(siol)) { if (dbGetTimeStamp(siol, &prec->time)) errlogPrintf("recGblGetTimeStampSimm: dbGetTimeStamp (sim mode) failed, %s.SIOL = %s\n", prec->name, siol->value.pv_link.pvname); return; } else { if (epicsTimeGetCurrent(&prec->time)) errlogPrintf("recGblGetTimeStampSimm: epicsTimeGetCurrent (sim mode) failed for %s.\n", prec->name); return; } } } } void recGblCheckDeadband(epicsFloat64 *poldval, const epicsFloat64 newval, const epicsFloat64 deadband, unsigned *monitor_mask, const unsigned add_mask) { double delta = 0; if (finite(newval) && finite(*poldval)) { /* both are finite -> compare delta with deadband */ delta = *poldval - newval; if (delta < 0.0) delta = -delta; } else if (!isnan(newval) != !isnan(*poldval) || !isinf(newval) != !isinf(*poldval)) { /* one is NaN or +-inf, the other not -> send update */ delta = epicsINF; } else if (isinf(newval) && newval != *poldval) { /* one is +inf, the other -inf -> send update */ delta = epicsINF; } if (delta > deadband) { /* add bits to monitor mask */ *monitor_mask |= add_mask; /* update last value monitored */ *poldval = newval; } } static void getMaxRangeValues(short field_type, double *pupper_limit, double *plower_limit) { switch(field_type){ case DBF_CHAR: *pupper_limit = (double) CHAR_MAX; *plower_limit = (double) CHAR_MIN; break; case DBF_UCHAR: *pupper_limit = (double) UCHAR_MAX; *plower_limit = 0.0; break; case DBF_SHORT: *pupper_limit = (double) SHRT_MAX; *plower_limit = (double) SHRT_MIN; break; case DBF_ENUM: case DBF_USHORT: *pupper_limit = (double) USHRT_MAX; *plower_limit = 0.0; break; case DBF_LONG: *pupper_limit = 2147483647.0; *plower_limit = -2147483648.0; break; case DBF_ULONG: *pupper_limit = 4294967295.0; *plower_limit = 0.0; break; case DBF_INT64: *pupper_limit = 9223372036854775808.0; *plower_limit = -9223372036854775808.0; break; case DBF_UINT64: *pupper_limit = 18446744073709551615.0; *plower_limit = 0.0; break; case DBF_FLOAT: *pupper_limit = 1e30; *plower_limit = -1e30; break; case DBF_DOUBLE: *pupper_limit = 1e300; *plower_limit = -1e300; break; } return; } void recGblSaveSimm(const epicsEnum16 sscn, epicsEnum16 *poldsimm, const epicsEnum16 simm) { if (sscn == USHRT_MAX) return; *poldsimm = simm; } void recGblCheckSimm(struct dbCommon *pcommon, epicsEnum16 *psscn, const epicsEnum16 oldsimm, const epicsEnum16 simm) { if (*psscn == USHRT_MAX) return; if (simm != oldsimm) { epicsUInt16 scan = pcommon->scan; scanDelete(pcommon); pcommon->scan = *psscn; scanAdd(pcommon); *psscn = scan; } } void recGblInitSimm(struct dbCommon *pcommon, epicsEnum16 *psscn, epicsEnum16 *poldsimm, epicsEnum16 *psimm, struct link *psiml) { if (dbLinkIsConstant(psiml)) { recGblSaveSimm(*psscn, poldsimm, *psimm); dbLoadLink(psiml, DBF_USHORT, psimm); recGblCheckSimm(pcommon, psscn, *poldsimm, *psimm); } } long recGblGetSimm(struct dbCommon *pcommon, epicsEnum16 *psscn, epicsEnum16 *poldsimm, epicsEnum16 *psimm, struct link *psiml) { long status; recGblSaveSimm(*psscn, poldsimm, *psimm); status = dbTryGetLink(psiml, DBR_USHORT, psimm, 0); if (status && !pcommon->nsev) pcommon->nsta = LINK_ALARM; recGblCheckSimm(pcommon, psscn, *poldsimm, *psimm); return 0; } base-7.0.3.1/modules/database/src/ioc/db/recGbl.h0000664000577000060420000000626713557101274020137 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* recGbl.h */ /* Record Global * Author: Marty Kraimer * Date: 13Jun95 */ #ifndef INCrecGblh #define INCrecGblh 1 #include "epicsTypes.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /*************************************************************************/ /* Structures needed for args */ struct link; struct dbAddr; struct dbr_alDouble; struct dbr_ctrlDouble; struct dbr_grDouble; struct dbCommon; /* Hook Routine */ typedef void (*RECGBL_ALARM_HOOK_ROUTINE)(struct dbCommon *prec, epicsEnum16 prev_sevr, epicsEnum16 prev_stat); epicsShareExtern RECGBL_ALARM_HOOK_ROUTINE recGblAlarmHook; /* Global Record Support Routines */ epicsShareFunc void recGblDbaddrError(long status, const struct dbAddr *paddr, const char *pcaller_name); epicsShareFunc void recGblRecordError(long status, void *precord, const char *pcaller_name); epicsShareFunc void recGblRecSupError(long status, const struct dbAddr *paddr, const char *pcaller_name, const char *psupport_name); epicsShareFunc void recGblGetGraphicDouble(const struct dbAddr *paddr, struct dbr_grDouble *pgd); epicsShareFunc void recGblGetControlDouble( const struct dbAddr *paddr, struct dbr_ctrlDouble *pcd); epicsShareFunc void recGblGetAlarmDouble(const struct dbAddr *paddr, struct dbr_alDouble *pad); epicsShareFunc void recGblGetPrec(const struct dbAddr *paddr, long *pprecision); epicsShareFunc int recGblInitConstantLink(struct link *plink, short dbftype, void *pdest); epicsShareFunc unsigned short recGblResetAlarms(void *precord); epicsShareFunc int recGblSetSevr(void *precord, epicsEnum16 new_stat, epicsEnum16 new_sevr); epicsShareFunc void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat, epicsEnum16 sevr); epicsShareFunc void recGblFwdLink(void *precord); epicsShareFunc void recGblGetTimeStamp(void *precord); epicsShareFunc void recGblGetTimeStampSimm(void *prec, const epicsEnum16 simm, struct link *siol); epicsShareFunc void recGblCheckDeadband(epicsFloat64 *poldval, const epicsFloat64 newval, const epicsFloat64 deadband, unsigned *monitor_mask, const unsigned add_mask); epicsShareFunc void recGblSaveSimm(const epicsEnum16 sscn, epicsEnum16 *poldsimm, const epicsEnum16 simm); epicsShareFunc void recGblCheckSimm(struct dbCommon *prec, epicsEnum16 *psscn, const epicsEnum16 oldsimm, const epicsEnum16 simm); epicsShareFunc void recGblInitSimm(struct dbCommon *prec, epicsEnum16 *psscn, epicsEnum16 *poldsimm, epicsEnum16 *psimm, struct link *psiml); epicsShareFunc long recGblGetSimm(struct dbCommon *prec, epicsEnum16 *psscn, epicsEnum16 *poldsimm, epicsEnum16 *psimm, struct link *psiml); #ifdef __cplusplus } #endif #endif /*INCrecGblh*/ base-7.0.3.1/modules/database/src/ioc/dbCore.rc0000664000577000060420000000222013557101274017710 0ustar anjaesctl#include #include "epicsVersion.h" VS_VERSION_INFO VERSIONINFO FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_UNKNOWN FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "Comments","Database Core Library for EPICS\0" VALUE "CompanyName", "The EPICS collaboration\0" VALUE "FileDescription", "Database Core Library\0" VALUE "FileVersion", EPICS_VERSION_STRING "\0" VALUE "InternalName", "dbCore\0" VALUE "LegalCopyright", "Copyright (C) Univ. of California, UChicago Argonne LLC\0" VALUE "OriginalFilename", "dbCore.dll\0" VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" VALUE "ProductVersion", EPICS_VERSION_STRING "\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END base-7.0.3.1/modules/database/src/ioc/dbStatic/Makefile0000664000577000060420000000170113557101274021364 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. SRC_DIRS += $(IOCDIR)/dbStatic INC += dbBase.h INC += dbFldTypes.h INC += dbStaticLib.h INC += dbStaticPvt.h INC += link.h INC += special.h INC += guigroup.h INC += devSup.h INC += drvSup.h INC += recSup.h INC += dbStaticIocRegister.h dbCore_SRCS += dbStaticLib.c dbCore_SRCS += dbYacc.c dbCore_SRCS += dbPvdLib.c dbCore_SRCS += dbStaticRun.c dbCore_SRCS += dbStaticIocRegister.c CLEANS += dbLex.c dbYacc.c base-7.0.3.1/modules/database/src/ioc/dbStatic/RULES0000664000577000060420000000120613557101274020541 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. # dbLexRoutines.c is included in dbYacc.c dbYacc.c: dbLex.c $(IOCDIR)/dbStatic/dbLexRoutines.c base-7.0.3.1/modules/database/src/ioc/dbStatic/dbBase.h0000664000577000060420000001303313557101274021256 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Current Author: Marty Kraimer * Date: 03-19-92 */ #ifndef INCdbBaseh #define INCdbBaseh 1 #include "epicsTypes.h" #include "dbFldTypes.h" #include "ellLib.h" #include "dbDefs.h" #include "recSup.h" typedef struct dbMenu { ELLNODE node; char *name; int nChoice; char **papChoiceName; char **papChoiceValue; }dbMenu; typedef struct drvSup { ELLNODE node; char *name; struct drvet *pdrvet; }drvSup; typedef struct devSup { ELLNODE node; char *name; char *choice; int link_type; /*Following only available on run time system*/ struct dset *pdset; struct dsxt *pdsxt; /* Extended device support */ }devSup; typedef struct linkSup { ELLNODE node; char *name; char *jlif_name; struct jlif *pjlif; } linkSup; typedef struct dbDeviceMenu { int nChoice; char **papChoice; }dbDeviceMenu; /* conversion types*/ typedef enum {CT_DECIMAL,CT_HEX} ctType; /* access level types */ typedef enum {ASL0,ASL1} asLevel; /*Breakpoint Tables */ typedef struct brkInt{ /* breakpoint interval */ double raw; /*raw value for beginning of interval */ double slope; /*slope for interval */ double eng; /*converted value for beginning of interval*/ }brkInt; typedef struct brkTable { /* breakpoint table */ ELLNODE node; char *name; /*breakpoint table name */ long number; /*number of brkInt in this table*/ struct brkInt *paBrkInt; /* ptr to array of brkInts */ }brkTable; typedef struct dbFldDes{ /* field description */ char *prompt; /*Prompt string for DCT*/ char *name; /*Field name*/ char *extra; /*C def for DBF_NOACCESS*/ struct dbRecordType *pdbRecordType; short indRecordType; /*within dbRecordType.papFldDes */ short special; /*Special processing requirements */ dbfType field_type; /*Field type as defined in dbFldTypes.h */ unsigned int process_passive:1;/*should dbPutField process passive */ unsigned int prop:1;/*field is a metadata, post DBE_PROPERTY on change*/ unsigned int isDevLink:1; /* true for INP/OUT fields */ ctType base; /*base for integer to string conversions*/ short promptgroup; /*prompt, i.e. gui group */ short interest; /*interest level */ asLevel as_level; /*access security level */ char *initial; /*initial value */ /*If (DBF_MENU,DBF_DEVICE) ftPvt is (pdbMenu,pdbDeviceMenu) */ void *ftPvt; /*On no runtime following only set for STRING */ short size; /*length in bytes of a field element */ /*The following are only available on run time system*/ unsigned short offset; /*Offset in bytes from beginning of record*/ }dbFldDes; typedef struct dbInfoNode { /*non-field per-record information*/ ELLNODE node; char *name; char *string; void *pointer; }dbInfoNode; #define DBRN_FLAGS_VISIBLE 1 #define DBRN_FLAGS_ISALIAS 2 #define DBRN_FLAGS_HASALIAS 4 typedef struct dbRecordNode { ELLNODE node; void *precord; char *recordname; ELLLIST infoList; /*LIST head of info nodes*/ int flags; struct dbRecordNode *aliasedRecnode; /* NULL unless flags|DBRN_FLAGS_ISALIAS */ }dbRecordNode; /*dbRecordAttribute is for "psuedo" fields */ /*pdbFldDes is so that other access routines work correctly*/ /*Until base supports char * value MUST be fixed length string*/ typedef struct dbRecordAttribute { ELLNODE node; char *name; dbFldDes *pdbFldDes; char value[MAX_STRING_SIZE]; }dbRecordAttribute; typedef struct dbText { ELLNODE node; char *text; }dbText; typedef struct dbVariableDef { ELLNODE node; char *name; char *type; }dbVariableDef; typedef struct dbRecordType { ELLNODE node; ELLLIST attributeList; /*LIST head of attributes*/ ELLLIST recList; /*LIST head of sorted dbRecordNodes*/ ELLLIST devList; /*List of associated device support*/ ELLLIST cdefList; /*LIST of Cdef text items*/ char *name; short no_fields; /* number of fields defined */ short no_prompt; /* number of fields to configure*/ short no_links; /* number of links */ short no_aliases; /* number of aliases in recList */ short *link_ind; /* addr of array of ind in papFldDes*/ char **papsortFldName;/* ptr to array of ptr to fld names*/ short *sortFldInd; /* addr of array of ind in papFldDes*/ dbFldDes *pvalFldDes; /*pointer dbFldDes for VAL field*/ short indvalFlddes; /*ind in papFldDes*/ dbFldDes **papFldDes; /* ptr to array of ptr to fldDes*/ /*The following are only available on run time system*/ rset *prset; int rec_size; /*record size in bytes */ }dbRecordType; struct dbPvd; /* Contents private to dbPvdLib code */ struct gphPvt; /* Contents private to gpHashLib code */ typedef struct dbBase { ELLLIST menuList; ELLLIST recordTypeList; ELLLIST drvList; ELLLIST linkList; ELLLIST registrarList; ELLLIST functionList; ELLLIST variableList; ELLLIST bptList; ELLLIST filterList; ELLLIST guiGroupList; void *pathPvt; struct dbPvd *ppvd; struct gphPvt *pgpHash; short ignoreMissingMenus; short loadCdefs; }dbBase; #endif base-7.0.3.1/modules/database/src/ioc/dbStatic/dbFldTypes.h0000664000577000060420000000463713557101274022150 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Marty Kraimer * Date: 6-1-90 */ #ifndef INCdbFldTypesh #define INCdbFldTypesh 1 #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /* field types */ typedef enum { DBF_STRING, DBF_CHAR, DBF_UCHAR, DBF_SHORT, DBF_USHORT, DBF_LONG, DBF_ULONG, DBF_INT64, DBF_UINT64, DBF_FLOAT, DBF_DOUBLE, DBF_ENUM, DBF_MENU, DBF_DEVICE, DBF_INLINK, DBF_OUTLINK, DBF_FWDLINK, DBF_NOACCESS }dbfType; #define DBF_NTYPES DBF_NOACCESS+1 typedef struct mapdbfType{ char *strvalue; dbfType value; }mapdbfType; epicsShareExtern mapdbfType pamapdbfType[]; #ifdef DBFLDTYPES_GBLSOURCE epicsShareDef mapdbfType pamapdbfType[DBF_NTYPES] = { {"DBF_STRING",DBF_STRING}, {"DBF_CHAR",DBF_CHAR}, {"DBF_UCHAR",DBF_UCHAR}, {"DBF_SHORT",DBF_SHORT}, {"DBF_USHORT",DBF_USHORT}, {"DBF_LONG",DBF_LONG}, {"DBF_ULONG",DBF_ULONG}, {"DBF_INT64",DBF_INT64}, {"DBF_UINT64",DBF_UINT64}, {"DBF_FLOAT",DBF_FLOAT}, {"DBF_DOUBLE",DBF_DOUBLE}, {"DBF_ENUM",DBF_ENUM}, {"DBF_MENU",DBF_MENU}, {"DBF_DEVICE",DBF_DEVICE}, {"DBF_INLINK",DBF_INLINK}, {"DBF_OUTLINK",DBF_OUTLINK}, {"DBF_FWDLINK",DBF_FWDLINK}, {"DBF_NOACCESS",DBF_NOACCESS} }; #endif /*DBFLDTYPES_GBLSOURCE*/ /* data request buffer types */ #define DBR_STRING DBF_STRING #define DBR_CHAR DBF_CHAR #define DBR_UCHAR DBF_UCHAR #define DBR_SHORT DBF_SHORT #define DBR_USHORT DBF_USHORT #define DBR_LONG DBF_LONG #define DBR_ULONG DBF_ULONG #define DBR_INT64 DBF_INT64 #define DBR_UINT64 DBF_UINT64 #define DBR_FLOAT DBF_FLOAT #define DBR_DOUBLE DBF_DOUBLE #define DBR_ENUM DBF_ENUM #define DBR_PUT_ACKT DBR_ENUM+1 #define DBR_PUT_ACKS DBR_PUT_ACKT+1 #define DBR_NOACCESS DBF_NOACCESS #define VALID_DB_REQ(x) ((x >= 0) && (x <= DBR_ENUM)) #define INVALID_DB_REQ(x) ((x < 0) || (x > DBR_ENUM)) #ifdef __cplusplus } #endif #endif /*INCdbFldTypesh*/ base-7.0.3.1/modules/database/src/ioc/dbStatic/dbLex.l0000664000577000060420000000630313557101274021142 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ newline "\n" backslash "\\" doublequote "\"" comment "#" whitespace [ \t\r\n] escape {backslash}. stringchar [^"\n\\] bareword [a-zA-Z0-9_\-+:.\[\]<>;] punctuation [:,\[\]{}] normalchar [^"\\\0-\x1f] barechar [a-zA-Z0-9_\-+.] escapedchar ({backslash}["\\/bfnrt]) hexdigit [0-9a-fA-F] unicodechar ({backslash}"u"{hexdigit}{4}) jsonchar ({normalchar}|{escapedchar}|{unicodechar}) jsondqstr ({doublequote}{jsonchar}*{doublequote}) int ("-"?([0-9]|[1-9][0-9]+)) frac ("."[0-9]+) exp ([eE][+-]?[0-9]+) number ({int}{frac}?{exp}?) %{ #undef YY_INPUT #define YY_INPUT(b,r,ms) (r=(*db_yyinput)((char *)b,ms)) static int yyreset(void) { BEGIN INITIAL; return(0); } %} %x JSON %% "include" return(tokenINCLUDE); "path" return(tokenPATH); "addpath" return(tokenADDPATH); "menu" return(tokenMENU); "choice" return(tokenCHOICE); "recordtype" return(tokenRECORDTYPE); "field" return(tokenFIELD); "device" return(tokenDEVICE); "driver" return(tokenDRIVER); "link" return(tokenLINK); "breaktable" return(tokenBREAKTABLE); "record" return(tokenRECORD); "grecord" return(tokenGRECORD); "alias" return(tokenALIAS); "info" return(tokenINFO); "registrar" return(tokenREGISTRAR); "function" return(tokenFUNCTION); "variable" return(tokenVARIABLE); {bareword}+ { /* unquoted string or number */ yylval.Str = dbmfStrdup((char *) yytext); return(tokenSTRING); } {doublequote}({stringchar}|{escape})*{doublequote} { /* quoted string */ yylval.Str = dbmfStrdup((char *) yytext+1); yylval.Str[strlen(yylval.Str)-1] = '\0'; return(tokenSTRING); } %.* { /*C definition in recordtype*/ yylval.Str = dbmfStrdup((char *) yytext+1); return(tokenCDEFS); } "{" return(yytext[0]); "}" return(yytext[0]); "(" return(yytext[0]); ")" return(yytext[0]); "," return(yytext[0]); {doublequote}({stringchar}|{escape})*{newline} { /* bad string */ yyerrorAbort("Newline in string, closing quote missing"); } "null" return jsonNULL; "true" return jsonTRUE; "false" return jsonFALSE; {punctuation} return yytext[0]; {jsondqstr} { yylval.Str = dbmfStrdup((char *) yytext); return jsonSTRING; } {number} { yylval.Str = dbmfStrdup((char *) yytext); return jsonNUMBER; } {barechar}+ { yylval.Str = dbmfStrdup((char *) yytext); return jsonBARE; } {comment}.* ; {whitespace} ; . { char message[40]; YY_BUFFER_STATE *dummy=0; if (isprint((int) yytext[0])) { sprintf(message, "Invalid character '%c'", yytext[0]); } else { sprintf(message, "Invalid character 0x%2.2x", yytext[0]); } yyerrorAbort(message); /*The following suppresses compiler warning messages*/ if(FALSE) yyunput('c',(unsigned char *) message); if(FALSE) yy_switch_to_buffer(*dummy); } %% base-7.0.3.1/modules/database/src/ioc/dbStatic/dbLexRoutines.c0000664000577000060420000010520713557101274022665 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Marty Kraimer Date: 13JUL95*/ /*The routines in this module are serially reusable NOT reentrant*/ #include #include #include #include #include #include "dbDefs.h" #include "dbmf.h" #include "ellLib.h" #include "epicsPrint.h" #include "epicsString.h" #include "errMdef.h" #include "freeList.h" #include "gpHash.h" #include "macLib.h" #define epicsExportSharedSymbols #include "dbBase.h" #include "dbFldTypes.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "epicsExport.h" #include "link.h" #include "special.h" /*global declarations*/ epicsShareDef char *makeDbdDepends=0; epicsShareDef int dbRecordsOnceOnly=0; epicsExportAddress(int,dbRecordsOnceOnly); epicsShareDef int dbBptNotMonotonic=0; epicsExportAddress(int,dbBptNotMonotonic); epicsShareDef int dbQuietMacroWarnings=0; epicsExportAddress(int,dbQuietMacroWarnings); epicsShareDef int dbRecordsAbcSorted=0; epicsExportAddress(int,dbRecordsAbcSorted); /*private routines */ static void yyerrorAbort(char *str); static void allocTemp(void *pvoid); static void *popFirstTemp(void); static void *getLastTemp(void); static int db_yyinput(char *buf,int max_size); static void dbIncludePrint(void); static void dbPathCmd(char *path); static void dbAddPathCmd(char *path); static void dbIncludeNew(char *include_file); static void dbMenuHead(char *name); static void dbMenuChoice(char *name,char *value); static void dbMenuBody(void); static void dbRecordtypeHead(char *name); static void dbRecordtypeEmpty(void); static void dbRecordtypeBody(void); static void dbRecordtypeFieldHead(char *name,char *type); static void dbRecordtypeFieldItem(char *name,char *value); static short findOrAddGuiGroup(const char *name); static void dbDevice(char *recordtype,char *linktype, char *dsetname,char *choicestring); static void dbDriver(char *name); static void dbLinkType(char *name, char *jlif_name); static void dbRegistrar(char *name); static void dbFunction(char *name); static void dbVariable(char *name, char *type); static void dbBreakHead(char *name); static void dbBreakItem(char *value); static void dbBreakBody(void); static void dbRecordHead(char *recordType,char*name,int visible); static void dbRecordField(char *name,char *value); static void dbRecordBody(void); /*private declarations*/ #define MY_BUFFER_SIZE 1024 static char *my_buffer=NULL; static char *mac_input_buffer=NULL; static char *my_buffer_ptr=NULL; static MAC_HANDLE *macHandle = NULL; typedef struct inputFile{ ELLNODE node; char *path; char *filename; FILE *fp; int line_num; }inputFile; static ELLLIST inputFileList = ELLLIST_INIT; static inputFile *pinputFileNow = NULL; static DBBASE *pdbbase = NULL; typedef struct tempListNode { ELLNODE node; void *item; }tempListNode; static ELLLIST tempList = ELLLIST_INIT; static void *freeListPvt = NULL; static int duplicate = FALSE; static void yyerrorAbort(char *str) { yyerror(str); yyAbort = TRUE; } static void allocTemp(void *pvoid) { tempListNode *ptempListNode; ptempListNode = freeListCalloc(freeListPvt); ptempListNode->item = pvoid; ellAdd(&tempList,&ptempListNode->node); } static void *popFirstTemp(void) { tempListNode *ptempListNode; void *ptemp; ptempListNode = (tempListNode *)ellFirst(&tempList); ptemp = ptempListNode->item; ellDelete(&tempList,(ELLNODE *)ptempListNode); freeListFree(freeListPvt,ptempListNode); return(ptemp); } static void *getLastTemp(void) { tempListNode *ptempListNode; ptempListNode = (tempListNode *)ellLast(&tempList); return(ptempListNode->item); } static char *dbOpenFile(DBBASE *pdbbase,const char *filename,FILE **fp) { ELLLIST *ppathList = (ELLLIST *)pdbbase->pathPvt; dbPathNode *pdbPathNode; char *fullfilename; *fp = 0; if (!filename) return 0; if (!ppathList || ellCount(ppathList) == 0 || strchr(filename, '/') || strchr(filename, '\\')) { *fp = fopen(filename, "r"); if (*fp && makeDbdDepends) fprintf(stdout, "%s:%s \n", makeDbdDepends, filename); return 0; } pdbPathNode = (dbPathNode *)ellFirst(ppathList); while (pdbPathNode) { fullfilename = dbMalloc(strlen(pdbPathNode->directory) + strlen(filename) + 2); strcpy(fullfilename, pdbPathNode->directory); strcat(fullfilename, "/"); strcat(fullfilename, filename); *fp = fopen(fullfilename, "r"); if (*fp && makeDbdDepends) fprintf(stdout, "%s:%s \n", makeDbdDepends, fullfilename); free((void *)fullfilename); if (*fp) return pdbPathNode->directory; pdbPathNode = (dbPathNode *)ellNext(&pdbPathNode->node); } return 0; } static void freeInputFileList(void) { inputFile *pinputFileNow; while((pinputFileNow=(inputFile *)ellFirst(&inputFileList))) { if(fclose(pinputFileNow->fp)) errPrintf(0,__FILE__, __LINE__, "Closing file %s",pinputFileNow->filename); free((void *)pinputFileNow->filename); ellDelete(&inputFileList,(ELLNODE *)pinputFileNow); free((void *)pinputFileNow); } } static int cmp_dbRecordNode(const ELLNODE *lhs, const ELLNODE *rhs) { dbRecordNode *LHS = (dbRecordNode*)lhs, *RHS = (dbRecordNode*)rhs; return strcmp(LHS->recordname, RHS->recordname); } static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, const char *path,const char *substitutions) { long status; inputFile *pinputFile = NULL; char *penv; char **macPairs; if(ellCount(&tempList)) { epicsPrintf("dbReadCOM: Parser stack dirty %d\n", ellCount(&tempList)); } if(*ppdbbase == 0) *ppdbbase = dbAllocBase(); pdbbase = *ppdbbase; if(path && strlen(path)>0) { dbPath(pdbbase,path); } else { penv = getenv("EPICS_DB_INCLUDE_PATH"); if(penv) { dbPath(pdbbase,penv); } else { dbPath(pdbbase,"."); } } my_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char)); freeListInitPvt(&freeListPvt,sizeof(tempListNode),100); if(substitutions) { if(macCreateHandle(&macHandle,NULL)) { epicsPrintf("macCreateHandle error\n"); status = -1; goto cleanup; } macParseDefns(macHandle,(char *)substitutions,&macPairs); if(macPairs ==NULL) { macDeleteHandle(macHandle); macHandle = NULL; } else { macInstallMacros(macHandle,macPairs); free((void *)macPairs); mac_input_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char)); } macSuppressWarning(macHandle,dbQuietMacroWarnings); } pinputFile = dbCalloc(1,sizeof(inputFile)); if (filename) { pinputFile->filename = macEnvExpand(filename); } if (!fp) { FILE *fp1 = 0; if (pinputFile->filename) pinputFile->path = dbOpenFile(pdbbase, pinputFile->filename, &fp1); if (!pinputFile->filename || !fp1) { errPrintf(0, __FILE__, __LINE__, "dbRead opening file %s",pinputFile->filename); free(pinputFile->filename); free(pinputFile); status = -1; goto cleanup; } pinputFile->fp = fp1; } else { pinputFile->fp = fp; } pinputFile->line_num = 0; pinputFileNow = pinputFile; my_buffer[0] = '\0'; my_buffer_ptr = my_buffer; ellAdd(&inputFileList,&pinputFile->node); status = pvt_yy_parse(); if (ellCount(&tempList) && !yyAbort) epicsPrintf("dbReadCOM: Parser stack dirty w/o error. %d\n", ellCount(&tempList)); while (ellCount(&tempList)) popFirstTemp(); /* Memory leak on parser failure */ dbFreePath(pdbbase); if(!status) { /*add RTYP and VERS as an attribute */ DBENTRY dbEntry; DBENTRY *pdbEntry = &dbEntry; long localStatus; dbInitEntry(pdbbase,pdbEntry); localStatus = dbFirstRecordType(pdbEntry); while(!localStatus) { localStatus = dbPutRecordAttribute(pdbEntry,"RTYP", dbGetRecordTypeName(pdbEntry)); if(!localStatus) { localStatus = dbPutRecordAttribute(pdbEntry,"VERS", "none specified"); } if(localStatus) { fprintf(stderr,"dbPutRecordAttribute status %ld\n",status); } else { localStatus = dbNextRecordType(pdbEntry); } } dbFinishEntry(pdbEntry); } cleanup: if(dbRecordsAbcSorted) { ELLNODE *cur; for(cur = ellFirst(&pdbbase->recordTypeList); cur; cur=ellNext(cur)) { dbRecordType *rtype = CONTAINER(cur, dbRecordType, node); ellSortStable(&rtype->recList, &cmp_dbRecordNode); } } if(macHandle) macDeleteHandle(macHandle); macHandle = NULL; if(mac_input_buffer) free((void *)mac_input_buffer); mac_input_buffer = NULL; if(freeListPvt) freeListCleanup(freeListPvt); freeListPvt = NULL; if(my_buffer) free((void *)my_buffer); my_buffer = NULL; freeInputFileList(); return(status); } long dbReadDatabase(DBBASE **ppdbbase,const char *filename, const char *path,const char *substitutions) {return (dbReadCOM(ppdbbase,filename,0,path,substitutions));} long dbReadDatabaseFP(DBBASE **ppdbbase,FILE *fp, const char *path,const char *substitutions) {return (dbReadCOM(ppdbbase,0,fp,path,substitutions));} static int db_yyinput(char *buf, int max_size) { size_t l,n; char *fgetsRtn; if(yyAbort) return(0); if(*my_buffer_ptr==0) { while(TRUE) { /*until we get some input*/ if(macHandle) { fgetsRtn = fgets(mac_input_buffer,MY_BUFFER_SIZE, pinputFileNow->fp); if(fgetsRtn) { int exp = macExpandString(macHandle,mac_input_buffer, my_buffer,MY_BUFFER_SIZE); if (exp < 0) { fprintf(stderr, "Warning: '%s' line %d has undefined macros\n", pinputFileNow->filename, pinputFileNow->line_num+1); } } } else { fgetsRtn = fgets(my_buffer,MY_BUFFER_SIZE,pinputFileNow->fp); } if(fgetsRtn) break; if(fclose(pinputFileNow->fp)) errPrintf(0,__FILE__, __LINE__, "Closing file %s",pinputFileNow->filename); free((void *)pinputFileNow->filename); ellDelete(&inputFileList,(ELLNODE *)pinputFileNow); free((void *)pinputFileNow); pinputFileNow = (inputFile *)ellLast(&inputFileList); if(!pinputFileNow) return(0); } if(dbStaticDebug) fprintf(stderr,"%s",my_buffer); pinputFileNow->line_num++; my_buffer_ptr = &my_buffer[0]; } l = strlen(my_buffer_ptr); n = (l<=max_size ? l : max_size); memcpy(buf,my_buffer_ptr,n); my_buffer_ptr += n; return (int)n; } static void dbIncludePrint(void) { inputFile *pinputFile = pinputFileNow; while (pinputFile) { epicsPrintf(" in"); if (pinputFile->path) epicsPrintf(" path \"%s\" ",pinputFile->path); if (pinputFile->filename) { epicsPrintf(" file \"%s\"",pinputFile->filename); } else { epicsPrintf(" standard input"); } epicsPrintf(" line %d\n",pinputFile->line_num); pinputFile = (inputFile *)ellPrevious(&pinputFile->node); } return; } static void dbPathCmd(char *path) { dbPath(pdbbase,path); } static void dbAddPathCmd(char *path) { dbAddPath(pdbbase,path); } static void dbIncludeNew(char *filename) { inputFile *pinputFile; FILE *fp; pinputFile = dbCalloc(1,sizeof(inputFile)); pinputFile->filename = macEnvExpand(filename); pinputFile->path = dbOpenFile(pdbbase, pinputFile->filename, &fp); if (!fp) { epicsPrintf("Can't open include file \"%s\"\n", filename); yyerror(NULL); free((void *)pinputFile->filename); free((void *)pinputFile); return; } pinputFile->fp = fp; ellAdd(&inputFileList,&pinputFile->node); pinputFileNow = pinputFile; } static void dbMenuHead(char *name) { dbMenu *pdbMenu; GPHENTRY *pgphentry; if (!*name) { yyerrorAbort("dbMenuHead: Menu name can't be empty"); return; } pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->menuList); if(pgphentry) { duplicate = TRUE; return; } if(ellCount(&tempList)) yyerrorAbort("dbMenuHead: tempList not empty"); pdbMenu = dbCalloc(1,sizeof(dbMenu)); pdbMenu->name = epicsStrDup(name); allocTemp(pdbMenu); } static void dbMenuChoice(char *name,char *value) { if (!*name) { yyerror("dbMenuChoice: Menu choice name can't be empty"); return; } if(duplicate) return; allocTemp(epicsStrDup(name)); allocTemp(epicsStrDup(value)); } static void dbMenuBody(void) { dbMenu *pnewMenu; dbMenu *pMenu; int nChoice; int i; GPHENTRY *pgphentry; if(duplicate) { duplicate = FALSE; return; } pnewMenu = (dbMenu *)popFirstTemp(); pnewMenu->nChoice = nChoice = ellCount(&tempList)/2; pnewMenu->papChoiceName = dbCalloc(pnewMenu->nChoice,sizeof(char *)); pnewMenu->papChoiceValue = dbCalloc(pnewMenu->nChoice,sizeof(char *)); for(i=0; ipapChoiceName[i] = (char *)popFirstTemp(); pnewMenu->papChoiceValue[i] = (char *)popFirstTemp(); } if(ellCount(&tempList)) yyerrorAbort("dbMenuBody: tempList not empty"); /* Add menu in sorted order */ pMenu = (dbMenu *)ellFirst(&pdbbase->menuList); while(pMenu && strcmp(pMenu->name,pnewMenu->name) >0 ) pMenu = (dbMenu *)ellNext(&pMenu->node); if(pMenu) ellInsert(&pdbbase->menuList,ellPrevious(&pMenu->node),&pnewMenu->node); else ellAdd(&pdbbase->menuList,&pnewMenu->node); pgphentry = gphAdd(pdbbase->pgpHash,pnewMenu->name,&pdbbase->menuList); if(!pgphentry) { yyerrorAbort("gphAdd failed"); } else { pgphentry->userPvt = pnewMenu; } } static void dbRecordtypeHead(char *name) { dbRecordType *pdbRecordType; GPHENTRY *pgphentry; if (!*name) { yyerrorAbort("dbRecordtypeHead: Recordtype name can't be empty"); return; } pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->recordTypeList); if(pgphentry) { duplicate = TRUE; return; } pdbRecordType = dbCalloc(1,sizeof(dbRecordType)); pdbRecordType->name = epicsStrDup(name); if (pdbbase->loadCdefs) ellInit(&pdbRecordType->cdefList); if(ellCount(&tempList)) yyerrorAbort("dbRecordtypeHead tempList not empty"); allocTemp(pdbRecordType); } static void dbRecordtypeFieldHead(char *name,char *type) { dbFldDes *pdbFldDes; int i; if (!*name) { yyerrorAbort("dbRecordtypeFieldHead: Field name can't be empty"); return; } if(duplicate) return; pdbFldDes = dbCalloc(1,sizeof(dbFldDes)); allocTemp(pdbFldDes); pdbFldDes->name = epicsStrDup(name); pdbFldDes->as_level = ASL1; pdbFldDes->isDevLink = strcmp(pdbFldDes->name, "INP")==0 || strcmp(pdbFldDes->name, "OUT")==0; i = dbFindFieldType(type); if (i < 0) yyerrorAbort("Illegal Field Type"); pdbFldDes->field_type = i; } static short findOrAddGuiGroup(const char *name) { dbGuiGroup *pdbGuiGroup; GPHENTRY *pgphentry; pgphentry = gphFind(pdbbase->pgpHash, name, &pdbbase->guiGroupList); if (!pgphentry) { pdbGuiGroup = dbCalloc(1,sizeof(dbGuiGroup)); pdbGuiGroup->name = epicsStrDup(name); ellAdd(&pdbbase->guiGroupList, &pdbGuiGroup->node); pdbGuiGroup->key = ellCount(&pdbbase->guiGroupList); pgphentry = gphAdd(pdbbase->pgpHash, pdbGuiGroup->name, &pdbbase->guiGroupList); pgphentry->userPvt = pdbGuiGroup; } return ((dbGuiGroup *)pgphentry->userPvt)->key; } static void dbRecordtypeFieldItem(char *name,char *value) { dbFldDes *pdbFldDes; if(duplicate) return; pdbFldDes = (dbFldDes *)getLastTemp(); if(strcmp(name,"asl")==0) { if(strcmp(value,"ASL0")==0) { pdbFldDes->as_level = ASL0; } else if(strcmp(value,"ASL1")==0) { pdbFldDes->as_level = ASL1; } else { yyerror("Illegal Access Security value: Must be ASL0 or ASL1"); } return; } if(strcmp(name,"initial")==0) { pdbFldDes->initial = epicsStrDup(value); return; } if(strcmp(name,"promptgroup")==0) { pdbFldDes->promptgroup = findOrAddGuiGroup(value); return; } if(strcmp(name,"prompt")==0) { pdbFldDes->prompt = epicsStrDup(value); return; } if(strcmp(name,"special")==0) { int i; for(i=0; ispecial = pamapspcType[i].value; return; } } if(sscanf(value,"%hd",&pdbFldDes->special)==1) { return; } yyerror("Illegal 'special' value."); return; } if(strcmp(name,"pp")==0) { if((strcmp(value,"YES")==0) || (strcmp(value,"TRUE")==0)) { pdbFldDes->process_passive = TRUE; } else if((strcmp(value,"NO")==0) || (strcmp(value,"FALSE")==0)) { pdbFldDes->process_passive = FALSE; } else { yyerror("Illegal 'pp' value, must be YES/NO/TRUE/FALSE"); } return; } if(strcmp(name,"interest")==0) { if(sscanf(value,"%hd",&pdbFldDes->interest)!=1) yyerror("Illegal 'interest' value, must be integer"); return; } if(strcmp(name,"base")==0) { if(strcmp(value,"DECIMAL")==0) { pdbFldDes->base = CT_DECIMAL; } else if(strcmp(value,"HEX")==0) { pdbFldDes->base = CT_HEX; } else { yyerror("Illegal 'base' value, must be DECIMAL/HEX"); } return; } if(strcmp(name,"size")==0) { if(sscanf(value,"%hd",&pdbFldDes->size)!=1) yyerror("Illegal 'size' value, must be integer"); return; } if(strcmp(name,"extra")==0) { pdbFldDes->extra = epicsStrDup(value); return; } if(strcmp(name,"menu")==0) { pdbFldDes->ftPvt = (dbMenu *)dbFindMenu(pdbbase,value); if(!pdbbase->ignoreMissingMenus && !pdbFldDes->ftPvt) yyerrorAbort("menu not found"); return; } if(strcmp(name,"prop")==0) { if(strcmp(value, "YES")==0) pdbFldDes->prop = 1; else pdbFldDes->prop = 0; return; } } static void dbRecordtypeCdef(char *text) { dbText *pdbCdef; tempListNode *ptempListNode; dbRecordType *pdbRecordType; if (!pdbbase->loadCdefs || duplicate) return; ptempListNode = (tempListNode *)ellFirst(&tempList); pdbRecordType = ptempListNode->item; pdbCdef = dbCalloc(1,sizeof(dbText)); if (text[0] == ' ') text++; /* strip leading space if present */ pdbCdef->text = epicsStrDup(text); ellAdd(&pdbRecordType->cdefList, &pdbCdef->node); return; } static void dbRecordtypeEmpty(void) { tempListNode *ptempListNode; dbRecordType *pdbRecordType; if (duplicate) { duplicate = FALSE; return; } ptempListNode = (tempListNode *)ellFirst(&tempList); pdbRecordType = ptempListNode->item; epicsPrintf("Declaration of recordtype(%s) preceeded full definition.\n", pdbRecordType->name); yyerrorAbort(NULL); } static void dbRecordtypeBody(void) { dbRecordType *pdbRecordType; dbFldDes *pdbFldDes; int i,j,ilink; GPHENTRY *pgphentry; int no_fields,no_prompt,no_links; dbfType field_type; char *psortFldNameTemp; short psortFldIndTemp; char **papsortFldName; short *sortFldInd; if(duplicate) { duplicate = FALSE; return; } pdbRecordType= (dbRecordType *)popFirstTemp(); pdbRecordType->no_fields = no_fields = ellCount(&tempList); pdbRecordType->papFldDes = dbCalloc(no_fields,sizeof(dbFldDes *)); pdbRecordType->papsortFldName = dbCalloc(no_fields,sizeof(char *)); pdbRecordType->sortFldInd = dbCalloc(no_fields,sizeof(short)); no_prompt = no_links = 0; for(i=0; ipdbRecordType = pdbRecordType; pdbFldDes->indRecordType = i; pdbRecordType->papFldDes[i] = pdbFldDes; if(pdbFldDes->promptgroup) no_prompt++; field_type = pdbFldDes->field_type; if((field_type>=DBF_INLINK) && (field_type<=DBF_FWDLINK))no_links++; if((field_type==DBF_STRING) && (pdbFldDes->size==0)) fprintf(stderr,"recordtype(%s).%s size not specified\n", pdbRecordType->name,pdbFldDes->name); if((field_type==DBF_NOACCESS) && (pdbFldDes->extra==0)) fprintf(stderr,"recordtype(%s).%s extra not specified\n", pdbRecordType->name,pdbFldDes->name); } if (ellCount(&tempList)) yyerrorAbort("dbRecordtypeBody: tempList not empty"); pdbRecordType->no_prompt = no_prompt; pdbRecordType->no_links = no_links; pdbRecordType->link_ind = dbCalloc(no_links,sizeof(short)); ilink = 0; for(i=0; ipapFldDes[i]; /* if prompt is null make it a null string */ if(!pdbFldDes->prompt) pdbFldDes->prompt = dbCalloc(1,sizeof(char)); field_type = pdbFldDes->field_type; if((field_type>=DBF_INLINK) && (field_type<=DBF_FWDLINK)) pdbRecordType->link_ind[ilink++] = i; if(strcmp(pdbFldDes->name,"VAL")==0) { pdbRecordType->pvalFldDes = pdbRecordType->papFldDes[i]; pdbRecordType->indvalFlddes = i; } pdbRecordType->papsortFldName[i] = pdbFldDes->name; pdbRecordType->sortFldInd[i] = i; } /*Now sort fields. Sorry dumb sort algorithm */ papsortFldName = pdbRecordType->papsortFldName; sortFldInd = pdbRecordType->sortFldInd; for(i=0; iattributeList); ellInit(&pdbRecordType->recList); ellInit(&pdbRecordType->devList); pgphentry = gphAdd(pdbbase->pgpHash,pdbRecordType->name, &pdbbase->recordTypeList); if(!pgphentry) { yyerrorAbort("gphAdd failed"); } else { pgphentry->userPvt = pdbRecordType; } ellAdd(&pdbbase->recordTypeList,&pdbRecordType->node); } static void dbDevice(char *recordtype,char *linktype, char *dsetname,char *choicestring) { devSup *pdevSup; dbRecordType *pdbRecordType; GPHENTRY *pgphentry; int i,link_type; pgphentry = gphFind(pdbbase->pgpHash,recordtype,&pdbbase->recordTypeList); if(!pgphentry) { epicsPrintf("Record type \"%s\" not found for device \"%s\"\n", recordtype, choicestring); yyerror(NULL); return; } link_type=-1; for(i=0; iuserPvt; pgphentry = gphFind(pdbbase->pgpHash,choicestring,&pdbRecordType->devList); if(pgphentry) { return; } pdevSup = dbCalloc(1,sizeof(devSup)); pdevSup->name = epicsStrDup(dsetname); pdevSup->choice = epicsStrDup(choicestring); pdevSup->link_type = link_type; pgphentry = gphAdd(pdbbase->pgpHash,pdevSup->choice,&pdbRecordType->devList); if(!pgphentry) { yyerrorAbort("gphAdd failed"); } else { pgphentry->userPvt = pdevSup; } ellAdd(&pdbRecordType->devList,&pdevSup->node); } static void dbDriver(char *name) { drvSup *pdrvSup; GPHENTRY *pgphentry; if (!*name) { yyerrorAbort("dbDriver: Driver name can't be empty"); return; } pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->drvList); if(pgphentry) { return; } pdrvSup = dbCalloc(1,sizeof(drvSup)); pdrvSup->name = epicsStrDup(name); pgphentry = gphAdd(pdbbase->pgpHash,pdrvSup->name,&pdbbase->drvList); if(!pgphentry) { yyerrorAbort("gphAdd failed"); } pgphentry->userPvt = pdrvSup; ellAdd(&pdbbase->drvList,&pdrvSup->node); } static void dbLinkType(char *name, char *jlif_name) { linkSup *pLinkSup; GPHENTRY *pgphentry; pgphentry = gphFind(pdbbase->pgpHash, name, &pdbbase->linkList); if (pgphentry) { return; } pLinkSup = dbCalloc(1,sizeof(linkSup)); pLinkSup->name = epicsStrDup(name); pLinkSup->jlif_name = epicsStrDup(jlif_name); pgphentry = gphAdd(pdbbase->pgpHash, pLinkSup->name, &pdbbase->linkList); if (!pgphentry) { yyerrorAbort("gphAdd failed"); } pgphentry->userPvt = pLinkSup; ellAdd(&pdbbase->linkList, &pLinkSup->node); } static void dbRegistrar(char *name) { dbText *ptext; GPHENTRY *pgphentry; if (!*name) { yyerrorAbort("dbRegistrar: Registrar name can't be empty"); return; } pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->registrarList); if(pgphentry) { return; } ptext = dbCalloc(1,sizeof(dbText)); ptext->text = epicsStrDup(name); pgphentry = gphAdd(pdbbase->pgpHash,ptext->text,&pdbbase->registrarList); if(!pgphentry) { yyerrorAbort("gphAdd failed"); } pgphentry->userPvt = ptext; ellAdd(&pdbbase->registrarList,&ptext->node); } static void dbFunction(char *name) { dbText *ptext; GPHENTRY *pgphentry; if (!*name) { yyerrorAbort("dbFunction: Function name can't be empty"); return; } pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->functionList); if(pgphentry) { return; } ptext = dbCalloc(1,sizeof(dbText)); ptext->text = epicsStrDup(name); pgphentry = gphAdd(pdbbase->pgpHash,ptext->text,&pdbbase->functionList); if(!pgphentry) { yyerrorAbort("gphAdd failed"); } pgphentry->userPvt = ptext; ellAdd(&pdbbase->functionList,&ptext->node); } static void dbVariable(char *name, char *type) { dbVariableDef *pvar; GPHENTRY *pgphentry; if (!*name) { yyerrorAbort("dbVariable: Variable name can't be empty"); return; } pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->variableList); if(pgphentry) { return; } pvar = dbCalloc(1,sizeof(dbVariableDef)); pvar->name = epicsStrDup(name); pvar->type = epicsStrDup(type); pgphentry = gphAdd(pdbbase->pgpHash,pvar->name,&pdbbase->variableList); if(!pgphentry) { yyerrorAbort("gphAdd failed"); } pgphentry->userPvt = pvar; ellAdd(&pdbbase->variableList,&pvar->node); } static void dbBreakHead(char *name) { brkTable *pbrkTable; GPHENTRY *pgphentry; if (!*name) { yyerrorAbort("dbBreakHead: Breaktable name can't be empty"); return; } pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->bptList); if(pgphentry) { duplicate = TRUE; return; } pbrkTable = dbCalloc(1,sizeof(brkTable)); pbrkTable->name = epicsStrDup(name); if(ellCount(&tempList)) yyerrorAbort("dbBreakHead:tempList not empty"); allocTemp(pbrkTable); } static void dbBreakItem(char *value) { double dummy; if (duplicate) return; if (epicsScanDouble(value, &dummy) != 1) { yyerrorAbort("Non-numeric value in breaktable"); } allocTemp(epicsStrDup(value)); } static void dbBreakBody(void) { brkTable *pnewbrkTable; brkInt *paBrkInt; brkTable *pbrkTable; int number, down=0; int i; GPHENTRY *pgphentry; if (duplicate) { duplicate = FALSE; return; } pnewbrkTable = (brkTable *)popFirstTemp(); number = ellCount(&tempList); if (number % 2) { yyerrorAbort("breaktable: Raw value missing"); return; } number /= 2; if (number < 2) { yyerrorAbort("breaktable: Must have at least two points!"); return; } pnewbrkTable->number = number; pnewbrkTable->paBrkInt = paBrkInt = dbCalloc(number, sizeof(brkInt)); for (i=0; ibptList); while (pbrkTable) { if (strcmp(pbrkTable->name, pnewbrkTable->name) > 0) { ellInsert(&pdbbase->bptList, ellPrevious((ELLNODE *)pbrkTable), (ELLNODE *)pnewbrkTable); break; } pbrkTable = (brkTable *)ellNext(&pbrkTable->node); } if (!pbrkTable) ellAdd(&pdbbase->bptList, &pnewbrkTable->node); pgphentry = gphAdd(pdbbase->pgpHash,pnewbrkTable->name,&pdbbase->bptList); if (!pgphentry) { yyerrorAbort("dbBreakBody: gphAdd failed"); return; } pgphentry->userPvt = pnewbrkTable; } static void dbRecordHead(char *recordType, char *name, int visible) { char *badch; DBENTRY *pdbentry; long status; if (!*name) { yyerrorAbort("dbRecordHead: Record name can't be empty"); return; } badch = strpbrk(name, " \"'.$"); if (badch) { epicsPrintf("Bad character '%c' in record name \"%s\"\n", *badch, name); } pdbentry = dbAllocEntry(pdbbase); if (ellCount(&tempList)) yyerrorAbort("dbRecordHead: tempList not empty"); allocTemp(pdbentry); if (recordType[0] == '*' && recordType[1] == 0) { if (dbRecordsOnceOnly) epicsPrintf("Record-type \"*\" not valid with dbRecordsOnceOnly\n"); else { status = dbFindRecord(pdbentry, name); if (status == 0) return; /* done */ epicsPrintf("Record \"%s\" not found\n", name); } yyerror(NULL); duplicate = TRUE; return; } status = dbFindRecordType(pdbentry, recordType); if (status) { epicsPrintf("Record \"%s\" is of unknown type \"%s\"\n", name, recordType); yyerrorAbort(NULL); return; } /*Duplicate records are ok if the same type */ status = dbCreateRecord(pdbentry,name); if (status == S_dbLib_recExists) { if (strcmp(recordType, dbGetRecordTypeName(pdbentry)) != 0) { epicsPrintf("Record \"%s\" of type \"%s\" redefined with new type " "\"%s\"\n", name, dbGetRecordTypeName(pdbentry), recordType); yyerror(NULL); duplicate = TRUE; return; } else if (dbRecordsOnceOnly) { epicsPrintf("Record \"%s\" already defined (dbRecordsOnceOnly is " "set)\n", name); yyerror(NULL); duplicate = TRUE; } } else if (status) { epicsPrintf("Can't create record \"%s\" of type \"%s\"\n", name, recordType); yyerrorAbort(NULL); } if (visible) dbVisibleRecord(pdbentry); } static void dbRecordField(char *name,char *value) { DBENTRY *pdbentry; tempListNode *ptempListNode; long status; if (duplicate) return; ptempListNode = (tempListNode *)ellFirst(&tempList); pdbentry = ptempListNode->item; status = dbFindField(pdbentry,name); if (status) { epicsPrintf("Record \"%s\" does not have a field \"%s\"\n", dbGetRecordName(pdbentry), name); yyerror(NULL); return; } if (pdbentry->indfield == 0) { epicsPrintf("Can't set \"NAME\" field of record \"%s\"\n", dbGetRecordName(pdbentry)); yyerror(NULL); return; } if (*value == '"') { /* jsonSTRING values still have their quotes */ value++; value[strlen(value) - 1] = 0; } dbTranslateEscape(value, value); /* in-place; safe & legal */ status = dbPutString(pdbentry,value); if (status) { char msg[128]; errSymLookup(status, msg, sizeof(msg)); epicsPrintf("Can't set \"%s.%s\" to \"%s\" %s\n", dbGetRecordName(pdbentry), name, value, msg); yyerror(NULL); return; } } static void dbRecordInfo(char *name, char *value) { DBENTRY *pdbentry; tempListNode *ptempListNode; long status; if (!*name) { yyerrorAbort("dbRecordInfo: Info item name can't be empty"); return; } if (duplicate) return; ptempListNode = (tempListNode *)ellFirst(&tempList); pdbentry = ptempListNode->item; if (*value == '"') { /* jsonSTRING values still have their quotes */ value++; value[strlen(value) - 1] = 0; } dbTranslateEscape(value, value); /* yuck: in-place, but safe */ status = dbPutInfo(pdbentry,name,value); if (status) { epicsPrintf("Can't set \"%s\" info \"%s\" to \"%s\"\n", dbGetRecordName(pdbentry), name, value); yyerror(NULL); return; } } static void dbRecordAlias(char *name) { DBENTRY *pdbentry; tempListNode *ptempListNode; long status; if (!*name) { yyerrorAbort("dbRecordAlias: Alias name can't be empty"); 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)); yyerror(NULL); return; } } static void dbAlias(char *name, char *alias) { DBENTRY dbEntry; DBENTRY *pdbEntry = &dbEntry; if (!*alias) { yyerrorAbort("dbAlias: Alias name can't be empty"); return; } dbInitEntry(pdbbase, pdbEntry); if (dbFindRecord(pdbEntry, name)) { epicsPrintf("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); yyerror(NULL); } dbFinishEntry(pdbEntry); } static void dbRecordBody(void) { DBENTRY *pdbentry; if (duplicate) { duplicate = FALSE; return; } pdbentry = (DBENTRY *)popFirstTemp(); if (ellCount(&tempList)) yyerrorAbort("dbRecordBody: tempList not empty"); dbFreeEntry(pdbentry); } base-7.0.3.1/modules/database/src/ioc/dbStatic/dbPvdLib.c0000664000577000060420000001375713557101274021574 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbPvdLib.c */ #include #include #include #include #include "dbDefs.h" #include "ellLib.h" #include "epicsMutex.h" #include "epicsStdio.h" #include "epicsString.h" #define epicsExportSharedSymbols #include "dbBase.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" typedef struct { ELLLIST list; epicsMutexId lock; } dbPvdBucket; typedef struct dbPvd { unsigned int size; unsigned int mask; dbPvdBucket **buckets; } dbPvd; unsigned int dbPvdHashTableSize = 0; #define MIN_SIZE 256 #define DEFAULT_SIZE 512 #define MAX_SIZE 65536 int dbPvdTableSize(int size) { if (size & (size - 1)) { printf("dbPvdTableSize: %d is not a power of 2\n", size); return -1; } if (size < MIN_SIZE) size = MIN_SIZE; if (size > MAX_SIZE) size = MAX_SIZE; dbPvdHashTableSize = size; return 0; } void dbPvdInitPvt(dbBase *pdbbase) { dbPvd *ppvd; if (pdbbase->ppvd) return; if (dbPvdHashTableSize == 0) { dbPvdHashTableSize = DEFAULT_SIZE; } ppvd = (dbPvd *)dbMalloc(sizeof(dbPvd)); ppvd->size = dbPvdHashTableSize; ppvd->mask = dbPvdHashTableSize - 1; ppvd->buckets = dbCalloc(ppvd->size, sizeof(dbPvdBucket *)); pdbbase->ppvd = ppvd; return; } PVDENTRY *dbPvdFind(dbBase *pdbbase, const char *name, size_t lenName) { dbPvd *ppvd = pdbbase->ppvd; dbPvdBucket *pbucket; PVDENTRY *ppvdNode; pbucket = ppvd->buckets[epicsMemHash(name, lenName, 0) & ppvd->mask]; if (pbucket == NULL) return NULL; epicsMutexMustLock(pbucket->lock); ppvdNode = (PVDENTRY *) ellFirst(&pbucket->list); while (ppvdNode) { const char *recordname = ppvdNode->precnode->recordname; if (strncmp(name, recordname, lenName) == 0 && strlen(recordname) == lenName) break; ppvdNode = (PVDENTRY *) ellNext((ELLNODE *)ppvdNode); } epicsMutexUnlock(pbucket->lock); return ppvdNode; } PVDENTRY *dbPvdAdd(dbBase *pdbbase, dbRecordType *precordType, dbRecordNode *precnode) { dbPvd *ppvd = pdbbase->ppvd; dbPvdBucket *pbucket; PVDENTRY *ppvdNode; char *name = precnode->recordname; unsigned int h; h = epicsStrHash(name, 0) & ppvd->mask; pbucket = ppvd->buckets[h]; if (pbucket == NULL) { pbucket = dbCalloc(1, sizeof(dbPvdBucket)); ellInit(&pbucket->list); pbucket->lock = epicsMutexCreate(); ppvd->buckets[h] = pbucket; } epicsMutexMustLock(pbucket->lock); ppvdNode = (PVDENTRY *) ellFirst(&pbucket->list); while (ppvdNode) { if (strcmp(name, ppvdNode->precnode->recordname) == 0) { epicsMutexUnlock(pbucket->lock); return NULL; } ppvdNode = (PVDENTRY *) ellNext((ELLNODE *)ppvdNode); } ppvdNode = dbCalloc(1, sizeof(PVDENTRY)); ppvdNode->precordType = precordType; ppvdNode->precnode = precnode; ellAdd(&pbucket->list, (ELLNODE *)ppvdNode); epicsMutexUnlock(pbucket->lock); return ppvdNode; } void dbPvdDelete(dbBase *pdbbase, dbRecordNode *precnode) { dbPvd *ppvd = pdbbase->ppvd; dbPvdBucket *pbucket; PVDENTRY *ppvdNode; char *name = precnode->recordname; pbucket = ppvd->buckets[epicsStrHash(name, 0) & ppvd->mask]; if (pbucket == NULL) return; epicsMutexMustLock(pbucket->lock); ppvdNode = (PVDENTRY *) ellFirst(&pbucket->list); while (ppvdNode) { if (ppvdNode->precnode && ppvdNode->precnode->recordname && strcmp(name, ppvdNode->precnode->recordname) == 0) { ellDelete(&pbucket->list, (ELLNODE *)ppvdNode); free(ppvdNode); break; } ppvdNode = (PVDENTRY *) ellNext((ELLNODE *)ppvdNode); } epicsMutexUnlock(pbucket->lock); return; } void dbPvdFreeMem(dbBase *pdbbase) { dbPvd *ppvd = pdbbase->ppvd; unsigned int h; if (ppvd == NULL) return; pdbbase->ppvd = NULL; for (h = 0; h < ppvd->size; h++) { dbPvdBucket *pbucket = ppvd->buckets[h]; PVDENTRY *ppvdNode; if (pbucket == NULL) continue; epicsMutexMustLock(pbucket->lock); ppvd->buckets[h] = NULL; while ((ppvdNode = (PVDENTRY *) ellFirst(&pbucket->list))) { ellDelete(&pbucket->list, (ELLNODE *)ppvdNode); free(ppvdNode); } epicsMutexUnlock(pbucket->lock); epicsMutexDestroy(pbucket->lock); free(pbucket); } free(ppvd->buckets); free(ppvd); } void dbPvdDump(dbBase *pdbbase, int verbose) { unsigned int empty = 0; dbPvd *ppvd; unsigned int h; if (!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return; } ppvd = pdbbase->ppvd; if (ppvd == NULL) return; printf("Process Variable Directory has %u buckets", ppvd->size); for (h = 0; h < ppvd->size; h++) { dbPvdBucket *pbucket = ppvd->buckets[h]; PVDENTRY *ppvdNode; int i = 1; if (pbucket == NULL) { empty++; continue; } epicsMutexMustLock(pbucket->lock); ppvdNode = (PVDENTRY *) ellFirst(&pbucket->list); printf("\n [%4u] %4d ", h, ellCount(&pbucket->list)); while (ppvdNode && verbose) { if (!(++i % 4)) printf("\n "); printf(" %s", ppvdNode->precnode->recordname); ppvdNode = (PVDENTRY *) ellNext((ELLNODE*)ppvdNode); } epicsMutexUnlock(pbucket->lock); } printf("\n%u buckets empty.\n", empty); } base-7.0.3.1/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c0000664000577000060420000001502013557101274023764 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "iocsh.h" #define epicsExportSharedSymbols #include "dbStaticIocRegister.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" /* common arguments */ static const iocshArg argPdbbase = { "pdbbase", iocshArgPdbbase}; static const iocshArg argRecType = { "recordTypeName", iocshArgString}; /* dbDumpPath */ static const iocshArg * const dbDumpPathArgs[] = {&argPdbbase}; static const iocshFuncDef dbDumpPathFuncDef = {"dbDumpPath",1,dbDumpPathArgs}; static void dbDumpPathCallFunc(const iocshArgBuf *args) { dbDumpPath(*iocshPpdbbase); } /* dbDumpRecord */ static const iocshArg dbDumpRecordArg2 = { "interest level",iocshArgInt}; static const iocshArg * const dbDumpRecordArgs[] = {&argPdbbase, &argRecType, &dbDumpRecordArg2}; static const iocshFuncDef dbDumpRecordFuncDef = {"dbDumpRecord",3,dbDumpRecordArgs}; static void dbDumpRecordCallFunc(const iocshArgBuf *args) { dbDumpRecord(*iocshPpdbbase,args[1].sval,args[2].ival); } /* dbDumpMenu */ static const iocshArg dbDumpMenuArg1 = { "menuName",iocshArgString}; static const iocshArg * const dbDumpMenuArgs[] = { &argPdbbase, &dbDumpMenuArg1}; static const iocshFuncDef dbDumpMenuFuncDef = {"dbDumpMenu",2,dbDumpMenuArgs}; static void dbDumpMenuCallFunc(const iocshArgBuf *args) { dbDumpMenu(*iocshPpdbbase,args[1].sval); } /* dbDumpRecordType */ static const iocshArg * const dbDumpRecordTypeArgs[] = {&argPdbbase, &argRecType}; static const iocshFuncDef dbDumpRecordTypeFuncDef = {"dbDumpRecordType",2,dbDumpRecordTypeArgs}; static void dbDumpRecordTypeCallFunc(const iocshArgBuf *args) { dbDumpRecordType(*iocshPpdbbase,args[1].sval); } /* dbDumpField */ static const iocshArg dbDumpFieldArg2 = { "fieldName",iocshArgString}; static const iocshArg * const dbDumpFieldArgs[] = {&argPdbbase, &argRecType,&dbDumpFieldArg2}; static const iocshFuncDef dbDumpFieldFuncDef = {"dbDumpField",3,dbDumpFieldArgs}; static void dbDumpFieldCallFunc(const iocshArgBuf *args) { dbDumpField(*iocshPpdbbase,args[1].sval,args[2].sval); } /* dbDumpDevice */ static const iocshArg * const dbDumpDeviceArgs[] = { &argPdbbase, &argRecType}; static const iocshFuncDef dbDumpDeviceFuncDef = {"dbDumpDevice",2,dbDumpDeviceArgs}; static void dbDumpDeviceCallFunc(const iocshArgBuf *args) { dbDumpDevice(*iocshPpdbbase,args[1].sval); } /* dbDumpDriver */ static const iocshArg * const dbDumpDriverArgs[] = { &argPdbbase}; static const iocshFuncDef dbDumpDriverFuncDef = {"dbDumpDriver",1,dbDumpDriverArgs}; static void dbDumpDriverCallFunc(const iocshArgBuf *args) { dbDumpDriver(*iocshPpdbbase); } /* dbDumpLink */ static const iocshArg * const dbDumpLinkArgs[] = { &argPdbbase}; static const iocshFuncDef dbDumpLinkFuncDef = {"dbDumpLink",1,dbDumpLinkArgs}; static void dbDumpLinkCallFunc(const iocshArgBuf *args) { dbDumpLink(*iocshPpdbbase); } /* dbDumpRegistrar */ static const iocshArg * const dbDumpRegistrarArgs[] = { &argPdbbase}; static const iocshFuncDef dbDumpRegistrarFuncDef = {"dbDumpRegistrar",1,dbDumpRegistrarArgs}; static void dbDumpRegistrarCallFunc(const iocshArgBuf *args) { dbDumpRegistrar(*iocshPpdbbase); } /* dbDumpFunction */ static const iocshArg * const dbDumpFunctionArgs[] = { &argPdbbase}; static const iocshFuncDef dbDumpFunctionFuncDef = {"dbDumpFunction",1,dbDumpFunctionArgs}; static void dbDumpFunctionCallFunc(const iocshArgBuf *args) { dbDumpFunction(*iocshPpdbbase); } /* dbDumpVariable */ static const iocshArg * const dbDumpVariableArgs[] = { &argPdbbase}; static const iocshFuncDef dbDumpVariableFuncDef = {"dbDumpVariable",1,dbDumpVariableArgs}; static void dbDumpVariableCallFunc(const iocshArgBuf *args) { dbDumpVariable(*iocshPpdbbase); } /* dbDumpBreaktable */ static const iocshArg dbDumpBreaktableArg1 = { "tableName",iocshArgString}; static const iocshArg * const dbDumpBreaktableArgs[] = {&argPdbbase,&dbDumpBreaktableArg1}; static const iocshFuncDef dbDumpBreaktableFuncDef = {"dbDumpBreaktable",2,dbDumpBreaktableArgs}; static void dbDumpBreaktableCallFunc(const iocshArgBuf *args) { dbDumpBreaktable(*iocshPpdbbase,args[1].sval); } /* dbPvdDump */ static const iocshArg dbPvdDumpArg1 = { "verbose",iocshArgInt}; static const iocshArg * const dbPvdDumpArgs[] = { &argPdbbase,&dbPvdDumpArg1}; static const iocshFuncDef dbPvdDumpFuncDef = {"dbPvdDump",2,dbPvdDumpArgs}; static void dbPvdDumpCallFunc(const iocshArgBuf *args) { dbPvdDump(*iocshPpdbbase,args[1].ival); } /* dbPvdTableSize */ static const iocshArg dbPvdTableSizeArg0 = { "size",iocshArgInt}; static const iocshArg * const dbPvdTableSizeArgs[1] = {&dbPvdTableSizeArg0}; static const iocshFuncDef dbPvdTableSizeFuncDef = {"dbPvdTableSize",1,dbPvdTableSizeArgs}; static void dbPvdTableSizeCallFunc(const iocshArgBuf *args) { dbPvdTableSize(args[0].ival); } /* dbReportDeviceConfig */ static const iocshArg * const dbReportDeviceConfigArgs[] = {&argPdbbase}; static const iocshFuncDef dbReportDeviceConfigFuncDef = { "dbReportDeviceConfig",1,dbReportDeviceConfigArgs}; static void dbReportDeviceConfigCallFunc(const iocshArgBuf *args) { dbReportDeviceConfig(*iocshPpdbbase,stdout); } void dbStaticIocRegister(void) { iocshRegister(&dbDumpPathFuncDef, dbDumpPathCallFunc); iocshRegister(&dbDumpRecordFuncDef, dbDumpRecordCallFunc); iocshRegister(&dbDumpMenuFuncDef, dbDumpMenuCallFunc); iocshRegister(&dbDumpRecordTypeFuncDef, dbDumpRecordTypeCallFunc); iocshRegister(&dbDumpFieldFuncDef, dbDumpFieldCallFunc); iocshRegister(&dbDumpDeviceFuncDef, dbDumpDeviceCallFunc); iocshRegister(&dbDumpDriverFuncDef, dbDumpDriverCallFunc); iocshRegister(&dbDumpLinkFuncDef, dbDumpLinkCallFunc); iocshRegister(&dbDumpRegistrarFuncDef,dbDumpRegistrarCallFunc); iocshRegister(&dbDumpFunctionFuncDef, dbDumpFunctionCallFunc); iocshRegister(&dbDumpVariableFuncDef, dbDumpVariableCallFunc); iocshRegister(&dbDumpBreaktableFuncDef, dbDumpBreaktableCallFunc); iocshRegister(&dbPvdDumpFuncDef, dbPvdDumpCallFunc); iocshRegister(&dbPvdTableSizeFuncDef,dbPvdTableSizeCallFunc); iocshRegister(&dbReportDeviceConfigFuncDef, dbReportDeviceConfigCallFunc); } base-7.0.3.1/modules/database/src/ioc/dbStatic/dbStaticIocRegister.h0000664000577000060420000000135313557101274023775 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_dbStaticIocRegister_H #define INC_dbStaticIocRegister_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void dbStaticIocRegister(void); #ifdef __cplusplus } #endif #endif /* INC_dbStaticIocRegister_H */ base-7.0.3.1/modules/database/src/ioc/dbStatic/dbStaticLib.c0000664000577000060420000031013413557101274022257 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include #include #include #include #include #include "cantProceed.h" #include "cvtFast.h" #include "epicsAssert.h" #include "dbDefs.h" #include "dbmf.h" #include "ellLib.h" #include "epicsPrint.h" #include "epicsStdio.h" #include "epicsStdlib.h" #include "epicsString.h" #include "errlog.h" #include "gpHash.h" #include "osiFileName.h" #include "postfix.h" #define DBFLDTYPES_GBLSOURCE #define SPECIAL_GBLSOURCE #define epicsExportSharedSymbols #include "dbChannel.h" #include "dbFldTypes.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "devSup.h" #include "drvSup.h" #include "link.h" #include "special.h" #include "dbCommon.h" #include "dbJLink.h" int dbStaticDebug = 0; static char *pNullString = ""; #define messagesize 276 #define RPCL_LEN INFIX_TO_POSTFIX_SIZE(80) /* Must be big enough to hold a 64-bit integer in base 10, but in * the future when fields hold large JSON objects this fixed size * allocation will probably have to become variable sized. */ STATIC_ASSERT(messagesize >= 21); static char *ppstring[5]={" NPP"," PP"," CA"," CP"," CPP"}; static char *msstring[4]={" NMS"," MS"," MSI"," MSS"}; epicsShareDef maplinkType pamaplinkType[LINK_NTYPES] = { {"CONSTANT",CONSTANT}, {"PV_LINK",PV_LINK}, {"VME_IO",VME_IO}, {"CAMAC_IO",CAMAC_IO}, {"AB_IO",AB_IO}, {"GPIB_IO",GPIB_IO}, {"BITBUS_IO",BITBUS_IO}, {"MACRO_LINK",MACRO_LINK}, {"JSON_LINK",JSON_LINK}, {"PN_LINK",PN_LINK}, {"DB_LINK",DB_LINK}, {"CA_LINK",CA_LINK}, {"INST_IO",INST_IO}, {"BBGPIB_IO",BBGPIB_IO}, {"RF_IO",RF_IO}, {"VXI_IO",VXI_IO} }; /*forward references for private routines*/ static void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) EPICS_PRINTF_STYLE(2,3); static long dbAddOnePath (DBBASE *pdbbase, const char *path, unsigned length); /* internal routines*/ static FILE *openOutstream(const char *filename) { FILE *stream; errno = 0; stream = fopen(filename,"w"); if(!stream) { fprintf(stderr,"error opening %s %s\n",filename,strerror(errno)); return 0; } return stream; } static void finishOutstream(FILE *stream) { if(stream==stdout) { fflush(stdout); } else { if(fclose(stream)) fprintf(stderr,"fclose error %s\n",strerror(errno)); } } void dbFreeLinkContents(struct link *plink) { char *parm = NULL; switch(plink->type) { case CONSTANT: free((void *)plink->value.constantStr); break; case MACRO_LINK: free((void *)plink->value.macro_link.macroStr); break; case PV_LINK: free((void *)plink->value.pv_link.pvname); break; case JSON_LINK: dbJLinkFree(plink->value.json.jlink); parm = plink->value.json.string; break; case VME_IO: parm = plink->value.vmeio.parm; break; case CAMAC_IO: parm = plink->value.camacio.parm; break; case AB_IO: parm = plink->value.abio.parm; break; case GPIB_IO: parm = plink->value.gpibio.parm; break; case BITBUS_IO: parm = plink->value.bitbusio.parm;break; case INST_IO: parm = plink->value.instio.string; break; case BBGPIB_IO: parm = plink->value.bbgpibio.parm;break; case RF_IO: break; case VXI_IO: parm = plink->value.vxiio.parm; break; default: epicsPrintf("dbFreeLink called but link type %d unknown\n", plink->type); } if(parm && (parm != pNullString)) free((void *)parm); if(plink->text) free(plink->text); plink->lset = NULL; plink->text = NULL; memset(&plink->value, 0, sizeof(union value)); } void dbFreePath(DBBASE *pdbbase) { ELLLIST *ppathList; dbPathNode *pdbPathNode; if(!pdbbase) return; ppathList = (ELLLIST *)pdbbase->pathPvt; if(!ppathList) return; while((pdbPathNode = (dbPathNode *)ellFirst(ppathList))) { ellDelete(ppathList,&pdbPathNode->node); free((void *)pdbPathNode->directory); free((void *)pdbPathNode); } free((void *)ppathList); pdbbase->pathPvt = 0; return; } static void zeroDbentry(DBENTRY *pdbentry) { /*NOTE that pdbbase and message MUST NOT be set to NULL*/ pdbentry->precordType=NULL; pdbentry->pflddes=NULL; pdbentry->precnode=NULL; pdbentry->pfield=NULL; pdbentry->indfield=0; } static char *getpMessage(DBENTRY *pdbentry) { char *msg = pdbentry->message; if (!msg) { msg = dbCalloc(1, messagesize); pdbentry->message = msg; } else *msg = '\0'; return msg; } static void dbMsgCpy(DBENTRY *pdbentry, const char *msg) { getpMessage(pdbentry); strncpy(pdbentry->message, msg, messagesize-1); pdbentry->message[messagesize-1] = '\0'; } static void dbMsgNCpy(DBENTRY *pdbentry, const char *msg, size_t len) { getpMessage(pdbentry); if (len >= messagesize) len = messagesize-1; /* FIXME: Quietly truncates */ strncpy(pdbentry->message, msg, len); pdbentry->message[len] = '\0'; } static void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) { va_list args; getpMessage(pdbentry); va_start(args, fmt); epicsVsnprintf(pdbentry->message, messagesize, fmt, args); va_end(args); } static void ulongToHexString(epicsUInt32 source, char *pdest) { static const char hex_digit_to_ascii[16] = "0123456789abcdef"; epicsUInt32 val,temp; char digit[10]; int i,j; if (source==0) { strcpy(pdest,"0x0"); return; } *pdest++ = '0'; *pdest++ = 'x'; val = source; for (i=0; val!=0; i++) { temp = val/16; digit[i] = hex_digit_to_ascii[val - temp*16]; val = temp; } for (j=i-1; j>=0; j--) { *pdest++ = digit[j]; } *pdest = 0; return; } static void realToString(double value, char *preturn, int isdouble) { static const double delta[2] = {1e-6, 1e-15}; static const int precision[2] = {6, 14}; double absvalue; int logval,prec; size_t end; char tstr[30]; char *ptstr = &tstr[0]; int round; int ise = FALSE; char *loce = NULL; if (value == 0) { strcpy(preturn, "0"); return; } absvalue = value < 0 ? -value : value; if (absvalue < (double)INT_MAX) { epicsInt32 intval = (epicsInt32) value; double diff = value - intval; if (diff < 0) diff = -diff; if (diff < absvalue * delta[isdouble]) { cvtLongToString(intval, preturn); return; } } /*Now starts the hard cases*/ if (value < 0) { *preturn++ = '-'; value = -value; } logval = (int)log10(value); if (logval > 6 || logval < -2) { int nout; ise = TRUE; prec = precision[isdouble]; nout = sprintf(ptstr, "%.*e", prec, value); loce = strchr(ptstr, 'e'); if (!loce) { ptstr[nout] = 0; strcpy(preturn, ptstr); return; } *loce++ = 0; } else { prec = precision[isdouble] - logval; if ( prec < 0) prec = 0; sprintf(ptstr, "%.*f", prec, value); } if (prec > 0) { end = strlen(ptstr) - 1; round = FALSE; while (end > 0) { if (tstr[end] == '.') {end--; break;} if (tstr[end] == '0') {end--; continue;} if (!round && end < precision[isdouble]) break; if (!round && tstr[end] < '8') break; if (tstr[end-1] == '.') { if (round) end = end-2; break; } if (tstr[end-1] != '9') break; round = TRUE; end--; } tstr[end+1] = 0; while (round) { if (tstr[end] < '9') {tstr[end]++; break;} if (end == 0) { *preturn++ = '1'; tstr[end] = '0'; break;} tstr[end--] = '0'; } } strcpy(preturn, &tstr[0]); if (ise) { if (!(strchr(preturn, '.'))) strcat(preturn, ".0"); strcat(preturn, "e"); strcat(preturn, loce); } } static void floatToString(float value, char *preturn) { realToString((double)value, preturn, 0); } static void doubleToString(double value, char *preturn) { realToString(value, preturn, 1); } /*Public only for dbStaticNoRun*/ dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry) { dbRecordType *precordType = pdbentry->precordType; dbFldDes *pflddes = pdbentry->pflddes; dbDeviceMenu *pdbDeviceMenu; devSup *pdevSup; int ind; int nChoice; if(!precordType) return(NULL); if(!pflddes) return(NULL); if(pflddes->field_type!=DBF_DEVICE) return(NULL); if(pflddes->ftPvt){ pdbDeviceMenu = (dbDeviceMenu *)pflddes->ftPvt; if(pdbDeviceMenu->nChoice == ellCount(&precordType->devList)) return(pdbDeviceMenu); free((void *)pdbDeviceMenu->papChoice); free((void *)pdbDeviceMenu); pflddes->ftPvt = NULL; } nChoice = ellCount(&precordType->devList); if(nChoice <= 0) return(NULL); pdbDeviceMenu = dbCalloc(1,sizeof(dbDeviceMenu)); pdbDeviceMenu->nChoice = nChoice; pdbDeviceMenu->papChoice = dbCalloc(pdbDeviceMenu->nChoice,sizeof(char *)); pdevSup = (devSup *)ellFirst(&precordType->devList); ind = 0; while(pdevSup) { pdbDeviceMenu->papChoice[ind] = pdevSup->choice; ind++; pdevSup = (devSup *)ellNext(&pdevSup->node); } pflddes->ftPvt = pdbDeviceMenu; return(pdbDeviceMenu); } /* Beginning of Public Routines */ #define INC_SIZE 256 void dbCatString(char **string,int *stringLength,char *src,char *separator) { if((*string==NULL) || ((strlen(*string)+strlen(src)+2) > (size_t)*stringLength)) { char *newString; size_t size; size = strlen(src); if(*string) size += strlen(*string); /*Make size multiple of INC_SIZE*/ size = ((size + 2 + INC_SIZE)/INC_SIZE) * INC_SIZE; newString = dbCalloc(size,sizeof(char)); if(*string) { strcpy(newString,*string); free((void *)(*string)); } *string = newString; } if(*stringLength>0) { strcat(*string,separator); *stringLength += (int) strlen(separator); } strcat(*string,src); *stringLength += (int) strlen(src); } dbBase * dbAllocBase(void) { dbBase *pdbbase; pdbbase = dbCalloc(1,sizeof(dbBase)); ellInit(&pdbbase->menuList); ellInit(&pdbbase->recordTypeList); ellInit(&pdbbase->drvList); ellInit(&pdbbase->registrarList); ellInit(&pdbbase->functionList); ellInit(&pdbbase->variableList); ellInit(&pdbbase->bptList); ellInit(&pdbbase->filterList); ellInit(&pdbbase->guiGroupList); gphInitPvt(&pdbbase->pgpHash,256); dbPvdInitPvt(pdbbase); return (pdbbase); } void dbFreeBase(dbBase *pdbbase) { dbMenu *pdbMenu; dbMenu *pdbMenuNext; dbRecordType *pdbRecordType; dbRecordType *pdbRecordTypeNext; dbFldDes * pdbFldDes; dbRecordAttribute *pAttribute; dbRecordAttribute *pAttributeNext; devSup *pdevSup; devSup *pdevSupNext; dbText *ptext; dbText *ptextNext; dbVariableDef *pvar; dbVariableDef *pvarNext; drvSup *pdrvSup; drvSup *pdrvSupNext; linkSup *plinkSup; brkTable *pbrkTable; brkTable *pbrkTableNext; chFilterPlugin *pfilt; chFilterPlugin *pfiltNext; dbGuiGroup *pguiGroup; dbGuiGroup *pguiGroupNext; int i; DBENTRY dbentry; long status; dbInitEntry(pdbbase,&dbentry); status = dbFirstRecordType(&dbentry); while(!status) { /* dbDeleteRecord() will remove alias or real record node. * For real record nodes, also removes the nodes of all aliases. * This complicates safe traversal, so we re-start iteration * from the first record after each call. */ while((status = dbFirstRecord(&dbentry))==0) { dbDeleteRecord(&dbentry); } assert(status==S_dbLib_recNotFound); status = dbNextRecordType(&dbentry); } dbFinishEntry(&dbentry); pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); while(pdbRecordType) { for(i=0; ino_fields; i++) { pdbFldDes = pdbRecordType->papFldDes[i]; free((void *)pdbFldDes->prompt); free((void *)pdbFldDes->name); free((void *)pdbFldDes->extra); free((void *)pdbFldDes->initial); if(pdbFldDes->field_type==DBF_DEVICE && pdbFldDes->ftPvt) { dbDeviceMenu *pdbDeviceMenu; pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt; free((void *)pdbDeviceMenu->papChoice); free((void *)pdbDeviceMenu); pdbFldDes->ftPvt=0; } free((void *)pdbFldDes); } pdevSup = (devSup *)ellFirst(&pdbRecordType->devList); while(pdevSup) { pdevSupNext = (devSup *)ellNext(&pdevSup->node); ellDelete(&pdbRecordType->devList,&pdevSup->node); free((void *)pdevSup->name); free((void *)pdevSup->choice); free((void *)pdevSup); pdevSup = pdevSupNext; } ptext = (dbText *)ellFirst(&pdbRecordType->cdefList); while(ptext) { ptextNext = (dbText *)ellNext(&ptext->node); ellDelete(&pdbRecordType->cdefList,&ptext->node); free((void *)ptext->text); free((void *)ptext); ptext = ptextNext; } pAttribute = (dbRecordAttribute *)ellFirst(&pdbRecordType->attributeList); while(pAttribute) { pAttributeNext = (dbRecordAttribute *)ellNext(&pAttribute->node); ellDelete(&pdbRecordType->attributeList,&pAttribute->node); free((void *)pAttribute->name); free((void *)pAttribute->pdbFldDes); free(pAttribute); pAttribute = pAttributeNext; } pdbRecordTypeNext = (dbRecordType *)ellNext(&pdbRecordType->node); gphDelete(pdbbase->pgpHash,pdbRecordType->name,&pdbbase->recordTypeList); ellDelete(&pdbbase->recordTypeList,&pdbRecordType->node); free((void *)pdbRecordType->name); free((void *)pdbRecordType->link_ind); free((void *)pdbRecordType->papsortFldName); free((void *)pdbRecordType->sortFldInd); free((void *)pdbRecordType->papFldDes); free((void *)pdbRecordType); pdbRecordType = pdbRecordTypeNext; } pdbMenu = (dbMenu *)ellFirst(&pdbbase->menuList); while(pdbMenu) { pdbMenuNext = (dbMenu *)ellNext(&pdbMenu->node); gphDelete(pdbbase->pgpHash,pdbMenu->name,&pdbbase->menuList); ellDelete(&pdbbase->menuList,&pdbMenu->node); for(i=0; i< pdbMenu->nChoice; i++) { free((void *)pdbMenu->papChoiceName[i]); free((void *)pdbMenu->papChoiceValue[i]); } free((void *)pdbMenu->papChoiceName); free((void *)pdbMenu->papChoiceValue); free((void *)pdbMenu ->name); free((void *)pdbMenu); pdbMenu = pdbMenuNext; } pdrvSup = (drvSup *)ellFirst(&pdbbase->drvList); while(pdrvSup) { pdrvSupNext = (drvSup *)ellNext(&pdrvSup->node); ellDelete(&pdbbase->drvList,&pdrvSup->node); free((void *)pdrvSup->name); free((void *)pdrvSup); pdrvSup = pdrvSupNext; } while ((plinkSup = (linkSup *) ellGet(&pdbbase->linkList))) { free(plinkSup->jlif_name); free(plinkSup->name); free(plinkSup); } ptext = (dbText *)ellFirst(&pdbbase->registrarList); while(ptext) { ptextNext = (dbText *)ellNext(&ptext->node); ellDelete(&pdbbase->registrarList,&ptext->node); free((void *)ptext->text); free((void *)ptext); ptext = ptextNext; } ptext = (dbText *)ellFirst(&pdbbase->functionList); while(ptext) { ptextNext = (dbText *)ellNext(&ptext->node); ellDelete(&pdbbase->functionList,&ptext->node); free((void *)ptext->text); free((void *)ptext); ptext = ptextNext; } pvar = (dbVariableDef *)ellFirst(&pdbbase->variableList); while(pvar) { pvarNext = (dbVariableDef *)ellNext(&pvar->node); ellDelete(&pdbbase->variableList,&pvar->node); free((void *)pvar->name); free((void *)pvar->type); free((void *)pvar); pvar = pvarNext; } pbrkTable = (brkTable *)ellFirst(&pdbbase->bptList); while(pbrkTable) { pbrkTableNext = (brkTable *)ellNext(&pbrkTable->node); gphDelete(pdbbase->pgpHash,pbrkTable->name,&pdbbase->bptList); ellDelete(&pdbbase->bptList,&pbrkTable->node); free(pbrkTable->name); free((void *)pbrkTable->paBrkInt); free((void *)pbrkTable); pbrkTable = pbrkTableNext; } pfilt = (chFilterPlugin *)ellFirst(&pdbbase->filterList); while(pfilt) { pfiltNext = (chFilterPlugin *)ellNext(&pfilt->node); free((char*)pfilt->name); if(pfilt->fif->priv_free) (*pfilt->fif->priv_free)(pfilt->puser); free(pfilt); pfilt = pfiltNext; } pguiGroup = (dbGuiGroup *)ellFirst(&pdbbase->guiGroupList); while (pguiGroup) { pguiGroupNext = (dbGuiGroup *)ellNext(&pguiGroup->node); gphDelete(pdbbase->pgpHash, pguiGroup->name, &pdbbase->guiGroupList); ellDelete(&pdbbase->guiGroupList, &pguiGroup->node); free(pguiGroup->name); free((void *)pguiGroup); pguiGroup = pguiGroupNext; } gphFreeMem(pdbbase->pgpHash); dbPvdFreeMem(pdbbase); dbFreePath(pdbbase); free((void *)pdbbase); pdbbase = NULL; return; } DBENTRY * dbAllocEntry(dbBase *pdbbase) { DBENTRY *pdbentry; pdbentry = dbmfMalloc(sizeof(DBENTRY)); memset(pdbentry,'\0',sizeof(DBENTRY)); pdbentry->pdbbase = pdbbase; return(pdbentry); } void dbFreeEntry(DBENTRY *pdbentry) { if (!pdbentry) return; if (pdbentry->message) free((void *)pdbentry->message); dbmfFree(pdbentry); } void dbInitEntry(dbBase *pdbbase,DBENTRY *pdbentry) { memset((char *)pdbentry,'\0',sizeof(DBENTRY)); pdbentry->pdbbase = pdbbase; } void dbFinishEntry(DBENTRY *pdbentry) { if(pdbentry->message) { free((void *)pdbentry->message); pdbentry->message = NULL; } } DBENTRY * dbCopyEntry(DBENTRY *pdbentry) { DBENTRY *pnew; pnew = dbAllocEntry(pdbentry->pdbbase); *pnew = *pdbentry; pnew->message = NULL; return(pnew); } void dbCopyEntryContents(DBENTRY *pfrom,DBENTRY *pto) { *pto = *pfrom; pto->message = NULL; } long dbPath(DBBASE *pdbbase,const char *path) { if(!pdbbase) return(-1); dbFreePath(pdbbase); if(!path || strlen(path)==0) return(dbAddPath(pdbbase,".")); return(dbAddPath(pdbbase,path)); } long dbAddPath(DBBASE *pdbbase,const char *path) { ELLLIST *ppathList; const char *pcolon; const char *plast; unsigned expectingPath; unsigned sawMissingPath; if(!pdbbase) return(-1); ppathList = (ELLLIST *)pdbbase->pathPvt; if(!ppathList) { ppathList = dbCalloc(1,sizeof(ELLLIST)); ellInit(ppathList); pdbbase->pathPvt = (void *)ppathList; } if (!path) return(0); /* Empty path strings are ignored */ /* care is taken to properly deal with white space * 1) preceding and trailing white space is removed from paths * 2) white space inbetween path separator counts as an empty name * (see below) */ expectingPath = FALSE; sawMissingPath = FALSE; while (*path) { size_t len; /* preceding white space is removed */ if (isspace((int)*path)) { path++; continue; } pcolon = strstr (path, OSI_PATH_LIST_SEPARATOR); if (pcolon==path) { sawMissingPath = TRUE; path += strlen (OSI_PATH_LIST_SEPARATOR); continue; } if (pcolon) { plast = pcolon - 1; expectingPath = TRUE; } else { plast = strlen (path) + path - 1; expectingPath = FALSE; } /* trailing white space is removed */ while (isspace((int)*plast)) { plast--; } /* * len is always nonzero because we found something that * 1) isnt white space * 2) isnt a path separator */ len = (plast - path) + 1; if (dbAddOnePath (pdbbase, path, (unsigned) len)) return (-1); path += len; if (pcolon) { path += strlen(OSI_PATH_LIST_SEPARATOR); } } /* * an empty name at beginning, middle, or end of a path string that isnt * empty means current directory */ if (expectingPath||sawMissingPath) { return dbAddOnePath (pdbbase, ".", 1); } return(0); } static long dbAddOnePath (DBBASE *pdbbase, const char *path, unsigned length) { ELLLIST *ppathList; dbPathNode *pdbPathNode; if(!pdbbase) return(-1); ppathList = (ELLLIST *)pdbbase->pathPvt; pdbPathNode = (dbPathNode *)dbCalloc(1, sizeof(dbPathNode)); pdbPathNode->directory = (char *)dbCalloc(length+1, sizeof(char)); strncpy(pdbPathNode->directory, path, length); pdbPathNode->directory[length] = '\0'; ellAdd(ppathList, &pdbPathNode->node); return 0; } char *dbGetPromptGroupNameFromKey(DBBASE *pdbbase, const short key) { dbGuiGroup *pdbGuiGroup; if (!pdbbase) return NULL; for (pdbGuiGroup = (dbGuiGroup *)ellFirst(&pdbbase->guiGroupList); pdbGuiGroup; pdbGuiGroup = (dbGuiGroup *)ellNext(&pdbGuiGroup->node)) { if (pdbGuiGroup->key == key) return pdbGuiGroup->name; } return NULL; } short dbGetPromptGroupKeyFromName(DBBASE *pdbbase, const char *name) { GPHENTRY *pgphentry; if (!pdbbase) return 0; pgphentry = gphFind(pdbbase->pgpHash, name, &pdbbase->guiGroupList); if (!pgphentry) { return 0; } else { return ((dbGuiGroup*)pgphentry->userPvt)->key; } } long dbWriteRecord(DBBASE *ppdbbase,const char *filename, const char *precordTypename,int level) { FILE *stream; long status; stream = openOutstream(filename); if(!stream) return -1; status = dbWriteRecordFP(ppdbbase,stream,precordTypename,level); finishOutstream(stream); return status; } long dbWriteRecordFP( DBBASE *pdbbase,FILE *fp,const char *precordTypename,int level) { DBENTRY dbentry; DBENTRY *pdbentry=&dbentry; long status; int dctonly; dctonly = ((level>1) ? FALSE : TRUE); dbInitEntry(pdbbase,pdbentry); if (precordTypename) { if (*precordTypename == 0 || *precordTypename == '*') precordTypename = 0; } if(!precordTypename) { status = dbFirstRecordType(pdbentry); if(status) { /* No record descriptions, so no record instances */ dbFinishEntry(pdbentry); return(0); } } else { status = dbFindRecordType(pdbentry,precordTypename); if(status) { fprintf(stderr,"dbWriteRecordFP: No record description for %s\n", precordTypename); dbFinishEntry(pdbentry); return(status); } } while(!status) { status = dbFirstRecord(pdbentry); while(!status) { if (dbIsAlias(pdbentry)) { status = dbNextRecord(pdbentry); continue; } if(dbIsVisibleRecord(pdbentry)) fprintf(fp,"grecord(%s,\"%s\") {\n", dbGetRecordTypeName(pdbentry),dbGetRecordName(pdbentry)); else fprintf(fp,"record(%s,\"%s\") {\n", dbGetRecordTypeName(pdbentry),dbGetRecordName(pdbentry)); status = dbFirstField(pdbentry,dctonly); while(!status) { if (!dbIsDefaultValue(pdbentry) || level>0) { char *pvalstring = dbGetString(pdbentry); if (!pvalstring) { fprintf(fp,"\tfield(%s,\"\")\n", dbGetFieldName(pdbentry)); } else { fprintf(fp,"\tfield(%s,\"", dbGetFieldName(pdbentry)); epicsStrPrintEscaped(fp,pvalstring,strlen(pvalstring)); fprintf(fp,"\")\n"); } } else if(level>0) { /*generate 0 length string*/ fprintf(fp,"\tfield(%s,\"\")\n",dbGetFieldName(pdbentry)); } status=dbNextField(pdbentry,dctonly); } status = dbFirstInfo(pdbentry); while (!status) { const char *pinfostr = dbGetInfoString(pdbentry); fprintf(fp, "\tinfo(\"%s\",\"", dbGetInfoName(pdbentry)); epicsStrPrintEscaped(fp, pinfostr, strlen(pinfostr)); fprintf(fp, "\")\n"); status = dbNextInfo(pdbentry); } fprintf(fp,"}\n"); status = dbNextRecord(pdbentry); } status = dbFirstRecord(pdbentry); while (!status) { if (!dbIsAlias(pdbentry)) { status = dbNextRecord(pdbentry); continue; } fprintf(fp, "alias(\"%s\",\"%s\")\n", dbRecordName(pdbentry), dbGetRecordName(pdbentry)); status = dbNextRecord(pdbentry); } if(precordTypename) break; status = dbNextRecordType(pdbentry); } dbFinishEntry(pdbentry); return(0); } long dbWriteMenu( DBBASE *ppdbbase,const char *filename,const char *menuName) { FILE *stream; long status; stream = openOutstream(filename); status = dbWriteMenuFP(ppdbbase,stream,menuName); finishOutstream(stream); return status; } long dbWriteMenuFP(DBBASE *pdbbase,FILE *fp,const char *menuName) { dbMenu *pdbMenu; int gotMatch; int i; if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return(-1); } if (menuName) { if (*menuName == 0 || *menuName == '*') menuName = 0; } pdbMenu = (dbMenu *)ellFirst(&pdbbase->menuList); while(pdbMenu) { if(menuName) { gotMatch = (strcmp(menuName,pdbMenu->name)==0) ? TRUE : FALSE; }else { gotMatch=TRUE; } if(gotMatch) { fprintf(fp,"menu(%s) {\n",pdbMenu->name); for(i=0; inChoice; i++) { fprintf(fp,"\tchoice(%s,\"%s\")\n",pdbMenu->papChoiceName[i], pdbMenu->papChoiceValue[i]); } fprintf(fp,"}\n"); if(menuName) break; } pdbMenu = (dbMenu *)ellNext(&pdbMenu->node); } return(0); } long dbWriteRecordType( DBBASE *pdbbase,const char *filename,const char *recordTypeName) { FILE *stream; long status; stream = openOutstream(filename); status = dbWriteRecordTypeFP(pdbbase,stream,recordTypeName); finishOutstream(stream); return status; } long dbWriteRecordTypeFP( DBBASE *pdbbase,FILE *fp,const char *recordTypeName) { dbRecordType *pdbRecordType; dbFldDes *pdbFldDes; int gotMatch; int i; if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return(-1); } if (recordTypeName) { if (*recordTypeName == 0 || *recordTypeName == '*') recordTypeName = 0; } for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { if(recordTypeName) { gotMatch = (strcmp(recordTypeName,pdbRecordType->name)==0) ? TRUE : FALSE; }else { gotMatch=TRUE; } if(!gotMatch) continue; fprintf(fp,"recordtype(%s) {\n",pdbRecordType->name); for(i=0; ino_fields; i++) { int j; pdbFldDes = pdbRecordType->papFldDes[i]; fprintf(fp,"\tfield(%s,%s) {\n",pdbFldDes->name, dbGetFieldTypeString(pdbFldDes->field_type)); if(pdbFldDes->prompt) fprintf(fp,"\t\tprompt(\"%s\")\n",pdbFldDes->prompt); if(pdbFldDes->initial) fprintf(fp,"\t\tinitial(\"%s\")\n",pdbFldDes->initial); if (pdbFldDes->promptgroup) { fprintf(fp,"\t\tpromptgroup(\"%s\")\n", dbGetPromptGroupNameFromKey(pdbbase, pdbFldDes->promptgroup)); } if(pdbFldDes->special) { if(pdbFldDes->special >= SPC_NTYPES) { fprintf(fp,"\t\tspecial(%d)\n",pdbFldDes->special); } else for(j=0; jspecial) { fprintf(fp,"\t\tspecial(%s)\n", pamapspcType[j].strvalue); break; } } } if(pdbFldDes->extra) fprintf(fp,"\t\textra(\"%s\")\n",pdbFldDes->extra); if(pdbFldDes->field_type==DBF_MENU) { if(pdbFldDes->ftPvt) fprintf(fp,"\t\tmenu(%s)\n", ((dbMenu *)pdbFldDes->ftPvt)->name); else fprintf(stderr,"\t\t menu: NOT FOUND\n"); } if(pdbFldDes->field_type==DBF_STRING) { fprintf(fp,"\t\tsize(%d)\n", pdbFldDes->size); } if(pdbFldDes->process_passive) fprintf(fp,"\t\tpp(TRUE)\n"); if(pdbFldDes->prop) fprintf(fp,"\t\tprop(YES)\n"); if(pdbFldDes->base) fprintf(fp,"\t\tbase(HEX)\n"); if(pdbFldDes->interest) fprintf(fp,"\t\tinterest(%d)\n",pdbFldDes->interest); if(!pdbFldDes->as_level) fprintf(fp,"\t\tasl(ASL0)\n"); fprintf(fp,"\t}\n"); } fprintf(fp,"}\n"); if(recordTypeName) break; } return(0); } long dbWriteDevice(DBBASE *pdbbase,const char *filename) { FILE *stream; long status; stream = openOutstream(filename); status = dbWriteDeviceFP(pdbbase,stream); finishOutstream(stream); return status; } long dbWriteDeviceFP(DBBASE *pdbbase,FILE *fp) { dbRecordType *pdbRecordType; devSup *pdevSup; if(!pdbbase) { fprintf(stderr,"dbWriteDeviceFP: pdbbase not specified\n"); return(-1); } for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { for(pdevSup = (devSup *)ellFirst(&pdbRecordType->devList); pdevSup; pdevSup = (devSup *)ellNext(&pdevSup->node)) { int j; for(j=0; j< LINK_NTYPES; j++) { if(pamaplinkType[j].value==pdevSup->link_type) break; } if(j>=LINK_NTYPES) { fprintf(fp,"link_type not valid\n"); continue; } fprintf(fp,"device(%s,%s,%s,\"%s\")\n", pdbRecordType->name, pamaplinkType[j].strvalue, pdevSup->name,pdevSup->choice); } } return(0); } long dbWriteDriver(DBBASE *pdbbase,const char *filename) { FILE *stream; long status; stream = openOutstream(filename); status = dbWriteDriverFP(pdbbase,stream); finishOutstream(stream); return status; } long dbWriteDriverFP(DBBASE *pdbbase,FILE *fp) { drvSup *pdrvSup; if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return(-1); } for(pdrvSup = (drvSup *)ellFirst(&pdbbase->drvList); pdrvSup; pdrvSup = (drvSup *)ellNext(&pdrvSup->node)) { fprintf(fp,"driver(%s)\n",pdrvSup->name); } return(0); } long dbWriteLinkFP(DBBASE *pdbbase, FILE *fp) { linkSup *plinkSup; if (!pdbbase) { fprintf(stderr, "pdbbase not specified\n"); return -1; } for (plinkSup = (linkSup *) ellFirst(&pdbbase->linkList); plinkSup; plinkSup = (linkSup *) ellNext(&plinkSup->node)) { fprintf(fp, "link(%s,%s)\n", plinkSup->name, plinkSup->jlif_name); } return 0; } long dbWriteRegistrarFP(DBBASE *pdbbase,FILE *fp) { dbText *ptext; if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return(-1); } for(ptext = (dbText *)ellFirst(&pdbbase->registrarList); ptext; ptext = (dbText *)ellNext(&ptext->node)) { fprintf(fp,"registrar(%s)\n",ptext->text); } return(0); } long dbWriteFunctionFP(DBBASE *pdbbase,FILE *fp) { dbText *ptext; if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return(-1); } for(ptext = (dbText *)ellFirst(&pdbbase->functionList); ptext; ptext = (dbText *)ellNext(&ptext->node)) { fprintf(fp,"function(%s)\n",ptext->text); } return(0); } long dbWriteVariableFP(DBBASE *pdbbase,FILE *fp) { dbVariableDef *pvar; if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return(-1); } for(pvar = (dbVariableDef *)ellFirst(&pdbbase->variableList); pvar; pvar = (dbVariableDef *)ellNext(&pvar->node)) { fprintf(fp,"variable(%s,%s)\n",pvar->name,pvar->type); } return(0); } long dbWriteBreaktable(DBBASE *pdbbase,const char *filename) { FILE *stream; long status; stream = openOutstream(filename); status = dbWriteBreaktableFP(pdbbase,stream); finishOutstream(stream); return status; } long dbWriteBreaktableFP(DBBASE *pdbbase,FILE *fp) { brkTable *pbrkTable; brkInt *pbrkInt; int i; if (!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return(-1); } for (pbrkTable = (brkTable *)ellFirst(&pdbbase->bptList); pbrkTable; pbrkTable = (brkTable *)ellNext(&pbrkTable->node)) { fprintf(fp,"breaktable(%s) {\n",pbrkTable->name); pbrkInt = pbrkTable->paBrkInt; for(i=0; i < pbrkTable->number; i++) { fprintf(fp,"\t%e, %e\n",pbrkInt->raw,pbrkInt->eng); pbrkInt++; } fprintf(fp,"}\n"); } return(0); } long dbFindRecordType(DBENTRY *pdbentry,const char *recordType) { dbBase *pdbbase = pdbentry->pdbbase; GPHENTRY *phash; zeroDbentry(pdbentry); phash = gphFind(pdbbase->pgpHash,recordType,&pdbbase->recordTypeList); if(!phash) return(S_dbLib_recordTypeNotFound); pdbentry->precordType = phash->userPvt; return(0); } long dbFirstRecordType(DBENTRY *pdbentry) { dbRecordType *precordType; zeroDbentry(pdbentry); precordType = (dbRecordType *)ellFirst(&pdbentry->pdbbase->recordTypeList); if(!precordType) return(S_dbLib_recordTypeNotFound); pdbentry->precordType = precordType; return(0); } long dbNextRecordType(DBENTRY *pdbentry) { dbRecordType *precordType = pdbentry->precordType; zeroDbentry(pdbentry); precordType = (dbRecordType *)ellNext(&precordType->node); if(!precordType) return(S_dbLib_recordTypeNotFound); pdbentry->precordType = precordType; return(0); } char * dbGetRecordTypeName(DBENTRY *pdbentry) { return(pdbentry->precordType->name); } int dbGetNRecordTypes(DBENTRY *pdbentry) { return(ellCount(&pdbentry->pdbbase->recordTypeList)); } long dbPutRecordAttribute( DBENTRY *pdbentry, const char *name, const char*value) { dbRecordType *precordType = pdbentry->precordType; int createNew = TRUE; int compare; dbRecordAttribute *pattribute; if(!precordType) return(S_dbLib_recordTypeNotFound); pattribute = (dbRecordAttribute *)ellFirst(&precordType->attributeList); /*put new attribute name in sort order*/ while(pattribute) { compare = strcmp(pattribute->name,name); if(compare==0) { createNew = FALSE; } if(compare>=0) break; pattribute = (dbRecordAttribute *)ellNext(&pattribute->node); } if(createNew) { dbRecordAttribute *pnew; dbFldDes *pdbFldDes; pnew = dbCalloc(1,sizeof(dbRecordAttribute)); if(pattribute) { ellInsert(&precordType->attributeList,pattribute->node.previous, &pnew->node); } else { ellAdd(&precordType->attributeList,&pnew->node); } pattribute = pnew; pattribute->name = dbCalloc(strlen(name)+1,sizeof(char)); strcpy(pattribute->name,name); pdbFldDes = dbCalloc(1,sizeof(dbFldDes)); pdbFldDes->name = pattribute->name; pdbFldDes->pdbRecordType = precordType; pdbFldDes->special = SPC_ATTRIBUTE; pdbFldDes->field_type = DBF_STRING; pdbFldDes->as_level = ASL1; pdbFldDes->size = MAX_STRING_SIZE; pattribute->pdbFldDes = pdbFldDes; } strncpy(pattribute->value,value,MAX_STRING_SIZE); pattribute->value[MAX_STRING_SIZE-1] = 0; return(0); } long dbGetAttributePart(DBENTRY *pdbentry, const char **ppname) { dbRecordType *precordType = pdbentry->precordType; const char *pname = *ppname; dbRecordAttribute *pattribute; if (!precordType) return S_dbLib_recordTypeNotFound; pattribute = (dbRecordAttribute *)ellFirst(&precordType->attributeList); while (pattribute) { size_t nameLen = strlen(pattribute->name); int compare = strncmp(pattribute->name, pname, nameLen); if (compare == 0) { int ch = pname[nameLen]; if (ch != '_' && !isalnum(ch)) { /* Any other character can't be in the attribute name */ pdbentry->pflddes = pattribute->pdbFldDes; pdbentry->pfield = pattribute->value; *ppname = &pname[nameLen]; return 0; } if (strlen(pname) > nameLen) { compare = -1; } } if (compare >= 0) break; pattribute = (dbRecordAttribute *)ellNext(&pattribute->node); } return S_dbLib_fieldNotFound; } long dbGetRecordAttribute(DBENTRY *pdbentry, const char *pname) { return dbGetAttributePart(pdbentry, &pname); } long dbFirstField(DBENTRY *pdbentry,int dctonly) { pdbentry->indfield = -1; return(dbNextField(pdbentry,dctonly)); } long dbNextField(DBENTRY *pdbentry,int dctonly) { dbRecordType *precordType = pdbentry->precordType; dbRecordNode *precnode = pdbentry->precnode; dbFldDes *pflddes; short indfield = pdbentry->indfield; if(!precordType) return(S_dbLib_recordTypeNotFound); indfield++; while(TRUE) { if(indfield>=precordType->no_fields) { pdbentry->indfield = 0; pdbentry->pflddes = NULL; pdbentry->pfield = NULL; return(S_dbLib_fieldNotFound); } pflddes = precordType->papFldDes[indfield]; if(!dctonly || pflddes->promptgroup) { /*Skip field if dctonly and no device support*/ if(!dctonly || (pflddes->field_type!=DBF_DEVICE) || (ellCount(&precordType->devList)>0)) { pdbentry->indfield = indfield; pdbentry->pflddes = pflddes; pdbentry->indfield = indfield; if(precnode) { dbGetFieldAddress(pdbentry); }else { pdbentry->pfield = NULL; } return(0); } } indfield++; } } int dbGetNFields(DBENTRY *pdbentry,int dctonly) { dbRecordType *precordType = pdbentry->precordType; dbFldDes *pflddes; int indfield,n; if(!precordType) return(S_dbLib_recordTypeNotFound); n = 0; for(indfield=0; indfieldno_fields; indfield++) { pflddes = precordType->papFldDes[indfield]; if(dctonly && (pflddes->field_type==DBF_DEVICE) && (ellCount(&precordType->devList)==0) ) continue; if(!dctonly || pflddes->promptgroup) n++; } return(n); } char * dbGetFieldName(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; if(!pflddes) return(NULL); return(pflddes->name); } char * dbGetDefault(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; if(!pflddes) return(NULL); return(pflddes->initial); } char * dbGetPrompt(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; if(!pflddes) return(NULL); return(&pflddes->prompt[0]); } int dbGetPromptGroup(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; if(!pflddes) return(0); return(pflddes->promptgroup); } long dbCreateRecord(DBENTRY *pdbentry,const char *precordName) { dbRecordType *precordType = pdbentry->precordType; dbFldDes *pdbFldDes; PVDENTRY *ppvd; ELLLIST *preclist = NULL; dbRecordNode *pNewRecNode = NULL; long status = 0; if(!precordType) return(S_dbLib_recordTypeNotFound); /*Get size of NAME field*/ pdbFldDes = precordType->papFldDes[0]; if(!pdbFldDes || (strcmp(pdbFldDes->name,"NAME")!=0)) return(S_dbLib_nameLength); if((int)strlen(precordName)>=pdbFldDes->size) return(S_dbLib_nameLength); /* clear callers entry */ zeroDbentry(pdbentry); if(!dbFindRecord(pdbentry,precordName)) return (S_dbLib_recExists); zeroDbentry(pdbentry); pdbentry->precordType = precordType; preclist = &precordType->recList; /* create a recNode */ pNewRecNode = dbCalloc(1,sizeof(dbRecordNode)); /* create a new record of this record type */ pdbentry->precnode = pNewRecNode; if((status = dbAllocRecord(pdbentry,precordName))) return(status); pNewRecNode->recordname = dbRecordName(pdbentry); ellInit(&pNewRecNode->infoList); ellAdd(preclist, &pNewRecNode->node); pdbentry->precnode = pNewRecNode; ppvd = dbPvdAdd(pdbentry->pdbbase,precordType,pNewRecNode); if(!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);} return(0); } long dbDeleteAliases(DBENTRY *pdbentry) { dbBase *pdbbase = pdbentry->pdbbase; dbRecordType *precordType = pdbentry->precordType; dbRecordNode *precnode = pdbentry->precnode; ELLLIST *preclist = &precordType->recList; dbRecordNode *pAliasNode, *pAliasNodeNext; DBENTRY dbentry; void *precord; if (!precnode) return S_dbLib_recNotFound; if (precnode->flags & DBRN_FLAGS_ISALIAS) return S_dbLib_recExists; precord = precnode->precord; dbInitEntry(pdbbase, &dbentry); pAliasNode = (dbRecordNode *)ellFirst(preclist); while (pAliasNode) { pAliasNodeNext = (dbRecordNode *)ellNext(&pAliasNode->node); if (pAliasNode->flags & DBRN_FLAGS_ISALIAS && pAliasNode->precord == precord && !dbFindRecord(&dbentry, pAliasNode->recordname)) { dbDeleteRecord(&dbentry); } pAliasNode = pAliasNodeNext; } precnode->flags &= ~DBRN_FLAGS_HASALIAS; return 0; } long dbDeleteRecord(DBENTRY *pdbentry) { dbBase *pdbbase = pdbentry->pdbbase; dbRecordType *precordType = pdbentry->precordType; dbRecordNode *precnode = pdbentry->precnode; ELLLIST *preclist; long status; if (!precnode) return S_dbLib_recNotFound; if (precnode->flags & DBRN_FLAGS_HASALIAS) dbDeleteAliases(pdbentry); preclist = &precordType->recList; ellDelete(preclist, &precnode->node); dbPvdDelete(pdbbase, precnode); while (!dbFirstInfo(pdbentry)) { dbDeleteInfo(pdbentry); } if (precnode->flags & DBRN_FLAGS_ISALIAS) { free(precnode->recordname); precordType->no_aliases--; } else { status = dbFreeRecord(pdbentry); if (status) return status; } free(precnode); pdbentry->precnode = NULL; return 0; } long dbFreeRecords(DBBASE *pdbbase) { DBENTRY dbentry; dbRecordType *pdbRecordType; dbRecordNode *pdbRecordNode; dbRecordNode *pdbRecordNodeNext; dbInitEntry(pdbbase,&dbentry); pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); while(pdbRecordType) { pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList); while(pdbRecordNode) { pdbRecordNodeNext = (dbRecordNode *)ellNext(&pdbRecordNode->node); if(!dbFindRecord(&dbentry,pdbRecordNode->recordname)) dbDeleteRecord(&dbentry); pdbRecordNode = pdbRecordNodeNext; } pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node); } dbFinishEntry(&dbentry); return(0); } long dbFindRecordPart(DBENTRY *pdbentry, const char **ppname) { dbBase *pdbbase = pdbentry->pdbbase; const char *pname = *ppname; const char *pfn; size_t lenName; PVDENTRY *ppvdNode; zeroDbentry(pdbentry); pfn = strchr(pname, '.'); if (pfn) { lenName = (size_t) (pfn - pname); } else { lenName = strlen(pname); } ppvdNode = dbPvdFind(pdbbase, pname, lenName); if (!ppvdNode) return S_dbLib_recNotFound; pdbentry->precnode = ppvdNode->precnode; pdbentry->precordType = ppvdNode->precordType; *ppname = pname + lenName; return 0; } long dbFindRecord(DBENTRY *pdbentry, const char *pname) { long status = dbFindRecordPart(pdbentry, &pname); if (status) return status; if (*pname == '.') return dbFindField(pdbentry, ++pname); return 0; } long dbFirstRecord(DBENTRY *pdbentry) { dbRecordType *precordType = pdbentry->precordType; dbRecordNode *precnode; zeroDbentry(pdbentry); if(!precordType) return(S_dbLib_recordTypeNotFound); pdbentry->precordType = precordType; precnode = (dbRecordNode *)ellFirst(&precordType->recList); if(!precnode) return(S_dbLib_recNotFound); pdbentry->precnode = precnode; return(0); } long dbNextRecord(DBENTRY *pdbentry) { dbRecordNode *precnode=pdbentry->precnode; long status=0; if(!precnode) return(S_dbLib_recNotFound); precnode = (dbRecordNode *)ellNext(&precnode->node); if(!precnode) status = S_dbLib_recNotFound; pdbentry->precnode = precnode; pdbentry->pfield = NULL; return(status); } int dbGetNRecords(DBENTRY *pdbentry) { dbRecordType *precordType = pdbentry->precordType; if(!precordType) return(0); return(ellCount(&precordType->recList)); } int dbGetNAliases(DBENTRY *pdbentry) { dbRecordType *precordType = pdbentry->precordType; if(!precordType) return(0); return(precordType->no_aliases); } char * dbGetRecordName(DBENTRY *pdbentry) { dbRecordType *pdbRecordType = pdbentry->precordType; dbRecordNode *precnode = pdbentry->precnode; if(!pdbRecordType) return NULL; if(!precnode) return NULL; return precnode->recordname; } long dbVisibleRecord(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; if(!precnode) return(S_dbLib_recNotFound); precnode->flags |= DBRN_FLAGS_VISIBLE; return 0; } long dbInvisibleRecord(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; if(!precnode) return(S_dbLib_recNotFound); precnode->flags &= ~DBRN_FLAGS_VISIBLE; return 0; } int dbIsVisibleRecord(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; if(!precnode) return 0; return precnode->flags & DBRN_FLAGS_VISIBLE ? 1 : 0; } long dbCreateAlias(DBENTRY *pdbentry, const char *alias) { dbRecordType *precordType = pdbentry->precordType; dbRecordNode *precnode = pdbentry->precnode; dbRecordNode *pnewnode; DBENTRY tempEntry; PVDENTRY *ppvd; if (!precordType) return S_dbLib_recordTypeNotFound; /* alias of alias still references actual record */ while (precnode && (precnode->flags & DBRN_FLAGS_ISALIAS)) precnode = precnode->aliasedRecnode; if (!precnode) return S_dbLib_recNotFound; dbInitEntry(pdbentry->pdbbase, &tempEntry); if (!dbFindRecord(&tempEntry, alias)) return S_dbLib_recExists; dbFinishEntry(&tempEntry); pnewnode = dbCalloc(1, sizeof(dbRecordNode)); pnewnode->recordname = epicsStrDup(alias); pnewnode->precord = precnode->precord; pnewnode->aliasedRecnode = precnode; pnewnode->flags = DBRN_FLAGS_ISALIAS; precnode->flags |= DBRN_FLAGS_HASALIAS; ellInit(&pnewnode->infoList); ellAdd(&precordType->recList, &pnewnode->node); precordType->no_aliases++; ppvd = dbPvdAdd(pdbentry->pdbbase, precordType, pnewnode); if (!ppvd) { errMessage(-1, "dbCreateAlias: Add to PVD failed"); return -1; } return 0; } int dbFollowAlias(DBENTRY *pdbentry) { if(!pdbentry->precnode) return S_dbLib_recNotFound; if(pdbentry->precnode->aliasedRecnode) pdbentry->precnode = pdbentry->precnode->aliasedRecnode; return 0; } int dbIsAlias(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; if(!precnode) return 0; return precnode->flags & DBRN_FLAGS_ISALIAS ? 1 : 0; } long dbCopyRecord(DBENTRY *pdbentry,const char *newRecordName,int overWriteOK) { dbRecordType *precordType = pdbentry->precordType; dbFldDes *pdbFldDes; dbRecordNode *precnode = pdbentry->precnode; long status; DBENTRY dbentry; char *pvalue; if(!precordType) return(S_dbLib_recordTypeNotFound); /*Get size of NAME field*/ pdbFldDes = precordType->papFldDes[0]; if(!pdbFldDes || (strcmp(pdbFldDes->name,"NAME")!=0)) return(S_dbLib_nameLength); if((int)strlen(newRecordName)>=pdbFldDes->size) return(S_dbLib_nameLength); if (!precnode || dbIsAlias(pdbentry)) return S_dbLib_recNotFound; dbInitEntry(pdbentry->pdbbase,&dbentry); status = dbFindRecord(&dbentry,newRecordName); if(!status) { if(!overWriteOK) { dbFinishEntry(&dbentry); return(S_dbLib_recExists); } status = dbDeleteRecord(&dbentry); if(status) return(status); } dbFinishEntry(&dbentry); if((status = dbFindRecordType(&dbentry,precordType->name))) return(status); if((status = dbCreateRecord(&dbentry,newRecordName))) return(status); if((status = dbFirstField(pdbentry,TRUE))) return(status); if((status = dbFirstField(&dbentry,TRUE))) return(status); while(!status) { if(!dbIsDefaultValue(pdbentry)) { pvalue = dbGetString(pdbentry); if((status = dbPutString(&dbentry,pvalue))) return(status); } status = dbNextField(pdbentry,TRUE); if(!status) status = dbNextField(&dbentry,TRUE); if(!status && (pdbentry->pflddes!=dbentry.pflddes)) { epicsPrintf("dbCopyRecord: Logic Error\n"); return(-1); } } /*Copy the info strings too*/ status = dbFirstInfo(pdbentry); while (!status) { status = dbPutInfo(&dbentry, dbGetInfoName(pdbentry), dbGetInfoString(pdbentry)); if (status) return (status); status = dbNextInfo(pdbentry); } /*Leave pdbentry pointing to newRecordName*/ return(dbFindRecord(pdbentry,newRecordName)); } long dbFindFieldPart(DBENTRY *pdbentry,const char **ppname) { dbRecordType *precordType = pdbentry->precordType; dbRecordNode *precnode = pdbentry->precnode; const char *pname = *ppname; short top, bottom, test; char **papsortFldName; short *sortFldInd; int ch; size_t nameLen; if (!precordType) return S_dbLib_recordTypeNotFound; if (!precnode) return S_dbLib_recNotFound; papsortFldName = precordType->papsortFldName; sortFldInd = precordType->sortFldInd; /* Measure field name length; name is a valid C identifier */ nameLen = 0; if ((ch = *pname) && (ch == '_' || isalpha(ch))) { while ((ch = pname[++nameLen])) if (!(ch == '_' || isalnum(ch))) break; } /* Handle absent field name */ if (nameLen == 0) { dbFldDes *pflddes = precordType->pvalFldDes; if (!pflddes) return S_dbLib_recordTypeNotFound; pdbentry->pflddes = pflddes; pdbentry->indfield = precordType->indvalFlddes; *ppname = &pname[nameLen]; return dbGetFieldAddress(pdbentry); } /* binary search through ordered field names */ top = precordType->no_fields - 1; bottom = 0; test = (top + bottom) / 2; while (1) { int compare = strncmp(papsortFldName[test], pname, nameLen); if (compare == 0) compare = (int) (strlen(papsortFldName[test]) - nameLen); if (compare == 0) { dbFldDes *pflddes = precordType->papFldDes[sortFldInd[test]]; if (!pflddes) return S_dbLib_recordTypeNotFound; pdbentry->pflddes = pflddes; pdbentry->indfield = sortFldInd[test]; *ppname = &pname[nameLen]; return dbGetFieldAddress(pdbentry); } else if (compare > 0) { top = test - 1; if (top < bottom) break; test = (top + bottom) / 2; } else { bottom = test + 1; if (top < bottom) break; test = (top + bottom) / 2; } } return S_dbLib_fieldNotFound; } long dbFindField(DBENTRY *pdbentry,const char *pname) { long status = dbFindFieldPart(pdbentry, &pname); int ch; if (status == S_dbLib_fieldNotFound) return dbGetRecordAttribute(pdbentry, pname); if (status) return status; ch = *pname; if (ch == 0 || isspace(ch)) return 0; return S_dbLib_recNotFound; } int dbFoundField(DBENTRY *pdbentry) { return((pdbentry->pfield) ? TRUE : FALSE); } char * dbGetString(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; void *pfield = pdbentry->pfield; DBLINK *plink; if (!pflddes) { dbMsgCpy(pdbentry, "fldDes not found"); return pdbentry->message; } switch (pflddes->field_type) { case DBF_STRING: case DBF_INLINK: case DBF_OUTLINK: case DBF_FWDLINK: if (!pfield) { dbMsgCpy(pdbentry, "Field not allocated (NULL)"); return pdbentry->message; } break; default: break; } switch (pflddes->field_type) { case DBF_STRING: /* Protect against a missing nil-terminator */ dbMsgNCpy(pdbentry, (char *)pfield, pflddes->size); break; case DBF_CHAR: case DBF_UCHAR: case DBF_SHORT: case DBF_USHORT: case DBF_ENUM: case DBF_LONG: case DBF_ULONG: case DBF_INT64: case DBF_UINT64: case DBF_FLOAT: case DBF_DOUBLE: case DBF_MENU: case DBF_DEVICE: return(dbGetStringNum(pdbentry)); case DBF_INLINK: case DBF_OUTLINK: plink = (DBLINK *)pfield; switch(plink->type) { case CONSTANT: if (plink->value.constantStr) { dbMsgCpy(pdbentry, plink->value.constantStr); } else if (plink->text) { dbMsgCpy(pdbentry, plink->text); } else { dbMsgCpy(pdbentry, ""); } break; case MACRO_LINK: if (plink->value.macro_link.macroStr) { dbMsgCpy(pdbentry, plink->value.macro_link.macroStr); } else { dbMsgCpy(pdbentry, ""); } break; case JSON_LINK: dbMsgCpy(pdbentry, plink->value.json.string); break; case PN_LINK: dbMsgPrint(pdbentry, "%s%s", plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "", msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]); break; case PV_LINK: case CA_LINK: case DB_LINK: { int ppind; short pvlMask; pvlMask = plink->value.pv_link.pvlMask; if (pvlMask&pvlOptPP) ppind=1; else if(pvlMask&pvlOptCA) ppind=2; else if(pvlMask&pvlOptCP) ppind=3; else if(pvlMask&pvlOptCPP) ppind=4; else ppind=0; dbMsgPrint(pdbentry, "%s%s%s%s", plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "", (plink->flags & DBLINK_FLAG_TSELisTIME) ? ".TIME" : "", ppstring[ppind], msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]); break; } case VME_IO: dbMsgPrint(pdbentry, "#C%d S%d @%s", plink->value.vmeio.card,plink->value.vmeio.signal, plink->value.vmeio.parm); break; case CAMAC_IO: dbMsgPrint(pdbentry, "#B%d C%d N%d A%d F%d @%s", plink->value.camacio.b,plink->value.camacio.c, plink->value.camacio.n,plink->value.camacio.a, plink->value.camacio.f,plink->value.camacio.parm); break; case RF_IO: dbMsgPrint(pdbentry, "#R%d M%d D%d E%d", plink->value.rfio.cryo, plink->value.rfio.micro, plink->value.rfio.dataset, plink->value.rfio.element); break; case AB_IO: dbMsgPrint(pdbentry, "#L%d A%d C%d S%d @%s", plink->value.abio.link,plink->value.abio.adapter, plink->value.abio.card,plink->value.abio.signal, plink->value.abio.parm); break; case GPIB_IO: dbMsgPrint(pdbentry, "#L%d A%d @%s", plink->value.gpibio.link,plink->value.gpibio.addr, plink->value.gpibio.parm); break; case BITBUS_IO: dbMsgPrint(pdbentry, "#L%u N%u P%u S%u @%s", plink->value.bitbusio.link,plink->value.bitbusio.node, plink->value.bitbusio.port,plink->value.bitbusio.signal, plink->value.bitbusio.parm); break; case BBGPIB_IO: dbMsgPrint(pdbentry, "#L%u B%u G%u @%s", plink->value.bbgpibio.link,plink->value.bbgpibio.bbaddr, plink->value.bbgpibio.gpibaddr,plink->value.bbgpibio.parm); break; case INST_IO: dbMsgPrint(pdbentry, "@%s", plink->value.instio.string); break; case VXI_IO : if (plink->value.vxiio.flag == VXIDYNAMIC) dbMsgPrint(pdbentry, "#V%d C%d S%d @%s", plink->value.vxiio.frame,plink->value.vxiio.slot, plink->value.vxiio.signal,plink->value.vxiio.parm); else dbMsgPrint(pdbentry, "#V%d S%d @%s", plink->value.vxiio.la,plink->value.vxiio.signal, plink->value.vxiio.parm); break; default : return(NULL); } break; case DBF_FWDLINK: { DBLINK *plink=(DBLINK *)pfield; switch(plink->type) { case CONSTANT: if (plink->value.constantStr) { dbMsgCpy(pdbentry, plink->value.constantStr); } else if (plink->text) { dbMsgCpy(pdbentry, plink->text); } else { dbMsgCpy(pdbentry, ""); } break; case MACRO_LINK: if (plink->value.macro_link.macroStr) { dbMsgCpy(pdbentry, plink->value.macro_link.macroStr); } else { dbMsgCpy(pdbentry, ""); } break; case JSON_LINK: dbMsgCpy(pdbentry, plink->value.json.string); break; case PV_LINK: case CA_LINK: case DB_LINK: { int ppind; short pvlMask; pvlMask = plink->value.pv_link.pvlMask; if (pvlMask&pvlOptCA) ppind=2; else ppind=0; dbMsgPrint(pdbentry, "%s%s", plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "", ppind ? ppstring[ppind] : ""); break; } default : return(NULL); } } break; default: return(NULL); } return pdbentry->message; } char *dbGetStringNum(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; void *pfield = pdbentry->pfield; char *message; unsigned char cvttype; /* the following assumes that messagesize is large enough * to hold the base 10 encoded value of a 32-bit integer. */ message = getpMessage(pdbentry); cvttype = pflddes->base; switch (pflddes->field_type) { case DBF_CHAR: if (cvttype == CT_DECIMAL) cvtCharToString(*(char *) pfield, message); else ulongToHexString(*(char *) pfield, message); break; case DBF_UCHAR: if (cvttype==CT_DECIMAL) cvtUcharToString(*(epicsUInt8 *) pfield, message); else ulongToHexString(*(epicsUInt8 *) pfield, message); break; case DBF_SHORT: if (cvttype==CT_DECIMAL) cvtShortToString(*(epicsInt16 *) pfield, message); else ulongToHexString(*(epicsInt16 *) pfield, message); break; case DBF_USHORT: case DBF_ENUM: if (cvttype==CT_DECIMAL) cvtUshortToString(*(epicsUInt16 *) pfield, message); else ulongToHexString(*(epicsUInt16 *) pfield, message); break; case DBF_LONG: if (cvttype==CT_DECIMAL) cvtLongToString(*(epicsInt32 *) pfield, message); else ulongToHexString(*(epicsInt32 *) pfield, message); break; case DBF_ULONG: if (cvttype==CT_DECIMAL) cvtUlongToString(*(epicsUInt32 *) pfield, message); else ulongToHexString(*(epicsUInt32 *) pfield, message); break; case DBF_INT64: if (cvttype==CT_DECIMAL) cvtInt64ToString(*(epicsInt64 *) pfield, message); else cvtInt64ToHexString(*(epicsInt64 *) pfield, message); break; case DBF_UINT64: if (cvttype==CT_DECIMAL) cvtUInt64ToString(*(epicsUInt32 *) pfield, message); else cvtUInt64ToHexString(*(epicsUInt32 *) pfield, message); break; case DBF_FLOAT: floatToString(*(epicsFloat32 *) pfield, message); break; case DBF_DOUBLE: doubleToString(*(epicsFloat64 *) pfield, message); break; case DBF_MENU: { dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; epicsEnum16 choice_ind; char *pchoice; if (!pfield) { dbMsgCpy(pdbentry, "Field not found"); return message; } choice_ind = *((epicsEnum16 *) pdbentry->pfield); if (!pdbMenu || choice_ind < 0 || choice_ind >= pdbMenu->nChoice) return NULL; pchoice = pdbMenu->papChoiceValue[choice_ind]; dbMsgCpy(pdbentry, pchoice); } break; case DBF_DEVICE: { dbDeviceMenu *pdbDeviceMenu; epicsEnum16 choice_ind; char *pchoice; if (!pfield) { dbMsgCpy(pdbentry, "Field not found"); return message; } pdbDeviceMenu = dbGetDeviceMenu(pdbentry); if (!pdbDeviceMenu) return NULL; choice_ind = *((epicsEnum16 *) pdbentry->pfield); if (choice_ind<0 || choice_ind>=pdbDeviceMenu->nChoice) return NULL; pchoice = pdbDeviceMenu->papChoice[choice_ind]; dbMsgCpy(pdbentry, pchoice); } break; default: return NULL; } return message; } long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) { short i; for (i=0; ino_links; i++) { dbLinkInfo link_info; dbFldDes *pflddes = rtyp->papFldDes[rtyp->link_ind[i]]; DBLINK *plink = (DBLINK *)(((char *)prec) + pflddes->offset); devSup *devsup = NULL; plink->precord = prec; /* link fields are zero'd on allocation. * so are effectively CONSTANT, but with constantStr==NULL. * Here we initialize them to have the correct link type, * with zero values and empty (but non-NULL) strings. */ if(pflddes->isDevLink) { devsup = (devSup *)ellNth(&rtyp->devList, prec->dtyp+1); } if(devsup) plink->type = devsup->link_type; else plink->type = CONSTANT; switch (plink->type) { /* constantStr is allowed to remain NULL if plink->text==NULL * constantStr==NULL has special meaning in recGblInitConstantLink() */ case CONSTANT: plink->value.constantStr = NULL; break; case PV_LINK: plink->value.pv_link.pvname = callocMustSucceed(1, 1, "init PV_LINK"); break; case JSON_LINK: plink->value.json.string = pNullString; break; case VME_IO: plink->value.vmeio.parm = pNullString; break; case CAMAC_IO: plink->value.camacio.parm = pNullString; break; case AB_IO: plink->value.abio.parm = pNullString; break; case GPIB_IO: plink->value.gpibio.parm = pNullString; break; case BITBUS_IO: plink->value.bitbusio.parm = pNullString; break; case INST_IO: plink->value.instio.string = pNullString; break; case BBGPIB_IO: plink->value.bbgpibio.parm = pNullString; break; case VXI_IO: plink->value.vxiio.parm = pNullString; break; } if(!plink->text) continue; if(dbParseLink(plink->text, pflddes->field_type, &link_info)!=0) { /* This was already parsed once when ->text was set. * Any syntax error messages were printed at that time. */ } else if(dbCanSetLink(plink, &link_info, devsup)!=0) { errlogPrintf("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); } else if(dbSetLink(plink, &link_info, devsup)) { errlogPrintf("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); } free(plink->text); plink->text = NULL; } return 0; } void dbFreeLinkInfo(dbLinkInfo *pinfo) { if (pinfo->ltype == JSON_LINK) { dbJLinkFree(pinfo->jlink); pinfo->jlink = NULL; } free(pinfo->target); pinfo->target = NULL; } long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) { char *pstr; size_t len; double value; memset(pinfo, 0, sizeof(*pinfo)); /* Strip leading white space */ while (*str && isspace((int)*str)) str++; len = strlen(str); /* Strip trailing white space */ while (len > 0 && isspace((int)str[len-1])) len--; pstr = malloc(len + 1); if (!pstr) return S_dbLib_outMem; pinfo->target = pstr; /* Check for Instrument I/O links */ if (*str == '@') { pinfo->ltype = INST_IO; /* Store everything after the '@' */ memcpy(pstr, str+1, --len); pstr[len] = '\0'; return 0; } /* Store the stripped string */ memcpy(pstr, str, len); pstr[len] = '\0'; /* Check for braces => JSON */ if (*str == '{' && str[len-1] == '}') { if (dbJLinkParse(str, len, ftype, &pinfo->jlink)) goto fail; pinfo->ltype = JSON_LINK; return 0; } /* Check for other HW link types */ if (*pstr == '#') { int ret; char junk = 0; char *parm = strchr(pstr, '@'); /* find start of parm string */ if (parm) { *parm++ = '\0'; /* isolate the parm string for later */ len -= (parm - pstr); } /* generalized extraction of ID charactor and integer pairs (eg. "#C15 S14") */ ret = sscanf(pinfo->target, "# %c%d %c%d %c%d %c%d %c%d %c", &pinfo->hwid[0], &pinfo->hwnums[0], &pinfo->hwid[1], &pinfo->hwnums[1], &pinfo->hwid[2], &pinfo->hwnums[2], &pinfo->hwid[3], &pinfo->hwnums[3], &pinfo->hwid[4], &pinfo->hwnums[4], &junk); /* ret<0 when pattern not matched * ret==11 when extra non-space before '@'. * ret is odd when a number is missing */ if (ret<0 || ret>10 || ret%2==1) goto fail; if (strcmp(pinfo->hwid, "CS")==0) pinfo->ltype = VME_IO; else if (strcmp(pinfo->hwid, "BCN")==0) pinfo->ltype = CAMAC_IO; else if (strcmp(pinfo->hwid, "BCNA")==0) pinfo->ltype = CAMAC_IO; else if (strcmp(pinfo->hwid, "BCNF")==0) pinfo->ltype = CAMAC_IO; else if (strcmp(pinfo->hwid, "BCNAF")==0) pinfo->ltype = CAMAC_IO; else if (strcmp(pinfo->hwid, "RMDE")==0) pinfo->ltype = RF_IO; else if (strcmp(pinfo->hwid, "LACS")==0) pinfo->ltype = AB_IO; else if (strcmp(pinfo->hwid, "LA")==0) pinfo->ltype = GPIB_IO; else if (strcmp(pinfo->hwid, "LNPS")==0) pinfo->ltype = BITBUS_IO; else if (strcmp(pinfo->hwid, "LBG")==0) pinfo->ltype = BBGPIB_IO; else if (strcmp(pinfo->hwid, "VCS")==0) pinfo->ltype = VXI_IO; else if (strcmp(pinfo->hwid, "VS")==0) pinfo->ltype = VXI_IO; else goto fail; if (pinfo->ltype != RF_IO) { if (!parm) { pinfo->target[0] = '\0'; } else { /* move parm string to beginning of buffer */ memmove(pinfo->target, parm, len + 1); } } else if (!parm && pinfo->ltype == RF_IO) { /* RF_IO, the string isn't needed at all */ free(pinfo->target); pinfo->target = NULL; } else goto fail; return 0; } /* Link is a constant if empty or it holds just a number */ if (len == 0 || epicsParseDouble(pstr, &value, NULL) == 0) { pinfo->ltype = CONSTANT; return 0; } /* Link may be an array constant */ if (pstr[0] == '[' && pstr[len-1] == ']' && (strchr(pstr, ',') || strchr(pstr, '"'))) { pinfo->ltype = CONSTANT; return 0; } pinfo->ltype = PV_LINK; pstr = strchr(pstr, ' '); /* find start of link modifiers (can't be seperated by tabs) */ if (pstr) { *pstr++ = '\0'; /* isolate modifiers. pinfo->target is PV name only for re-use in struct pv_link */ /* Space seperation of modifiers isn't required, and other chars are ignored. * Order of comparisons resolves ambiguity by checking for * longer matches first. * eg. "QQCPPXMSITT" is pvlOptCPP|pvlOptMSI */ if (strstr(pstr, "NPP")) pinfo->modifiers = 0; else if (strstr(pstr, "CPP")) pinfo->modifiers = pvlOptCPP; else if (strstr(pstr, "PP")) pinfo->modifiers = pvlOptPP; else if (strstr(pstr, "CA")) pinfo->modifiers = pvlOptCA; else if (strstr(pstr, "CP")) pinfo->modifiers = pvlOptCP; if (strstr(pstr, "NMS")) pinfo->modifiers |= pvlOptNMS; else if (strstr(pstr, "MSI")) pinfo->modifiers |= pvlOptMSI; else if (strstr(pstr, "MSS")) pinfo->modifiers |= pvlOptMSS; else if (strstr(pstr, "MS")) pinfo->modifiers |= pvlOptMS; /* filter modifiers based on link type */ switch(ftype) { case DBF_INLINK: /* accept all */ break; case DBF_OUTLINK: pinfo->modifiers &= ~pvlOptCPP; break; case DBF_FWDLINK: pinfo->modifiers &= pvlOptCA; break; } } return 0; fail: dbFreeLinkInfo(pinfo); return S_dbLib_badField; } long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup) { /* Release pinfo resources on failure */ int expected_type = devsup ? devsup->link_type : CONSTANT; if (pinfo->ltype == expected_type) return 0; switch (pinfo->ltype) { case CONSTANT: case JSON_LINK: case PV_LINK: if (expected_type == CONSTANT || expected_type == JSON_LINK || expected_type == PV_LINK) return 0; default: dbFreeLinkInfo(pinfo); return 1; } } static void dbSetLinkConst(DBLINK *plink, dbLinkInfo *pinfo) { plink->type = CONSTANT; plink->value.constantStr = pinfo->target; pinfo->target = NULL; } static void dbSetLinkPV(DBLINK *plink, dbLinkInfo *pinfo) { plink->type = PV_LINK; plink->value.pv_link.pvname = pinfo->target; plink->value.pv_link.pvlMask = pinfo->modifiers; pinfo->target = NULL; } static void dbSetLinkJSON(DBLINK *plink, dbLinkInfo *pinfo) { plink->type = JSON_LINK; plink->value.json.string = pinfo->target; plink->value.json.jlink = pinfo->jlink; pinfo->target = NULL; pinfo->jlink = NULL; } static void dbSetLinkHW(DBLINK *plink, dbLinkInfo *pinfo) { switch(pinfo->ltype) { case JSON_LINK: plink->value.json.string = pinfo->target; break; case INST_IO: plink->value.instio.string = pinfo->target; break; case VME_IO: plink->value.vmeio.card = pinfo->hwnums[0]; plink->value.vmeio.signal = pinfo->hwnums[1]; plink->value.vmeio.parm = pinfo->target; break; case CAMAC_IO: plink->value.camacio.b = pinfo->hwnums[0]; plink->value.camacio.c = pinfo->hwnums[1]; plink->value.camacio.n = pinfo->hwnums[2]; plink->value.camacio.a = pinfo->hwnums[3]; plink->value.camacio.f = pinfo->hwnums[4]; plink->value.camacio.parm = pinfo->target; break; case RF_IO: plink->value.rfio.cryo = pinfo->hwnums[0]; plink->value.rfio.micro = pinfo->hwnums[1]; plink->value.rfio.dataset = pinfo->hwnums[2]; plink->value.rfio.element = pinfo->hwnums[3]; break; case AB_IO: plink->value.abio.link = pinfo->hwnums[0]; plink->value.abio.adapter = pinfo->hwnums[1]; plink->value.abio.card = pinfo->hwnums[2]; plink->value.abio.signal = pinfo->hwnums[3]; plink->value.abio.parm = pinfo->target; break; case GPIB_IO: plink->value.gpibio.link = pinfo->hwnums[0]; plink->value.gpibio.addr = pinfo->hwnums[1]; plink->value.gpibio.parm = pinfo->target; break; case BITBUS_IO: plink->value.bitbusio.link = pinfo->hwnums[0]; plink->value.bitbusio.node = pinfo->hwnums[1]; plink->value.bitbusio.port = pinfo->hwnums[2]; plink->value.bitbusio.signal = pinfo->hwnums[3]; plink->value.bitbusio.parm = pinfo->target; break; case BBGPIB_IO: plink->value.bbgpibio.link = pinfo->hwnums[0]; plink->value.bbgpibio.bbaddr = pinfo->hwnums[1]; plink->value.bbgpibio.gpibaddr = pinfo->hwnums[2]; plink->value.bbgpibio.parm = pinfo->target; break; case VXI_IO: if(strcmp(pinfo->hwid, "VCS")==0) { plink->value.vxiio.flag=VXIDYNAMIC; plink->value.vxiio.frame = pinfo->hwnums[0]; plink->value.vxiio.slot = pinfo->hwnums[1]; plink->value.vxiio.signal = pinfo->hwnums[2]; } else if(strcmp(pinfo->hwid, "VS")==0) { plink->value.vxiio.flag=VXISTATIC; plink->value.vxiio.la = pinfo->hwnums[0]; plink->value.vxiio.signal = pinfo->hwnums[1]; } else { cantProceed("dbSetLinkHW: logic error, unknown VXI_IO variant"); } plink->value.vxiio.parm = pinfo->target; break; default: cantProceed("dbSetLinkHW: logic error, unhandled link type"); return; } plink->type = pinfo->ltype; pinfo->target = NULL; /* now owned by link field */ } long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup) { int expected_type = devsup ? devsup->link_type : CONSTANT; if (expected_type == CONSTANT || expected_type == JSON_LINK || expected_type == PV_LINK) { switch (pinfo->ltype) { case CONSTANT: dbFreeLinkContents(plink); dbSetLinkConst(plink, pinfo); break; case PV_LINK: dbFreeLinkContents(plink); dbSetLinkPV(plink, pinfo); break; case JSON_LINK: dbFreeLinkContents(plink); dbSetLinkJSON(plink, pinfo); break; default: errlogMessage("Warning: dbSetLink: forgot to test with dbCanSetLink() or logic error"); goto fail; /* can't assign HW link */ } } else if (expected_type == pinfo->ltype) { dbFreeLinkContents(plink); dbSetLinkHW(plink, pinfo); } else goto fail; return 0; fail: dbFreeLinkInfo(pinfo); return S_dbLib_badField; } long dbPutString(DBENTRY *pdbentry,const char *pstring) { dbFldDes *pflddes = pdbentry->pflddes; void *pfield = pdbentry->pfield; long status=0; int macroIsOk; int stringHasMacro=FALSE; if(!pflddes) return(S_dbLib_flddesNotFound); macroIsOk = dbIsMacroOk(pdbentry); stringHasMacro = strstr(pstring,"$(") || strstr(pstring,"${"); if(!macroIsOk && stringHasMacro) { epicsPrintf("%s.%s Has unexpanded macro\n", dbGetRecordName(pdbentry),dbGetFieldName(pdbentry)); return(S_dbLib_badField); } switch (pflddes->field_type) { case DBF_STRING: if(!pfield) return(S_dbLib_fieldNotFound); if(strlen(pstring) >= (size_t)pflddes->size) return S_dbLib_strLen; strncpy((char *)pfield, pstring, pflddes->size-1); ((char *)pfield)[pflddes->size-1] = 0; if((pflddes->special == SPC_CALC) && !stringHasMacro) { char rpcl[RPCL_LEN]; short err; if (postfix(pstring,rpcl,&err)) { status = S_dbLib_badField; errlogPrintf("%s in CALC expression '%s'\n", calcErrorStr(err), pstring); } } break; case DBF_CHAR: case DBF_SHORT: case DBF_LONG: case DBF_INT64: case DBF_UCHAR: case DBF_USHORT: case DBF_ULONG: case DBF_UINT64: case DBF_ENUM: case DBF_FLOAT: case DBF_DOUBLE: case DBF_MENU: case DBF_DEVICE: status = dbPutStringNum(pdbentry,pstring); break; case DBF_INLINK: case DBF_OUTLINK: case DBF_FWDLINK: { dbLinkInfo link_info; DBLINK *plink = (DBLINK *)pfield; status = dbParseLink(pstring, pflddes->field_type, &link_info); if (status) break; if (plink->type==CONSTANT && plink->value.constantStr==NULL) { /* links not yet initialized by dbInitRecordLinks() */ free(plink->text); plink->text = epicsStrDup(pstring); dbFreeLinkInfo(&link_info); } else { /* assignment after init (eg. autosave restore) */ struct dbCommon *prec = pdbentry->precnode->precord; devSup *devsup = (devSup *)ellNth(&pdbentry->precordType->devList, prec->dtyp+1); status = dbCanSetLink(plink, &link_info, devsup); if (status == 0) status = dbSetLink(plink, &link_info, devsup); } } break; default: return S_dbLib_badField; } if (!status && strcmp(pflddes->name, "VAL") == 0) { DBENTRY dbentry; dbCopyEntryContents(pdbentry, &dbentry); if (!dbFindField(&dbentry, "UDF")) { dbPutString(&dbentry, "0"); } dbFinishEntry(&dbentry); } return(status); } char * dbVerify(DBENTRY *pdbentry, const char *pstring) { dbFldDes *pflddes = pdbentry->pflddes; char *message = getpMessage(pdbentry); long status; union { epicsInt8 i8; epicsUInt8 u8; epicsInt16 i16; epicsUInt16 u16; epicsInt32 i32; epicsUInt32 u32; epicsInt64 i64; epicsUInt64 u64; epicsFloat32 f32; epicsFloat64 f64; } val; if (!pflddes) { strcpy(message, "fldDes not found"); return message; } if (strstr(pstring,"$(") || strstr(pstring,"${")) return NULL; switch (pflddes->field_type) { case DBF_STRING: { size_t length = strlen(pstring); if (length >= pflddes->size) { sprintf(message, "String too long, max %d characters", pflddes->size - 1); return message; } if (pflddes->special == SPC_CALC) { char rpcl[RPCL_LEN]; short err; status = postfix(pstring, rpcl, &err); if (status) { sprintf(message,"%s in CALC expression '%s'", calcErrorStr(err), pstring); return message; } } return NULL; } case DBF_CHAR: status = epicsParseInt8(pstring, &val.i8, 0, NULL); break; case DBF_UCHAR: status = epicsParseUInt8(pstring, &val.u8, 0, NULL); break; case DBF_SHORT: status = epicsParseInt16(pstring, &val.i16, 0, NULL); break; case DBF_ENUM: case DBF_USHORT: status = epicsParseUInt16(pstring, &val.u16, 0, NULL); break; case DBF_LONG: status = epicsParseInt32(pstring, &val.i32, 0, NULL); break; case DBF_ULONG: status = epicsParseUInt32(pstring, &val.u32, 0, NULL); break; case DBF_INT64: status = epicsParseInt64(pstring, &val.i64, 0, NULL); break; case DBF_UINT64: status = epicsParseUInt64(pstring, &val.u64, 0, NULL); break; case DBF_FLOAT: status = epicsParseFloat32(pstring, &val.f32, NULL); break; case DBF_DOUBLE: status = epicsParseFloat64(pstring, &val.f64, NULL); break; case DBF_MENU: { dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; int i; if (!pdbMenu) return NULL; for (i = 0; i < pdbMenu->nChoice; i++) { const char *pchoice = pdbMenu->papChoiceValue[i]; if (!pchoice) continue; if (strcmp(pchoice, pstring) == 0) { return NULL; } } } strcpy(message, "Not a valid menu choice"); return message; case DBF_DEVICE: { dbDeviceMenu *pdbDeviceMenu = dbGetDeviceMenu(pdbentry); int i; if (!pdbDeviceMenu || pdbDeviceMenu->nChoice == 0) return NULL; for (i = 0; i < pdbDeviceMenu->nChoice; i++) { const char *pchoice = pdbDeviceMenu->papChoice[i]; if (!pchoice) continue; if (strcmp(pchoice, pstring) == 0) { return NULL; } } } strcpy(message, "Not a valid device type"); return message; case DBF_INLINK: case DBF_OUTLINK: case DBF_FWDLINK: return NULL; default: strcpy(message, "Not a valid field type"); return message; } switch (status) { case 0: message = NULL; break; case S_stdlib_noConversion: strcpy(message, "Not a valid integer"); break; case S_stdlib_badBase: strcpy(message, "Internal error (badBase)"); break; case S_stdlib_overflow: strcpy(message, "Number too large for field type"); break; case S_stdlib_underflow: strcpy(message, "Number too small for field type"); break; case S_stdlib_extraneous: strcpy(message, "Extraneous characters after number"); break; default: strcpy(message, "Unknown numeric conversion error"); } return message; } long dbFirstInfo(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; pdbentry->pinfonode = NULL; if (!precnode) return (S_dbLib_recNotFound); pdbentry->pinfonode = (dbInfoNode *)ellFirst(&precnode->infoList); return (pdbentry->pinfonode ? 0 : S_dbLib_infoNotFound); } long dbNextInfo(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; dbInfoNode *pinfo; if (!precnode) return (S_dbLib_recNotFound); pinfo = pdbentry->pinfonode; if (!pinfo) return (S_dbLib_infoNotFound); pinfo = (dbInfoNode *)ellNext(&pinfo->node); pdbentry->pinfonode = pinfo; return (pinfo ? 0 : S_dbLib_infoNotFound); } long dbNextMatchingInfo(DBENTRY *pdbentry, const char *pattern) { long status; if (!pdbentry->precordType) { status = dbFirstRecordType(pdbentry); goto first; } while(1) { status = dbNextInfo(pdbentry); while (status) { status = dbNextRecord(pdbentry); while (status) { status = dbNextRecordType(pdbentry); first: if (status) return status; status = dbFirstRecord(pdbentry); } status = dbFirstInfo(pdbentry); } if (!pattern || !*pattern) return 0; if (epicsStrGlobMatch(dbGetInfoName(pdbentry), pattern)) return 0; } } long dbFindInfo(DBENTRY *pdbentry,const char *name) { dbRecordNode *precnode = pdbentry->precnode; dbInfoNode *pinfo; pdbentry->pinfonode = NULL; if (!precnode) return(S_dbLib_recNotFound); pinfo = (dbInfoNode *)ellFirst(&precnode->infoList); while (pinfo) { if (!strcmp(pinfo->name, name)) { pdbentry->pinfonode = pinfo; return (0); } pinfo = (dbInfoNode *)ellNext(&pinfo->node); } return (S_dbLib_infoNotFound); } long dbDeleteInfo(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; dbInfoNode *pinfo = pdbentry->pinfonode; if (!precnode) return (S_dbLib_recNotFound); if (!pinfo) return (S_dbLib_infoNotFound); ellDelete(&precnode->infoList,&pinfo->node); free(pinfo->name); free(pinfo->string); free(pinfo); pdbentry->pinfonode = NULL; return (0); } const char * dbGetInfoName(DBENTRY *pdbentry) { dbInfoNode *pinfo = pdbentry->pinfonode; if (!pinfo) return (NULL); return (pinfo->name); } const char * dbGetInfoString(DBENTRY *pdbentry) { dbInfoNode *pinfo = pdbentry->pinfonode; if (!pinfo) return (NULL); return (pinfo->string); } long dbPutInfoString(DBENTRY *pdbentry,const char *string) { dbInfoNode *pinfo = pdbentry->pinfonode; char *newstring; if (!pinfo) return (S_dbLib_infoNotFound); newstring = realloc(pinfo->string,1+strlen(string)); if (!newstring) return (S_dbLib_outMem); strcpy(newstring, string); pinfo->string = newstring; return (0); } long dbPutInfoPointer(DBENTRY *pdbentry, void *pointer) { dbInfoNode *pinfo = pdbentry->pinfonode; if (!pinfo) return (S_dbLib_infoNotFound); pinfo->pointer = pointer; return (0); } void * dbGetInfoPointer(DBENTRY *pdbentry) { dbInfoNode *pinfo = pdbentry->pinfonode; if (!pinfo) return (NULL); return (pinfo->pointer); } const char * dbGetInfo(DBENTRY *pdbentry,const char *name) { if (dbFindInfo(pdbentry, name)) return NULL; return dbGetInfoString(pdbentry); } long dbPutInfo(DBENTRY *pdbentry,const char *name,const char *string) { dbInfoNode *pinfo; dbRecordNode *precnode = pdbentry->precnode; if (!precnode) return (S_dbLib_recNotFound); dbFindInfo(pdbentry, name); pinfo = pdbentry->pinfonode; if (pinfo) return (dbPutInfoString(pdbentry, string)); /*Create new info node*/ pinfo = calloc(1,sizeof(dbInfoNode)); if (!pinfo) return (S_dbLib_outMem); pinfo->name = calloc(1,1+strlen(name)); if (!pinfo->name) { free(pinfo); return (S_dbLib_outMem); } strcpy(pinfo->name, name); pinfo->string = calloc(1,1+strlen(string)); if (!pinfo->string) { free(pinfo->name); free(pinfo); return (S_dbLib_outMem); } strcpy(pinfo->string, string); ellAdd(&precnode->infoList,&pinfo->node); pdbentry->pinfonode = pinfo; return (0); } brkTable * dbFindBrkTable(dbBase *pdbbase,const char *name) { GPHENTRY *pgph; pgph = gphFind(pdbbase->pgpHash,name,(void *)&pdbbase->bptList); if(!pgph) return(NULL); return((brkTable *)pgph->userPvt); } const char * dbGetFieldTypeString(int dbfType) { int i; for (i=0; i < DBF_NTYPES; i++) { if (pamapdbfType[i].value == dbfType) { return pamapdbfType[i].strvalue; } } return "BAD_DBF_TYPE"; } int dbFindFieldType(const char *type) { int i; for (i = 0; i < DBF_NTYPES; i++) { if (strcmp(type, pamapdbfType[i].strvalue) == 0) { return pamapdbfType[i].value; } } return -1; } dbMenu * dbFindMenu(dbBase *pdbbase,const char *name) { GPHENTRY *pgph; pgph = gphFind(pdbbase->pgpHash,name,(void *)&pdbbase->menuList); if(!pgph) return(NULL); return((dbMenu *)pgph->userPvt); } char ** dbGetMenuChoices(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; if(!pflddes) return(NULL); switch (pflddes->field_type) { case DBF_MENU: { dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; if(!pdbMenu) return(NULL); return(pdbMenu->papChoiceValue); } case DBF_DEVICE: { dbDeviceMenu *pdbDeviceMenu; pdbDeviceMenu = dbGetDeviceMenu(pdbentry); if(!pdbDeviceMenu) return(NULL); return(pdbDeviceMenu->papChoice); } default: return(NULL); } } int dbGetNMenuChoices(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; if(!pflddes) return(-1); switch (pflddes->field_type) { case DBF_MENU: { dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; if(!pdbMenu) return(0); return(pdbMenu->nChoice); } case DBF_DEVICE: { dbDeviceMenu *pdbDeviceMenu; pdbDeviceMenu = dbGetDeviceMenu(pdbentry); if(!pdbDeviceMenu) return(0); return(pdbDeviceMenu->nChoice); } default: break; } return (-1); } char * dbGetMenuStringFromIndex(DBENTRY *pdbentry, int index) { dbFldDes *pflddes = pdbentry->pflddes; if(!pflddes) return(NULL); switch (pflddes->field_type) { case DBF_MENU: { dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; if(!pdbMenu) return(NULL); if(index<0 || index>=pdbMenu->nChoice) return(NULL); return(pdbMenu->papChoiceValue[index]); } case DBF_DEVICE: { dbDeviceMenu *pdbDeviceMenu; pdbDeviceMenu = dbGetDeviceMenu(pdbentry); if(!pdbDeviceMenu) return(NULL); if(index<0 || index>=pdbDeviceMenu->nChoice) return(NULL); return(pdbDeviceMenu->papChoice[index]); } default: break; } return (NULL); } int dbGetMenuIndexFromString(DBENTRY *pdbentry, const char *choice) { dbFldDes *pflddes = pdbentry->pflddes; int ind; int nChoice = 0; char **papChoice = NULL; if(!pflddes) return(-1); switch (pflddes->field_type) { case DBF_MENU: { dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; if(!pdbMenu) return(-1); papChoice = pdbMenu->papChoiceValue; nChoice = pdbMenu->nChoice; break; } case DBF_DEVICE: { dbDeviceMenu *pdbDeviceMenu; pdbDeviceMenu = dbGetDeviceMenu(pdbentry); if(!pdbDeviceMenu) return(-1); papChoice = pdbDeviceMenu->papChoice; nChoice = pdbDeviceMenu->nChoice; break; } default: return(-1); } if(nChoice<=0 || !papChoice) return(-1); for(ind=0; indpgpHash,name,&pdbbase->drvList); if (!pgph) return NULL; return (drvSup *) pgph->userPvt; } char * dbGetRelatedField(DBENTRY *psave) { DBENTRY dbEntry; DBENTRY *pdbentry= &dbEntry; dbFldDes *pflddes; char *rtnval = NULL; long status; pflddes = psave->pflddes; if(pflddes->field_type !=DBF_DEVICE) return(NULL); dbCopyEntryContents(psave,pdbentry); pflddes = pdbentry->pflddes; status = dbFindField(pdbentry,"INP"); if(status) status = dbFindField(pdbentry,"OUT"); if(!status) rtnval = pdbentry->pflddes->name; dbFinishEntry(pdbentry); return(rtnval); } linkSup* dbFindLinkSup(dbBase *pdbbase, const char *name) { GPHENTRY *pgph = gphFind(pdbbase->pgpHash,name,&pdbbase->linkList); if (!pgph) return NULL; return (linkSup *) pgph->userPvt; } int dbGetNLinks(DBENTRY *pdbentry) { dbRecordType *precordType = pdbentry->precordType; if(!precordType) return(S_dbLib_recordTypeNotFound); return((int)precordType->no_links); } long dbGetLinkField(DBENTRY *pdbentry, int index) { dbRecordType *precordType = pdbentry->precordType; dbFldDes *pflddes; if (!precordType) return S_dbLib_recordTypeNotFound; if (index < 0 || index >= precordType->no_links) return S_dbLib_badLink; pdbentry->indfield = precordType->link_ind[index]; pdbentry->pflddes = pflddes = precordType->papFldDes[pdbentry->indfield]; dbGetFieldAddress(pdbentry); return 0; } void dbDumpPath(DBBASE *pdbbase) { ELLLIST *ppathList; dbPathNode *pdbPathNode; if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return; } ppathList = (ELLLIST *)pdbbase->pathPvt; if(!ppathList || !(pdbPathNode = (dbPathNode *)ellFirst(ppathList))) { printf("no path defined\n"); return; } while(pdbPathNode) { printf("%s",pdbPathNode->directory); pdbPathNode = (dbPathNode *)ellNext(&pdbPathNode->node); if(pdbPathNode) printf("%s", OSI_PATH_LIST_SEPARATOR); } printf("\n"); return; } void dbDumpRecord( dbBase *pdbbase,const char *precordTypename,int level) { if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return; } dbWriteRecordFP(pdbbase,stdout,precordTypename,level); } void dbDumpMenu(dbBase *pdbbase,const char *menuName) { if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return; } dbWriteMenuFP(pdbbase,stdout,menuName); } void dbDumpRecordType(DBBASE *pdbbase,const char *recordTypeName) { dbRecordType *pdbRecordType; dbFldDes *pdbFldDes; int gotMatch; int i; if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return; } for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { if(recordTypeName) { gotMatch = (strcmp(recordTypeName,pdbRecordType->name)==0) ? TRUE : FALSE; }else { gotMatch=TRUE; } if(!gotMatch) continue; printf("name(%s) no_fields(%hd) no_prompt(%hd) no_links(%hd)\n", pdbRecordType->name,pdbRecordType->no_fields, pdbRecordType->no_prompt,pdbRecordType->no_links); printf("index name\tsortind sortname\n"); for(i=0; ino_fields; i++) { pdbFldDes = pdbRecordType->papFldDes[i]; printf("%5d %s\t%7d %s\n", i,pdbFldDes->name, pdbRecordType->sortFldInd[i],pdbRecordType->papsortFldName[i]); } printf("link_ind "); for(i=0; ino_links; i++) printf(" %hd",pdbRecordType->link_ind[i]); printf("\n"); printf("indvalFlddes %d name %s\n",pdbRecordType->indvalFlddes, pdbRecordType->pvalFldDes->name); printf("rset * %p rec_size %d\n", (void *)pdbRecordType->prset,pdbRecordType->rec_size); if(recordTypeName) break; } } void dbDumpField( DBBASE *pdbbase,const char *recordTypeName,const char *fname) { dbRecordType *pdbRecordType; dbFldDes *pdbFldDes; int gotMatch; int i; dbRecordAttribute *pAttribute; if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return; } for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { if(recordTypeName) { gotMatch = (strcmp(recordTypeName,pdbRecordType->name)==0) ? TRUE : FALSE; }else { gotMatch=TRUE; } if(!gotMatch) continue; printf("recordtype(%s) \n",pdbRecordType->name); for(i=0; ino_fields; i++) { int j; pdbFldDes = pdbRecordType->papFldDes[i]; if(fname && strcmp(fname,pdbFldDes->name)!=0) continue; printf(" %s\n", pdbFldDes->name); printf("\t prompt: %s\n", (pdbFldDes->prompt ? pdbFldDes->prompt : "")); printf("\t extra: %s\n", (pdbFldDes->extra ? pdbFldDes->extra: "")); printf("\t indRecordType: %hd\n",pdbFldDes->indRecordType); printf("\t special: %hd ",pdbFldDes->special); if(pdbFldDes->special) { for(j=0; jspecial) { printf("%s",pamapspcType[j].strvalue); break; } } } printf("\n"); printf("\t field_type: %s\n", dbGetFieldTypeString(pdbFldDes->field_type)); printf("\tprocess_passive: %u\n",pdbFldDes->process_passive); printf("\t property: %u\n",pdbFldDes->prop); printf("\t base: %d\n",pdbFldDes->base); if(!pdbFldDes->promptgroup) { printf("\t promptgroup: %d\n",pdbFldDes->promptgroup); } else { printf("\t promptgroup: %s\n", dbGetPromptGroupNameFromKey(pdbbase, pdbFldDes->promptgroup)); } printf("\t interest: %hd\n", pdbFldDes->interest); printf("\t as_level: %d\n",pdbFldDes->as_level); printf("\t initial: %s\n", (pdbFldDes->initial ? pdbFldDes->initial : "")); if(pdbFldDes->field_type==DBF_MENU) { if(pdbFldDes->ftPvt) printf("\t\t menu: %s\n", ((dbMenu *)pdbFldDes->ftPvt)->name); else printf("\t\t menu: NOT FOUND\n"); } if(pdbFldDes->field_type==DBF_DEVICE) { printf("\t ftPvt: %p\n",pdbFldDes->ftPvt); } printf("\t size: %hd\n",pdbFldDes->size); printf("\t offset: %hd\n",pdbFldDes->offset); } pAttribute = (dbRecordAttribute *)ellFirst(&pdbRecordType->attributeList); while(pAttribute) { printf("Attribute: name %s value %s\n", pAttribute->name,pAttribute->value); pAttribute = (dbRecordAttribute *)ellNext(&pAttribute->node); } if(recordTypeName) break; } } void dbDumpDevice(DBBASE *pdbbase,const char *recordTypeName) { dbRecordType *pdbRecordType; devSup *pdevSup; int gotMatch; if (recordTypeName) { if (*recordTypeName == 0 || *recordTypeName == '*') recordTypeName = 0; } if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return; } for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { if(recordTypeName) { gotMatch = (strcmp(recordTypeName,pdbRecordType->name)==0) ? TRUE : FALSE; }else { gotMatch=TRUE; } if(!gotMatch) continue; printf("recordtype(%s)\n",pdbRecordType->name); for(pdevSup = (devSup *)ellFirst(&pdbRecordType->devList); pdevSup; pdevSup = (devSup *)ellNext(&pdevSup->node)) { printf(" device name: %s\n",pdevSup->name); printf("\tchoice: %s\n",pdevSup->choice); printf("\tlink_type: %d\n",pdevSup->link_type); printf("\tpdset: %p\n",(void *)pdevSup->pdset); if (pdevSup->pdset) { static const char *names[] = { " - report()", " - init()", " - init_record()", " - get_ioint_info()" }; int i, n = pdevSup->pdset->number; DEVSUPFUN *pfunc = &pdevSup->pdset->report; printf("\t number: %d\n", n); for (i = 0; i < n; ++i, ++pfunc) { const char *name = (i < NELEMENTS(names)) ? names[i] : ""; printf("\t func %d: %p%s\n", i, (void *)*pfunc, name); } } printf("\tpdsxt: %p\n",(void *)pdevSup->pdsxt); if (pdevSup->pdsxt) { printf("\t add_record: %p\n", (void *)pdevSup->pdsxt->add_record); printf("\t del_record: %p\n", (void *)pdevSup->pdsxt->del_record); } } if(recordTypeName) break; } } void dbDumpDriver(DBBASE *pdbbase) { if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return; } dbWriteDriverFP(pdbbase,stdout); } void dbDumpLink(DBBASE *pdbbase) { if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return; } dbWriteLinkFP(pdbbase,stdout); } void dbDumpRegistrar(DBBASE *pdbbase) { if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return; } dbWriteRegistrarFP(pdbbase,stdout); } void dbDumpFunction(DBBASE *pdbbase) { if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return; } dbWriteFunctionFP(pdbbase,stdout); } void dbDumpVariable(DBBASE *pdbbase) { if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return; } dbWriteVariableFP(pdbbase,stdout); } void dbDumpBreaktable(DBBASE *pdbbase,const char *name) { brkTable *pbrkTable; brkInt *pbrkInt; int ind; if(!pdbbase) { fprintf(stderr,"pdbbase not specified\n"); return; } for(pbrkTable = (brkTable *)ellFirst(&pdbbase->bptList); pbrkTable; pbrkTable = (brkTable *)ellNext(&pbrkTable->node)) { if (name && strcmp(name,pbrkTable->name)!=0) continue; printf("breaktable(%s) {\n",pbrkTable->name); pbrkInt = pbrkTable->paBrkInt; for(ind=0; ind < pbrkTable->number; ind++) { printf("\traw=%f slope=%e eng=%f\n", pbrkInt->raw, pbrkInt->slope, pbrkInt->eng); pbrkInt++; } printf("}\n"); } return; } static char *bus[LINK_NTYPES] = { "", /* CONSTANT */ NULL, /* PV_LINK */ "VME", "CAMAC", "AB", "GPIB", "BITBUS", NULL, /* MACRO_LINK */ NULL, /* JSON_LINK */ NULL, /* PN_LINK */ NULL, /* DB_LINK */ NULL, /* CA_LINK */ "INST", "BBGPIB", "VXI" }; void dbReportDeviceConfig(dbBase *pdbbase, FILE *report) { DBENTRY dbentry, *pdbentry = &dbentry; long status; FILE *stream = report ? report : stdout; if (!pdbbase) { fprintf(stderr, "dbReportDeviceConfig: pdbbase not specified\n"); return; } dbInitEntry(pdbbase,pdbentry); status = dbFirstRecordType(pdbentry); while (!status) { const int nlinks = dbGetNLinks(pdbentry); status = dbFirstRecord(pdbentry); while (!status) { int ilink; for (ilink=0; ilinkpfield; linkType = plink->type; if (plink->text) { /* Not yet parsed */ dbLinkInfo linfo; if (dbParseLink(plink->text, pdbentry->pflddes->field_type, &linfo)) continue; linkType = linfo.ltype; if (linkType && bus[linkType]) strncpy(linkValue, plink->text, messagesize-1); dbFreeLinkInfo(&linfo); } else { strncpy(linkValue, dbGetString(pdbentry), messagesize-1); } if (!linkType || !bus[linkType]) continue; linkValue[messagesize-1] = '\0'; status = dbFindField(pdbentry, "DTYP"); if (status) break; /* Next record type */ strcpy(dtypValue, dbGetString(pdbentry)); status = dbFindField(pdbentry, "LINR"); if (status) { cvtValue[0] = 0; } else { if (strcmp(dbGetString(pdbentry), "LINEAR") != 0) { cvtValue[0] = 0; } else { strcpy(cvtValue,"cvt("); status = dbFindField(pdbentry, "EGUL"); if (!status) strcat(cvtValue, dbGetString(pdbentry)); status = dbFindField(pdbentry, "EGUF"); if (!status) { strcat(cvtValue, ","); strcat(cvtValue, dbGetString(pdbentry)); } strcat(cvtValue, ")"); } } fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n", bus[linkType], linkValue, dtypValue, dbGetRecordName(pdbentry), cvtValue); break; } status = dbNextRecord(pdbentry); } status = dbNextRecordType(pdbentry); } dbFinishEntry(pdbentry); finishOutstream(stream); return; } base-7.0.3.1/modules/database/src/ioc/dbStatic/dbStaticLib.h0000664000577000060420000002427213557101274022271 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Marty Kraimer * Date: 06-08-93 */ #ifndef INCdbStaticLibh #define INCdbStaticLibh 1 #include #include #include "shareLib.h" #include "dbFldTypes.h" #include "dbBase.h" #include "link.h" #include "errMdef.h" #include "cantProceed.h" #ifdef __cplusplus extern "C" { #endif typedef dbBase DBBASE; typedef struct dbEntry { DBBASE *pdbbase; dbRecordType *precordType; dbFldDes *pflddes; dbRecordNode *precnode; dbInfoNode *pinfonode; void *pfield; char *message; short indfield; } DBENTRY; /* Static database access routines*/ epicsShareFunc DBBASE * dbAllocBase(void); epicsShareFunc void dbFreeBase(DBBASE *pdbbase); epicsShareFunc DBENTRY * dbAllocEntry(DBBASE *pdbbase); epicsShareFunc void dbFreeEntry(DBENTRY *pdbentry); epicsShareFunc void dbInitEntry(DBBASE *pdbbase, DBENTRY *pdbentry); epicsShareFunc void dbFinishEntry(DBENTRY *pdbentry); epicsShareFunc DBENTRY * dbCopyEntry(DBENTRY *pdbentry); epicsShareFunc void dbCopyEntryContents(DBENTRY *pfrom, DBENTRY *pto); epicsShareExtern int dbBptNotMonotonic; epicsShareFunc long dbReadDatabase(DBBASE **ppdbbase, const char *filename, const char *path, const char *substitutions); epicsShareFunc long dbReadDatabaseFP(DBBASE **ppdbbase, FILE *fp, const char *path, const char *substitutions); epicsShareFunc long dbPath(DBBASE *pdbbase, const char *path); epicsShareFunc long dbAddPath(DBBASE *pdbbase, const char *path); epicsShareFunc char * dbGetPromptGroupNameFromKey(DBBASE *pdbbase, const short key); epicsShareFunc short dbGetPromptGroupKeyFromName(DBBASE *pdbbase, const char *name); epicsShareFunc long dbWriteRecord(DBBASE *ppdbbase, const char *filename, const char *precordTypename, int level); epicsShareFunc long dbWriteRecordFP(DBBASE *ppdbbase, FILE *fp, const char *precordTypename, int level); epicsShareFunc long dbWriteMenu(DBBASE *pdbbase, const char *filename, const char *menuName); epicsShareFunc long dbWriteMenuFP(DBBASE *pdbbase, FILE *fp, const char *menuName); epicsShareFunc long dbWriteRecordType(DBBASE *pdbbase, const char *filename, const char *recordTypeName); epicsShareFunc long dbWriteRecordTypeFP(DBBASE *pdbbase, FILE *fp, const char *recordTypeName); epicsShareFunc long dbWriteDevice(DBBASE *pdbbase, const char *filename); epicsShareFunc long dbWriteDeviceFP(DBBASE *pdbbase, FILE *fp); epicsShareFunc long dbWriteDriver(DBBASE *pdbbase, const char *filename); epicsShareFunc long dbWriteDriverFP(DBBASE *pdbbase, FILE *fp); epicsShareFunc long dbWriteLinkFP(DBBASE *pdbbase, FILE *fp); epicsShareFunc long dbWriteRegistrarFP(DBBASE *pdbbase, FILE *fp); epicsShareFunc long dbWriteFunctionFP(DBBASE *pdbbase, FILE *fp); epicsShareFunc long dbWriteVariableFP(DBBASE *pdbbase, FILE *fp); epicsShareFunc long dbWriteBreaktable(DBBASE *pdbbase, const char *filename); epicsShareFunc long dbWriteBreaktableFP(DBBASE *pdbbase, FILE *fp); epicsShareFunc long dbFindRecordType(DBENTRY *pdbentry, const char *recordTypename); epicsShareFunc long dbFirstRecordType(DBENTRY *pdbentry); epicsShareFunc long dbNextRecordType(DBENTRY *pdbentry); epicsShareFunc char * dbGetRecordTypeName(DBENTRY *pdbentry); epicsShareFunc int dbGetNRecordTypes(DBENTRY *pdbentry); epicsShareFunc long dbPutRecordAttribute(DBENTRY *pdbentry, const char *name, const char*value); epicsShareFunc long dbGetRecordAttribute(DBENTRY *pdbentry, const char *name); epicsShareFunc long dbGetAttributePart(DBENTRY *pdbentry, const char **ppname); epicsShareFunc long dbFirstField(DBENTRY *pdbentry, int dctonly); epicsShareFunc long dbNextField(DBENTRY *pdbentry, int dctonly); epicsShareFunc int dbGetNFields(DBENTRY *pdbentry, int dctonly); epicsShareFunc char * dbGetFieldName(DBENTRY *pdbentry); epicsShareFunc char * dbGetDefault(DBENTRY *pdbentry); epicsShareFunc char * dbGetPrompt(DBENTRY *pdbentry); epicsShareFunc int dbGetPromptGroup(DBENTRY *pdbentry); epicsShareFunc long dbCreateRecord(DBENTRY *pdbentry, const char *pname); epicsShareFunc long dbDeleteRecord(DBENTRY *pdbentry); epicsShareFunc long dbFreeRecords(DBBASE *pdbbase); epicsShareFunc long dbFindRecordPart(DBENTRY *pdbentry, const char **ppname); epicsShareFunc long dbFindRecord(DBENTRY *pdbentry, const char *pname); epicsShareFunc long dbFirstRecord(DBENTRY *pdbentry); epicsShareFunc long dbNextRecord(DBENTRY *pdbentry); epicsShareFunc int dbGetNRecords(DBENTRY *pdbentry); epicsShareFunc int dbGetNAliases(DBENTRY *pdbentry); epicsShareFunc char * dbGetRecordName(DBENTRY *pdbentry); epicsShareFunc long dbCopyRecord(DBENTRY *pdbentry, const char *newRecordName, int overWriteOK); epicsShareFunc long dbVisibleRecord(DBENTRY *pdbentry); epicsShareFunc long dbInvisibleRecord(DBENTRY *pdbentry); epicsShareFunc int dbIsVisibleRecord(DBENTRY *pdbentry); epicsShareFunc long dbCreateAlias(DBENTRY *pdbentry, const char *paliasName); epicsShareFunc int dbIsAlias(DBENTRY *pdbentry); /* Follow alias to actual record */ epicsShareFunc int dbFollowAlias(DBENTRY *pdbentry); epicsShareFunc long dbDeleteAliases(DBENTRY *pdbentry); epicsShareFunc long dbFindFieldPart(DBENTRY *pdbentry, const char **ppname); epicsShareFunc long dbFindField(DBENTRY *pdbentry, const char *pfieldName); epicsShareFunc int dbFoundField(DBENTRY *pdbentry); epicsShareFunc char * dbGetString(DBENTRY *pdbentry); epicsShareFunc long dbPutString(DBENTRY *pdbentry, const char *pstring); epicsShareFunc char * dbVerify(DBENTRY *pdbentry, const char *pstring); epicsShareFunc int dbIsDefaultValue(DBENTRY *pdbentry); epicsShareFunc long dbFirstInfo(DBENTRY *pdbentry); epicsShareFunc long dbNextInfo(DBENTRY *pdbentry); epicsShareFunc long dbFindInfo(DBENTRY *pdbentry, const char *name); epicsShareFunc long dbNextMatchingInfo(DBENTRY *pdbentry, const char *pattern); epicsShareFunc long dbDeleteInfo(DBENTRY *pdbentry); epicsShareFunc const char * dbGetInfoName(DBENTRY *pdbentry); epicsShareFunc const char * dbGetInfoString(DBENTRY *pdbentry); epicsShareFunc long dbPutInfoString(DBENTRY *pdbentry, const char *string); epicsShareFunc long dbPutInfoPointer(DBENTRY *pdbentry, void *pointer); epicsShareFunc void * dbGetInfoPointer(DBENTRY *pdbentry); epicsShareFunc const char * dbGetInfo(DBENTRY *pdbentry, const char *name); epicsShareFunc long dbPutInfo(DBENTRY *pdbentry, const char *name, const char *string); epicsShareFunc brkTable * dbFindBrkTable(DBBASE *pdbbase, const char *name); epicsShareFunc const char * dbGetFieldTypeString(int dbfType); epicsShareFunc int dbFindFieldType(const char *type); epicsShareFunc dbMenu * dbFindMenu(DBBASE *pdbbase, const char *name); epicsShareFunc char ** dbGetMenuChoices(DBENTRY *pdbentry); epicsShareFunc int dbGetMenuIndex(DBENTRY *pdbentry); epicsShareFunc long dbPutMenuIndex(DBENTRY *pdbentry, int index); epicsShareFunc int dbGetNMenuChoices(DBENTRY *pdbentry); epicsShareFunc char * dbGetMenuStringFromIndex(DBENTRY *pdbentry, int index); epicsShareFunc int dbGetMenuIndexFromString(DBENTRY *pdbentry, const char *choice); epicsShareFunc drvSup * dbFindDriver(dbBase *pdbbase, const char *name); epicsShareFunc char * dbGetRelatedField(DBENTRY *pdbentry); epicsShareFunc linkSup * dbFindLinkSup(dbBase *pdbbase, const char *name); epicsShareFunc int dbGetNLinks(DBENTRY *pdbentry); epicsShareFunc long dbGetLinkField(DBENTRY *pdbentry, int index); /* Dump routines */ epicsShareFunc void dbDumpPath(DBBASE *pdbbase); epicsShareFunc void dbDumpRecord(DBBASE *pdbbase, const char *precordTypename, int level); epicsShareFunc void dbDumpMenu(DBBASE *pdbbase, const char *menuName); epicsShareFunc void dbDumpRecordType(DBBASE *pdbbase, const char *recordTypeName); epicsShareFunc void dbDumpField(DBBASE *pdbbase, const char *recordTypeName, const char *fname); epicsShareFunc void dbDumpDevice(DBBASE *pdbbase, const char *recordTypeName); epicsShareFunc void dbDumpDriver(DBBASE *pdbbase); epicsShareFunc void dbDumpLink(DBBASE *pdbbase); epicsShareFunc void dbDumpRegistrar(DBBASE *pdbbase); epicsShareFunc void dbDumpFunction(DBBASE *pdbbase); epicsShareFunc void dbDumpVariable(DBBASE *pdbbase); epicsShareFunc void dbDumpBreaktable(DBBASE *pdbbase, const char *name); epicsShareFunc void dbPvdDump(DBBASE *pdbbase, int verbose); epicsShareFunc void dbReportDeviceConfig(DBBASE *pdbbase, FILE *report); /* Misc useful routines*/ #define dbCalloc(nobj,size) callocMustSucceed(nobj,size,"dbCalloc") #define dbMalloc(size) mallocMustSucceed(size,"dbMalloc") epicsShareFunc void dbCatString(char **string, int *stringLength, char *pnew, char *separator); extern int dbStaticDebug; extern int dbConvertStrict; #define S_dbLib_recordTypeNotFound (M_dbLib|1) /* Record Type does not exist */ #define S_dbLib_recExists (M_dbLib|3) /* Record Already exists */ #define S_dbLib_recNotFound (M_dbLib|5) /* Record Not Found */ #define S_dbLib_flddesNotFound (M_dbLib|7) /* Field Description Not Found */ #define S_dbLib_fieldNotFound (M_dbLib|9) /* Field Not Found */ #define S_dbLib_badField (M_dbLib|11) /* Bad Field value */ #define S_dbLib_menuNotFound (M_dbLib|13) /* Menu not found */ #define S_dbLib_badLink (M_dbLib|15) /* Bad Link Field */ #define S_dbLib_nameLength (M_dbLib|17) /* Record Name is too long */ #define S_dbLib_noRecSup (M_dbLib|19) /* Record support not found */ #define S_dbLib_strLen (M_dbLib|21) /* String is too long */ #define S_dbLib_noSizeOffset (M_dbLib|23) /* Missing SizeOffset Routine - No record support? */ #define S_dbLib_outMem (M_dbLib|27) /* Out of memory */ #define S_dbLib_infoNotFound (M_dbLib|29) /* Info item Not Found */ #ifdef __cplusplus } #endif #endif /*INCdbStaticLibh*/ base-7.0.3.1/modules/database/src/ioc/dbStatic/dbStaticPvt.h0000664000577000060420000000634113557101274022331 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* dbStaticPvt.h */ /* * Author: Marty Kraimer * Date: 06Jun95 */ #ifndef INCdbStaticPvth #define INCdbStaticPvth 1 #ifdef __cplusplus extern "C" { #endif /*Following are not intended for client code */ dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry); void dbFreeLinkContents(struct link *plink); void dbFreePath(DBBASE *pdbbase); int dbIsMacroOk(DBENTRY *pdbentry); /*The following routines have different versions for run-time no-run-time*/ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName); long dbFreeRecord(DBENTRY *pdbentry); long dbGetFieldAddress(DBENTRY *pdbentry); char *dbRecordName(DBENTRY *pdbentry); char *dbGetStringNum(DBENTRY *pdbentry); long dbPutStringNum(DBENTRY *pdbentry,const char *pstring); struct jlink; typedef struct dbLinkInfo { short ltype; /* full link string for CONSTANT and PV_LINK, * parm string for HW links, JSON for JSON_LINK */ char *target; /* for PV_LINK */ short modifiers; /* for HW links */ char hwid[6]; /* one extra element for a nil */ int hwnums[5]; /* for JSON_LINK */ struct jlink *jlink; } dbLinkInfo; long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec); /* Parse link string. no record locks needed. * on success caller must free pinfo->target */ epicsShareFunc long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo); /* Check if link type allow the parsed link value pinfo * to be assigned to the given link. * Record containing plink must be locked. * Frees pinfo->target on failure. */ long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup); /* Set link field. source record must be locked (target record too * when a DB_LINK is created) * Unconditionally takes ownership of pinfo->target */ long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *dset); /* Free dbLinkInfo storage */ epicsShareFunc void dbFreeLinkInfo(dbLinkInfo *pinfo); /* The following is for path */ typedef struct dbPathNode { ELLNODE node; char *directory; } dbPathNode; /* Element of the global gui group list */ typedef struct dbGuiGroup { ELLNODE node; short key; char *name; } dbGuiGroup; /*The following are in dbPvdLib.c*/ /*directory*/ typedef struct{ ELLNODE node; dbRecordType *precordType; dbRecordNode *precnode; }PVDENTRY; epicsShareFunc int dbPvdTableSize(int size); extern int dbStaticDebug; void dbPvdInitPvt(DBBASE *pdbbase); PVDENTRY *dbPvdFind(DBBASE *pdbbase,const char *name,size_t lenname); PVDENTRY *dbPvdAdd(DBBASE *pdbbase,dbRecordType *precordType,dbRecordNode *precnode); void dbPvdDelete(DBBASE *pdbbase,dbRecordNode *precnode); void dbPvdFreeMem(DBBASE *pdbbase); #ifdef __cplusplus } #endif #endif /*INCdbStaticPvth*/ base-7.0.3.1/modules/database/src/ioc/dbStatic/dbStaticRun.c0000664000577000060420000004003613557101274022316 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /*dbStaticLibRun.c*/ #include #include #include #include #include #include "cvtFast.h" #include "dbDefs.h" #include "ellLib.h" #include "epicsPrint.h" #include "epicsStdlib.h" #include "epicsTypes.h" #include "errMdef.h" #include "epicsExport.h" /* #define epicsExportSharedSymbols */ #include "dbBase.h" #include "dbCommonPvt.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "devSup.h" #include "special.h" epicsShareDef int dbConvertStrict = 0; epicsExportAddress(int, dbConvertStrict); static long do_nothing(struct dbCommon *precord) { return 0; } /* Dummy DSXT used for soft device supports */ struct dsxt devSoft_DSXT = { do_nothing, do_nothing }; static devSup *pthisDevSup = NULL; void dbInitDevSup(devSup *pdevSup, dset *pdset) { pdevSup->pdset = pdset; if (pdevSup->link_type == CONSTANT) pdevSup->pdsxt = &devSoft_DSXT; if (pdset->init) { pthisDevSup = pdevSup; pdset->init(0); pthisDevSup = NULL; } } void devExtend(dsxt *pdsxt) { if (!pthisDevSup) errlogPrintf("devExtend() called outside of dbInitDevSup()\n"); else { pthisDevSup->pdsxt = pdsxt; } } long dbAllocRecord(DBENTRY *pdbentry,const char *precordName) { dbRecordType *pdbRecordType = pdbentry->precordType; dbRecordNode *precnode = pdbentry->precnode; dbFldDes *pflddes; int i; dbCommonPvt *ppvt; dbCommon *precord; char *pfield; if(!pdbRecordType) return(S_dbLib_recordTypeNotFound); if(!precnode) return(S_dbLib_recNotFound); if(pdbRecordType->rec_size == 0) { printf("\t*** Did you run x_RegisterRecordDeviceDriver(pdbbase) yet? ***\n"); epicsPrintf("dbAllocRecord(%s) with %s rec_size = 0\n", precordName, pdbRecordType->name); return(S_dbLib_noRecSup); } else if(pdbRecordType->rec_sizename); epicsPrintf("dbAllocRecord(%s) with %s rec_size = %d\n", precordName, pdbRecordType->name, pdbRecordType->rec_size); return(S_dbLib_noRecSup); } ppvt = dbCalloc(1, offsetof(dbCommonPvt, common) + pdbRecordType->rec_size); precord = &ppvt->common; ppvt->recnode = precnode; precord->rdes = pdbRecordType; precnode->precord = precord; pflddes = pdbRecordType->papFldDes[0]; if(!pflddes) { epicsPrintf("dbAllocRecord pflddes for NAME not found\n"); return(S_dbLib_flddesNotFound); } assert(pflddes->offset == 0); assert(pflddes->size == sizeof(precord->name)); if(strlen(precordName) >= sizeof(precord->name)) { epicsPrintf("dbAllocRecord: NAME(%s) too long\n",precordName); return(S_dbLib_nameLength); } strcpy(precord->name, precordName); for(i=1; ino_fields; i++) { pflddes = pdbRecordType->papFldDes[i]; if(!pflddes) continue; pfield = (char*)precord + pflddes->offset; pdbentry->pfield = (void *)pfield; pdbentry->pflddes = pflddes; pdbentry->indfield = i; switch(pflddes->field_type) { case DBF_STRING: if(pflddes->initial) { if(strlen(pflddes->initial) >= pflddes->size) { epicsPrintf("initial size > size for %s.%s\n", pdbRecordType->name,pflddes->name); } else { strcpy(pfield,pflddes->initial); } } break; case DBF_CHAR: case DBF_UCHAR: case DBF_SHORT: case DBF_USHORT: case DBF_LONG: case DBF_ULONG: case DBF_INT64: case DBF_UINT64: case DBF_FLOAT: case DBF_DOUBLE: case DBF_ENUM: case DBF_MENU: if(pflddes->initial) { long status; status = dbPutStringNum(pdbentry,pflddes->initial); if(status) epicsPrintf("Error initializing %s.%s initial %s\n", pdbRecordType->name,pflddes->name,pflddes->initial); } break; case DBF_DEVICE: if(!pflddes->ftPvt) dbGetDeviceMenu(pdbentry); break; case DBF_INLINK: case DBF_OUTLINK: case DBF_FWDLINK: { DBLINK *plink = (DBLINK *)pfield; plink->type = CONSTANT; if(pflddes->initial) { plink->text = dbCalloc(strlen(pflddes->initial)+1,sizeof(char)); strcpy(plink->text,pflddes->initial); } } break; case DBF_NOACCESS: break; default: epicsPrintf("dbAllocRecord: Illegal field type\n"); } } return(0); } long dbFreeRecord(DBENTRY *pdbentry) { dbRecordType *pdbRecordType = pdbentry->precordType; dbRecordNode *precnode = pdbentry->precnode; if(!pdbRecordType) return(S_dbLib_recordTypeNotFound); if(!precnode) return(S_dbLib_recNotFound); if(!precnode->precord) return(S_dbLib_recNotFound); free(dbRec2Pvt(precnode->precord)); precnode->precord = NULL; return(0); } long dbGetFieldAddress(DBENTRY *pdbentry) { dbRecordType *pdbRecordType = pdbentry->precordType; dbRecordNode *precnode = pdbentry->precnode; dbFldDes *pflddes = pdbentry->pflddes; if(!pdbRecordType) return(S_dbLib_recordTypeNotFound); if(!precnode) return(S_dbLib_recNotFound); if(!pflddes) return(S_dbLib_flddesNotFound); if(!precnode->precord) return(0); pdbentry->pfield = ((char *)precnode->precord) + pflddes->offset; return(0); } char *dbRecordName(DBENTRY *pdbentry) { dbRecordType *pdbRecordType = pdbentry->precordType; dbRecordNode *precnode = pdbentry->precnode; dbFldDes *pflddes; char *precord; if(!pdbRecordType) return(0); if(!precnode) return(0); if(!precnode->precord) return(0); precord = (char *)precnode->precord; pflddes = pdbRecordType->papFldDes[0]; if(!pflddes) return(NULL); return(precord + pflddes->offset); } int dbIsMacroOk(DBENTRY *pdbentry) { return(FALSE); } epicsShareFunc int dbIsDefaultValue(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; void *pfield = pdbentry->pfield; if (!pflddes || !pfield) return FALSE; switch (pflddes->field_type) { case DBF_STRING: { char *p = (char *)pfield; return pflddes->initial ? ! strcmp(pflddes->initial, p) : ! strlen(p); } case DBF_CHAR: { epicsInt8 field = *(epicsInt8 *)pfield; epicsInt8 def; if (!pflddes->initial) return field == 0; return ! epicsParseInt8(pflddes->initial, &def, 0, NULL) && field == def; } case DBF_UCHAR: { epicsUInt8 field = *(epicsUInt8 *)pfield; epicsUInt8 def; if (!pflddes->initial) return field == 0; return ! epicsParseUInt8(pflddes->initial, &def, 0, NULL) && field == def; } case DBF_SHORT: { epicsInt16 field = *(epicsInt16 *)pfield; epicsInt16 def; if (!pflddes->initial) return field == 0; return ! epicsParseInt16(pflddes->initial, &def, 0, NULL) && field == def; } case DBF_ENUM: case DBF_USHORT: { epicsUInt16 field = *(epicsUInt16 *)pfield; epicsUInt16 def; if (!pflddes->initial) return field == 0; return ! epicsParseUInt16(pflddes->initial, &def, 0, NULL) && field == def; } case DBF_LONG: { epicsInt32 field = *(epicsInt32 *)pfield; epicsInt32 def; if (!pflddes->initial) return field == 0; return ! epicsParseInt32(pflddes->initial, &def, 0, NULL) && field == def; } case DBF_ULONG: { epicsUInt32 field = *(epicsUInt32 *)pfield; epicsUInt32 def; if (!pflddes->initial) return field == 0; return ! epicsParseUInt32(pflddes->initial, &def, 0, NULL) && field == def; } case DBF_INT64: { epicsInt64 field = *(epicsInt64 *)pfield; epicsInt64 def; if (!pflddes->initial) return field == 0; return ! epicsParseInt64(pflddes->initial, &def, 0, NULL) && field == def; } case DBF_UINT64: { epicsUInt64 field = *(epicsUInt64 *)pfield; epicsUInt64 def; if (!pflddes->initial) return field == 0; return ! epicsParseUInt64(pflddes->initial, &def, 0, NULL) && field == def; } case DBF_FLOAT: { epicsFloat32 field = *(epicsFloat32 *)pfield; epicsFloat32 def; if (!pflddes->initial) return field == 0; return ! epicsParseFloat32(pflddes->initial, &def, NULL) && field == def; } case DBF_DOUBLE: { epicsFloat64 field = *(epicsFloat64 *)pfield; epicsFloat64 def; if (!pflddes->initial) return field == 0; return ! epicsParseFloat64(pflddes->initial, &def, NULL) && field == def; } case DBF_MENU: { epicsEnum16 field = *(epicsEnum16 *)pfield; epicsEnum16 def; int index; if (!pflddes->initial) return field == 0; index = dbGetMenuIndexFromString(pdbentry, pflddes->initial); if (index < 0) { if (epicsParseUInt16(pflddes->initial, &def, 0, NULL)) return FALSE; } else def = index; return field == def; } case DBF_DEVICE: { dbRecordType *precordType = pdbentry->precordType; if (!precordType) { epicsPrintf("dbIsDefaultValue: pdbRecordType is NULL??\n"); return FALSE; } return ellCount(&precordType->devList) == 0; } case DBF_INLINK: case DBF_OUTLINK: case DBF_FWDLINK: { struct link *plink = (struct link *)pfield; if (!plink || plink->type != CONSTANT) return FALSE; /* These conditions don't make a lot of sense... */ if (!plink->value.constantStr) return TRUE; if (!pflddes->initial) /* Default value for a link field? */ return FALSE; return !strcmp(plink->value.constantStr, pflddes->initial); } default: return TRUE; } } long dbPutStringNum(DBENTRY *pdbentry, const char *pstring) { dbFldDes *pflddes = pdbentry->pflddes; void *pfield = pdbentry->pfield; long status; epicsUInt64 u64; epicsInt64 i64; if (!pfield) return S_dbLib_fieldNotFound; /* empty string is the same as writing numeric zero */ if (pstring[0] == '\0') pstring = "0"; switch (pflddes->field_type) { case DBF_CHAR: if (dbConvertStrict) return epicsParseInt8(pstring, pfield, 0, NULL); goto lax_signed; case DBF_SHORT: if (dbConvertStrict) return epicsParseInt16(pstring, pfield, 0, NULL); goto lax_signed; case DBF_LONG: if (dbConvertStrict) return epicsParseInt32(pstring, pfield, 0, NULL); goto lax_signed; case DBF_INT64: if (dbConvertStrict) return epicsParseInt64(pstring, pfield, 0, NULL); lax_signed: status = epicsParseInt64(pstring, &i64, 0, NULL); if (status) return status; switch (pflddes->field_type) { case DBF_CHAR: *(epicsInt8 *)pfield = (epicsInt8) i64; break; case DBF_SHORT: *(epicsInt16*)pfield = (epicsInt16)i64; break; case DBF_LONG: *(epicsInt32*)pfield = (epicsInt32)i64; break; case DBF_INT64: *(epicsInt64*)pfield = (epicsInt64)i64; break; default: break; } return status; case DBF_UCHAR: if (dbConvertStrict) return epicsParseUInt8(pstring, pfield, 0, NULL); goto lax_unsigned; case DBF_ENUM: case DBF_USHORT: if (dbConvertStrict) return epicsParseUInt16(pstring, pfield, 0, NULL); goto lax_unsigned; case DBF_ULONG: if (dbConvertStrict) return epicsParseUInt32(pstring, pfield, 0, NULL); goto lax_unsigned; case DBF_UINT64: if (dbConvertStrict) return epicsParseUInt64(pstring, pfield, 0, NULL); lax_unsigned: status = epicsParseUInt64(pstring, &u64, 0, NULL); if (status) return status; switch (pflddes->field_type) { case DBF_UCHAR: *(epicsUInt8 *)pfield = (epicsInt8) u64; break; case DBF_ENUM: case DBF_USHORT: *(epicsUInt16*)pfield = (epicsInt16)u64; break; case DBF_ULONG: *(epicsUInt32*)pfield = (epicsInt32)u64; break; case DBF_UINT64: *(epicsUInt64*)pfield = (epicsInt64)u64; break; default: break; } return status; case DBF_FLOAT: return epicsParseFloat32(pstring, pfield, NULL); case DBF_DOUBLE: return epicsParseFloat64(pstring, pfield, NULL); case DBF_MENU: case DBF_DEVICE: { epicsEnum16 *field = (epicsEnum16 *) pfield; int index = dbGetMenuIndexFromString(pdbentry, pstring); if (index < 0) { epicsEnum16 value; long status = epicsParseUInt16(pstring, &value, 0, NULL); if (status) return status; index = dbGetNMenuChoices(pdbentry); if (value > index && index > 0 && value < USHRT_MAX) return S_dbLib_badField; *field = value; } else *field = index; return 0; } default: return S_dbLib_badField; } } epicsShareFunc int dbGetMenuIndex(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; void *pfield = pdbentry->pfield; if (!pflddes || !pfield) return -1; switch (pflddes->field_type) { case DBF_MENU: case DBF_DEVICE: return * (epicsEnum16 *) pfield; default: epicsPrintf("dbGetMenuIndex: Called for field type %d\n", pflddes->field_type); } return -1; } epicsShareFunc long dbPutMenuIndex(DBENTRY *pdbentry, int index) { dbFldDes *pflddes = pdbentry->pflddes; epicsEnum16 *pfield = pdbentry->pfield; if (!pflddes) return S_dbLib_flddesNotFound; if (!pfield) return S_dbLib_fieldNotFound; switch (pflddes->field_type) { case DBF_MENU: { dbMenu *pdbMenu = (dbMenu *) pflddes->ftPvt; if (!pdbMenu) return S_dbLib_menuNotFound; if (index < 0 || index >= pdbMenu->nChoice) return S_dbLib_badField; *pfield = index; return 0; } case DBF_DEVICE: { dbDeviceMenu *pdbDeviceMenu = dbGetDeviceMenu(pdbentry); if (!pdbDeviceMenu) return S_dbLib_menuNotFound; if (index < 0 || index >= pdbDeviceMenu->nChoice) return S_dbLib_badField; return dbPutString(pdbentry, pdbDeviceMenu->papChoice[index]); } default: break; } return S_dbLib_badField; } base-7.0.3.1/modules/database/src/ioc/dbStatic/dbYacc.y0000664000577000060420000002115413557101274021307 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ %{ static int yyerror(); static int yy_start; static long pvt_yy_parse(void); static int yyFailed = 0; static int yyAbort = 0; #include "dbLexRoutines.c" %} %start database %union { char *Str; } %token tokenINCLUDE tokenPATH tokenADDPATH %token tokenALIAS tokenMENU tokenCHOICE tokenRECORDTYPE %token tokenFIELD tokenINFO tokenREGISTRAR %token tokenDEVICE tokenDRIVER tokenLINK tokenBREAKTABLE %token tokenRECORD tokenGRECORD tokenVARIABLE tokenFUNCTION %token tokenSTRING tokenCDEFS %token jsonNULL jsonTRUE jsonFALSE %token jsonNUMBER jsonSTRING jsonBARE %type json_value json_object json_array %type json_members json_pair json_elements json_string %% database: /* empty */ | database_item_list ; database_item_list: database_item_list database_item | database_item ; database_item: include | path | addpath | tokenMENU menu_head menu_body | tokenRECORDTYPE recordtype_head recordtype_body | device | driver | link | registrar | function | variable | tokenBREAKTABLE break_head break_body | tokenRECORD record_head record_body | tokenGRECORD grecord_head record_body | alias ; include: tokenINCLUDE tokenSTRING { if(dbStaticDebug>2) printf("include : %s\n",$2); dbIncludeNew($2); dbmfFree($2); }; path: tokenPATH tokenSTRING { if(dbStaticDebug>2) printf("path : %s\n",$2); dbPathCmd($2); dbmfFree($2); }; addpath: tokenADDPATH tokenSTRING { if(dbStaticDebug>2) printf("addpath : %s\n",$2); dbAddPathCmd($2); dbmfFree($2); }; menu_head: '(' tokenSTRING ')' { if(dbStaticDebug>2) printf("menu_head %s\n",$2); dbMenuHead($2); dbmfFree($2); }; menu_body: '{' choice_list '}' { if(dbStaticDebug>2) printf("menu_body\n"); dbMenuBody(); }; choice_list: choice_list choice | choice; choice: tokenCHOICE '(' tokenSTRING ',' tokenSTRING ')' { if(dbStaticDebug>2) printf("choice %s %s\n",$3,$5); dbMenuChoice($3,$5); dbmfFree($3); dbmfFree($5); } | include; recordtype_head: '(' tokenSTRING ')' { if(dbStaticDebug>2) printf("recordtype_head %s\n",$2); dbRecordtypeHead($2); dbmfFree($2); }; recordtype_body: '{' '}' { if(dbStaticDebug>2) printf("empty recordtype_body\n"); dbRecordtypeEmpty(); } | '{' recordtype_field_list '}' { if(dbStaticDebug>2) printf("recordtype_body\n"); dbRecordtypeBody(); }; recordtype_field_list: recordtype_field_list recordtype_field | recordtype_field; recordtype_field: tokenFIELD recordtype_field_head recordtype_field_body | tokenCDEFS { if(dbStaticDebug>2) printf("recordtype_cdef %s", $1); dbRecordtypeCdef($1); dbmfFree($1); } | include ; recordtype_field_head: '(' tokenSTRING ',' tokenSTRING ')' { if(dbStaticDebug>2) printf("recordtype_field_head %s %s\n",$2,$4); dbRecordtypeFieldHead($2,$4); dbmfFree($2); dbmfFree($4); }; recordtype_field_body: '{' recordtype_field_item_list '}' ; recordtype_field_item_list: recordtype_field_item_list recordtype_field_item | recordtype_field_item; recordtype_field_item: tokenSTRING '(' tokenSTRING ')' { if(dbStaticDebug>2) printf("recordtype_field_item %s %s\n",$1,$3); dbRecordtypeFieldItem($1,$3); dbmfFree($1); dbmfFree($3); } | tokenMENU '(' tokenSTRING ')' { if(dbStaticDebug>2) printf("recordtype_field_item %s (%s)\n","menu",$3); dbRecordtypeFieldItem("menu",$3); dbmfFree($3); }; device: tokenDEVICE '(' tokenSTRING ',' tokenSTRING ',' tokenSTRING ',' tokenSTRING ')' { if(dbStaticDebug>2) printf("device %s %s %s %s\n",$3,$5,$7,$9); dbDevice($3,$5,$7,$9); dbmfFree($3); dbmfFree($5); dbmfFree($7); dbmfFree($9); }; driver: tokenDRIVER '(' tokenSTRING ')' { if(dbStaticDebug>2) printf("driver %s\n",$3); dbDriver($3); dbmfFree($3); }; link: tokenLINK '(' tokenSTRING ',' tokenSTRING ')' { if(dbStaticDebug>2) printf("link %s %s\n",$3,$5); dbLinkType($3,$5); dbmfFree($3); dbmfFree($5); }; registrar: tokenREGISTRAR '(' tokenSTRING ')' { if(dbStaticDebug>2) printf("registrar %s\n",$3); dbRegistrar($3); dbmfFree($3); }; function: tokenFUNCTION '(' tokenSTRING ')' { if(dbStaticDebug>2) printf("function %s\n",$3); dbFunction($3); dbmfFree($3); }; variable: tokenVARIABLE '(' tokenSTRING ')' { if(dbStaticDebug>2) printf("variable %s\n",$3); dbVariable($3,"int"); dbmfFree($3); } | tokenVARIABLE '(' tokenSTRING ',' tokenSTRING ')' { if(dbStaticDebug>2) printf("variable %s, %s\n",$3,$5); dbVariable($3,$5); dbmfFree($3); dbmfFree($5); }; break_head: '(' tokenSTRING ')' { if(dbStaticDebug>2) printf("break_head %s\n",$2); dbBreakHead($2); dbmfFree($2); }; break_body : '{' break_list '}' { if(dbStaticDebug>2) printf("break_body\n"); dbBreakBody(); }; break_list: break_list ',' break_item | break_list break_item | break_item; break_item: tokenSTRING { if(dbStaticDebug>2) printf("break_item tokenSTRING %s\n",$1); dbBreakItem($1); dbmfFree($1); }; grecord_head: '(' tokenSTRING ',' tokenSTRING ')' { if(dbStaticDebug>2) printf("grecord_head %s %s\n",$2,$4); dbRecordHead($2,$4,1); dbmfFree($2); dbmfFree($4); }; record_head: '(' tokenSTRING ',' tokenSTRING ')' { if(dbStaticDebug>2) printf("record_head %s %s\n",$2,$4); dbRecordHead($2,$4,0); dbmfFree($2); dbmfFree($4); }; record_body: /* empty */ { if(dbStaticDebug>2) printf("null record_body\n"); dbRecordBody(); } | '{' '}' { if(dbStaticDebug>2) printf("empty record_body\n"); dbRecordBody(); } | '{' record_field_list '}' { if(dbStaticDebug>2) printf("record_body\n"); dbRecordBody(); }; record_field_list: record_field_list record_field | record_field; record_field: tokenFIELD '(' tokenSTRING ',' { BEGIN JSON; } json_value { BEGIN INITIAL; } ')' { if(dbStaticDebug>2) printf("record_field %s %s\n",$3,$6); dbRecordField($3,$6); dbmfFree($3); dbmfFree($6); } | tokenINFO '(' tokenSTRING ',' { BEGIN JSON; } json_value { BEGIN INITIAL; } ')' { if(dbStaticDebug>2) printf("record_info %s %s\n",$3,$6); dbRecordInfo($3,$6); dbmfFree($3); dbmfFree($6); } | tokenALIAS '(' tokenSTRING ')' { if(dbStaticDebug>2) printf("record_alias %s\n",$3); dbRecordAlias($3); dbmfFree($3); } | include ; alias: tokenALIAS '(' tokenSTRING ',' tokenSTRING ')' { if(dbStaticDebug>2) printf("alias %s %s\n",$3,$5); dbAlias($3,$5); dbmfFree($3); dbmfFree($5); }; json_object: '{' '}' { $$ = dbmfStrdup("{}"); if (dbStaticDebug>2) printf("json %s\n", $$); } | '{' json_members '}' { $$ = dbmfStrcat3("{", $2, "}"); dbmfFree($2); if (dbStaticDebug>2) printf("json %s\n", $$); }; json_members: json_pair | json_pair ',' | json_pair ',' json_members { $$ = dbmfStrcat3($1, ",", $3); dbmfFree($1); dbmfFree($3); if (dbStaticDebug>2) printf("json %s\n", $$); }; json_pair: json_string ':' json_value { $$ = dbmfStrcat3($1, ":", $3); dbmfFree($1); dbmfFree($3); if (dbStaticDebug>2) printf("json %s\n", $$); }; json_string: jsonSTRING | jsonBARE { $$ = dbmfStrcat3("\"", $1, "\""); dbmfFree($1); if (dbStaticDebug>2) printf("json %s\n", $$); }; json_array: '[' ']' { $$ = dbmfStrdup("[]"); if (dbStaticDebug>2) printf("json %s\n", $$); } | '[' json_elements ']' { $$ = dbmfStrcat3("[", $2, "]"); dbmfFree($2); if (dbStaticDebug>2) printf("json %s\n", $$); }; json_elements: json_value | json_value ',' { /* Retain the trailing ',' so link parser can distinguish a * 1-element const list from a PV name (commas are illegal) */ $$ = dbmfStrcat3($1, ",", ""); dbmfFree($1); if (dbStaticDebug>2) printf("json %s\n", $$); }; | json_value ',' json_elements { $$ = dbmfStrcat3($1, ",", $3); dbmfFree($1); dbmfFree($3); if (dbStaticDebug>2) printf("json %s\n", $$); }; json_value: jsonNULL { $$ = dbmfStrdup("null"); } | jsonTRUE { $$ = dbmfStrdup("true"); } | jsonFALSE { $$ = dbmfStrdup("false"); } | jsonNUMBER | json_string | json_array | json_object ; %% #include "dbLex.c" static int yyerror(char *str) { if (str) epicsPrintf("Error: %s\n", str); else epicsPrintf("Error"); if (!yyFailed) { /* Only print this stuff once */ epicsPrintf(" at or before \"%s\"", yytext); dbIncludePrint(); yyFailed = TRUE; } return(0); } static long pvt_yy_parse(void) { static int FirstFlag = 1; long rtnval; if (!FirstFlag) { yyAbort = FALSE; yyFailed = FALSE; yyreset(); yyrestart(NULL); } FirstFlag = 0; rtnval = yyparse(); if(rtnval!=0 || yyFailed) return(-1); else return(0); } base-7.0.3.1/modules/database/src/ioc/dbStatic/devSup.h0000664000577000060420000001413313557101274021346 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /** @file devSup.h * * @brief Device support routines */ /* * Author: Marty Kraimer * Date: 6-1-90 */ #ifndef INCdevSuph #define INCdevSuph 1 #include "errMdef.h" #include "shareLib.h" /* structures defined elsewhere */ struct dbCommon; struct devSup; typedef struct ioscan_head *IOSCANPVT; struct link; /* aka DBLINK */ /** Type safe version of 'struct dset' * * Recommended usage: * * In Makefile: @code USR_CFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET @endcode * * In C source file: @code #include #include // For IOCSCANPVT ... #include // defines epicsExportSharedSymbols ... static long init_record(dbCommon *prec); static long get_iointr_info(int detach, dbCommon *prec, IOCSCANPVT* pscan); static long longin_read(longinRecord *prec); const struct { dset common; long (*read)(longinRecord *prec); } devLiDevName = { { 5, // 4 from dset + 1 from longinRecord NULL, NULL, &init_record, &get_iointr_info }, &longin_read }; epicsExportAddress(dset, devLiDevName); @endcode */ typedef struct typed_dset { /** Number of function pointers which follow. * The value depends on the recordtype, but must be >=4 */ long number; /** Called from dbior() */ long (*report)(int lvl); /** Called twice during iocInit(). * First with @a after = 0 before init_record() or array field allocation. * Again with @a after = 1 after init_record() has finished. */ long (*init)(int after); /** Called once per record instance */ long (*init_record)(struct dbCommon *prec); /** Called when SCAN="I/O Intr" on startup, or after SCAN is changed. * * Caller must assign the third arguement (IOCSCANPVT*). eg. @code struct mpvt { IOSCANPVT drvlist; }; ... // init_record() routine calls scanIoInit(&pvt->drvlist); ... static long get_ioint_info(int detach, struct dbCommon *prec, IOCSCANPVT* pscan) { if(prec->dpvt) *pscan = &((mypvt*)prec->dpvt)->drvlist; @endcode * * When a particular record instance can/will only used a single scan list, * the @a detach argument can be ignored. * * If this is not the case, then the following should be noted. * + get_ioint_info() is called with @a detach = 0 to fetch the scan list to * which this record will be added. * + get_ioint_info() is called later with @a detach = 1 to fetch the scan * list from which this record should be removed. * + Calls will be balanced, so a call with @a detach = 0 will be followed * by one with @a detach = 1. * * @note get_ioint_info() will be called during IOC shutdown if the * dsxt::del_record() extended callback is defined. (from 3.15.0.1) */ long (*get_ioint_info)(int detach, struct dbCommon *prec, IOSCANPVT* pscan); /* Any further functions are specified by the record type. */ } typed_dset; /** Device support extension table. * * Optional routines to allow run-time address modifications to be communicated * to device support, which must register a struct dsxt by calling devExtend() * from its init() routine. */ typedef struct dsxt { /** Optional, called to offer device support a new record to control. * * Routine may return a non-zero error code to refuse record. */ long (*add_record)(struct dbCommon *precord); /** Optional, called to remove record from device support control. * * Routine return a non-zero error code to refuse record removal. */ long (*del_record)(struct dbCommon *precord); /* Only future Base releases may extend this table. */ } dsxt; #ifdef __cplusplus extern "C" { typedef long (*DEVSUPFUN)(void *); /* ptr to device support function*/ #else typedef long (*DEVSUPFUN)(); /* ptr to device support function*/ #endif #ifndef USE_TYPED_DSET typedef struct dset { /* device support entry table */ long number; /*number of support routines*/ DEVSUPFUN report; /*print report*/ DEVSUPFUN init; /*init support layer*/ DEVSUPFUN init_record; /*init device for particular record*/ DEVSUPFUN get_ioint_info; /* get io interrupt information*/ /*other functions are record dependent*/ } dset; #else typedef typed_dset dset; #endif /* USE_TYPED_DSET */ /** Fetch INP or OUT link (or NULL if record type has neither). * * Recommended for use in device support init_record() */ epicsShareFunc struct link* dbGetDevLink(struct dbCommon* prec); epicsShareExtern dsxt devSoft_DSXT; /* Allow anything table */ epicsShareFunc void devExtend(dsxt *pdsxt); epicsShareFunc void dbInitDevSup(struct devSup *pdevSup, dset *pdset); #define S_dev_noDevSup (M_devSup| 1) /*SDR_DEVSUP: Device support missing*/ #define S_dev_noDSET (M_devSup| 3) /*Missing device support entry table*/ #define S_dev_missingSup (M_devSup| 5) /*Missing device support routine*/ #define S_dev_badInpType (M_devSup| 7) /*Bad INP link type*/ #define S_dev_badOutType (M_devSup| 9) /*Bad OUT link type*/ #define S_dev_badInitRet (M_devSup|11) /*Bad init_rec return value */ #define S_dev_badBus (M_devSup|13) /*Illegal bus type*/ #define S_dev_badCard (M_devSup|15) /*Illegal or nonexistant module*/ #define S_dev_badSignal (M_devSup|17) /*Illegal signal*/ #define S_dev_NoInit (M_devSup|19) /*No init*/ #define S_dev_Conflict (M_devSup|21) /*Multiple records accessing same signal*/ #define S_dev_noDeviceFound (M_devSup|23) /*No device found at specified address*/ #ifdef __cplusplus } /* extern "C" */ #endif #endif base-7.0.3.1/modules/database/src/ioc/dbStatic/drvSup.h0000664000577000060420000000331013557101274021356 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /** @file drvSup.h * * @brief Driver support routines. */ /* * Author: Marty Kraimer * Date: 6-1-90 */ #ifndef INCdrvSuph #define INCdrvSuph 1 #include "errMdef.h" /** Driver entry table */ typedef struct typed_drvet { /** Number of function pointers which follow. Must be >=2 */ long number; /** Called from dbior() */ long (*report)(int lvl); /** Called during iocInit() */ long (*init)(void); /* Any further functions are driver-specific */ } typed_drvet; #ifdef USE_TYPED_DRVET typedef typed_drvet drvet; #else /* These interfaces may eventually get deprecated */ typedef long (*DRVSUPFUN) (); /* ptr to driver support function */ typedef struct drvet { /* driver entry table */ long number; /* number of support routines */ DRVSUPFUN report; /* print report */ DRVSUPFUN init; /* init support */ /* Any further functions are driver-specific */ } drvet; #define DRVETNUMBER ( (sizeof(struct drvet) -sizeof(long))/sizeof(DRVSUPFUN) ) #endif /* USE_TYPED_DRVET */ #define S_drv_noDrvSup (M_drvSup| 1) /*SDR_DRVSUP: Driver support missing*/ #define S_drv_noDrvet (M_drvSup| 3) /*Missing driver support entry table*/ #endif base-7.0.3.1/modules/database/src/ioc/dbStatic/guigroup.h0000664000577000060420000000216613557101274021744 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* these are used in the pmt (prompt) field of the record support ascii files. They represent field groupings for dct tools */ #ifndef __gui_group_h__ #define __gui_group_h__ #error As of Base 3.15.4, the promptgroup implementation has changed. \ This header file (guigroup.h) is invalid and will be removed shortly. \ Instead, you should include dbStaticLib.h, parse the DBD, \ and use dbGetPromptGroupNameFromKey() and dbGetPromptGroupKeyFromName() \ that have been added to dbStaticLib. \ More details in the 3.15.4 release notes and the AppDev Guide. #endif /*__gui_group_h__*/ base-7.0.3.1/modules/database/src/ioc/dbStatic/link.h0000664000577000060420000001203313557101274021032 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* link.h */ /* * Original Authors: Bob Dalesio, Marty Kraimer */ #ifndef INC_link_H #define INC_link_H #include "dbDefs.h" #include "ellLib.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /* link types */ #define CONSTANT 0 #define PV_LINK 1 #define VME_IO 2 #define CAMAC_IO 3 #define AB_IO 4 #define GPIB_IO 5 #define BITBUS_IO 6 #define MACRO_LINK 7 #define JSON_LINK 8 #define PN_LINK 9 #define DB_LINK 10 #define CA_LINK 11 #define INST_IO 12 /* instrument */ #define BBGPIB_IO 13 /* bitbus -> gpib */ #define RF_IO 14 #define VXI_IO 15 #define LINK_NTYPES 16 typedef struct maplinkType { char *strvalue; int value; } maplinkType; epicsShareExtern maplinkType pamaplinkType[]; #define VXIDYNAMIC 0 #define VXISTATIC 1 /* structure of a PV_LINK DB_LINK and a CA_LINK */ /*Options defined by pvlMask */ #define pvlOptMsMode 0x3 /*Maximize Severity mode selection*/ #define pvlOptNMS 0 /*Don't Maximize Severity*/ #define pvlOptMS 1 /*Maximize Severity always*/ #define pvlOptMSI 2 /*Maximize Severity if INVALID*/ #define pvlOptMSS 3 /*Maximize Severity and copy Status*/ #define pvlOptPP 0x4 /*Process Passive*/ #define pvlOptCA 0x8 /*Always make it a CA link*/ #define pvlOptCP 0x10 /*CA + process on monitor*/ #define pvlOptCPP 0x20 /*CA + process passive record on monitor*/ #define pvlOptFWD 0x40 /*Generate ca_put for forward link*/ #define pvlOptInpNative 0x80 /*Input native*/ #define pvlOptInpString 0x100 /*Input as string*/ #define pvlOptOutNative 0x200 /*Output native*/ #define pvlOptOutString 0x400 /*Output as string*/ /* DBLINK Flag bits */ #define DBLINK_FLAG_INITIALIZED 1 /* dbInitLink() called */ #define DBLINK_FLAG_TSELisTIME 2 /* Use TSEL to get timeStamp */ struct macro_link { char *macroStr; }; struct dbCommon; typedef long (*LINKCVT)(); struct pv_link { ELLNODE backlinknode; char *pvname; /* pvname link points to */ void *pvt; /* CA or DB private */ LINKCVT getCvt; /* input conversion function */ short pvlMask; /* Options mask */ short lastGetdbrType; /* last dbrType for DB or CA get */ }; struct jlink; struct json_link { char *string; struct jlink *jlink; }; /* structure of a VME io channel */ struct vmeio { short card; short signal; char *parm; }; /* structure of a CAMAC io channel */ struct camacio { short b; short c; short n; short a; short f; char *parm; }; /* structure of a RF io channel */ struct rfio { short branch; short cryo; short micro; short dataset; short element; long ext; }; /* structure of a Allen-Bradley io channel */ struct abio { short link; short adapter; short card; short signal; char *parm; }; /* structure of a gpib io channel */ struct gpibio { short link; short addr; /* device address */ char *parm; }; /* structure of a bitbus io channel */ struct bitbusio { unsigned char link; unsigned char node; unsigned char port; unsigned char signal; char *parm; }; /* structure of a bitbus to gpib io channel */ struct bbgpibio { unsigned char link; unsigned char bbaddr; unsigned char gpibaddr; unsigned char pad; char *parm; }; /* structure of an instrument io link */ struct instio { char *string; }; /* structure of a vxi link */ struct vxiio { short flag; /* 0 = frame/slot, 1 = SA */ short frame; short slot; short la; /* logical address if flag =1 */ short signal; char *parm; }; /* union of possible address structures */ union value { char *constantStr; /*constant string*/ struct macro_link macro_link; /* link containing macro substitution*/ struct json_link json; /* JSON-encoded link */ struct pv_link pv_link; /* link to process variable*/ struct vmeio vmeio; /* vme io point */ struct camacio camacio; /* camac io point */ struct rfio rfio; /* CEBAF RF buffer interface */ struct abio abio; /* allen-bradley io point */ struct gpibio gpibio; struct bitbusio bitbusio; struct instio instio; /* instrument io link */ struct bbgpibio bbgpibio; /* bitbus to gpib io link */ struct vxiio vxiio; /* vxi io */ }; struct lset; struct link { struct dbCommon *precord; /* Pointer to record owning link */ short type; short flags; struct lset *lset; char *text; /* Raw link text */ union value value; }; typedef struct link DBLINK; #ifdef __cplusplus } #endif #endif /* INC_link_H */ base-7.0.3.1/modules/database/src/ioc/dbStatic/recSup.h0000664000577000060420000000663113557101274021345 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* recSup.h * Record Support * Author: Marty Kraimer * Date: 6-1-90 */ #ifndef INCrecSuph #define INCrecSuph 1 #include "errMdef.h" #include "compilerDependencies.h" #ifdef __cplusplus extern "C" { #endif /* RSET definition */ /* defined elsewhere */ struct dbAddr; struct dbCommon; struct dbr_enumStrs; struct dbr_grDouble; struct dbr_ctrlDouble; struct dbr_alDouble; /* record support entry table */ struct typed_rset { long number; /* number of support routines */ long (*report)(void *precord); long (*init)(); long (*init_record)(struct dbCommon *precord, int pass); long (*process)(struct dbCommon *precord); long (*special)(struct dbAddr *paddr, int after); long (*get_value)(void); /* DEPRECATED set to NULL */ long (*cvt_dbaddr)(struct dbAddr *paddr); long (*get_array_info)(struct dbAddr *paddr, long *no_elements, long *offset); long (*put_array_info)(struct dbAddr *paddr, long nNew); long (*get_units)(struct dbAddr *paddr, char *units); long (*get_precision)(const struct dbAddr *paddr, long *precision); long (*get_enum_str)(const struct dbAddr *paddr, char *pbuffer); long (*get_enum_strs)(const struct dbAddr *paddr, struct dbr_enumStrs *p); long (*put_enum_str)(const struct dbAddr *paddr, const char *pbuffer); long (*get_graphic_double)(struct dbAddr *paddr, struct dbr_grDouble *p); long (*get_control_double)(struct dbAddr *paddr, struct dbr_ctrlDouble *p); long (*get_alarm_double)(struct dbAddr *paddr, struct dbr_alDouble *p); }; #ifdef USE_TYPED_RSET typedef struct typed_rset rset; #else /* pre-3.16 old untyped RSET definition - DEPRECATED */ typedef long (*RECSUPFUN) () EPICS_DEPRECATED; /* ptr to record support function*/ struct rset { /* record support entry table */ long number; /*number of support routines */ long (*report)(); /*print report */ long (*init)(); /*init support */ long (*init_record)(); /*init record */ long (*process)(); /*process record */ long (*special)(); /*special processing */ long (*get_value)(); /*no longer used */ long (*cvt_dbaddr)(); /*cvt dbAddr */ long (*get_array_info)(); long (*put_array_info)(); long (*get_units)(); long (*get_precision)(); long (*get_enum_str)(); /*get string from enum item*/ long (*get_enum_strs)();/*get all enum strings */ long (*put_enum_str)(); /*put string from enum item*/ long (*get_graphic_double)(); long (*get_control_double)(); long (*get_alarm_double)(); } EPICS_DEPRECATED; typedef struct rset rset EPICS_DEPRECATED; #endif #define RSETNUMBER 17 #define S_rec_noRSET (M_recSup| 1) /*Missing record support entry table*/ #define S_rec_noSizeOffset (M_recSup| 2) /*Missing SizeOffset Routine*/ #define S_rec_outMem (M_recSup| 3) /*Out of Memory*/ #ifdef __cplusplus } /* extern "C" */ #endif #endif /*INCrecSuph*/ base-7.0.3.1/modules/database/src/ioc/dbStatic/special.h0000664000577000060420000000412513557101274021520 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* special.h */ /* * Author: Marty Kraimer * Date: 6-1-90 */ #ifndef INCspecialh #define INCspecialh 1 #ifdef __cplusplus extern "C" { #endif /*NOTE Do NOT add aditional definitions with out modifying dbLexRoutines.c */ /* types 1-99 are global. Record specific must start with 100 */ #define SPC_NOMOD 1 /*Field must not be modified */ #define SPC_DBADDR 2 /*db_name_to_addr must call cvt_dbaddr */ #define SPC_SCAN 3 /*A scan related field is being changed */ #define SPC_ALARMACK 5 /*Special Alarm Acknowledgement*/ #define SPC_AS 6 /* Access Security*/ #define SPC_ATTRIBUTE 7 /* psuedo field, i.e. attribute field*/ /* useful when record support must be notified of a field changing value*/ #define SPC_MOD 100 /* used by all records that support a reset field */ #define SPC_RESET 101 /*The res field is being modified*/ /* Specific to conversion (Currently only ai */ #define SPC_LINCONV 102 /*A linear conversion field is being changed*/ /* Specific to calculation records */ #define SPC_CALC 103 /*The CALC field is being changed*/ #define SPC_NTYPES 9 typedef struct mapspcType{ char *strvalue; int value; }mapspcType; #ifndef SPECIAL_GBLSOURCE extern mapspcType pamapspcType[]; #else mapspcType pamapspcType[SPC_NTYPES] = { {"SPC_NOMOD",SPC_NOMOD}, {"SPC_DBADDR",SPC_DBADDR}, {"SPC_SCAN",SPC_SCAN}, {"SPC_ALARMACK",SPC_ALARMACK}, {"SPC_AS",SPC_AS}, {"SPC_MOD",SPC_MOD}, {"SPC_RESET",SPC_RESET}, {"SPC_LINCONV",SPC_LINCONV}, {"SPC_CALC",SPC_CALC} }; #endif /*SPECIAL_GBLSOURCE*/ #ifdef __cplusplus } #endif #endif /*INCspecialh*/ base-7.0.3.1/modules/database/src/ioc/dbtemplate/Makefile0000664000577000060420000000147413557101274021757 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. SRC_DIRS += $(IOCDIR)/dbtemplate PROD_HOST += msi msi_SRCS = msi.cpp msi_LIBS += Com HTMLS += msi.html INC += dbLoadTemplate.h INC += dbtoolsIocRegister.h dbCore_SRCS += dbLoadTemplate.c dbCore_SRCS += dbtoolsIocRegister.c CLEANS += dbLoadTemplate_lex.c dbLoadTemplate.c base-7.0.3.1/modules/database/src/ioc/dbtemplate/RULES0000664000577000060420000000125413557101274021130 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. # dbLoadTemplate_lex.c is included by dbLoadTemplate.c dbLoadTemplate.c: dbLoadTemplate_lex.c $(IOCDIR)/dbtemplate/dbLoadTemplate.h base-7.0.3.1/modules/database/src/ioc/dbtemplate/dbLoadTemplate.h0000664000577000060420000000132213557101274023341 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbLoadTemplate.h */ #ifndef INCdbLoadTemplateh #define INCdbLoadTemplateh #include "shareLib.h" epicsShareFunc int dbLoadTemplate( const char *sub_file, const char *cmd_collect); #endif /*INCdbLoadTemplateh*/ base-7.0.3.1/modules/database/src/ioc/dbtemplate/dbLoadTemplate.y0000664000577000060420000002377213557101274023377 0ustar anjaesctl%{ /*************************************************************************\ * Copyright (c) 2006 UChicago, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include #include #include "osiUnistd.h" #include "macLib.h" #include "dbmf.h" #include "epicsExport.h" #include "dbAccess.h" #include "dbLoadTemplate.h" static int line_num; static int yyerror(char* str); static char *sub_collect = NULL; static char *sub_locals; static char **vars = NULL; static char *db_file_name = NULL; static int var_count, sub_count; /* We allocate MAX_VAR_FACTOR chars in the sub_collect string for each * "variable=value," segment, and will accept at most dbTemplateMaxVars * template variables. The user can adjust that variable to increase * the number of variables or the length allocated for the buffer. */ #define MAX_VAR_FACTOR 50 int dbTemplateMaxVars = 100; epicsExportAddress(int, dbTemplateMaxVars); %} %start substitution_file %token WORD QUOTE %token DBFILE %token PATTERN %token GLOBAL %token EQUALS COMMA %left O_PAREN C_PAREN %left O_BRACE C_BRACE %union { int Int; char Char; char *Str; double Real; } %% substitution_file: global_or_template | substitution_file global_or_template ; global_or_template: global_definitions | template_substitutions ; global_definitions: GLOBAL O_BRACE C_BRACE | GLOBAL O_BRACE variable_definitions C_BRACE { #ifdef ERROR_STUFF fprintf(stderr, "global_definitions: %s\n", sub_collect+1); #endif sub_locals += strlen(sub_locals); } ; template_substitutions: template_filename O_BRACE C_BRACE { #ifdef ERROR_STUFF fprintf(stderr, "template_substitutions: %s unused\n", db_file_name); #endif dbmfFree(db_file_name); db_file_name = NULL; } | template_filename O_BRACE substitutions C_BRACE { #ifdef ERROR_STUFF fprintf(stderr, "template_substitutions: %s finished\n", db_file_name); #endif dbmfFree(db_file_name); db_file_name = NULL; } ; template_filename: DBFILE WORD { #ifdef ERROR_STUFF fprintf(stderr, "template_filename: %s\n", $2); #endif var_count = 0; db_file_name = dbmfMalloc(strlen($2)+1); strcpy(db_file_name, $2); dbmfFree($2); } | DBFILE QUOTE { #ifdef ERROR_STUFF fprintf(stderr, "template_filename: \"%s\"\n", $2); #endif var_count = 0; db_file_name = dbmfMalloc(strlen($2)+1); strcpy(db_file_name, $2); dbmfFree($2); } ; substitutions: pattern_substitutions | variable_substitutions ; pattern_substitutions: PATTERN O_BRACE C_BRACE | PATTERN O_BRACE C_BRACE pattern_definitions | PATTERN O_BRACE pattern_names C_BRACE | PATTERN O_BRACE pattern_names C_BRACE pattern_definitions ; pattern_names: pattern_name | pattern_names COMMA | pattern_names pattern_name ; pattern_name: WORD { #ifdef ERROR_STUFF fprintf(stderr, "pattern_name: [%d] = %s\n", var_count, $1); #endif if (var_count >= dbTemplateMaxVars) { fprintf(stderr, "More than dbTemplateMaxVars = %d macro variables used\n", dbTemplateMaxVars); yyerror(NULL); } else { vars[var_count] = dbmfMalloc(strlen($1)+1); strcpy(vars[var_count], $1); var_count++; dbmfFree($1); } } ; pattern_definitions: pattern_definition | pattern_definitions pattern_definition ; pattern_definition: global_definitions | O_BRACE C_BRACE { #ifdef ERROR_STUFF fprintf(stderr, "pattern_definition: pattern_values empty\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif dbLoadRecords(db_file_name, sub_collect+1); } | O_BRACE pattern_values C_BRACE { #ifdef ERROR_STUFF fprintf(stderr, "pattern_definition:\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif dbLoadRecords(db_file_name, sub_collect+1); *sub_locals = '\0'; sub_count = 0; } | WORD O_BRACE pattern_values C_BRACE { /* DEPRECATED SYNTAX */ fprintf(stderr, "dbLoadTemplate: Substitution file uses deprecated syntax.\n" " the string '%s' on line %d that comes just before the\n" " '{' character is extraneous and should be removed.\n", $1, line_num); #ifdef ERROR_STUFF fprintf(stderr, "pattern_definition:\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif dbLoadRecords(db_file_name, sub_collect+1); dbmfFree($1); *sub_locals = '\0'; sub_count = 0; } ; pattern_values: pattern_value | pattern_values COMMA | pattern_values pattern_value ; pattern_value: QUOTE { #ifdef ERROR_STUFF fprintf(stderr, "pattern_value: [%d] = \"%s\"\n", sub_count, $1); #endif if (sub_count < var_count) { strcat(sub_locals, ","); strcat(sub_locals, vars[sub_count]); strcat(sub_locals, "=\""); strcat(sub_locals, $1); strcat(sub_locals, "\""); sub_count++; } else { fprintf(stderr, "dbLoadTemplate: Too many values given, line %d.\n", line_num); } dbmfFree($1); } | WORD { #ifdef ERROR_STUFF fprintf(stderr, "pattern_value: [%d] = %s\n", sub_count, $1); #endif if (sub_count < var_count) { strcat(sub_locals, ","); strcat(sub_locals, vars[sub_count]); strcat(sub_locals, "="); strcat(sub_locals, $1); sub_count++; } else { fprintf(stderr, "dbLoadTemplate: Too many values given, line %d.\n", line_num); } dbmfFree($1); } ; variable_substitutions: variable_substitution | variable_substitutions variable_substitution ; variable_substitution: global_definitions | O_BRACE C_BRACE { #ifdef ERROR_STUFF fprintf(stderr, "variable_substitution: variable_definitions empty\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif dbLoadRecords(db_file_name, sub_collect+1); } | O_BRACE variable_definitions C_BRACE { #ifdef ERROR_STUFF fprintf(stderr, "variable_substitution:\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif dbLoadRecords(db_file_name, sub_collect+1); *sub_locals = '\0'; } | WORD O_BRACE variable_definitions C_BRACE { /* DEPRECATED SYNTAX */ fprintf(stderr, "dbLoadTemplate: Substitution file uses deprecated syntax.\n" " the string '%s' on line %d that comes just before the\n" " '{' character is extraneous and should be removed.\n", $1, line_num); #ifdef ERROR_STUFF fprintf(stderr, "variable_substitution:\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif dbLoadRecords(db_file_name, sub_collect+1); dbmfFree($1); *sub_locals = '\0'; } ; variable_definitions: variable_definition | variable_definitions COMMA | variable_definitions variable_definition ; variable_definition: WORD EQUALS WORD { #ifdef ERROR_STUFF fprintf(stderr, "variable_definition: %s = %s\n", $1, $3); #endif strcat(sub_locals, ","); strcat(sub_locals, $1); strcat(sub_locals, "="); strcat(sub_locals, $3); dbmfFree($1); dbmfFree($3); } | WORD EQUALS QUOTE { #ifdef ERROR_STUFF fprintf(stderr, "variable_definition: %s = \"%s\"\n", $1, $3); #endif strcat(sub_locals, ","); strcat(sub_locals, $1); strcat(sub_locals, "=\""); strcat(sub_locals, $3); strcat(sub_locals, "\""); dbmfFree($1); dbmfFree($3); } ; %% #include "dbLoadTemplate_lex.c" static int yyerror(char* str) { if (str) fprintf(stderr, "Substitution file error: %s\n", str); else fprintf(stderr, "Substitution file error.\n"); fprintf(stderr, "line %d: '%s'\n", line_num, yytext); return 0; } static int is_not_inited = 1; int dbLoadTemplate(const char *sub_file, const char *cmd_collect) { FILE *fp; int i; line_num = 1; if (!sub_file || !*sub_file) { fprintf(stderr, "must specify variable substitution file\n"); return -1; } if (dbTemplateMaxVars < 1) { fprintf(stderr,"Error: dbTemplateMaxVars = %d, must be +ve\n", dbTemplateMaxVars); return -1; } fp = fopen(sub_file, "r"); if (!fp) { fprintf(stderr, "dbLoadTemplate: error opening sub file %s\n", sub_file); return -1; } vars = malloc(dbTemplateMaxVars * sizeof(char*)); sub_collect = malloc(dbTemplateMaxVars * MAX_VAR_FACTOR); if (!vars || !sub_collect) { free(vars); free(sub_collect); fclose(fp); fprintf(stderr, "dbLoadTemplate: Out of memory!\n"); return -1; } strcpy(sub_collect, ","); if (cmd_collect && *cmd_collect) { strcat(sub_collect, cmd_collect); sub_locals = sub_collect + strlen(sub_collect); } else { sub_locals = sub_collect; *sub_locals = '\0'; } var_count = 0; sub_count = 0; if (is_not_inited) { yyin = fp; is_not_inited = 0; } else { yyrestart(fp); } yyparse(); for (i = 0; i < var_count; i++) { dbmfFree(vars[i]); } free(vars); free(sub_collect); vars = NULL; fclose(fp); if (db_file_name) { dbmfFree(db_file_name); db_file_name = NULL; } return 0; } base-7.0.3.1/modules/database/src/ioc/dbtemplate/dbLoadTemplate_lex.l0000664000577000060420000000262213557101274024221 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ newline "\n" backslash "\\" doublequote "\"" singlequote "'" comment "#" whitespace [ \t\r] escape {backslash}. dstringchar [^"\n\\] sstringchar [^'\n\\] bareword [a-zA-Z0-9_\-+:./\\\[\]<>;] %% "pattern" { return(PATTERN); } "file" { return(DBFILE); } "global" { return(GLOBAL); } {doublequote}({dstringchar}|{escape})*{doublequote} | {singlequote}({sstringchar}|{escape})*{singlequote} { yylval.Str = dbmfStrdup((char *) yytext+1); yylval.Str[strlen(yylval.Str)-1] = '\0'; return(QUOTE); } {bareword}+ { yylval.Str = dbmfStrdup((char *) yytext); return(WORD); } "=" { return(EQUALS); } "," { return(COMMA); } "{" { return(O_BRACE); } "}" { return(C_BRACE); } {comment}.* ; {whitespace} ; {newline} { line_num++; } . { char message[40]; sprintf(message, "invalid character '%c'", yytext[0]); yyerror(message); /* Suppress compiler warning messages */ if (0) yyunput('c',NULL); if (0) yy_switch_to_buffer(NULL); } %% base-7.0.3.1/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c0000664000577000060420000000210613557101274024262 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "iocsh.h" #define epicsExportSharedSymbols #include "dbtoolsIocRegister.h" #include "dbLoadTemplate.h" /* dbLoadTemplate */ static const iocshArg dbLoadTemplateArg0 = {"filename", iocshArgString}; static const iocshArg dbLoadTemplateArg1 = {"var=value", iocshArgString}; static const iocshArg * const dbLoadTemplateArgs[2] = { &dbLoadTemplateArg0, &dbLoadTemplateArg1 }; static const iocshFuncDef dbLoadTemplateFuncDef = {"dbLoadTemplate", 2, dbLoadTemplateArgs}; static void dbLoadTemplateCallFunc(const iocshArgBuf *args) { iocshSetError(dbLoadTemplate(args[0].sval, args[1].sval)); } void dbtoolsIocRegister(void) { iocshRegister(&dbLoadTemplateFuncDef, dbLoadTemplateCallFunc); } base-7.0.3.1/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.h0000664000577000060420000000116013557101274024266 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_dbtoolsIocRegister_H #define INC_dbtoolsIocRegister_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void dbtoolsIocRegister(void); #ifdef __cplusplus } #endif #endif /* INC_dbtoolsIocRegister_H */ base-7.0.3.1/modules/database/src/ioc/dbtemplate/msi.cpp0000664000577000060420000007271013557101274021614 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS Base is distributed subject to a Software License Agreement found * in the file LICENSE that is included with this distribution. \*************************************************************************/ /* msi - macro substitutions and include */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_BUFFER_SIZE 4096 #define MAX_DEPS 1024 #if 0 /* Debug Tracing */ int din = 0; #define ENTER fprintf(stderr, "%*sEntering %s\n", 2*din++, "", __FUNCTION__) #define STEP(s) fprintf(stderr, "%*s%s: %s\n", 2*din, "", __FUNCTION__, s) #define STEPS(s, v) fprintf(stderr, "%*s%s: %s '%s'\n", 2*din, "", __FUNCTION__, s, v) #define STEPD(s, v) fprintf(stderr, "%*s%s: %s %d\n", 2*din, "", __FUNCTION__, s, v) #define EXIT fprintf(stderr, "%*s%s: Returning\n", 2*din--, "", __FUNCTION__) #define EXITD(r) fprintf(stderr, "%*s%s: Returning %d\n", 2*din--, "", __FUNCTION__, r) #define EXITS(r) fprintf(stderr, "%*s%s: Returning '%s'\n", 2*din--, "", __FUNCTION__, r) #else #define ENTER #define STEP(s) #define STEPS(s, v) #define STEPD(s, v) #define EXIT #define EXITD(r) #define EXITS(r) #endif /* Module to read the template files */ typedef struct inputData inputData; 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 char *inputNextLine(inputData * const pvt); static void inputNewIncludeFile(inputData * const pvt, const char * const name); static void inputErrPrint(const inputData * const pvt); /* Module to read the substitution file */ typedef struct subInfo subInfo; static void substituteOpen(subInfo **ppvt, const std::string& substitutionName); static void substituteDestruct(subInfo * const pvt); static bool substituteGetNextSet(subInfo * const pvt, char **filename); static bool substituteGetGlobalSet(subInfo * const pvt); static const char *substituteGetReplacements(subInfo * const pvt); static const char *substituteGetGlobalReplacements(subInfo * const pvt); /* Forward references to local routines */ static void usageExit(const int status); static void abortExit(const int status); 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); /*Global variables */ static int opt_V = 0; static bool opt_D = false; static char *outFile = 0; static int numDeps = 0, depHashes[MAX_DEPS]; int main(int argc,char **argv) { inputData *inputPvt; MAC_HANDLE *macPvt; char *pval; std::string substitutionName; char *templateName = 0; bool localScope = true; inputConstruct(&inputPvt); macCreateHandle(&macPvt, 0); while ((argc > 1) && (argv[1][0] == '-')) { int narg = (strlen(argv[1]) == 2) ? 2 : 1; pval = (narg == 1) ? (argv[1] + 2) : argv[2]; if (strncmp(argv[1], "-I", 2) == 0) { inputAddPath(inputPvt, pval); } else if (strcmp(argv[1], "-D") == 0) { opt_D = true; narg = 1; /* no argument for this option */ } else if(strncmp(argv[1], "-o", 2) == 0) { outFile = epicsStrDup(pval); } else if(strncmp(argv[1], "-M", 2) == 0) { addMacroReplacements(macPvt, pval); } else if(strncmp(argv[1], "-S", 2) == 0) { substitutionName = pval; } else if (strcmp(argv[1], "-V") == 0) { opt_V = 1; narg = 1; /* no argument for this option */ } else if (strcmp(argv[1], "-g") == 0) { localScope = false; narg = 1; /* no argument for this option */ } else if (strcmp(argv[1], "-h") == 0) { usageExit(0); } else { fprintf(stderr, "msi: Bad argument \"%s\"\n", argv[1]); usageExit(1); } argc -= narg; for (int i = 1; i < argc; i++) argv[i] = argv[i + narg]; } if (!opt_V) macSuppressWarning(macPvt, 1); if (argc > 2) { fprintf(stderr, "msi: Too many arguments\n"); usageExit(1); } if (opt_D) { if (!outFile) { fprintf(stderr, "msi: Option -D requires -o for Makefile target\n"); exit(1); } printf("%s:", outFile); } else if (outFile && freopen(outFile, "w", stdout) == NULL) { fprintf(stderr, "msi: Can't open %s for writing: %s\n", outFile, strerror(errno)); exit(1); } if (argc == 2) templateName = epicsStrDup(argv[1]); if (substitutionName.empty()) { STEP("Single template+substitutions file"); makeSubstitutions(inputPvt, macPvt, templateName); } else { subInfo *substitutePvt; char *filename = 0; bool isGlobal, isFile; STEPS("Substitutions from file", substitutionName.c_str()); substituteOpen(&substitutePvt, substitutionName); do { isGlobal = substituteGetGlobalSet(substitutePvt); if (isGlobal) { STEP("Handling global macros"); const char *macStr = substituteGetGlobalReplacements(substitutePvt); if (macStr) addMacroReplacements(macPvt, macStr); } else if ((isFile = substituteGetNextSet(substitutePvt, &filename))) { if (templateName) filename = templateName; if (!filename) { fprintf(stderr, "msi: No template file\n"); usageExit(1); } STEPS("Handling template file", filename); const char *macStr; while ((macStr = substituteGetReplacements(substitutePvt))) { if (localScope) macPushScope(macPvt); addMacroReplacements(macPvt, macStr); makeSubstitutions(inputPvt, macPvt, filename); if (localScope) macPopScope(macPvt); } } } while (isGlobal || isFile); substituteDestruct(substitutePvt); } macDeleteHandle(macPvt); errlogFlush(); // macLib calls errlogPrintf() inputDestruct(inputPvt); if (opt_D) { printf("\n"); } fflush(stdout); free(templateName); return opt_V & 2; } void usageExit(const int status) { fprintf(stderr, "Usage: msi [options] [template]\n" " stdin is used if neither template nor substitution file is given\n" " options:\n" " -h Print this help message\n" " -D Output file dependencies, not substitutions\n" " -V Undefined macros generate an error\n" " -g All macros have global scope\n" " -o Send output to \n" " -I

Add to include file search path\n" " -M Add to (global) macro definitions\n" " ( takes the form VAR=VALUE,...)\n" " -S Expand the substitutions in FILE\n"); exit(status); } void abortExit(const int status) { if (outFile) { fclose(stdout); unlink(outFile); } exit(status); } static void addMacroReplacements(MAC_HANDLE * const macPvt, const char * const pval) { char **pairs; long status; status = macParseDefns(macPvt, pval, &pairs); if (status == -1) { fprintf(stderr, "msi: Error from macParseDefns\n"); usageExit(1); } if (status) { status = macInstallMacros(macPvt, pairs); if (!status) { fprintf(stderr, "Error from macInstallMacros\n"); usageExit(1); } free(pairs); } } typedef enum {cmdInclude,cmdSubstitute} cmdType; static const char *cmdNames[] = {"include","substitute"}; static void makeSubstitutions(inputData * const inputPvt, MAC_HANDLE * const macPvt, const char * const templateName) { char *input; static char buffer[MAX_BUFFER_SIZE]; int n; ENTER; inputBegin(inputPvt, templateName); while ((input = inputNextLine(inputPvt))) { int expand=1; char *p; char *command = 0; p = input; /*skip whitespace at beginning of line*/ while (*p && (isspace((int) *p))) ++p; /*Look for i or s */ if (*p && (*p=='i' || *p=='s')) command = p; if (command) { char *pstart; char *pend; int cmdind=-1; int i; for (i = 0; i < NELEMENTS(cmdNames); i++) { if (strstr(command, cmdNames[i])) { cmdind = i; } } if (cmdind < 0) goto endcmd; p = command + strlen(cmdNames[cmdind]); /*skip whitespace after command*/ while (*p && (isspace((int) *p))) ++p; /*Next character must be quote*/ if ((*p == 0) || (*p != '"')) goto endcmd; pstart = ++p; /*Look for end quote*/ while (*p && (*p != '"')) { /*allow escape for embeded quote*/ if ((p[0] == '\\') && p[1] == '"') { p += 2; continue; } else { if (*p == '"') break; } ++p; } pend = p; if (*p == 0) goto endcmd; /*skip quote and any trailing blanks*/ while (*++p == ' ') ; if (*p != '\n' && *p != 0) goto endcmd; std::string copy = std::string(pstart, pend); switch(cmdind) { case cmdInclude: inputNewIncludeFile(inputPvt, copy.c_str()); break; case cmdSubstitute: addMacroReplacements(macPvt, copy.c_str()); break; default: fprintf(stderr, "msi: Logic error in makeSubstitutions\n"); inputErrPrint(inputPvt); abortExit(1); } expand = 0; } endcmd: if (expand && !opt_D) { STEP("Expanding to output stream"); n = macExpandString(macPvt, input, buffer, MAX_BUFFER_SIZE - 1); fputs(buffer, stdout); if (opt_V == 1 && n < 0) { fprintf(stderr, "msi: Error - undefined macros present\n"); opt_V++; } } } EXIT; } typedef struct inputFile { std::string filename; FILE *fp; int lineNum; } inputFile; struct inputData { std::list inputFileList; std::list pathList; char inputBuffer[MAX_BUFFER_SIZE]; inputData() { memset(inputBuffer, 0, sizeof(inputBuffer) * sizeof(inputBuffer[0])); }; }; static void inputOpenFile(inputData *pinputData, const char * const filename); static void inputCloseFile(inputData *pinputData); static void inputCloseAllFiles(inputData *pinputData); static void inputConstruct(inputData **ppvt) { *ppvt = new inputData; } static void inputDestruct(inputData * const pinputData) { inputCloseAllFiles(pinputData); delete(pinputData); } static void inputAddPath(inputData * const pinputData, const char * const path) { const char *pcolon; const char *pdir; size_t len; const char sep = *OSI_PATH_LIST_SEPARATOR; ENTER; pdir = path; /*an empty name at beginning, middle, or end means current directory*/ while (pdir && *pdir) { bool emptyName = (*pdir == sep); if (emptyName) ++pdir; std::string directory; if (!emptyName) { pcolon = strchr(pdir, sep); len = (pcolon ? (pcolon - pdir) : strlen(pdir)); if (len > 0) { directory = std::string(pdir, len); pdir = pcolon; /*unless at end skip past first colon*/ if (pdir && *(pdir + 1) != 0) ++pdir; } else { /*must have been trailing : */ emptyName = true; } } if (emptyName) { directory = "."; } pinputData->pathList.push_back(directory); } EXIT; } static void inputBegin(inputData * const pinputData, const char * const fileName) { ENTER; inputCloseAllFiles(pinputData); inputOpenFile(pinputData, fileName); EXIT; } static char *inputNextLine(inputData * const pinputData) { std::list& inFileList = pinputData->inputFileList; ENTER; while (!inFileList.empty()) { inputFile& inFile = inFileList.front(); char *pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, inFile.fp); if (pline) { ++inFile.lineNum; EXITS(pline); return pline; } inputCloseFile(pinputData); } EXITD(0); return 0; } static void inputNewIncludeFile(inputData * const pinputData, const char * const name) { ENTER; inputOpenFile(pinputData,name); EXIT; } static void inputErrPrint(const inputData *const pinputData) { ENTER; fprintf(stderr, "input: '%s' at ", pinputData->inputBuffer); const std::list& inFileList = pinputData->inputFileList; std::list::const_iterator inFileIt = inFileList.begin(); while (inFileIt != inFileList.end()) { fprintf(stderr, "line %d of ", inFileIt->lineNum); if (!inFileIt->filename.empty()) { fprintf(stderr, " file %s\n", inFileIt->filename.c_str()); } else { fprintf(stderr, "stdin:\n"); } if (++inFileIt != inFileList.end()) { fprintf(stderr, " included from "); } else { fprintf(stderr,"\n"); } } fprintf(stderr,"\n"); EXIT; } static void inputOpenFile(inputData *pinputData, const char * const filename) { std::list& pathList = pinputData->pathList; std::list::iterator pathIt = pathList.end(); std::string fullname; FILE *fp = 0; ENTER; if (!filename) { STEP("Using stdin"); fp = stdin; } else if (pathList.empty() || strchr(filename, '/')){ STEPS("Opening ", filename); fp = fopen(filename, "r"); } else { pathIt = pathList.begin(); while(pathIt != pathList.end()) { fullname = *pathIt + "/" + filename; STEPS("Trying", filename); fp = fopen(fullname.c_str(), "r"); if (fp) break; ++pathIt; } } if (!fp) { fprintf(stderr, "msi: Can't open file '%s'\n", filename); inputErrPrint(pinputData); abortExit(1); } STEP("File opened"); inputFile inFile = inputFile(); if (pathIt != pathList.end()) { inFile.filename = fullname; } else if (filename) { inFile.filename = filename; } else { inFile.filename = "stdin"; } if (opt_D) { int hash = epicsStrHash(inFile.filename.c_str(), 12345); int i = 0; int match = 0; while (i < numDeps) { if (hash == depHashes[i++]) { match = 1; break; } } if (!match) { const char *wrap = numDeps ? " \\\n" : ""; printf("%s %s", wrap, inFile.filename.c_str()); if (numDeps < MAX_DEPS) { depHashes[numDeps++] = hash; } else { fprintf(stderr, "msi: More than %d dependencies!\n", MAX_DEPS); depHashes[0] = hash; } } } inFile.fp = fp; pinputData->inputFileList.push_front(inFile); EXIT; } static void inputCloseFile(inputData *pinputData) { std::list& inFileList = pinputData->inputFileList; ENTER; if(!inFileList.empty()) { inputFile& inFile = inFileList.front(); if (fclose(inFile.fp)) fprintf(stderr, "msi: Can't close input file '%s'\n", inFile.filename.c_str()); inFileList.erase(inFileList.begin()); } EXIT; } static void inputCloseAllFiles(inputData *pinputData) { ENTER; const std::list& inFileList = pinputData->inputFileList; while(!inFileList.empty()) { inputCloseFile(pinputData); } EXIT; } /*start of code that handles substitution file*/ typedef enum { tokenLBrace, tokenRBrace, tokenSeparator, tokenString, tokenEOF } tokenType; typedef struct subFile { std::string substitutionName; FILE *fp; int lineNum; char inputBuffer[MAX_BUFFER_SIZE]; char *pnextChar; tokenType token; char string[MAX_BUFFER_SIZE]; } subFile; struct subInfo { subFile *psubFile; bool isFile; char *filename; bool isPattern; std::list patternList; std::string macroReplacements; subInfo() : psubFile(NULL), isFile(false), filename(NULL), isPattern(false) {}; }; static char *subGetNextLine(subFile *psubFile); static tokenType subGetNextToken(subFile *psubFile); static void subFileErrPrint(subFile *psubFile, const char * message); static void freeSubFile(subInfo *psubInfo); static void freePattern(subInfo *psubInfo); static void catMacroReplacements(subInfo *psubInfo,const char *value); void freeSubFile(subInfo *psubInfo) { subFile *psubFile = psubInfo->psubFile; ENTER; if (psubFile->fp) { if (fclose(psubFile->fp)) fprintf(stderr, "msi: Can't close substitution file\n"); } delete(psubFile); free(psubInfo->filename); psubInfo->psubFile = 0; EXIT; } void freePattern(subInfo *psubInfo) { ENTER; psubInfo->patternList.clear(); psubInfo->isPattern = false; EXIT; } static void substituteDestruct(subInfo * const psubInfo) { ENTER; freeSubFile(psubInfo); freePattern(psubInfo); delete(psubInfo); EXIT; } static void substituteOpen(subInfo **ppvt, const std::string& substitutionName) { subInfo *psubInfo; subFile *psubFile; FILE *fp; ENTER; psubInfo = new subInfo; *ppvt = psubInfo; psubFile = new subFile; psubInfo->psubFile = psubFile; fp = fopen(substitutionName.c_str(), "r"); if (!fp) { fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName.c_str()); abortExit(1); } psubFile->substitutionName = substitutionName; psubFile->fp = fp; psubFile->lineNum = 1; psubFile->inputBuffer[0] = 0; psubFile->pnextChar = &psubFile->inputBuffer[0]; subGetNextToken(psubFile); EXIT; } static bool substituteGetGlobalSet(subInfo * const psubInfo) { subFile *psubFile = psubInfo->psubFile; ENTER; while (psubFile->token == tokenSeparator) subGetNextToken(psubFile); if (psubFile->token == tokenString && strcmp(psubFile->string, "global") == 0) { subGetNextToken(psubFile); EXITD(1); return true; } EXITD(0); return false; } static bool substituteGetNextSet(subInfo * const psubInfo,char **filename) { subFile *psubFile = psubInfo->psubFile; ENTER; *filename = 0; while (psubFile->token == tokenSeparator) subGetNextToken(psubFile); if (psubFile->token == tokenEOF) { EXITD(0); return false; } if (psubFile->token == tokenString && strcmp(psubFile->string, "file") == 0) { size_t len; STEP("Parsed 'file'"); psubInfo->isFile = true; if (subGetNextToken(psubFile) != tokenString) { subFileErrPrint(psubFile, "Parse error, expecting a filename"); abortExit(1); } freePattern(psubInfo); free(psubInfo->filename); len = strlen(psubFile->string); if (psubFile->string[0] == '"' && psubFile->string[len - 1] == '"') { psubFile->string[len - 1] = '\0'; psubInfo->filename = macEnvExpand(psubFile->string + 1); } else psubInfo->filename = macEnvExpand(psubFile->string); STEPS("Parsed filename", psubInfo->filename); while (subGetNextToken(psubFile) == tokenSeparator); if (psubFile->token != tokenLBrace) { subFileErrPrint(psubFile, "Parse error, expecting '{'"); abortExit(1); } STEP("Parsed '{'"); subGetNextToken(psubFile); } *filename = psubInfo->filename; while (psubFile->token == tokenSeparator) subGetNextToken(psubFile); if (psubFile->token == tokenLBrace) { EXITD(1); return true; } if (psubFile->token == tokenRBrace) { subFileErrPrint(psubFile, "Parse error, unexpected '}'"); abortExit(1); } if (psubFile->token != tokenString || strcmp(psubFile->string, "pattern") != 0) { subFileErrPrint(psubFile, "Parse error, expecting 'pattern'"); abortExit(1); } STEP("Parsed 'pattern'"); freePattern(psubInfo); psubInfo->isPattern = true; while (subGetNextToken(psubFile) == tokenSeparator); if (psubFile->token != tokenLBrace) { subFileErrPrint(psubFile, "Parse error, expecting '{'"); abortExit(1); } STEP("Parsed '{'"); while (true) { while (subGetNextToken(psubFile) == tokenSeparator); if (psubFile->token != tokenString) break; psubInfo->patternList.push_back(psubFile->string); } if (psubFile->token != tokenRBrace) { subFileErrPrint(psubFile, "Parse error, expecting '}'"); abortExit(1); } subGetNextToken(psubFile); EXITD(1); return true; } static const char *substituteGetGlobalReplacements(subInfo * const psubInfo) { subFile *psubFile = psubInfo->psubFile; ENTER; psubInfo->macroReplacements.clear(); while (psubFile->token == tokenSeparator) subGetNextToken(psubFile); if (psubFile->token == tokenRBrace && psubInfo->isFile) { psubInfo->isFile = false; free(psubInfo->filename); psubInfo->filename = 0; freePattern(psubInfo); subGetNextToken(psubFile); EXITD(0); return 0; } if (psubFile->token == tokenEOF) { EXITD(0); return 0; } if (psubFile->token != tokenLBrace) { EXITD(0); return 0; } while (true) { switch(subGetNextToken(psubFile)) { case tokenRBrace: subGetNextToken(psubFile); EXITS(psubInfo->macroReplacements.c_str()); return psubInfo->macroReplacements.c_str(); case tokenSeparator: catMacroReplacements(psubInfo, ","); break; case tokenString: catMacroReplacements(psubInfo, psubFile->string); break; case tokenLBrace: subFileErrPrint(psubFile, "Parse error, unexpected '{'"); abortExit(1); case tokenEOF: subFileErrPrint(psubFile, "Parse error, incomplete file?"); abortExit(1); } } } static const char *substituteGetReplacements(subInfo * const psubInfo) { subFile *psubFile = psubInfo->psubFile; ENTER; psubInfo->macroReplacements.clear(); while (psubFile->token == tokenSeparator) subGetNextToken(psubFile); if (psubFile->token==tokenRBrace && psubInfo->isFile) { psubInfo->isFile = false; free(psubInfo->filename); psubInfo->filename = 0; freePattern(psubInfo); subGetNextToken(psubFile); EXITD(0); return 0; } if (psubFile->token == tokenEOF) { EXITD(0); return 0; } if (psubFile->token != tokenLBrace) { EXITD(0); return 0; } if (psubInfo->isPattern) { bool gotFirstPattern = false; while (subGetNextToken(psubFile) == tokenSeparator); std::list& patternList = psubInfo->patternList; std::list::iterator patternIt = patternList.begin(); while (true) { if (psubFile->token == tokenRBrace) { subGetNextToken(psubFile); EXITS(psubInfo->macroReplacements.c_str()); return psubInfo->macroReplacements.c_str(); } if (psubFile->token != tokenString) { subFileErrPrint(psubFile,"Parse error, expecting macro value"); abortExit(1); } if (gotFirstPattern) catMacroReplacements(psubInfo, ","); gotFirstPattern = true; if (patternIt != patternList.end()) { catMacroReplacements(psubInfo, patternIt->c_str()); catMacroReplacements(psubInfo, "="); catMacroReplacements(psubInfo, psubFile->string); ++patternIt; } else { subFileErrPrint(psubFile, "Warning, too many values given"); } while (subGetNextToken(psubFile) == tokenSeparator); } } else while(true) { switch(subGetNextToken(psubFile)) { case tokenRBrace: subGetNextToken(psubFile); EXITS(psubInfo->macroReplacements.c_str()); return psubInfo->macroReplacements.c_str(); case tokenSeparator: catMacroReplacements(psubInfo, ","); break; case tokenString: catMacroReplacements(psubInfo, psubFile->string); break; case tokenLBrace: subFileErrPrint(psubFile, "Parse error, unexpected '{'"); abortExit(1); case tokenEOF: subFileErrPrint(psubFile, "Parse error, incomplete file?"); abortExit(1); } } } static char *subGetNextLine(subFile *psubFile) { char *pline; ENTER; do { pline = fgets(psubFile->inputBuffer, MAX_BUFFER_SIZE, psubFile->fp); ++psubFile->lineNum; } while (pline && psubFile->inputBuffer[0] == '#'); if (!pline) { psubFile->token = tokenEOF; psubFile->inputBuffer[0] = 0; psubFile->pnextChar = 0; EXITD(0); return 0; } psubFile->pnextChar = &psubFile->inputBuffer[0]; EXITS(&psubFile->inputBuffer[0]); return &psubFile->inputBuffer[0]; } static void subFileErrPrint(subFile *psubFile, const char * message) { fprintf(stderr, "msi: %s\n",message); fprintf(stderr, " in substitution file '%s' at line %d:\n %s", psubFile->substitutionName.c_str(), psubFile->lineNum, psubFile->inputBuffer); } static tokenType subGetNextToken(subFile *psubFile) { char *p, *pto; ENTER; p = psubFile->pnextChar; if (!p) { STEP("Got EOF"); psubFile->token = tokenEOF; goto done; } if (*p == 0 || *p == '\n' || *p == '#') { STEP("Got newline/comment"); p = subGetNextLine(psubFile); psubFile->token = p ? tokenSeparator : tokenEOF; goto done; } while (isspace((int) *p)) p++; if (*p == '{') { STEP("Got '{'"); psubFile->token = tokenLBrace; psubFile->pnextChar = ++p; goto done; } if (*p == '}') { STEP("Got '}'"); psubFile->token = tokenRBrace; psubFile->pnextChar = ++p; goto done; } if (*p == 0 || isspace((int) *p) || *p == ',') { STEP("Got space/comma"); while (isspace((int) *p) || *p == ',') p++; psubFile->token = tokenSeparator; psubFile->pnextChar = p; goto done; } /*now handle quoted strings*/ if (*p == '"') { STEP("Got '\"'"); pto = &psubFile->string[0]; *pto++ = *p++; while (*p != '"') { if (*p == 0 || *p == '\n') { subFileErrPrint(psubFile, "Strings must be on single line\n"); abortExit(1); } /*allow escape for embeded quote*/ if ((p[0] == '\\') && p[1] == '"') { *pto++ = *p++; *pto++ = *p++; continue; } *pto++ = *p++; } *pto++ = *p++; psubFile->pnextChar = p; *pto = 0; psubFile->token = tokenString; goto done; } /*Now take anything up to next non String token and not space*/ pto = &psubFile->string[0]; while (!isspace((int) *p) && (strspn(p, "\",{}") == 0)) *pto++ = *p++; *pto = 0; STEPS("Got bareword", psubFile->string); psubFile->pnextChar = p; psubFile->token = tokenString; done: EXITD(psubFile->token); return psubFile->token; } static void catMacroReplacements(subInfo *psubInfo, const char *value) { ENTER; STEPS("Appending", value); psubInfo->macroReplacements += value; EXIT; } base-7.0.3.1/modules/database/src/ioc/dbtemplate/msi.html0000664000577000060420000003311213557101274021767 0ustar anjaesctl

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. . (the current directory)
  3. .. (the parent of the current directory)
-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
base-7.0.3.1/modules/database/src/ioc/misc/Makefile0000664000577000060420000000164013557101274020564 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. SRC_DIRS += $(IOCDIR)/misc DBD += system.dbd DBD += dlload.dbd DBD += dbCore.dbd INC += epicsRelease.h INC += iocInit.h INC += miscIocRegister.h INC += iocshRegisterCommon.h dbCore_SRCS += epicsRelease.c dbCore_SRCS += iocInit.c dbCore_SRCS += miscIocRegister.c dbCore_SRCS += dlload.c dbCore_SRCS += iocshRegisterCommon.c miscIocRegister_CFLAGS_iOS = -DSYSTEM_UNAVAILABLE base-7.0.3.1/modules/database/src/ioc/misc/dbCore.dbd0000664000577000060420000000166213557101274021001 0ustar anjaesctl# dbCore.dbd # # This file provides iocsh access to variables that control some lesser-used # and debugging features of the IOC database code. # show epicsAtExit callbacks as they are run variable(atExitDebug,int) # Access security subroutines variable(asCaDebug,int) # CA server debug flag (very verbose) range[0,5] variable(CASDEBUG,int) # Link parsing debug variable(dbJLinkDebug,int) # Static database access variables variable(dbRecordsOnceOnly,int) variable(dbRecordsAbcSorted,int) variable(dbBptNotMonotonic,int) variable(dbQuietMacroWarnings,int) variable(dbConvertStrict,int) # PUTF/RPRO tracing; set TPRO on records to trace variable(dbAccessDebugPUTF,int) # dbLoadTemplate settings variable(dbTemplateMaxVars,int) # Default number of parallel callback threads variable(callbackParallelThreadsDefault,int) # Real-time operation variable(dbThreadRealtimeLock,int) # show logClient network activity variable(logClientDebug,int) base-7.0.3.1/modules/database/src/ioc/misc/dlload.c0000664000577000060420000000200213557101274020520 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "epicsFindSymbol.h" #include "iocsh.h" #include "epicsExport.h" IOCSH_STATIC_FUNC void dlload(const char* name) { if (!epicsLoadLibrary(name)) { printf("epicsLoadLibrary failed: %s\n", epicsLoadError()); } } static const iocshArg dlloadArg0 = { "path/library.so", iocshArgString}; static const iocshArg * const dlloadArgs[] = {&dlloadArg0}; static const iocshFuncDef dlloadFuncDef = {"dlload", 1, dlloadArgs}; static void dlloadCallFunc(const iocshArgBuf *args) { dlload(args[0].sval); } static void dlloadRegistar(void) { iocshRegister(&dlloadFuncDef, dlloadCallFunc); } epicsExportRegistrar(dlloadRegistar); base-7.0.3.1/modules/database/src/ioc/misc/dlload.dbd0000664000577000060420000000014113557101274021031 0ustar anjaesctl# Including this DBD file adds the 'dlload' command to the IOC shell. registrar(dlloadRegistar) base-7.0.3.1/modules/database/src/ioc/misc/epicsRelease.c0000664000577000060420000000201313557101274021667 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include "compilerDependencies.h" #include "epicsStdio.h" #include "epicsVersion.h" #define epicsExportSharedSymbols #include "epicsRelease.h" epicsShareFunc int coreRelease(void) { printf ( "############################################################################\n" ); printf ( "## %s\n", epicsReleaseVersion ); printf ( "## %s\n", "EPICS Base built " __DATE__ ); printf ( "############################################################################\n" ); return 0; } base-7.0.3.1/modules/database/src/ioc/misc/epicsRelease.h0000664000577000060420000000137513557101274021706 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* epicsRelease.h */ #ifndef INCepicsReleaseh #define INCepicsReleaseh #ifdef __cplusplus extern "C" { #endif #include "shareLib.h" epicsShareFunc int coreRelease(void); #ifdef __cplusplus } #endif #endif /*INCepicsReleaseh*/ base-7.0.3.1/modules/database/src/ioc/misc/iocInit.c0000664000577000060420000004572513557101274020702 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * Copyright (c) 2013 Helmholtz-Zentrum Berlin * für Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Author: Marty Kraimer * Date: 06-01-91 */ #include #include #include #include #include #include #include #include "dbDefs.h" #include "ellLib.h" #include "envDefs.h" #include "epicsExit.h" #include "epicsGeneralTime.h" #include "epicsPrint.h" #include "epicsSignal.h" #include "epicsThread.h" #include "errMdef.h" #include "iocsh.h" #include "taskwd.h" #include "caeventmask.h" #include "epicsExport.h" /* defines epicsExportSharedSymbols */ #include "alarm.h" #include "asDbLib.h" #include "callback.h" #include "dbAccess.h" #include "db_access_routines.h" #include "dbAddr.h" #include "dbBase.h" #include "dbBkpt.h" #include "dbCa.h" #include "dbChannel.h" #include "dbCommon.h" #include "dbFldTypes.h" #include "dbLock.h" #include "dbNotify.h" #include "dbScan.h" #include "dbServer.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "devSup.h" #include "drvSup.h" #include "epicsRelease.h" #include "initHooks.h" #include "iocInit.h" #include "link.h" #include "menuConvert.h" #include "menuPini.h" #include "recGbl.h" #include "recSup.h" #include "registryDeviceSupport.h" #include "registryDriverSupport.h" #include "registryJLinks.h" #include "registryRecordType.h" static enum { iocVirgin, iocBuilding, iocBuilt, iocRunning, iocPaused, iocStopped } iocState = iocVirgin; static enum { buildServers, buildIsolated } iocBuildMode; /* define forward references*/ static int checkDatabase(dbBase *pdbbase); static void checkGeneralTime(void); static void initDrvSup(void); static void initRecSup(void); static void initDevSup(void); static void finishDevSup(void); static void initDatabase(void); static void initialProcess(void); static void exitDatabase(void *dummy); /* * Iterate through all record instances (but not aliases), * calling a function for each one. */ typedef void (*recIterFunc)(dbRecordType *rtyp, dbCommon *prec, void *user); static void iterateRecords(recIterFunc func, void *user); int dbThreadRealtimeLock = 1; epicsExportAddress(int, dbThreadRealtimeLock); /* * Initialize EPICS on the IOC. */ int iocInit(void) { return iocBuild() || iocRun(); } static int iocBuild_1(void) { if (iocState != iocVirgin && iocState != iocStopped) { errlogPrintf("iocBuild: IOC can only be initialized from uninitialized or stopped state\n"); return -1; } errlogInit(0); initHookAnnounce(initHookAtIocBuild); if (!epicsThreadIsOkToBlock()) { epicsThreadSetOkToBlock(1); } errlogPrintf("Starting iocInit\n"); if (checkDatabase(pdbbase)) { errlogPrintf("iocBuild: Aborting, bad database definition (DBD)!\n"); return -1; } epicsSignalInstallSigHupIgnore(); initHookAnnounce(initHookAtBeginning); coreRelease(); iocState = iocBuilding; checkGeneralTime(); taskwdInit(); callbackInit(); initHookAnnounce(initHookAfterCallbackInit); return 0; } static void prepareLinks(dbRecordType *rtyp, dbCommon *prec, void *junk) { dbInitRecordLinks(rtyp, prec); } static int iocBuild_2(void) { initHookAnnounce(initHookAfterCaLinkInit); initDrvSup(); initHookAnnounce(initHookAfterInitDrvSup); initRecSup(); initHookAnnounce(initHookAfterInitRecSup); initDevSup(); initHookAnnounce(initHookAfterInitDevSup); /* used by autosave pass 0 */ iterateRecords(prepareLinks, NULL); dbLockInitRecords(pdbbase); initDatabase(); dbBkptInit(); initHookAnnounce(initHookAfterInitDatabase); /* used by autosave pass 1 */ finishDevSup(); initHookAnnounce(initHookAfterFinishDevSup); scanInit(); if (asInit()) { errlogPrintf("iocBuild: asInit Failed.\n"); return -1; } dbProcessNotifyInit(); epicsThreadSleep(.5); initHookAnnounce(initHookAfterScanInit); initialProcess(); initHookAnnounce(initHookAfterInitialProcess); return 0; } static int iocBuild_3(void) { initHookAnnounce(initHookAfterCaServerInit); iocState = iocBuilt; initHookAnnounce(initHookAfterIocBuilt); return 0; } int iocBuild(void) { int status; status = iocBuild_1(); if (status) return status; dbCaLinkInit(); status = iocBuild_2(); if (status) return status; dbInitServers(); status = iocBuild_3(); if (dbThreadRealtimeLock) epicsThreadRealtimeLock(); if (!status) iocBuildMode = buildServers; return status; } int iocBuildIsolated(void) { int status; status = iocBuild_1(); if (status) return status; dbCaLinkInitIsolated(); status = iocBuild_2(); if (status) return status; status = iocBuild_3(); if (!status) iocBuildMode = buildIsolated; return status; } int iocRun(void) { if (iocState != iocPaused && iocState != iocBuilt) { errlogPrintf("iocRun: IOC not paused\n"); return -1; } initHookAnnounce(initHookAtIocRun); /* Enable scan tasks and some driver support functions. */ scanRun(); dbCaRun(); initHookAnnounce(initHookAfterDatabaseRunning); if (iocState == iocBuilt) initHookAnnounce(initHookAfterInterruptAccept); if (iocBuildMode == buildServers) { dbRunServers(); initHookAnnounce(initHookAfterCaServerRunning); } if (iocState == iocBuilt) initHookAnnounce(initHookAtEnd); errlogPrintf("iocRun: %s\n", iocState == iocBuilt ? "All initialization complete" : "IOC restarted"); iocState = iocRunning; initHookAnnounce(initHookAfterIocRunning); return 0; } int iocPause(void) { if (iocState != iocRunning) { errlogPrintf("iocPause: IOC not running\n"); return -1; } initHookAnnounce(initHookAtIocPause); if (iocBuildMode == buildServers) { dbPauseServers(); initHookAnnounce(initHookAfterCaServerPaused); } dbCaPause(); scanPause(); initHookAnnounce(initHookAfterDatabasePaused); iocState = iocPaused; errlogPrintf("iocPause: IOC suspended\n"); initHookAnnounce(initHookAfterIocPaused); return 0; } /* * Database sanity checks * * This is not an attempt to sanity-check the whole .dbd file, only * two menus normally get modified by users: menuConvert and menuScan. * * The menuConvert checks were added to flag problems with IOCs * converted from 3.13.x, where the SLOPE choice didn't exist. * * The menuScan checks make sure the user didn't fiddle too much * when creating new periodic scan choices. */ static int checkDatabase(dbBase *pdbbase) { const dbMenu *pMenu; if (!pdbbase) { errlogPrintf("checkDatabase: No database definitions loaded.\n"); return -1; } pMenu = dbFindMenu(pdbbase, "menuConvert"); if (!pMenu) { errlogPrintf("checkDatabase: menuConvert not defined.\n"); return -1; } if (pMenu->nChoice <= menuConvertLINEAR) { errlogPrintf("checkDatabase: menuConvert has too few choices.\n"); return -1; } if (strcmp(pMenu->papChoiceName[menuConvertNO_CONVERSION], "menuConvertNO_CONVERSION")) { errlogPrintf("checkDatabase: menuConvertNO_CONVERSION doesn't match.\n"); return -1; } if (strcmp(pMenu->papChoiceName[menuConvertSLOPE], "menuConvertSLOPE")) { errlogPrintf("checkDatabase: menuConvertSLOPE doesn't match.\n"); return -1; } if (strcmp(pMenu->papChoiceName[menuConvertLINEAR], "menuConvertLINEAR")) { errlogPrintf("checkDatabase: menuConvertLINEAR doesn't match.\n"); return -1; } pMenu = dbFindMenu(pdbbase, "menuScan"); if (!pMenu) { errlogPrintf("checkDatabase: menuScan not defined.\n"); return -1; } if (pMenu->nChoice <= menuScanI_O_Intr) { errlogPrintf("checkDatabase: menuScan has too few choices.\n"); return -1; } if (strcmp(pMenu->papChoiceName[menuScanPassive], "menuScanPassive")) { errlogPrintf("checkDatabase: menuScanPassive doesn't match.\n"); return -1; } if (strcmp(pMenu->papChoiceName[menuScanEvent], "menuScanEvent")) { errlogPrintf("checkDatabase: 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"); return -1; } if (pMenu->nChoice <= SCAN_1ST_PERIODIC) { errlogPrintf("checkDatabase: menuScan has no periodic choices.\n"); return -1; } return 0; } static void checkGeneralTime(void) { epicsTimeStamp ts; epicsTimeGetCurrent(&ts); if (ts.secPastEpoch < 2*24*60*60) { static const char * const tsfmt = "%Y-%m-%d %H:%M:%S.%09f"; char buff[40]; epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts); errlogPrintf("iocInit: Time provider has not yet synchronized.\n"); } epicsTimeGetEvent(&ts, 1); /* Prime gtPvt.lastEventProvider for ISRs */ } static void initDrvSup(void) /* Locate all driver support entry tables */ { drvSup *pdrvSup; for (pdrvSup = (drvSup *)ellFirst(&pdbbase->drvList); pdrvSup; pdrvSup = (drvSup *)ellNext(&pdrvSup->node)) { struct drvet *pdrvet = registryDriverSupportFind(pdrvSup->name); if (!pdrvet) { errlogPrintf("iocInit: driver %s not found\n", pdrvSup->name); continue; } pdrvSup->pdrvet = pdrvet; if (pdrvet->init) pdrvet->init(); } } static void initRecSup(void) { dbRecordType *pdbRecordType; for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { recordTypeLocation *precordTypeLocation = registryRecordTypeFind(pdbRecordType->name); rset *prset; if (!precordTypeLocation) { errlogPrintf("iocInit record support for %s not found\n", pdbRecordType->name); continue; } prset = precordTypeLocation->prset; pdbRecordType->prset = prset; if (prset->init) { prset->init(); } } } static void initDevSup(void) { dbRecordType *pdbRecordType; for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { devSup *pdevSup; for (pdevSup = (devSup *)ellFirst(&pdbRecordType->devList); pdevSup; pdevSup = (devSup *)ellNext(&pdevSup->node)) { struct dset *pdset = registryDeviceSupportFind(pdevSup->name); if (!pdset) { errlogPrintf("device support %s not found\n",pdevSup->name); continue; } dbInitDevSup(pdevSup, pdset); /* Calls pdset->init(0) */ } } } static void finishDevSup(void) { dbRecordType *pdbRecordType; for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { devSup *pdevSup; for (pdevSup = (devSup *)ellFirst(&pdbRecordType->devList); pdevSup; pdevSup = (devSup *)ellNext(&pdevSup->node)) { struct dset *pdset = pdevSup->pdset; if (pdset && pdset->init) pdset->init(1); } } } static void iterateRecords(recIterFunc func, void *user) { dbRecordType *pdbRecordType; for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { dbRecordNode *pdbRecordNode; for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList); pdbRecordNode; pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { dbCommon *precord = pdbRecordNode->precord; if (!precord->name[0] || pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) continue; func(pdbRecordType, precord, user); } } return; } static void doInitRecord0(dbRecordType *pdbRecordType, dbCommon *precord, void *user) { rset *prset = pdbRecordType->prset; devSup *pdevSup; if (!prset) return; /* unlikely */ precord->rset = prset; precord->mlok = epicsMutexMustCreate(); ellInit(&precord->mlis); /* Reset the process active field */ precord->pact = FALSE; /* Initial UDF severity */ if (precord->udf && precord->stat == UDF_ALARM) precord->sevr = precord->udfs; /* Init DSET NOTE that result may be NULL */ pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); precord->dset = pdevSup ? pdevSup->pdset : NULL; if (prset->init_record) prset->init_record(precord, 0); } static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord, void *user) { dbFldDes **papFldDes = pdbRecordType->papFldDes; short *link_ind = pdbRecordType->link_ind; int j; /* For all the links in the record type... */ for (j = 0; j < pdbRecordType->no_links; j++) { dbFldDes *pdbFldDes = papFldDes[link_ind[j]]; DBLINK *plink = (DBLINK*)((char*)precord + pdbFldDes->offset); if (ellCount(&precord->rdes->devList) > 0 && pdbFldDes->isDevLink) { devSup *pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); if (pdevSup) { struct dsxt *pdsxt = pdevSup->pdsxt; if (pdsxt && pdsxt->add_record) { pdsxt->add_record(precord); } } } dbInitLink(plink, pdbFldDes->field_type); } } static void doInitRecord1(dbRecordType *pdbRecordType, dbCommon *precord, void *user) { rset *prset = pdbRecordType->prset; if (!prset) return; /* unlikely */ if (prset->init_record) prset->init_record(precord, 1); } static void initDatabase(void) { dbChannelInit(); iterateRecords(doInitRecord0, NULL); iterateRecords(doResolveLinks, NULL); iterateRecords(doInitRecord1, NULL); epicsAtExit(exitDatabase, NULL); return; } /* * Process database records at initialization ordered by phase * if their pini (process at init) field is set. */ typedef struct { int this; int next; epicsEnum16 pini; } phaseData_t; static void doRecordPini(dbRecordType *rtype, dbCommon *precord, void *user) { phaseData_t *pphase = (phaseData_t *)user; int phas; if (precord->pini != pphase->pini) return; phas = precord->phas; if (phas == pphase->this) { dbScanLock(precord); dbProcess(precord); dbScanUnlock(precord); } else if (phas > pphase->this && phas < pphase->next) pphase->next = phas; } static void piniProcess(int pini) { phaseData_t phase; phase.next = MIN_PHASE; phase.pini = pini; /* This scans through the whole database as many times as needed. * During the first pass it is unlikely to find any records with * PHAS = MIN_PHASE, but during each iteration it looks for the * phase value of the next pass to run. Note that PHAS fields can * be changed at runtime, so we have to look for the lowest value * of PHAS each time. */ do { phase.this = phase.next; phase.next = MAX_PHASE + 1; iterateRecords(doRecordPini, &phase); } while (phase.next != MAX_PHASE + 1); } static void piniProcessHook(initHookState state) { switch (state) { case initHookAtIocRun: piniProcess(menuPiniRUN); break; case initHookAfterIocRunning: piniProcess(menuPiniRUNNING); break; case initHookAtIocPause: piniProcess(menuPiniPAUSE); break; case initHookAfterIocPaused: piniProcess(menuPiniPAUSED); break; default: break; } } static void initialProcess(void) { initHookRegister(piniProcessHook); piniProcess(menuPiniYES); } /* * set DB_LINK and CA_LINK to PV_LINK * Delete record scans */ static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord, void *user) { devSup *pdevSup; struct dsxt *pdsxt; int j; int locked = 0; for (j = 0; j < pdbRecordType->no_links; j++) { dbFldDes *pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); if (plink->type == CA_LINK || plink->type == JSON_LINK || (plink->type == DB_LINK && iocBuildMode == buildIsolated)) { if (!locked) { dbScanLock(precord); locked = 1; } dbRemoveLink(NULL, plink); } } if (precord->dset && (pdevSup = dbDSETtoDevSup(pdbRecordType, precord->dset)) && (pdsxt = pdevSup->pdsxt) && pdsxt->del_record) { if (!locked) { dbScanLock(precord); locked = 1; } scanDelete(precord); /* Being consistent... */ pdsxt->del_record(precord); } if (locked) { precord->pact = TRUE; dbScanUnlock(precord); } } static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord, void *user) { int j; for (j = 0; j < pdbRecordType->no_links; j++) { dbFldDes *pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); dbFreeLinkContents(plink); } epicsMutexDestroy(precord->mlok); free(precord->ppnr); /* may be allocated in dbNotify.c */ } int iocShutdown(void) { if (iocState == iocVirgin || iocState == iocStopped) return 0; iterateRecords(doCloseLinks, NULL); if (iocBuildMode == buildIsolated) { /* stop and "join" threads */ scanStop(); callbackStop(); } else dbStopServers(); dbCaShutdown(); /* must be before dbFreeRecord and dbChannelExit */ if (iocBuildMode == buildIsolated) { /* free resources */ scanCleanup(); callbackCleanup(); iterateRecords(doFreeRecord, NULL); dbLockCleanupRecords(pdbbase); asShutdown(); dbChannelExit(); dbProcessNotifyExit(); iocshFree(); } iocState = iocStopped; iocBuildMode = buildServers; return 0; } static void exitDatabase(void *dummy) { iocShutdown(); } base-7.0.3.1/modules/database/src/ioc/misc/iocInit.h0000664000577000060420000000162413557101274020675 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* iocInit.h ioc initialization */ #ifndef INCiocInith #define INCiocInith #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc int iocInit(void); epicsShareFunc int iocBuild(void); epicsShareFunc int iocBuildIsolated(void); epicsShareFunc int iocRun(void); epicsShareFunc int iocPause(void); epicsShareFunc int iocShutdown(void); #ifdef __cplusplus } #endif #endif /*INCiocInith*/ base-7.0.3.1/modules/database/src/ioc/misc/iocshRegisterCommon.c0000664000577000060420000000353413557101274023257 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "envDefs.h" #include "epicsVersion.h" #include "iocsh.h" #include "libComRegister.h" #define epicsExportSharedSymbols #include "asIocRegister.h" #include "dbAccess.h" #include "dbIocRegister.h" #include "dbStaticIocRegister.h" #include "dbtoolsIocRegister.h" #include "iocshRegisterCommon.h" #include "miscIocRegister.h" #include "registryIocRegister.h" #define quote(v) #v #define str(v) quote(v) void iocshRegisterCommon(void) { const char *targetArch = envGetConfigParamPtr(&EPICS_BUILD_TARGET_ARCH); iocshPpdbbase = &pdbbase; /* This uses a config param so the user can override it */ if (targetArch) { epicsEnvSet("ARCH", targetArch); } /* Base build version variables */ epicsEnvSet("EPICS_VERSION_MAJOR", str(EPICS_VERSION)); epicsEnvSet("EPICS_VERSION_MIDDLE", str(EPICS_REVISION)); epicsEnvSet("EPICS_VERSION_MINOR", str(EPICS_MODIFICATION)); epicsEnvSet("EPICS_VERSION_PATCH", str(EPICS_PATCH_LEVEL)); epicsEnvSet("EPICS_VERSION_SNAPSHOT", EPICS_DEV_SNAPSHOT); epicsEnvSet("EPICS_VERSION_SITE", EPICS_SITE_VERSION); epicsEnvSet("EPICS_VERSION_SHORT", EPICS_VERSION_SHORT); epicsEnvSet("EPICS_VERSION_FULL", EPICS_VERSION_FULL); dbStaticIocRegister(); registryIocRegister(); dbIocRegister(); dbtoolsIocRegister(); asIocRegister(); miscIocRegister(); libComRegister(); } base-7.0.3.1/modules/database/src/ioc/misc/iocshRegisterCommon.h0000664000577000060420000000152013557101274023255 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* iocshRegisterCommon.h */ /* Author: Marty Kraimer Date: 27APR2000 */ #ifndef INCiocshRegisterCommonH #define INCiocshRegisterCommonH #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /* register many useful commands */ epicsShareFunc void iocshRegisterCommon(void); #ifdef __cplusplus } #endif #endif /*INCiocshRegisterCommonH*/ base-7.0.3.1/modules/database/src/ioc/misc/miscIocRegister.c0000664000577000060420000000523413557101274022366 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include "iocsh.h" #include "errlog.h" #define epicsExportSharedSymbols #include "iocInit.h" #include "epicsExport.h" #include "epicsRelease.h" #include "miscIocRegister.h" /* iocInit */ static const iocshFuncDef iocInitFuncDef = {"iocInit",0,NULL}; static void iocInitCallFunc(const iocshArgBuf *args) { iocshSetError(iocInit()); } /* iocBuild */ static const iocshFuncDef iocBuildFuncDef = {"iocBuild",0,NULL}; static void iocBuildCallFunc(const iocshArgBuf *args) { iocshSetError(iocBuild()); } /* iocRun */ static const iocshFuncDef iocRunFuncDef = {"iocRun",0,NULL}; static void iocRunCallFunc(const iocshArgBuf *args) { iocshSetError(iocRun()); } /* iocPause */ static const iocshFuncDef iocPauseFuncDef = {"iocPause",0,NULL}; static void iocPauseCallFunc(const iocshArgBuf *args) { iocshSetError(iocPause()); } /* coreRelease */ static const iocshFuncDef coreReleaseFuncDef = {"coreRelease",0,NULL}; static void coreReleaseCallFunc(const iocshArgBuf *args) { coreRelease (); } void miscIocRegister(void) { iocshRegister(&iocInitFuncDef,iocInitCallFunc); iocshRegister(&iocBuildFuncDef,iocBuildCallFunc); iocshRegister(&iocRunFuncDef,iocRunCallFunc); iocshRegister(&iocPauseFuncDef,iocPauseCallFunc); iocshRegister(&coreReleaseFuncDef, coreReleaseCallFunc); } /* system -- escape to system command interpreter. * * Disabled by default for security reasons, not available on all OSs. * To enable this command, add * registrar(iocshSystemCommand) * to an application dbd file, or include system.dbd */ #ifndef SYSTEM_UNAVAILABLE static const iocshArg systemArg0 = { "command string",iocshArgString}; static const iocshArg * const systemArgs[] = {&systemArg0}; static const iocshFuncDef systemFuncDef = {"system",1,systemArgs}; static void systemCallFunc(const iocshArgBuf *args) { iocshSetError(system(args[0].sval)); } #endif static void iocshSystemCommand(void) { #ifndef SYSTEM_UNAVAILABLE if (system(NULL)) iocshRegister(&systemFuncDef, systemCallFunc); else #endif errlogPrintf ("Can't register 'system' command -- no command interpreter available.\n"); } epicsExportRegistrar(iocshSystemCommand); base-7.0.3.1/modules/database/src/ioc/misc/miscIocRegister.h0000664000577000060420000000134013557101274022365 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_miscIocRegister_H #define INC_miscIocRegister_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void miscIocRegister(void); #ifdef __cplusplus } #endif #endif /* INC_miscIocRegister_H */ base-7.0.3.1/modules/database/src/ioc/misc/system.dbd0000664000577000060420000000014313557101274021120 0ustar anjaesctl# Including this DBD file adds a 'system' command to the IOC shell. registrar(iocshSystemCommand) base-7.0.3.1/modules/database/src/ioc/registry/Makefile0000664000577000060420000000200313557101274021473 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. SRC_DIRS += $(IOCDIR)/registry INC += registryRecordType.h INC += registryDeviceSupport.h INC += registryDriverSupport.h INC += registryJLinks.h INC += registryFunction.h INC += registryCommon.h INC += registryIocRegister.h dbCore_SRCS += registryRecordType.c dbCore_SRCS += registryDeviceSupport.c dbCore_SRCS += registryDriverSupport.c dbCore_SRCS += registryJLinks.c dbCore_SRCS += registryFunction.c dbCore_SRCS += registryCommon.c dbCore_SRCS += registryIocRegister.c base-7.0.3.1/modules/database/src/ioc/registry/registryCommon.c0000664000577000060420000000562213557101274023232 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* registryCommon.c */ /* Author: Andrew Johnson * Date: 2004-03-19 */ #include "errlog.h" #define epicsExportSharedSymbols #include "registryCommon.h" #include "registryDeviceSupport.h" #include "registryDriverSupport.h" #include "registryJLinks.h" void registerRecordTypes(DBBASE *pbase, int nRecordTypes, const char * const *recordTypeNames, const recordTypeLocation *rtl) { int i; for (i = 0; i < nRecordTypes; i++) { recordTypeLocation *precordTypeLocation; computeSizeOffset sizeOffset; DBENTRY dbEntry; if (registryRecordTypeFind(recordTypeNames[i])) continue; if (!registryRecordTypeAdd(recordTypeNames[i], &rtl[i])) { errlogPrintf("registryRecordTypeAdd failed %s\n", recordTypeNames[i]); continue; } dbInitEntry(pbase,&dbEntry); precordTypeLocation = registryRecordTypeFind(recordTypeNames[i]); sizeOffset = precordTypeLocation->sizeOffset; if (dbFindRecordType(&dbEntry, recordTypeNames[i])) { errlogPrintf("registerRecordDeviceDriver failed %s\n", recordTypeNames[i]); } else { sizeOffset(dbEntry.precordType); } } } void registerDevices(DBBASE *pbase, int nDevices, const char * const *deviceSupportNames, const dset * const *devsl) { int i; for (i = 0; i < nDevices; i++) { if (registryDeviceSupportFind(deviceSupportNames[i])) continue; if (!registryDeviceSupportAdd(deviceSupportNames[i], devsl[i])) { errlogPrintf("registryDeviceSupportAdd failed %s\n", deviceSupportNames[i]); continue; } } } void registerDrivers(DBBASE *pbase, int nDrivers, const char * const * driverSupportNames, struct drvet * const *drvsl) { int i; for (i = 0; i < nDrivers; i++) { if (registryDriverSupportFind(driverSupportNames[i])) continue; if (!registryDriverSupportAdd(driverSupportNames[i], drvsl[i])) { errlogPrintf("registryDriverSupportAdd failed %s\n", driverSupportNames[i]); continue; } } } void registerJLinks(DBBASE *pbase, int nLinks, jlif * const *jlifsl) { int i; for (i = 0; i < nLinks; i++) { if (!registryJLinkAdd(pbase, jlifsl[i])) { errlogPrintf("registryJLinkAdd failed %s\n", jlifsl[i]->name); continue; } } } base-7.0.3.1/modules/database/src/ioc/registry/registryCommon.h0000664000577000060420000000244013557101274023232 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_registryCommon_H #define INC_registryCommon_H #include "dbStaticLib.h" #include "devSup.h" #include "dbJLink.h" #include "registryRecordType.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void registerRecordTypes( DBBASE *pbase, int nRecordTypes, const char * const *recordTypeNames, const recordTypeLocation *rtl); epicsShareFunc void registerDevices( DBBASE *pbase, int nDevices, const char * const *deviceSupportNames, const dset * const *devsl); epicsShareFunc void registerDrivers( DBBASE *pbase, int nDrivers, const char * const *driverSupportNames, struct drvet * const *drvsl); epicsShareFunc void registerJLinks( DBBASE *pbase, int nDrivers, jlif * const *jlifsl); #ifdef __cplusplus } #endif #endif /* INC_registryCommon_H */ base-7.0.3.1/modules/database/src/ioc/registry/registryDeviceSupport.c0000664000577000060420000000174013557101274024573 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* registryDeviceSupport.c */ /* Author: Marty Kraimer Date: 08JUN99 */ #define epicsExportSharedSymbols #include "registry.h" #include "registryDeviceSupport.h" static void *registryID = "device support"; epicsShareFunc int registryDeviceSupportAdd( const char *name, const struct dset *pdset) { return registryAdd(registryID, name, (void *)pdset); } epicsShareFunc struct dset * registryDeviceSupportFind( const char *name) { return registryFind(registryID, name); } base-7.0.3.1/modules/database/src/ioc/registry/registryDeviceSupport.h0000664000577000060420000000160413557101274024577 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_registryDeviceSupport_H #define INC_registryDeviceSupport_H #include "devSup.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc int registryDeviceSupportAdd( const char *name, const struct dset *pdset); epicsShareFunc struct dset * registryDeviceSupportFind( const char *name); #ifdef __cplusplus } #endif #endif /* INC_registryDeviceSupport_H */ base-7.0.3.1/modules/database/src/ioc/registry/registryDriverSupport.c0000664000577000060420000000172613557101274024633 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* registryDriverSupport.c */ /* Author: Marty Kraimer Date: 08JUN99 */ #define epicsExportSharedSymbols #include "registry.h" #include "registryDriverSupport.h" static void *registryID = "driver support"; epicsShareFunc int registryDriverSupportAdd( const char *name, struct drvet *pdrvet) { return registryAdd(registryID, name, pdrvet); } epicsShareFunc struct drvet * registryDriverSupportFind( const char *name) { return registryFind(registryID, name); } base-7.0.3.1/modules/database/src/ioc/registry/registryDriverSupport.h0000664000577000060420000000160113557101274024630 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_registryDriverSupport_H #define INC_registryDriverSupport_H #include "drvSup.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc int registryDriverSupportAdd( const char *name, struct drvet *pdrvet); epicsShareFunc struct drvet * registryDriverSupportFind( const char *name); #ifdef __cplusplus } #endif #endif /* INC_registryDriverSupport_H */ base-7.0.3.1/modules/database/src/ioc/registry/registryFunction.c0000664000577000060420000000272413557101274023567 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* registryFunction.c */ /* Author: Marty Kraimer Date: 01MAY2000 */ #include #define epicsExportSharedSymbols #include "registry.h" #include "registryFunction.h" static void * const registryID = "function"; epicsShareFunc int registryFunctionAdd( const char *name, REGISTRYFUNCTION func) { return registryAdd(registryID, name, func); } epicsShareFunc REGISTRYFUNCTION registryFunctionFind( const char *name) { REGISTRYFUNCTION func = registryFind(registryID, name); if (!func) { func = registryFind(0, name); if (func) registryFunctionAdd(name, func); } return func; } epicsShareFunc int registryFunctionRefAdd( registryFunctionRef ref[], int nfunctions) { int i; for (i = 0; i < nfunctions; i++) { if (!registryFunctionAdd(ref[i].name, ref[i].addr)) { printf("registryFunctionRefAdd: could not register %s\n", ref[i].name); return 0; } } return 1; } base-7.0.3.1/modules/database/src/ioc/registry/registryFunction.h0000664000577000060420000000211213557101274023563 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_registryFunction_H #define INC_registryFunction_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif typedef void (*REGISTRYFUNCTION)(void); typedef struct registryFunctionRef { const char * name; REGISTRYFUNCTION addr; } registryFunctionRef; epicsShareFunc int registryFunctionAdd( const char *name, REGISTRYFUNCTION func); epicsShareFunc REGISTRYFUNCTION registryFunctionFind( const char *name); epicsShareFunc int registryFunctionRefAdd( registryFunctionRef ref[], int nfunctions); #ifdef __cplusplus } #endif #endif /* INC_registryFunction_H */ base-7.0.3.1/modules/database/src/ioc/registry/registryIocRegister.c0000664000577000060420000000451713557101274024223 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "iocsh.h" #define epicsExportSharedSymbols #include "registryDeviceSupport.h" #include "registryDriverSupport.h" #include "registryFunction.h" #include "registryIocRegister.h" #include "registryRecordType.h" static const iocshArg registryXxxFindArg0 = { "name",iocshArgString}; static const iocshArg * const registryXxxFindArgs[1] = {®istryXxxFindArg0}; /* registryRecordTypeFind */ static const iocshFuncDef registryRecordTypeFindFuncDef = { "registryRecordTypeFind",1,registryXxxFindArgs}; static void registryRecordTypeFindCallFunc(const iocshArgBuf *args) { printf("%p\n", (void*) registryRecordTypeFind(args[0].sval)); } /* registryDeviceSupportFind */ static const iocshFuncDef registryDeviceSupportFindFuncDef = { "registryDeviceSupportFind",1,registryXxxFindArgs}; static void registryDeviceSupportFindCallFunc(const iocshArgBuf *args) { printf("%p\n", (void*) registryDeviceSupportFind(args[0].sval)); } /* registryDriverSupportFind */ static const iocshFuncDef registryDriverSupportFindFuncDef = { "registryDriverSupportFind",1,registryXxxFindArgs}; static void registryDriverSupportFindCallFunc(const iocshArgBuf *args) { printf("%p\n", (void*) registryDriverSupportFind(args[0].sval)); } /* registryFunctionFind */ static const iocshFuncDef registryFunctionFindFuncDef = { "registryFunctionFind",1,registryXxxFindArgs}; static void registryFunctionFindCallFunc(const iocshArgBuf *args) { printf("%p\n", (void*) registryFunctionFind(args[0].sval)); } void registryIocRegister(void) { iocshRegister(®istryRecordTypeFindFuncDef,registryRecordTypeFindCallFunc); iocshRegister(®istryDeviceSupportFindFuncDef,registryDeviceSupportFindCallFunc); iocshRegister(®istryDriverSupportFindFuncDef,registryDriverSupportFindCallFunc); iocshRegister(®istryFunctionFindFuncDef,registryFunctionFindCallFunc); } base-7.0.3.1/modules/database/src/ioc/registry/registryIocRegister.h0000664000577000060420000000135313557101274024223 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_registryIocRegister_H #define INC_registryIocRegister_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void registryIocRegister(void); #ifdef __cplusplus } #endif #endif /* INC_registryIocRegister_H */ base-7.0.3.1/modules/database/src/ioc/registry/registryJLinks.c0000664000577000060420000000137113557101274023171 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* registryJLinks.c */ #include "registry.h" #define epicsExportSharedSymbols #include "dbBase.h" #include "dbStaticLib.h" #include "registryJLinks.h" #include "dbJLink.h" epicsShareFunc int registryJLinkAdd(DBBASE *pbase, struct jlif *pjlif) { linkSup *plinkSup = dbFindLinkSup(pbase, pjlif->name); if (plinkSup) plinkSup->pjlif = pjlif; return !!plinkSup; } base-7.0.3.1/modules/database/src/ioc/registry/registryJLinks.h0000664000577000060420000000143713557101274023201 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_registryJLinks_H #define INC_registryJLinks_H #include "dbBase.h" #include "dbJLink.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc int registryJLinkAdd(DBBASE *pbase, jlif *pjlif); #ifdef __cplusplus } #endif #endif /* INC_registryDriverSupport_H */ base-7.0.3.1/modules/database/src/ioc/registry/registryRecordType.c0000664000577000060420000000174413557101274024063 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* registryRecordType.c */ /* Author: Marty Kraimer Date: 08JUN99 */ #define epicsExportSharedSymbols #include "registry.h" #include "registryRecordType.h" static void * const registryID = "record type"; epicsShareFunc int registryRecordTypeAdd( const char *name, const recordTypeLocation *prtl) { return registryAdd(registryID, name, (void *)prtl); } epicsShareFunc recordTypeLocation * registryRecordTypeFind( const char *name) { return registryFind(registryID, name); } base-7.0.3.1/modules/database/src/ioc/registry/registryRecordType.h0000664000577000060420000000224113557101274024061 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_registryRecordType_H #define INC_registryRecordType_H #include "recSup.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif struct dbRecordType; struct dbBase; typedef int (*computeSizeOffset)(struct dbRecordType *pdbRecordType); typedef struct recordTypeLocation { struct typed_rset *prset; computeSizeOffset sizeOffset; }recordTypeLocation; epicsShareFunc int registryRecordTypeAdd( const char *name, const recordTypeLocation *prtl); epicsShareFunc recordTypeLocation * registryRecordTypeFind( const char *name); int registerRecordDeviceDriver(struct dbBase *pdbbase); #ifdef __cplusplus } #endif #endif /* INC_registryRecordType_H */ base-7.0.3.1/modules/database/src/ioc/rsrv/Makefile0000664000577000060420000000167113557101274020631 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. SRC_DIRS += $(IOCDIR)/rsrv # These need access to net_convert.h from the CA client caserverio_INCLUDES = -I$(SRC)/ca/client camessage_INCLUDES = -I$(SRC)/ca/client INC += rsrv.h DBD += rsrv.dbd dbCore_SRCS += caserverio.c dbCore_SRCS += caservertask.c dbCore_SRCS += camsgtask.c dbCore_SRCS += camessage.c dbCore_SRCS += cast_server.c dbCore_SRCS += online_notify.c dbCore_SRCS += rsrvIocRegister.c base-7.0.3.1/modules/database/src/ioc/rsrv/camessage.c0000664000577000060420000022016313557101274021264 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeffrey O. Hill * * Ralph Lange */ #include #include #include #include #include #include #include "epicsEvent.h" #include "epicsMutex.h" #include "epicsStdio.h" #include "epicsString.h" #include "epicsThread.h" #include "epicsTime.h" #include "errlog.h" #include "freeList.h" #include "osiPoolStatus.h" #include "osiSock.h" #include "caerr.h" #include "net_convert.h" #define epicsExportSharedSymbols #include "asDbLib.h" #include "callback.h" #include "db_access.h" #include "db_access_routines.h" #include "dbChannel.h" #include "dbCommon.h" #include "dbEvent.h" #include "db_field_log.h" #include "dbNotify.h" #include "rsrv.h" #include "server.h" #include "special.h" #define RECORD_NAME(CHAN) (dbChannelRecord(CHAN)->name) static EVENTFUNC read_reply; #define logBadId(CLIENT, MP, PPL)\ logBadIdWithFileAndLineno(CLIENT, MP, PPL, __FILE__, __LINE__) /* * for tracking db put notifies */ typedef struct rsrv_put_notify { ELLNODE node; processNotify dbPutNotify; caHdrLargeArray msg; /* * Include a union of all scalar types * including fixed length strings so * that in many cases we can avoid * allocating another buffer and only * use an rsrv_put_notify from its * free list. */ union { dbr_string_t strval; dbr_short_t shrtval; dbr_short_t intval; dbr_float_t fltval; dbr_enum_t enmval; dbr_char_t charval; dbr_long_t longval; dbr_double_t doubleval; } dbrScalarValue; /* arguments for db_put_field */ void *pbuffer; long nRequest; short dbrType; /* end arguments for db_put_field */ void * asWritePvt; unsigned valueSize; /* size of block pointed to by pbuffer */ char busy; /* put notify in progress */ char onExtraLaborQueue; } RSRVPUTNOTIFY; /* * casCalloc() * * (dont drop below some max block threshold) */ static void *casCalloc(size_t count, size_t size) { if ( UINT_MAX / size >= count ) { if (!osiSufficentSpaceInPool(size*count)) { return NULL; } return calloc(count, size); } else { return NULL; } } /* * MPTOPCIU() * * used to be a macro */ static struct channel_in_use *MPTOPCIU (const caHdrLargeArray *mp) { struct channel_in_use *pciu; const unsigned id = mp->m_cid; LOCK_CLIENTQ; pciu = bucketLookupItemUnsignedId (pCaBucket, &id); UNLOCK_CLIENTQ; return pciu; } /* vsend_err() * * reflect error msg back to the client * * send buffer lock must be on while in this routine * */ static void vsend_err( const caHdrLargeArray *curp, int status, struct client *client, const char *pformat, va_list args ) { static const ca_uint32_t maxDiagLen = 512; struct channel_in_use *pciu; caHdr *pReqOut; char *pMsgString; ca_uint32_t size; ca_uint32_t cid; int localStatus; switch ( curp->m_cmmd ) { case CA_PROTO_EVENT_ADD: case CA_PROTO_EVENT_CANCEL: case CA_PROTO_READ: case CA_PROTO_READ_NOTIFY: case CA_PROTO_WRITE: case CA_PROTO_WRITE_NOTIFY: pciu = MPTOPCIU(curp); if(pciu){ cid = pciu->cid; } else{ cid = 0xffffffff; } break; case CA_PROTO_SEARCH: cid = curp->m_cid; break; case CA_PROTO_EVENTS_ON: case CA_PROTO_EVENTS_OFF: case CA_PROTO_READ_SYNC: case CA_PROTO_SNAPSHOT: default: cid = 0xffffffff; break; } /* * allocate plenty of space for a sprintf() buffer */ localStatus = cas_copy_in_header ( client, CA_PROTO_ERROR, maxDiagLen, 0, 0, cid, status, ( void * ) &pReqOut ); if ( localStatus != ECA_NORMAL ) { errlogPrintf ( "caserver: Unable to deliver err msg \"%s\" to client because \"%s\"\n", ca_message (status), ca_message (localStatus) ); errlogVprintf ( pformat, args ); return; } /* * copy back the request protocol * (in network byte order) */ if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && CA_V49( client->minor_version_number ) ) { ca_uint32_t *pLW = ( ca_uint32_t * ) ( pReqOut + 1 ); pReqOut->m_cmmd = htons ( curp->m_cmmd ); pReqOut->m_postsize = htons ( 0xffff ); pReqOut->m_dataType = htons ( curp->m_dataType ); pReqOut->m_count = htons ( 0u ); pReqOut->m_cid = htonl ( curp->m_cid ); pReqOut->m_available = htonl ( curp->m_available ); pLW[0] = htonl ( curp->m_postsize ); pLW[1] = htonl ( curp->m_count ); pMsgString = ( char * ) ( pLW + 2 ); size = sizeof ( caHdr ) + 2 * sizeof ( *pLW ); } else { pReqOut->m_cmmd = htons (curp->m_cmmd); pReqOut->m_postsize = htons ( ( (ca_uint16_t) curp->m_postsize ) ); pReqOut->m_dataType = htons (curp->m_dataType); pReqOut->m_count = htons ( ( (ca_uint16_t) curp->m_count ) ); pReqOut->m_cid = htonl (curp->m_cid); pReqOut->m_available = htonl (curp->m_available); pMsgString = ( char * ) ( pReqOut + 1 ); size = sizeof ( caHdr ); } /* * add their context string into the protocol */ localStatus = epicsVsnprintf ( pMsgString, maxDiagLen - size, pformat, args ); if ( localStatus >= 1 ) { unsigned diagLen = ( unsigned ) localStatus; if ( diagLen < maxDiagLen - size ) { size += (ca_uint32_t) (diagLen + 1u); } else { errlogPrintf ( "caserver: vsend_err: epicsVsnprintf detected " "error message truncation, pFormat = \"%s\"\n", pformat ); size = maxDiagLen; pMsgString [ maxDiagLen - 1 ] = '\0'; } } cas_commit_msg ( client, size ); } /* send_err() * * reflect error msg back to the client * * send buffer lock must be on while in this routine * */ static void send_err ( const caHdrLargeArray *curp, int status, struct client *client, const char *pformat, ... ) { va_list args; va_start ( args, pformat ); vsend_err ( curp, status, client, pformat, args ); va_end ( args ); } /* log_header() * * Debug aid - print the header part of a message. * */ static void log_header ( const char *pContext, struct client *client, const caHdrLargeArray *mp, const void *pPayLoad, unsigned mnum ) { struct channel_in_use *pciu; char hostName[256]; ipAddrToDottedIP (&client->addr, hostName, sizeof(hostName)); pciu = MPTOPCIU(mp); if (pContext) { epicsPrintf ("CAS: request from %s => %s\n", hostName, pContext); } epicsPrintf ( "CAS: Request from %s => cmmd=%d cid=0x%x type=%d count=%d postsize=%u\n", hostName, mp->m_cmmd, mp->m_cid, mp->m_dataType, mp->m_count, mp->m_postsize); epicsPrintf ( "CAS: Request from %s => available=0x%x \tN=%u paddr=%p\n", hostName, mp->m_available, mnum, (pciu ? (void *)&pciu->dbch : NULL)); if (mp->m_cmmd==CA_PROTO_WRITE && mp->m_dataType==DBF_STRING && pPayLoad ) { epicsPrintf ( "CAS: Request from %s => Wrote string \"%s\"\n", hostName, (char *)pPayLoad ); } } /* * logBadIdWithFileAndLineno() */ static void logBadIdWithFileAndLineno( struct client *client, caHdrLargeArray *mp, const void *pPayload, char *pFileName, unsigned lineno ) { log_header ( "bad resource ID", client, mp, pPayload, 0 ); SEND_LOCK ( client ); send_err ( mp, ECA_INTERNAL, client, "Bad Resource ID at %s.%d", pFileName, lineno ); SEND_UNLOCK ( client ); } /* * bad_udp_cmd_action() */ static int bad_udp_cmd_action ( caHdrLargeArray *mp, void *pPayload, struct client *pClient ) { if (CASDEBUG > 0) log_header ("invalid (damaged?) request code from UDP", pClient, mp, pPayload, 0); return RSRV_ERROR; } /* * bad_tcp_cmd_action() */ static int bad_tcp_cmd_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { const char *pCtx = "invalid (damaged?) request code from TCP"; log_header ( pCtx, client, mp, pPayload, 0 ); /* * by default, clients dont recover * from this */ SEND_LOCK (client); send_err (mp, ECA_INTERNAL, client, pCtx); SEND_UNLOCK (client); return RSRV_ERROR; } /* * tcp_version_action() */ static int tcp_version_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { double tmp; unsigned epicsPriorityNew; unsigned epicsPrioritySelf; client->minor_version_number = mp->m_count; if (!CA_VSUPPORTED(mp->m_count)) { DLOG ( 2, ( "CAS: Ignore version from unsupported client %u\n", mp->m_count ) ); return RSRV_ERROR; } if ( mp->m_dataType > CA_PROTO_PRIORITY_MAX ) { return RSRV_ERROR; } tmp = mp->m_dataType - CA_PROTO_PRIORITY_MIN; tmp *= epicsThreadPriorityCAServerHigh - epicsThreadPriorityCAServerLow; tmp /= CA_PROTO_PRIORITY_MAX - CA_PROTO_PRIORITY_MIN; tmp += epicsThreadPriorityCAServerLow; epicsPriorityNew = (unsigned) tmp; epicsPrioritySelf = epicsThreadGetPrioritySelf(); if ( epicsPriorityNew != epicsPrioritySelf ) { epicsThreadBooleanStatus tbs; unsigned priorityOfEvents; tbs = epicsThreadHighestPriorityLevelBelow ( epicsPriorityNew, &priorityOfEvents ); if ( tbs != epicsThreadBooleanStatusSuccess ) { priorityOfEvents = epicsPriorityNew; } if ( epicsPriorityNew > epicsPrioritySelf ) { epicsThreadSetPriority ( epicsThreadGetIdSelf(), epicsPriorityNew ); db_event_change_priority ( client->evuser, priorityOfEvents ); } else { db_event_change_priority ( client->evuser, priorityOfEvents ); epicsThreadSetPriority ( epicsThreadGetIdSelf(), epicsPriorityNew ); } client->priority = mp->m_dataType; } return RSRV_OK; } /* * tcp_echo_action() */ static int tcp_echo_action ( caHdrLargeArray *mp, void *pPayload, struct client *pClient ) { char *pPayloadOut; int status; SEND_LOCK ( pClient ); status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, ( void * ) &pPayloadOut ); if ( status == ECA_NORMAL ) { memcpy ( pPayloadOut, pPayload, mp->m_postsize ); cas_commit_msg ( pClient, mp->m_postsize ); } SEND_UNLOCK ( pClient ); return RSRV_OK; } /* * events_on_action () */ static int events_on_action ( caHdrLargeArray *mp, void *pPayload, struct client *pClient ) { db_event_flow_ctrl_mode_off ( pClient->evuser ); return RSRV_OK; } /* * events_off_action () */ static int events_off_action ( caHdrLargeArray *mp, void *pPayload, struct client *pClient ) { db_event_flow_ctrl_mode_on ( pClient->evuser ); return RSRV_OK; } /* * no_read_access_event() * * !! LOCK needs to applied by caller !! * * substantial complication introduced here by the need for backwards * compatibility */ static void no_read_access_event ( struct client *pClient, struct event_ext *pevext ) { char *pPayloadOut; int status; /* * New clients recv the status of the * operation directly to the * event/put/get callback. * * Fetched value is zerod in case they * use it even when the status indicates * failure. * * The m_cid field in the protocol * header is abused to carry the status */ status = cas_copy_in_header ( pClient, pevext->msg.m_cmmd, pevext->size, pevext->msg.m_dataType, pevext->msg.m_count, ECA_NORDACCESS, pevext->msg.m_available, ( void * ) &pPayloadOut ); if ( status == ECA_NORMAL ) { memset ( pPayloadOut, 0, pevext->size ); cas_commit_msg ( pClient, pevext->size ); } else { send_err ( &pevext->msg, status, pClient, "server unable to load read access denied response into protocol buffer PV=\"%s max bytes=%u\"", RECORD_NAME ( pevext->pciu->dbch ), rsrvSizeofLargeBufTCP ); } } /* * read_reply() */ static void read_reply ( void *pArg, struct dbChannel *dbch, int eventsRemaining, db_field_log *pfl ) { ca_uint32_t cid; void *pPayload; struct event_ext *pevext = pArg; struct client *pClient = pevext->pciu->client; struct channel_in_use *pciu = pevext->pciu; const int readAccess = asCheckGet ( pciu->asClientPVT ); int status; int autosize; int local_fl = 0; long item_count; ca_uint32_t payload_size; dbAddr *paddr=&dbch->addr; SEND_LOCK ( pClient ); cid = ECA_NORMAL; /* If the client has requested a zero element count we interpret this as a * request for all avaiable elements. In this case we initialise the * header with the maximum element size specified by the database. */ autosize = pevext->msg.m_count == 0; item_count = autosize ? paddr->no_elements : pevext->msg.m_count; payload_size = dbr_size_n(pevext->msg.m_dataType, item_count); status = cas_copy_in_header( pClient, pevext->msg.m_cmmd, payload_size, pevext->msg.m_dataType, item_count, cid, pevext->msg.m_available, &pPayload ); if ( status != ECA_NORMAL ) { send_err ( &pevext->msg, status, pClient, "server unable to load read (or subscription update) response " "into protocol buffer PV=\"%s\" max bytes=%u", RECORD_NAME ( dbch ), rsrvSizeofLargeBufTCP ); if ( ! eventsRemaining ) cas_send_bs_msg ( pClient, FALSE ); SEND_UNLOCK ( pClient ); return; } /* * verify read access */ if ( ! readAccess ) { no_read_access_event ( pClient, pevext ); if ( ! eventsRemaining ) cas_send_bs_msg ( pClient, FALSE ); SEND_UNLOCK ( pClient ); return; } /* If filters are involved in a read, create field log and run filters */ if (!pfl && (ellCount(&dbch->pre_chain) || ellCount(&dbch->post_chain))) { pfl = db_create_read_log(dbch); if (pfl) { local_fl = 1; pfl = dbChannelRunPreChain(dbch, pfl); pfl = dbChannelRunPostChain(dbch, pfl); } } status = dbChannel_get_count ( dbch, pevext->msg.m_dataType, pPayload, &item_count, pfl); if (local_fl) db_delete_field_log(pfl); if ( status < 0 ) { /* Clients recv the status of the operation directly to the * event/put/get callback. (from CA_V41()) * * Fetched value is set to zero in case they use it even when the * status indicates failure -- unless the client selected autosizing * data, in which case they'd better know what they're doing! * * The m_cid field in the protocol header is abused to carry the * status */ if (autosize) { payload_size = dbr_size_n(pevext->msg.m_dataType, 0); cas_set_header_count(pClient, 0); } memset ( pPayload, 0, payload_size ); cas_set_header_cid ( pClient, ECA_GETFAIL ); cas_commit_msg ( pClient, payload_size ); } else { int cacStatus = caNetConvert ( pevext->msg.m_dataType, pPayload, pPayload, TRUE /* host -> net format */, item_count ); if ( cacStatus == ECA_NORMAL ) { ca_uint32_t data_size = dbr_size_n(pevext->msg.m_dataType, item_count); if (autosize) { payload_size = data_size; cas_set_header_count(pClient, item_count); } else if (payload_size > data_size) memset( (char *) pPayload + data_size, 0, payload_size - data_size); } else { if (autosize) { payload_size = dbr_size_n(pevext->msg.m_dataType, 0); cas_set_header_count(pClient, 0); } memset ( pPayload, 0, payload_size ); cas_set_header_cid ( pClient, cacStatus ); } cas_commit_msg ( pClient, payload_size ); } /* * Ensures timely response for events, but does queue * them up like db requests when the OPI does not keep up. */ if ( ! eventsRemaining ) cas_send_bs_msg ( pClient, FALSE ); SEND_UNLOCK ( pClient ); return; } /* * read_action () */ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *pClient ) { struct channel_in_use *pciu = MPTOPCIU ( mp ); int readAccess; ca_uint32_t payloadSize; void *pPayload; int status; int local_fl = 0; db_field_log *pfl = NULL; if ( ! pciu ) { logBadId ( pClient, mp, 0 ); return RSRV_ERROR; } readAccess = asCheckGet ( pciu->asClientPVT ); SEND_LOCK ( pClient ); if ( INVALID_DB_REQ ( mp->m_dataType ) ) { send_err ( mp, ECA_BADTYPE, pClient, RECORD_NAME ( pciu->dbch ) ); SEND_UNLOCK ( pClient ); return RSRV_ERROR; } payloadSize = dbr_size_n ( mp->m_dataType, mp->m_count ); status = cas_copy_in_header ( pClient, mp->m_cmmd, payloadSize, mp->m_dataType, mp->m_count, pciu->cid, mp->m_available, &pPayload ); if ( status != ECA_NORMAL ) { send_err ( mp, status, pClient, "server unable to load read response into protocol buffer PV=\"%s\" max bytes=%u", RECORD_NAME ( pciu->dbch ), rsrvSizeofLargeBufTCP ); SEND_UNLOCK ( pClient ); return RSRV_OK; } /* * verify read access */ if ( ! readAccess ) { status = ECA_NORDACCESS; send_err ( mp, status, pClient, RECORD_NAME ( pciu->dbch ) ); SEND_UNLOCK ( pClient ); return RSRV_OK; } /* If filters are involved in a read, create field log and run filters */ if (ellCount(&pciu->dbch->pre_chain) || ellCount(&pciu->dbch->post_chain)) { pfl = db_create_read_log(pciu->dbch); if (pfl) { local_fl = 1; pfl = dbChannelRunPreChain(pciu->dbch, pfl); pfl = dbChannelRunPostChain(pciu->dbch, pfl); } } status = dbChannel_get ( pciu->dbch, mp->m_dataType, pPayload, mp->m_count, pfl ); if (local_fl) db_delete_field_log(pfl); if ( status < 0 ) { send_err ( mp, ECA_GETFAIL, pClient, RECORD_NAME ( pciu->dbch ) ); SEND_UNLOCK ( pClient ); return RSRV_OK; } status = caNetConvert ( mp->m_dataType, pPayload, pPayload, TRUE /* host -> net format */, mp->m_count ); if ( status != ECA_NORMAL ) { send_err ( mp, status, pClient, RECORD_NAME ( pciu->dbch ) ); SEND_UNLOCK ( pClient ); return RSRV_OK; } /* * force string message size to be the true size rounded to even * boundary */ if ( mp->m_dataType == DBR_STRING && mp->m_count == 1 ) { char * pStr = (char *) pPayload; size_t strcnt = epicsStrnLen( pStr, payloadSize ); if ( strcnt < payloadSize ) { payloadSize = ( ca_uint32_t ) ( strcnt + 1u ); } else { pStr[payloadSize-1] = '\0'; errlogPrintf ( "caserver: read_action: detected DBR_STRING w/o nill termination " "in response from db_get_field, pPayload = \"%s\"\n", pStr ); } } cas_commit_msg ( pClient, payloadSize ); SEND_UNLOCK ( pClient ); return RSRV_OK; } /* * read_notify_action() */ static int read_notify_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { struct channel_in_use *pciu; struct event_ext evext; if ( INVALID_DB_REQ(mp->m_dataType) ) { return RSRV_ERROR; } pciu = MPTOPCIU ( mp ); if ( !pciu ) { logBadId ( client, mp, pPayload ); return RSRV_ERROR; } evext.msg = *mp; evext.pciu = pciu; evext.pdbev = NULL; evext.size = dbr_size_n ( mp->m_dataType, mp->m_count ); /* * Arguments to this routine organized in * favor of the standard db event calling * mechanism- routine(userarg, paddr). See * events added above. * * Hold argument set true so the send message * buffer is not flushed once each call. */ read_reply ( &evext, pciu->dbch, TRUE, NULL ); return RSRV_OK; } /* * write_action() */ static int write_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { struct channel_in_use *pciu; int status; long dbStatus; void *asWritePvt; pciu = MPTOPCIU(mp); if(!pciu){ logBadId(client, mp, pPayload); return RSRV_ERROR; } if(!rsrvCheckPut(pciu)){ status = ECA_NOWTACCESS; SEND_LOCK(client); send_err( mp, status, client, RECORD_NAME ( pciu->dbch )); SEND_UNLOCK(client); return RSRV_OK; } status = caNetConvert ( mp->m_dataType, pPayload, pPayload, FALSE /* net -> host format */, mp->m_count ); if ( status != ECA_NORMAL ) { log_header ("invalid data type", client, mp, pPayload, 0); SEND_LOCK(client); send_err( mp, status, client, RECORD_NAME ( pciu->dbch )); SEND_UNLOCK(client); return RSRV_ERROR; } asWritePvt = asTrapWriteWithData ( pciu->asClientPVT, pciu->client->pUserName ? pciu->client->pUserName : "", pciu->client->pHostName ? pciu->client->pHostName : "", pciu->dbch, mp->m_dataType, mp->m_count, pPayload ); dbStatus = dbChannel_put( pciu->dbch, mp->m_dataType, pPayload, mp->m_count); asTrapWriteAfter(asWritePvt); if (dbStatus < 0) { SEND_LOCK(client); send_err( mp, ECA_PUTFAIL, client, RECORD_NAME ( pciu->dbch )); SEND_UNLOCK(client); } return RSRV_OK; } /* * host_name_action() */ static int host_name_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { ca_uint32_t size; char *pName; char *pMalloc; int chanCount; epicsMutexMustLock ( client->chanListLock ); chanCount = ellCount ( &client->chanList ) + ellCount ( &client->chanPendingUpdateARList ); epicsMutexUnlock( client->chanListLock ); if ( chanCount != 0 ) { SEND_LOCK ( client ); send_err( mp, ECA_INTERNAL, client, "attempts to use protocol to set host name " "after creating first channel ignored by server" ); SEND_UNLOCK ( client ); return RSRV_OK; } pName = (char *) pPayload; size = epicsStrnLen(pName, mp->m_postsize)+1; if (size > 512 || size > mp->m_postsize) { log_header ( "bad (very long) host name", client, mp, pPayload, 0 ); SEND_LOCK(client); send_err( mp, ECA_INTERNAL, client, "bad (very long) host name"); SEND_UNLOCK(client); return RSRV_ERROR; } /* after all validation */ if(asCheckClientIP) { DLOG (2, ( "CAS: host_name_action for \"%s\" ignores client provided host name\n", client->pHostName ) ); return RSRV_OK; } /* * user name will not change if there isnt enough memory */ pMalloc = malloc(size); if(!pMalloc){ log_header ( "no space in pool for new host name", client, mp, pPayload, 0 ); SEND_LOCK(client); send_err( mp, ECA_ALLOCMEM, client, "no space in pool for new host name"); SEND_UNLOCK(client); return RSRV_ERROR; } strncpy( pMalloc, pName, size-1); pMalloc[size-1]='\0'; pName = client->pHostName; client->pHostName = pMalloc; if ( pName ) { free ( pName ); } DLOG (2, ( "CAS: host_name_action for \"%s\"\n", client->pHostName ? client->pHostName : "" ) ); return RSRV_OK; } /* * client_name_action() */ static int client_name_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { ca_uint32_t size; char *pName; char *pMalloc; int chanCount; epicsMutexMustLock ( client->chanListLock ); chanCount = ellCount ( &client->chanList ) + ellCount ( &client->chanPendingUpdateARList ); epicsMutexUnlock( client->chanListLock ); if ( chanCount != 0 ) { SEND_LOCK ( client ); send_err( mp, ECA_INTERNAL, client, "attempts to use protocol to set user name " "after creating first channel ignored by server" ); SEND_UNLOCK ( client ); return RSRV_OK; } pName = (char *) pPayload; size = epicsStrnLen(pName, mp->m_postsize)+1; if (size > 512 || size > mp->m_postsize) { log_header ("a very long user name was specified", client, mp, pPayload, 0); SEND_LOCK(client); send_err( mp, ECA_INTERNAL, client, "a very long user name was specified"); SEND_UNLOCK(client); return RSRV_ERROR; } /* * user name will not change if there isnt enough memory */ pMalloc = malloc(size); if(!pMalloc){ log_header ("no memory for new user name", client, mp, pPayload, 0); SEND_LOCK(client); send_err( mp, ECA_ALLOCMEM, client, "no memory for new user name"); SEND_UNLOCK(client); return RSRV_ERROR; } strncpy( pMalloc, pName, size-1); pMalloc[size-1]='\0'; pName = client->pUserName; client->pUserName = pMalloc; if ( pName ) { free ( pName ); } return RSRV_OK; } /* * casCreateChannel () */ static struct channel_in_use *casCreateChannel ( struct client *client, struct dbChannel *dbch, unsigned cid ) { static unsigned bucketID; unsigned *pCID; struct channel_in_use *pchannel; int status; /* get block off free list if possible */ pchannel = (struct channel_in_use *) freeListCalloc(rsrvChanFreeList); if (!pchannel) { return NULL; } ellInit(&pchannel->eventq); epicsTimeGetCurrent(&pchannel->time_at_creation); pchannel->dbch = dbch; pchannel->client = client; /* * bypass read only warning */ pCID = (unsigned *) &pchannel->cid; *pCID = cid; /* * allocate a server id and enter the channel pointer * in the table * * NOTE: This detects the case where the PV id wraps * around and we attempt to have two resources on the same id. * The lock is applied here because on some architectures the * ++ operator isnt atomic. */ LOCK_CLIENTQ; do { /* * bypass read only warning */ pCID = (unsigned *) &pchannel->sid; *pCID = bucketID++; /* * Verify that this id is not in use */ status = bucketAddItemUnsignedId ( pCaBucket, &pchannel->sid, pchannel); } while (status == S_bucket_idInUse); if ( status == S_bucket_success ) { rsrvChannelCount++; } UNLOCK_CLIENTQ; if(status!=S_bucket_success){ freeListFree(rsrvChanFreeList, pchannel); errMessage (status, "Unable to allocate server id"); return NULL; } epicsMutexMustLock( client->chanListLock ); pchannel->state = rsrvCS_pendConnectResp; ellAdd ( &client->chanList, &pchannel->node ); epicsMutexUnlock ( client->chanListLock ); return pchannel; } /* * casAccessRightsCB() * * If access right state changes then inform the client. * asLock is held */ static void casAccessRightsCB(ASCLIENTPVT ascpvt, asClientStatus type) { struct client * pclient; struct channel_in_use * pciu; struct event_ext * pevext; pciu = asGetClientPvt(ascpvt); assert(pciu); pclient = pciu->client; assert(pclient); if(pclient->proto==IPPROTO_UDP){ return; } switch(type) { case asClientCOAR: { const int readAccess = asCheckGet ( pciu->asClientPVT ); unsigned sigReq = 0; epicsMutexMustLock ( pclient->chanListLock ); if ( pciu->state == rsrvCS_pendConnectResp ) { ellDelete ( &pclient->chanList, &pciu->node ); pciu->state = rsrvCS_pendConnectRespUpdatePendAR; ellAdd ( &pclient->chanPendingUpdateARList, &pciu->node ); sigReq = 1; } else if ( pciu->state == rsrvCS_inService ) { ellDelete ( &pclient->chanList, &pciu->node ); pciu->state = rsrvCS_inServiceUpdatePendAR; ellAdd ( &pclient->chanPendingUpdateARList, &pciu->node ); sigReq = 1; } epicsMutexUnlock ( pclient->chanListLock ); /* * Update all event call backs */ epicsMutexMustLock(pclient->eventqLock); for (pevext = (struct event_ext *) ellFirst(&pciu->eventq); pevext; pevext = (struct event_ext *) ellNext(&pevext->node)){ if ( pevext->pdbev ) { if ( readAccess ){ db_event_enable ( pevext->pdbev ); db_post_single_event ( pevext->pdbev ); } else { db_post_single_event ( pevext->pdbev ); db_event_disable ( pevext->pdbev ); } } } epicsMutexUnlock(pclient->eventqLock); if ( sigReq ) { db_post_extra_labor( pclient->evuser ); } break; } default: break; } } /* * access_rights_reply() */ static void access_rights_reply ( struct channel_in_use * pciu ) { unsigned ar; int status; assert ( pciu->client->proto!=IPPROTO_UDP ); ar = 0; /* none */ if ( asCheckGet ( pciu->asClientPVT ) ) { ar |= CA_PROTO_ACCESS_RIGHT_READ; } if ( rsrvCheckPut ( pciu ) ) { ar |= CA_PROTO_ACCESS_RIGHT_WRITE; } SEND_LOCK ( pciu->client ); status = cas_copy_in_header ( pciu->client, CA_PROTO_ACCESS_RIGHTS, 0, 0, 0, pciu->cid, ar, 0 ); /* * OK to just ignore the request if the connection drops */ if ( status == ECA_NORMAL ) { cas_commit_msg ( pciu->client, 0u ); } SEND_UNLOCK ( pciu->client ); } /* * claim_ciu_reply() */ static void claim_ciu_reply ( struct channel_in_use * pciu ) { int status; ca_uint32_t nElem; long dbElem; access_rights_reply ( pciu ); SEND_LOCK ( pciu->client ); dbElem = dbChannelFinalElements(pciu->dbch); if ( dbElem < 0 ) { nElem = 0; } else { if ( ! CA_V49 ( pciu->client->minor_version_number ) ) { if ( dbElem >= 0xffff ) { nElem = 0xfffe; } else { nElem = (ca_uint32_t) dbElem; } } else { nElem = (ca_uint32_t) dbElem; } } status = cas_copy_in_header ( pciu->client, CA_PROTO_CREATE_CHAN, 0u, dbChannelFinalCAType(pciu->dbch), nElem, pciu->cid, pciu->sid, NULL ); if ( status == ECA_NORMAL ) { cas_commit_msg ( pciu->client, 0u ); } SEND_UNLOCK(pciu->client); } /* * claim_ciu_action() */ static int claim_ciu_action ( caHdrLargeArray *mp, void *pPayload, client *client ) { int status; struct channel_in_use *pciu; struct dbChannel *dbch; char *pName = (char *) pPayload; /* * The available field is used (abused) * here to communicate the miner version number * starting with CA 4.1. The field was set to zero * prior to 4.1 */ client->minor_version_number = mp->m_available; if (!CA_V44(client->minor_version_number)) return RSRV_ERROR; /* shouldn't actually get here due to VSUPPORTED test in camessage() */ /* * check the sanity of the message */ if (mp->m_postsize<=1) { log_header ( "empty PV name in UDP search request?", client, mp, pPayload, 0 ); return RSRV_OK; } pName[mp->m_postsize-1] = '\0'; dbch = dbChannel_create (pName); if (!dbch) { SEND_LOCK(client); status = cas_copy_in_header ( client, CA_PROTO_CREATE_CH_FAIL, 0, 0, 0, mp->m_cid, 0, NULL ); if (status == ECA_NORMAL) cas_commit_msg ( client, 0u ); SEND_UNLOCK(client); return RSRV_OK; } DLOG ( 2, ("CAS: claim_ciu_action found '%s', type %d, count %d\n", pName, dbChannelCAType(dbch), dbChannelElements(dbch)) ); pciu = casCreateChannel ( client, dbch, mp->m_cid); if (!pciu) { log_header ("no memory to create new channel", client, mp, pPayload, 0); SEND_LOCK(client); send_err(mp, ECA_ALLOCMEM, client, RECORD_NAME(dbch)); SEND_UNLOCK(client); dbChannelDelete(dbch); return RSRV_ERROR; } /* * set up access security for this channel */ status = asAddClient( &pciu->asClientPVT, asDbGetMemberPvt(pciu->dbch), asDbGetAsl(pciu->dbch), client->pUserName ? client->pUserName : "", client->pHostName ? client->pHostName : ""); if(status != 0 && status != S_asLib_asNotActive){ log_header ("No room for security table", client, mp, pPayload, 0); SEND_LOCK(client); send_err(mp, ECA_ALLOCMEM, client, "No room for security table"); SEND_UNLOCK(client); return RSRV_ERROR; } /* * store ptr to channel in use block * in access security private */ asPutClientPvt(pciu->asClientPVT, pciu); /* * register for asynch updates of access rights changes */ status = asRegisterClientCallback( pciu->asClientPVT, casAccessRightsCB); if ( status == S_asLib_asNotActive ) { epicsMutexMustLock ( client->chanListLock ); pciu->state = rsrvCS_inService; epicsMutexUnlock ( client->chanListLock ); /* * force the initial AR update followed by claim response */ claim_ciu_reply ( pciu ); } else if (status!=0) { log_header ("No room for access security state change subscription", client, mp, pPayload, 0); SEND_LOCK(client); send_err(mp, ECA_ALLOCMEM, client, "No room for access security state change subscription"); SEND_UNLOCK(client); return RSRV_ERROR; } return RSRV_OK; } /* * write_notify_put_callback() * * (called by the db call back thread) */ LOCAL int write_notify_put_callback(processNotify *ppn,notifyPutType type) { struct channel_in_use * pciu = (struct channel_in_use *) ppn->usrPvt; struct rsrv_put_notify *pNotify; if(ppn->status==notifyCanceled) return 0; /* * No locking in this method because only a dbNotifyCancel could interrupt * and it does not return until cancel is done. */ assert(pciu); assert(pciu->pPutNotify); pNotify = pciu->pPutNotify; return db_put_process(ppn,type, pNotify->dbrType,pNotify->pbuffer,pNotify->nRequest); } /* * write_notify_done_callback() * * (called by the db call back thread) */ LOCAL void write_notify_done_callback(processNotify *ppn) { struct channel_in_use * pciu = (struct channel_in_use *) ppn->usrPvt; struct client * pClient; /* * independent lock used here in order to * avoid any possibility of blocking * the database (or indirectly blocking * one client on another client). */ assert(pciu); assert(pciu->pPutNotify); pClient = pciu->client; epicsMutexMustLock(pClient->putNotifyLock); if ( ! pciu->pPutNotify->busy || pciu->pPutNotify->onExtraLaborQueue ) { epicsMutexUnlock(pClient->putNotifyLock); errlogPrintf("Double DB put notify call back!!\n"); return; } ellAdd(&pClient->putNotifyQue, &pciu->pPutNotify->node); pciu->pPutNotify->onExtraLaborQueue = TRUE; epicsMutexUnlock(pClient->putNotifyLock); /* * offload the labor for this to the * event task so that we never block * the db or another client. */ db_post_extra_labor(pClient->evuser); } /* * write_notify_reply() * (called by the CA server event task via the extra labor interface) */ static void write_notify_reply ( struct client * pClient ) { while(TRUE){ caHdrLargeArray msgtmp; void * asWritePvtTmp; ca_uint32_t status; int localStatus; /* * independent lock used here in order to * avoid any possibility of blocking * the database (or indirectly blocking * one client on another client). */ epicsMutexMustLock(pClient->putNotifyLock); { RSRVPUTNOTIFY * ppnb = (RSRVPUTNOTIFY *) ellGet ( &pClient->putNotifyQue ); if ( ! ppnb ) { epicsMutexUnlock(pClient->putNotifyLock); break; } /* * * Map from DB status to CA status * */ if ( ppnb->dbPutNotify.status != notifyOK ) { status = ECA_PUTFAIL; } else{ status = ECA_NORMAL; } msgtmp = ppnb->msg; asWritePvtTmp = ppnb->asWritePvt; ppnb->asWritePvt = 0; ppnb->onExtraLaborQueue = FALSE; ppnb->busy = FALSE; } epicsMutexUnlock(pClient->putNotifyLock); asTrapWriteAfter ( asWritePvtTmp ); /* * the channel id field is being abused to carry * status here */ SEND_LOCK(pClient); localStatus = cas_copy_in_header ( pClient, CA_PROTO_WRITE_NOTIFY, 0u, msgtmp.m_dataType, msgtmp.m_count, status, msgtmp.m_available, 0 ); if ( localStatus != ECA_NORMAL ) { /* * inability to aquire buffer space * Indicates corruption */ errlogPrintf("CA server corrupted - put call back(s) discarded\n"); SEND_UNLOCK ( pClient ); break; } /* commit the message */ cas_commit_msg ( pClient, 0u ); SEND_UNLOCK ( pClient ); } /* * wakeup the TCP thread if it is waiting for a cb to complete */ epicsEventSignal ( pClient->blockSem ); } /* * sendAllUpdateAS() */ static void sendAllUpdateAS ( struct client *client ) { struct channel_in_use *pciu; epicsMutexMustLock ( client->chanListLock ); pciu = ( struct channel_in_use * ) ellGet ( & client->chanPendingUpdateARList ); while ( pciu ) { if ( pciu->state == rsrvCS_pendConnectRespUpdatePendAR ) { claim_ciu_reply ( pciu ); } else if ( pciu->state == rsrvCS_inServiceUpdatePendAR ) { access_rights_reply ( pciu ); } else if ( pciu->state == rsrvCS_shutdown ) { /* no-op */ } else { errlogPrintf ( "%s at %d: corrupt channel state detected durring AR update\n", __FILE__, __LINE__); } pciu->state = rsrvCS_inService; ellAdd ( & client->chanList, & pciu->node ); pciu = ( struct channel_in_use * ) ellGet ( & client->chanPendingUpdateARList ); } epicsMutexUnlock( client->chanListLock ); } /* * rsrv_extra_labor() * (called by the CA server event task via the extra labor interface) */ void rsrv_extra_labor ( void * pArg ) { struct client * pClient = pArg; write_notify_reply ( pClient ); sendAllUpdateAS ( pClient ); cas_send_bs_msg ( pClient, TRUE ); } /* * putNotifyErrorReply */ static void putNotifyErrorReply ( struct client *client, caHdrLargeArray *mp, int statusCA ) { int status; SEND_LOCK ( client ); /* * the cid field abused to contain status * during put cb replies */ status = cas_copy_in_header ( client, CA_PROTO_WRITE_NOTIFY, 0u, mp->m_dataType, mp->m_count, statusCA, mp->m_available, 0 ); if ( status != ECA_NORMAL ) { SEND_UNLOCK ( client ); errlogPrintf ("%s at %d: should always get sufficent space for put notify error reply\n", __FILE__, __LINE__); return; } cas_commit_msg ( client, 0u ); SEND_UNLOCK ( client ); } void initializePutNotifyFreeList (void) { if ( ! rsrvPutNotifyFreeList ) { freeListInitPvt ( &rsrvPutNotifyFreeList, sizeof(struct rsrv_put_notify), 512 ); assert ( rsrvPutNotifyFreeList ); } } static struct rsrv_put_notify * rsrvAllocPutNotify ( struct channel_in_use * pciu ) { struct rsrv_put_notify *pNotify; if ( rsrvPutNotifyFreeList ) { pNotify = (RSRVPUTNOTIFY *) freeListCalloc ( rsrvPutNotifyFreeList ); if ( pNotify ) { pNotify->pbuffer = &pNotify->dbrScalarValue; pNotify->valueSize = sizeof (pNotify->dbrScalarValue); pNotify->dbPutNotify.usrPvt = pciu; pNotify->dbPutNotify.chan = pciu->dbch; pNotify->dbPutNotify.putCallback = write_notify_put_callback; pNotify->dbPutNotify.doneCallback = write_notify_done_callback; pNotify->dbPutNotify.requestType = putProcessRequest; } } else { pNotify = NULL; } return pNotify; } static int rsrvExpandPutNotify ( struct rsrv_put_notify * pNotify, unsigned sizeNeeded ) { int booleanStatus; if ( sizeNeeded > pNotify->valueSize ) { /* * try to use the union embeded in the free list * item, but allocate a random sized block if they * writing a vector. */ if ( pNotify->valueSize > sizeof (pNotify->dbrScalarValue) ) { free ( pNotify->pbuffer ); } pNotify->pbuffer = casCalloc(1,sizeNeeded); if ( pNotify->pbuffer ) { pNotify->valueSize = sizeNeeded; booleanStatus = TRUE; } else { /* * revert back to the embedded union */ pNotify->pbuffer = &pNotify->dbrScalarValue; pNotify->valueSize = sizeof (pNotify->dbrScalarValue); booleanStatus = FALSE; } } else { booleanStatus = TRUE; } return booleanStatus; } unsigned rsrvSizeOfPutNotify ( struct rsrv_put_notify *pNotify ) { unsigned size = sizeof ( *pNotify ); if ( pNotify ) { if ( pNotify->valueSize > sizeof ( pNotify->dbrScalarValue ) ) { size += pNotify->valueSize; } } return size; } void rsrvFreePutNotify ( client *pClient, struct rsrv_put_notify *pNotify ) { if ( pNotify ) { char busyTmp; void * asWritePvtTmp = 0; epicsMutexMustLock ( pClient->putNotifyLock ); busyTmp = pNotify->busy; epicsMutexUnlock ( pClient->putNotifyLock ); /* * if any possiblity that the put notify is * outstanding then cancel it */ if ( busyTmp ) { dbNotifyCancel ( &pNotify->dbPutNotify ); } epicsMutexMustLock ( pClient->putNotifyLock ); if ( pNotify->onExtraLaborQueue ) { ellDelete ( &pClient->putNotifyQue, &pNotify->node ); } busyTmp = pNotify->busy; asWritePvtTmp = pNotify->asWritePvt; pNotify->asWritePvt = 0; epicsMutexUnlock ( pClient->putNotifyLock ); if ( busyTmp ) { asTrapWriteAfter ( asWritePvtTmp ); } if ( pNotify->valueSize > sizeof(pNotify->dbrScalarValue) ) { free ( pNotify->pbuffer ); } freeListFree ( rsrvPutNotifyFreeList, pNotify ); } } /* * write_notify_action() */ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { unsigned size; int status; struct channel_in_use *pciu; pciu = MPTOPCIU(mp); if(!pciu){ logBadId ( client, mp, pPayload ); return RSRV_ERROR; } if (mp->m_dataType > LAST_BUFFER_TYPE) { log_header ("bad put notify data type", client, mp, pPayload, 0); putNotifyErrorReply (client, mp, ECA_BADTYPE); return RSRV_ERROR; } if(!rsrvCheckPut(pciu)){ putNotifyErrorReply (client, mp, ECA_NOWTACCESS); return RSRV_OK; } size = dbr_size_n (mp->m_dataType, mp->m_count); if ( pciu->pPutNotify ) { /* * serialize concurrent put notifies */ epicsMutexMustLock(client->putNotifyLock); while(pciu->pPutNotify->busy){ epicsMutexUnlock(client->putNotifyLock); status = epicsEventWaitWithTimeout(client->blockSem,60.0); if ( status != epicsEventWaitOK ) { char busyTmp; void * asWritePvtTmp = 0; epicsMutexMustLock(client->putNotifyLock); busyTmp = pciu->pPutNotify->busy; epicsMutexUnlock(client->putNotifyLock); /* * if any possibility of put notify still running * then cancel it */ if ( busyTmp ) { dbNotifyCancel(&pciu->pPutNotify->dbPutNotify); } epicsMutexMustLock(client->putNotifyLock); busyTmp = pciu->pPutNotify->busy; if ( busyTmp ) { if ( pciu->pPutNotify->onExtraLaborQueue ) { ellDelete ( &client->putNotifyQue, &pciu->pPutNotify->node ); } pciu->pPutNotify->busy = FALSE; asWritePvtTmp = pciu->pPutNotify->asWritePvt; pciu->pPutNotify->asWritePvt = 0; } epicsMutexUnlock(client->putNotifyLock); if ( busyTmp ) { log_header("put call back time out", client, &pciu->pPutNotify->msg, pciu->pPutNotify->pbuffer, 0); asTrapWriteAfter ( asWritePvtTmp ); putNotifyErrorReply (client, &pciu->pPutNotify->msg, ECA_PUTCBINPROG); } } epicsMutexMustLock(client->putNotifyLock); } epicsMutexUnlock(client->putNotifyLock); } else { pciu->pPutNotify = rsrvAllocPutNotify ( pciu ); if ( ! pciu->pPutNotify ) { /* * send error and go to next request * if there isnt enough memory left */ log_header ( "no memory to initiate put notify", client, mp, pPayload, 0 ); putNotifyErrorReply (client, mp, ECA_ALLOCMEM); return RSRV_ERROR; } } if ( ! rsrvExpandPutNotify ( pciu->pPutNotify, size ) ) { log_header ( "no memory to initiate vector put notify", client, mp, pPayload, 0 ); putNotifyErrorReply ( client, mp, ECA_ALLOCMEM ); return RSRV_ERROR; } pciu->pPutNotify->busy = TRUE; pciu->pPutNotify->onExtraLaborQueue = FALSE; pciu->pPutNotify->msg = *mp; pciu->pPutNotify->nRequest = mp->m_count; status = caNetConvert ( mp->m_dataType, pPayload, pciu->pPutNotify->pbuffer, FALSE /* net -> host format */, mp->m_count ); if ( status != ECA_NORMAL ) { log_header ("invalid data type", client, mp, pPayload, 0); putNotifyErrorReply ( client, mp, status ); return RSRV_ERROR; } pciu->pPutNotify->dbrType = mp->m_dataType; pciu->pPutNotify->asWritePvt = asTrapWriteWithData ( pciu->asClientPVT, pciu->client->pUserName ? pciu->client->pUserName : "", pciu->client->pHostName ? pciu->client->pHostName : "", pciu->dbch, mp->m_dataType, mp->m_count, pciu->pPutNotify->pbuffer ); dbProcessNotify(&pciu->pPutNotify->dbPutNotify); return RSRV_OK; } /* * * event_add_action() * */ static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client *client) { struct mon_info *pmi = (struct mon_info *) pPayload; int spaceAvailOnFreeList; struct channel_in_use *pciu; struct event_ext *pevext; if ( INVALID_DB_REQ(mp->m_dataType) ) { return RSRV_ERROR; } pciu = MPTOPCIU ( mp ); if ( ! pciu ) { logBadId ( client, mp, pPayload ); return RSRV_ERROR; } /* * stop further use of server if memory becomes scarce */ spaceAvailOnFreeList = freeListItemsAvail ( rsrvEventFreeList ) > 0; if ( osiSufficentSpaceInPool(sizeof(*pevext)) || spaceAvailOnFreeList ) { pevext = (struct event_ext *) freeListCalloc (rsrvEventFreeList); } else { pevext = 0; } if (!pevext) { log_header ("no memory to add subscription", client, mp, pPayload, 0); SEND_LOCK(client); send_err( mp, ECA_ALLOCMEM, client, RECORD_NAME(pciu->dbch)); SEND_UNLOCK(client); return RSRV_ERROR; } pevext->msg = *mp; pevext->pciu = pciu; pevext->size = dbr_size_n(mp->m_dataType, mp->m_count); pevext->mask = ntohs ( pmi->m_mask ); epicsMutexMustLock(client->eventqLock); ellAdd( &pciu->eventq, &pevext->node); epicsMutexUnlock(client->eventqLock); pevext->pdbev = db_add_event (client->evuser, pciu->dbch, read_reply, pevext, pevext->mask); if (pevext->pdbev == NULL) { log_header ("no memory to add subscription to db", client, mp, pPayload, 0); SEND_LOCK(client); send_err (mp, ECA_ALLOCMEM, client, "subscription install into record %s failed", RECORD_NAME(pciu->dbch)); SEND_UNLOCK(client); return RSRV_ERROR; } /* * always send it once at event add */ /* * if the client program issues many monitors * in a row then I recv when the send side * of the socket would block. This prevents * a application program initiated deadlock. * * However when I am reconnecting I reissue * the monitors and I could get deadlocked. * The client is blocked sending and the server * task for the client is blocked sending in * this case. I cant check the recv part of the * socket in the client since I am still handling an * outstanding recv ( they must be processed in order). * I handle this problem in the server by using * post_single_event() below instead of calling * read_reply() in this module. This is a complete * fix since a monitor setup is the only request * soliciting a reply in the client which is * issued from inside of service.c (from inside * of the part of the ca client which services * messages sent by the server). */ DLOG ( 3, ("event_add_action: db_post_single_event (0x%X)\n", pevext->pdbev) ); db_post_single_event(pevext->pdbev); /* * enable future labor if we have read access */ if(asCheckGet(pciu->asClientPVT)){ db_event_enable(pevext->pdbev); } else { DLOG ( 3, ( "Disable event because cannot read\n" ) ); } return RSRV_OK; } /* * clear_channel_reply() */ static int clear_channel_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { struct event_ext *pevext; struct channel_in_use *pciu; int status; /* * * Verify the channel * */ pciu = MPTOPCIU(mp); if(pciu?pciu->client!=client:TRUE){ logBadId ( client, mp, pPayload ); return RSRV_ERROR; } rsrvFreePutNotify ( client, pciu->pPutNotify ); while (TRUE){ epicsMutexMustLock(client->eventqLock); pevext = (struct event_ext *) ellGet(&pciu->eventq); epicsMutexUnlock(client->eventqLock); if(!pevext){ break; } if (pevext->pdbev) { db_cancel_event (pevext->pdbev); } freeListFree(rsrvEventFreeList, pevext); } db_flush_extra_labor_event ( client->evuser ); /* * send delete confirmed message */ SEND_LOCK(client); status = cas_copy_in_header ( client, CA_PROTO_CLEAR_CHANNEL, 0u, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, NULL ); if ( status != ECA_NORMAL ) { SEND_UNLOCK(client); return RSRV_ERROR; } cas_commit_msg ( client, 0u ); SEND_UNLOCK(client); /* * remove from access control list */ status = asRemoveClient(&pciu->asClientPVT); if(status != 0 && status != S_asLib_asNotActive){ errMessage(status, RECORD_NAME(pciu->dbch)); return RSRV_ERROR; } epicsMutexMustLock ( client->chanListLock ); if ( pciu->state == rsrvCS_inService || pciu->state == rsrvCS_pendConnectResp ) { ellDelete ( &client->chanList, &pciu->node ); pciu->state = rsrvCS_shutdown; } else if ( pciu->state == rsrvCS_inServiceUpdatePendAR || pciu->state == rsrvCS_pendConnectRespUpdatePendAR ) { ellDelete ( &client->chanPendingUpdateARList, &pciu->node ); pciu->state = rsrvCS_shutdown; } else if ( pciu->state == rsrvCS_shutdown ) { /* no-op */ } else { epicsMutexUnlock( client->chanListLock ); SEND_LOCK(client); send_err(mp, ECA_INTERNAL, client, "channel was in strange state or corrupted during cleanup"); SEND_UNLOCK(client); return RSRV_ERROR; } epicsMutexUnlock( client->chanListLock ); LOCK_CLIENTQ; status = bucketRemoveItemUnsignedId (pCaBucket, &pciu->sid); if(status != S_bucket_success){ UNLOCK_CLIENTQ; errMessage (status, "Bad resource id during channel clear"); logBadId ( client, mp, pPayload ); return RSRV_ERROR; } rsrvChannelCount--; UNLOCK_CLIENTQ; dbChannelDelete(pciu->dbch); freeListFree(rsrvChanFreeList, pciu); return RSRV_OK; } /* * * event_cancel_reply() * * * Much more efficient now since the event blocks hang off the channel in use * blocks not all together off the client block. */ static int event_cancel_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { struct channel_in_use *pciu; struct event_ext *pevext; int status; /* * * Verify the channel * */ pciu = MPTOPCIU(mp); if (pciu?pciu->client!=client:TRUE) { logBadId ( client, mp, pPayload ); return RSRV_ERROR; } /* * search events on this channel for a match * (there are usually very few monitors per channel) */ epicsMutexMustLock(client->eventqLock); for (pevext = (struct event_ext *) ellFirst(&pciu->eventq); pevext; pevext = (struct event_ext *) ellNext(&pevext->node)){ if (pevext->msg.m_available == mp->m_available) { ellDelete(&pciu->eventq, &pevext->node); break; } } epicsMutexUnlock(client->eventqLock); /* * Not Found- return an exception event */ if(!pevext){ SEND_LOCK(client); send_err(mp, ECA_BADMONID, client, RECORD_NAME(pciu->dbch)); SEND_UNLOCK(client); return RSRV_ERROR; } /* * cancel monitor activity in progress */ if (pevext->pdbev) { db_cancel_event (pevext->pdbev); } /* * send delete confirmed message */ SEND_LOCK(client); status = cas_copy_in_header ( client, pevext->msg.m_cmmd, 0u, pevext->msg.m_dataType, pevext->msg.m_count, pevext->msg.m_cid, pevext->msg.m_available, NULL ); if ( status != ECA_NORMAL ) { SEND_UNLOCK(client); return RSRV_ERROR; } cas_commit_msg ( client, 0 ); SEND_UNLOCK(client); freeListFree (rsrvEventFreeList, pevext); return RSRV_OK; } /* * read_sync_reply() */ static int read_sync_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { int status; SEND_LOCK(client); status = cas_copy_in_header ( client, mp->m_cmmd, 0u, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, NULL ); if ( status != ECA_NORMAL ) { SEND_UNLOCK(client); return RSRV_ERROR; } cas_commit_msg ( client, 0 ); SEND_UNLOCK(client); return RSRV_OK; } /* * search_fail_reply() * * Only when requested by the client * send search failed reply */ static void search_fail_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client) { int status; SEND_LOCK ( client ); status = cas_copy_in_header ( client, CA_PROTO_NOT_FOUND, 0u, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, NULL ); if ( status != ECA_NORMAL ) { SEND_UNLOCK ( client ); errlogPrintf ( "%s at %d: should always get sufficent space for search fail reply?\n", __FILE__, __LINE__ ); return; } cas_commit_msg ( client, 0 ); SEND_UNLOCK ( client ); } /* * udp_version_action() */ static int udp_version_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { client->minor_version_number = mp->m_count; if (!CA_VSUPPORTED(mp->m_count)) { DLOG ( 2, ( "CAS: Ignore version from unsupported client %u\n", mp->m_count ) ); return RSRV_ERROR; } if ( CA_V411 ( mp->m_count ) ) { client->seqNoOfReq = mp->m_cid; } else { client->seqNoOfReq = 0; } return RSRV_OK; } /* * rsrv_version_reply() */ int rsrv_version_reply ( struct client *client ) { int status; SEND_LOCK ( client ); /* * sequence number is specified zero when we copy in the * header because we dont know it until we receive a datagram * from the client */ status = cas_copy_in_header ( client, CA_PROTO_VERSION, 0, 0, CA_MINOR_PROTOCOL_REVISION, 0, 0, 0 ); if ( status != ECA_NORMAL ) { SEND_UNLOCK ( client ); return RSRV_ERROR; } cas_commit_msg ( client, 0 ); SEND_UNLOCK ( client ); return RSRV_OK; } /* * search_reply_udp () */ static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { ca_uint16_t *pMinorVersion; char *pName = (char *) pPayload; int status; unsigned sid; ca_uint16_t count; ca_uint16_t type; int spaceAvailOnFreeList; size_t spaceNeeded; size_t reasonableMonitorSpace = 10; if (!CA_VSUPPORTED(mp->m_count)) { DLOG ( 2, ( "CAS: Ignore search from unsupported client %u\n", mp->m_count ) ); return RSRV_ERROR; } /* * check the sanity of the message */ if (mp->m_postsize<=1) { log_header ("empty PV name in UDP search request?", client, mp, pPayload, 0); return RSRV_OK; } pName[mp->m_postsize-1] = '\0'; /* Exit quickly if channel not on this node */ if (dbChannelTest(pName)) { DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pPayLoad ) ); return RSRV_OK; } /* * stop further use of server if memory becomes scarce */ spaceAvailOnFreeList = freeListItemsAvail ( rsrvChanFreeList ) > 0 && freeListItemsAvail ( rsrvEventFreeList ) > reasonableMonitorSpace; spaceNeeded = sizeof (struct channel_in_use) + reasonableMonitorSpace * sizeof (struct event_ext); if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { return RSRV_ERROR; } /* * starting with V4.4 the count field is used (abused) * to store the minor version number of the client. * * New versions dont alloc the channel in response * to a search request. * For these, allocation has been moved to claim_ciu_action(). * * m_count, m_cid are already in host format... */ if (CA_V44(mp->m_count)) { sid = ~0U; count = 0; type = ca_server_port; } else { /* shouldn't actually get here due to VSUPPORTED test */ return RSRV_ERROR; } SEND_LOCK ( client ); status = cas_copy_in_header ( client, CA_PROTO_SEARCH, sizeof(*pMinorVersion), type, count, sid, mp->m_available, ( void * ) &pMinorVersion ); if ( status != ECA_NORMAL ) { SEND_UNLOCK ( client ); return RSRV_ERROR; } /* * Starting with CA V4.1 the minor version number * is appended to the end of each search reply. * This value is ignored by earlier clients. */ *pMinorVersion = htons ( CA_MINOR_PROTOCOL_REVISION ); cas_commit_msg ( client, sizeof ( *pMinorVersion ) ); SEND_UNLOCK ( client ); return RSRV_OK; } /* * search_reply_tcp () */ static int search_reply_tcp ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { char *pName = (char *) pPayload; int status; int spaceAvailOnFreeList; size_t spaceNeeded; size_t reasonableMonitorSpace = 10; if (!CA_VSUPPORTED(mp->m_count)) { DLOG ( 2, ( "CAS: Ignore search from unsupported client %u\n", mp->m_count ) ); return RSRV_ERROR; } /* * check the sanity of the message */ if (mp->m_postsize<=1) { log_header ("empty PV name in UDP search request?", client, mp, pPayload, 0); return RSRV_OK; } pName[mp->m_postsize-1] = '\0'; /* Exit quickly if channel not on this node */ if (dbChannelTest(pName)) { DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pPayLoad ) ); if (mp->m_dataType == DOREPLY) search_fail_reply ( mp, pPayload, client ); return RSRV_OK; } /* * stop further use of server if memory becomes scarse */ spaceAvailOnFreeList = freeListItemsAvail ( rsrvChanFreeList ) > 0 && freeListItemsAvail ( rsrvEventFreeList ) > reasonableMonitorSpace; spaceNeeded = sizeof (struct channel_in_use) + reasonableMonitorSpace * sizeof (struct event_ext); if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { SEND_LOCK(client); send_err ( mp, ECA_ALLOCMEM, client, "Server memory exhausted" ); SEND_UNLOCK(client); return RSRV_OK; } SEND_LOCK ( client ); status = cas_copy_in_header ( client, CA_PROTO_SEARCH, 0, ca_server_port, 0, ~0U, mp->m_available, 0 ); if ( status != ECA_NORMAL ) { SEND_UNLOCK ( client ); return RSRV_ERROR; } cas_commit_msg ( client, 0 ); SEND_UNLOCK ( client ); return RSRV_OK; } typedef int (*pProtoStubTCP) (caHdrLargeArray *mp, void *pPayload, struct client *client); /* * TCP protocol jump table */ static const pProtoStubTCP tcpJumpTable[] = { tcp_version_action, event_add_action, event_cancel_reply, read_action, write_action, bad_tcp_cmd_action, search_reply_tcp, bad_tcp_cmd_action, events_off_action, events_on_action, read_sync_reply, bad_tcp_cmd_action, clear_channel_reply, bad_tcp_cmd_action, bad_tcp_cmd_action, read_notify_action, bad_tcp_cmd_action, bad_tcp_cmd_action, claim_ciu_action, write_notify_action, client_name_action, host_name_action, bad_tcp_cmd_action, tcp_echo_action, bad_tcp_cmd_action, bad_tcp_cmd_action, bad_tcp_cmd_action, bad_tcp_cmd_action }; /* * UDP protocol jump table */ typedef int (*pProtoStubUDP) (caHdrLargeArray *mp, void *pPayload, struct client *client); static const pProtoStubUDP udpJumpTable[] = { udp_version_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, search_reply_udp, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action }; /* * CAMESSAGE() */ int camessage ( struct client *client ) { unsigned nmsg = 0; unsigned msgsize; unsigned bytes_left; int status = RSRV_ERROR; assert(pCaBucket); /* drain remnents of large messages that will not fit */ if ( client->recvBytesToDrain ) { if ( client->recvBytesToDrain >= client->recv.cnt ) { client->recvBytesToDrain -= client->recv.cnt; client->recv.stk = client->recv.cnt; return RSRV_OK; } else { client->recv.stk += client->recvBytesToDrain; client->recvBytesToDrain = 0u; } } DLOG ( 2, ( "CAS: Parsing %d(decimal) bytes\n", recv->cnt ) ); while ( 1 ) { caHdrLargeArray msg; caHdr *mp; void *pBody; /* wait for at least a complete caHdr */ bytes_left = client->recv.cnt - client->recv.stk; if ( bytes_left < sizeof(*mp) ) { status = RSRV_OK; break; } mp = (caHdr *) &client->recv.buf[client->recv.stk]; msg.m_cmmd = ntohs ( mp->m_cmmd ); msg.m_postsize = ntohs ( mp->m_postsize ); msg.m_dataType = ntohs ( mp->m_dataType ); msg.m_count = ntohs ( mp->m_count ); msg.m_cid = ntohl ( mp->m_cid ); msg.m_available = ntohl ( mp->m_available ); if ( CA_V49(client->minor_version_number) && msg.m_postsize == 0xffff ) { ca_uint32_t *pLW = ( ca_uint32_t * ) ( mp + 1 ); if ( bytes_left < sizeof(*mp) + 2 * sizeof(*pLW) ) { status = RSRV_OK; break; } msg.m_postsize = ntohl ( pLW[0] ); msg.m_count = ntohl ( pLW[1] ); msgsize = msg.m_postsize + sizeof(*mp) + 2 * sizeof ( *pLW ); pBody = ( void * ) ( pLW + 2 ); } else { msgsize = msg.m_postsize + sizeof(*mp); pBody = ( void * ) ( mp + 1 ); } /* ignore deprecated clients, but let newer clients identify themselves. */ if (msg.m_cmmd!=CA_PROTO_VERSION && !CA_VSUPPORTED(client->minor_version_number)) { if (client->proto==IPPROTO_TCP) { /* log and error for too old clients, but keep the connection open to avoid a * re-connect loop. */ SEND_LOCK(client); send_err ( &msg, ECA_DEFUNCT, client, "CAS: Client version %u too old", client->minor_version_number ); SEND_UNLOCK(client); log_header ( "CAS: Client version too old", client, &msg, 0, nmsg ); client->recvBytesToDrain = msgsize - bytes_left; client->recv.stk = client->recv.cnt; status = RSRV_OK; } else { /* silently ignore UDP from old clients */ status = RSRV_ERROR; } break; } /* * disconnect clients that dont send 8 byte * aligned payloads */ if ( msgsize & 0x7 ) { if (client->proto==IPPROTO_TCP) { SEND_LOCK(client); send_err ( &msg, ECA_INTERNAL, client, "CAS: Missaligned protocol rejected" ); SEND_UNLOCK(client); log_header ( "CAS: Missaligned protocol rejected", client, &msg, 0, nmsg ); } status = RSRV_ERROR; break; } /* problem: we have a complete header, * but before we check msgsize we don't know * if we have a complete message body * -> we may be called again with the same header * after receiving the full message */ if ( msgsize > client->recv.maxstk ) { casExpandRecvBuffer ( client, msgsize ); if ( msgsize > client->recv.maxstk ) { if (client->proto==IPPROTO_TCP) { SEND_LOCK(client); send_err ( &msg, ECA_TOLARGE, client, "CAS: Server unable to load large request message. Max bytes=%lu", rsrvSizeofLargeBufTCP ); SEND_UNLOCK(client); log_header ( "CAS: server unable to load large request message", client, &msg, 0, nmsg ); } assert ( client->recv.cnt <= client->recv.maxstk ); assert ( msgsize >= bytes_left ); client->recvBytesToDrain = msgsize - bytes_left; client->recv.stk = client->recv.cnt; status = RSRV_OK; break; } } /* * wait for complete message body */ if ( msgsize > bytes_left ) { status = RSRV_OK; break; } nmsg++; if ( CASDEBUG > 2 ) log_header (NULL, client, &msg, pBody, nmsg); if ( client->proto==IPPROTO_UDP ) { if ( msg.m_cmmd < NELEMENTS ( udpJumpTable ) ) { status = ( *udpJumpTable[msg.m_cmmd] )( &msg, pBody, client ); if (status!=RSRV_OK) { status = RSRV_ERROR; break; } } else { status = bad_udp_cmd_action ( &msg, pBody, client ); break; } } else { if ( msg.m_cmmd < NELEMENTS(tcpJumpTable) ) { status = ( *tcpJumpTable[msg.m_cmmd] ) ( &msg, pBody, client ); if ( status != RSRV_OK ) { status = RSRV_ERROR; break; } } else { return bad_tcp_cmd_action ( &msg, pBody, client ); } } client->recv.stk += msgsize; } return status; } /* * rsrvCheckPut () */ int rsrvCheckPut (const struct channel_in_use *pciu) { /* * SPC_NOMOD fields are always unwritable */ if (dbChannelSpecial(pciu->dbch) == SPC_NOMOD) { return 0; } else { return asCheckPut (pciu->asClientPVT); } } base-7.0.3.1/modules/database/src/ioc/rsrv/camsgtask.c0000664000577000060420000001220313557101274021303 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeffrey O. Hill * hill@luke.lanl.gov * (505) 665 1831 * Date: 6-88 */ #include #include #include #include #include "dbDefs.h" #include "epicsStdio.h" #include "epicsTime.h" #include "errlog.h" #include "osiSock.h" #include "taskwd.h" #include "caerr.h" #define epicsExportSharedSymbols #include "db_access.h" #include "rsrv.h" #include "server.h" /* * camsgtask() * * CA server TCP client task (one spawned for each client) */ void camsgtask ( void *pParm ) { struct client *client = (struct client *) pParm; casAttachThreadToClient ( client ); while (castcp_ctl == ctlRun && !client->disconnect) { osiSockIoctl_t check_nchars; long nchars; int status; /* * allow message to batch up if more are comming */ status = socket_ioctl (client->sock, FIONREAD, &check_nchars); if (status < 0) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf("CAS: FIONREAD error: %s\n", sockErrBuf); cas_send_bs_msg(client, TRUE); } else if (check_nchars == 0){ cas_send_bs_msg(client, TRUE); } client->recv.stk = 0; assert ( client->recv.maxstk >= client->recv.cnt ); nchars = recv ( client->sock, &client->recv.buf[client->recv.cnt], (int) ( client->recv.maxstk - client->recv.cnt ), 0 ); if ( nchars == 0 ){ if ( CASDEBUG > 0 ) { /* convert to u long so that %lu works on both 32 and 64 bit archs */ unsigned long cnt = sizeof ( client->recv.buf ) - client->recv.cnt; errlogPrintf ( "CAS: nill message disconnect ( %lu bytes request )\n", cnt ); } break; } else if ( nchars < 0 ) { int anerrno = SOCKERRNO; if ( anerrno == SOCK_EINTR ) { continue; } if ( anerrno == SOCK_ENOBUFS ) { errlogPrintf ( "CAS: Out of network buffers, retring receive in 15 seconds\n" ); epicsThreadSleep ( 15.0 ); continue; } /* * normal conn lost conditions */ if ( ( anerrno != SOCK_ECONNABORTED && anerrno != SOCK_ECONNRESET && anerrno != SOCK_ETIMEDOUT ) || CASDEBUG > 2 ) { char sockErrBuf[64]; epicsSocketConvertErrorToString( sockErrBuf, sizeof ( sockErrBuf ), anerrno); errlogPrintf ( "CAS: Client disconnected - %s\n", sockErrBuf ); } break; } epicsTimeGetCurrent ( &client->time_at_last_recv ); client->recv.cnt += ( unsigned ) nchars; status = camessage ( client ); if (status == 0) { /* * if there is a partial message * align it with the start of the buffer */ if (client->recv.cnt > client->recv.stk) { unsigned bytes_left; bytes_left = client->recv.cnt - client->recv.stk; /* * overlapping regions handled * properly by memmove */ memmove (client->recv.buf, &client->recv.buf[client->recv.stk], bytes_left); client->recv.cnt = bytes_left; } else { client->recv.cnt = 0ul; } } else { char buf[64]; /* flush any queued messages before shutdown */ cas_send_bs_msg(client, 1); client->recv.cnt = 0ul; /* * disconnect when there are severe message errors */ ipAddrToDottedIP (&client->addr, buf, sizeof(buf)); epicsPrintf ("CAS: forcing disconnect from %s\n", buf); break; } } LOCK_CLIENTQ; ellDelete ( &clientQ, &client->node ); UNLOCK_CLIENTQ; destroy_tcp_client ( client ); } int casClientInitiatingCurrentThread ( char * pBuf, size_t bufSize ) { struct client * pClient = ( struct client * ) epicsThreadPrivateGet ( rsrvCurrentClient ); if ( ! pClient ) return RSRV_ERROR; if ( pBuf && bufSize ) { epicsSnprintf(pBuf, bufSize, "ca:%s@%s", pClient->pUserName, pClient->pHostName); } return RSRV_OK; } base-7.0.3.1/modules/database/src/ioc/rsrv/caserverio.c0000664000577000060420000002736713557101274021511 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeffrey O. Hill * hill@luke.lanl.gov * (505) 665 1831 * Date: 060791 */ #include #include #include #include #include #include #include "dbDefs.h" #include "epicsSignal.h" #include "epicsTime.h" #include "errlog.h" #include "osiSock.h" #include "caerr.h" #include "net_convert.h" #define epicsExportSharedSymbols #include "server.h" /* * cas_send_bs_msg() * * (channel access server send message) * * * Set lock_needed=1 unless SEND_LOCK() is held by caller */ void cas_send_bs_msg ( struct client *pclient, int lock_needed ) { int status; if ( lock_needed ) { SEND_LOCK ( pclient ); } if ( CASDEBUG > 2 && pclient->send.stk ) { errlogPrintf ( "CAS: Sending a message of %d bytes\n", pclient->send.stk ); } if ( pclient->disconnect ) { if ( CASDEBUG > 2 ) { errlogPrintf ( "CAS: msg Discard for sock %d addr %x\n", pclient->sock, (unsigned) pclient->addr.sin_addr.s_addr ); } pclient->send.stk = 0u; if(lock_needed) SEND_UNLOCK(pclient); return; } while ( pclient->send.stk && ! pclient->disconnect ) { status = send ( pclient->sock, pclient->send.buf, pclient->send.stk, 0 ); if ( status >= 0 ) { unsigned transferSize = (unsigned) status; if ( transferSize >= pclient->send.stk ) { pclient->send.stk = 0; epicsTimeGetCurrent ( &pclient->time_at_last_send ); break; } else { unsigned bytesLeft = pclient->send.stk - transferSize; memmove ( pclient->send.buf, &pclient->send.buf[transferSize], bytesLeft ); pclient->send.stk = bytesLeft; } } else { int causeWasSocketHangup = 0; int anerrno = SOCKERRNO; char buf[64]; if ( pclient->disconnect ) { pclient->send.stk = 0u; break; } if ( anerrno == SOCK_EINTR ) { continue; } if ( anerrno == SOCK_ENOBUFS ) { errlogPrintf ( "CAS: Out of network buffers, retrying send in 15 seconds\n" ); epicsThreadSleep ( 15.0 ); continue; } ipAddrToDottedIP ( &pclient->addr, buf, sizeof(buf) ); if ( anerrno == SOCK_ECONNABORTED || anerrno == SOCK_ECONNRESET || anerrno == SOCK_EPIPE || anerrno == SOCK_ETIMEDOUT ) { causeWasSocketHangup = 1; } else { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAS: TCP send to %s failed: %s\n", buf, sockErrBuf); } pclient->disconnect = TRUE; pclient->send.stk = 0u; /* * wakeup the receive thread */ if ( ! causeWasSocketHangup ) { enum epicsSocketSystemCallInterruptMechanismQueryInfo info = epicsSocketSystemCallInterruptMechanismQuery (); switch ( info ) { case esscimqi_socketCloseRequired: if ( pclient->sock != INVALID_SOCKET ) { epicsSocketDestroy ( pclient->sock ); pclient->sock = INVALID_SOCKET; } break; case esscimqi_socketBothShutdownRequired: { int status = shutdown ( pclient->sock, SHUT_RDWR ); if ( status ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ("CAS: Socket shutdown error: %s\n", sockErrBuf ); } } break; case esscimqi_socketSigAlarmRequired: epicsSignalRaiseSigAlarm ( pclient->tid ); break; default: break; }; break; } } } if ( lock_needed ) { SEND_UNLOCK(pclient); } DLOG ( 3, ( "------------------------------\n\n" ) ); return; } /* * cas_send_dg_msg() * * (channel access server send udp message) */ void cas_send_dg_msg ( struct client * pclient ) { int status; int sizeDG; char * pDG; caHdr * pMsg; if ( CASDEBUG > 2 && pclient->send.stk ) { errlogPrintf ( "CAS: Sending a udp message of %d bytes\n", pclient->send.stk ); } SEND_LOCK ( pclient ); if ( pclient->send.stk <= sizeof (caHdr) ) { SEND_UNLOCK(pclient); return; } pDG = pclient->send.buf; pMsg = ( caHdr * ) pDG; sizeDG = pclient->send.stk; assert ( ntohs ( pMsg->m_cmmd ) == CA_PROTO_VERSION ); if ( CA_V411 ( pclient->minor_version_number ) ) { pMsg->m_cid = htonl ( pclient->seqNoOfReq ); pMsg->m_dataType = htons ( sequenceNoIsValid ); } else { pDG += sizeof (caHdr); sizeDG -= sizeof (caHdr); } status = sendto ( pclient->sock, pDG, sizeDG, 0, (struct sockaddr *)&pclient->addr, sizeof(pclient->addr) ); if ( status >= 0 ) { if ( status >= sizeDG ) { epicsTimeGetCurrent ( &pclient->time_at_last_send ); } else { errlogPrintf ( "CAS: System failed to send entire udp frame?\n" ); } } else { char sockErrBuf[64]; char buf[128]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); ipAddrToDottedIP ( &pclient->addr, buf, sizeof(buf) ); errlogPrintf( "CAS: UDP send to %s failed: %s\n", buf, sockErrBuf); } pclient->send.stk = 0u; /* * add placeholder for the first version message should it be needed */ rsrv_version_reply ( pclient ); SEND_UNLOCK(pclient); DLOG ( 3, ( "------------------------------\n\n" ) ); return; } /* * * cas_copy_in_header() * * Allocate space in the outgoing message buffer and * copy in message header. Return pointer to message body. * * send lock must be on while in this routine * * Returns a valid ptr to message body or NULL if the msg * will not fit. */ int cas_copy_in_header ( struct client *pclient, ca_uint16_t response, ca_uint32_t payloadSize, ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, ca_uint32_t responseSpecific, void **ppPayload ) { unsigned msgSize; ca_uint32_t alignedPayloadSize; caHdr *pMsg; if ( payloadSize > UINT_MAX - sizeof ( caHdr ) - 8u ) { return ECA_TOLARGE; } alignedPayloadSize = CA_MESSAGE_ALIGN ( payloadSize ); msgSize = alignedPayloadSize + sizeof ( caHdr ); if ( alignedPayloadSize >= 0xffff || nElem >= 0xffff ) { if ( ! CA_V49 ( pclient->minor_version_number ) ) { return ECA_16KARRAYCLIENT; } msgSize += 2 * sizeof ( ca_uint32_t ); } if ( msgSize > pclient->send.maxstk ) { casExpandSendBuffer ( pclient, msgSize ); if ( msgSize > pclient->send.maxstk ) { return ECA_TOLARGE; } } if ( pclient->send.stk > pclient->send.maxstk - msgSize ) { if ( pclient->disconnect ) { pclient->send.stk = 0; } else{ if ( pclient->proto == IPPROTO_TCP) { cas_send_bs_msg ( pclient, FALSE ); } else if ( pclient->proto == IPPROTO_UDP ) { cas_send_dg_msg ( pclient ); } else { return ECA_INTERNAL; } } } pMsg = (caHdr *) &pclient->send.buf[pclient->send.stk]; pMsg->m_cmmd = htons(response); pMsg->m_dataType = htons(dataType); pMsg->m_cid = htonl(cid); pMsg->m_available = htonl(responseSpecific); if (alignedPayloadSize < 0xffff && nElem < 0xffff) { pMsg->m_postsize = htons(((ca_uint16_t) alignedPayloadSize)); pMsg->m_count = htons(((ca_uint16_t) nElem)); if (ppPayload) *ppPayload = (void *) (pMsg + 1); } else { ca_uint32_t *pW32 = (ca_uint32_t *) (pMsg + 1); pMsg->m_postsize = htons(0xffff); pMsg->m_count = htons(0u); pW32[0] = htonl(alignedPayloadSize); pW32[1] = htonl(nElem); if (ppPayload) *ppPayload = (void *) (pW32 + 2); } /* zero out pad bytes */ if ( alignedPayloadSize > payloadSize ) { char *p = ( char * ) *ppPayload; memset ( p + payloadSize, '\0', alignedPayloadSize - payloadSize ); } return ECA_NORMAL; } void cas_set_header_cid ( struct client *pClient, ca_uint32_t cid ) { caHdr *pMsg = ( caHdr * ) &pClient->send.buf[pClient->send.stk]; pMsg->m_cid = htonl ( cid ); } void cas_set_header_count (struct client *pClient, ca_uint32_t count) { caHdr *pMsg = (caHdr *) &pClient->send.buf[pClient->send.stk]; if (pMsg->m_postsize == htons(0xffff)) { ca_uint32_t *pLW; assert(pMsg->m_count == 0); pLW = (ca_uint32_t *) (pMsg + 1); pLW[1] = htonl(count); } else { assert(count < 65536); pMsg->m_count = htons((ca_uint16_t) count); } } void cas_commit_msg ( struct client *pClient, ca_uint32_t size ) { caHdr * pMsg = ( caHdr * ) &pClient->send.buf[pClient->send.stk]; size = CA_MESSAGE_ALIGN ( size ); if ( pMsg->m_postsize == htons ( 0xffff ) ) { ca_uint32_t * pLW = ( ca_uint32_t * ) ( pMsg + 1 ); assert ( size <= ntohl ( *pLW ) ); pLW[0] = htonl ( size ); size += sizeof ( caHdr ) + 2 * sizeof ( *pLW ); } else { assert ( size <= ntohs ( pMsg->m_postsize ) ); pMsg->m_postsize = htons ( (ca_uint16_t) size ); size += sizeof ( caHdr ); } pClient->send.stk += size; } /* * this assumes that we have already checked to see * if sufficent bytes are available */ ca_uint16_t rsrvGetUInt16 ( struct message_buffer *recv ) { ca_uint8_t *pBuf = ( ca_uint8_t * ) recv->buf; ca_uint16_t result; /* * this assumes that we have already checked to see * if sufficent bytes are available */ assert ( recv->cnt - recv->stk >= 2u ); result = pBuf[recv->stk++] << 8u; result |= pBuf[recv->stk++] << 0u; return result; } /* * this assumes that we have already checked to see * if sufficent bytes are available */ ca_uint32_t rsrvGetUInt32 ( struct message_buffer *recv ) { ca_uint8_t *pBuf = ( ca_uint8_t * ) recv->buf; ca_uint32_t result; /* * this assumes that we have already checked to see * if sufficent bytes are available */ assert ( recv->cnt - recv->stk >= 4u ); result = pBuf[recv->stk++] << 24u; result |= pBuf[recv->stk++] << 16u; result |= pBuf[recv->stk++] << 8u; result |= pBuf[recv->stk++] << 0u; return result; } base-7.0.3.1/modules/database/src/ioc/rsrv/caservertask.c0000664000577000060420000014530713557101274022037 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 Michael Davidsaver * Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven * National Laboratory. * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeffrey O. Hill */ #include #include #include #include #include #include #include "addrList.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "epicsSignal.h" #include "epicsStdio.h" #include "epicsTime.h" #include "errlog.h" #include "freeList.h" #include "osiPoolStatus.h" #include "osiSock.h" #include "taskwd.h" #include "cantProceed.h" #include "epicsExport.h" #define epicsExportSharedSymbols #include "dbChannel.h" #include "dbCommon.h" #include "dbEvent.h" #include "db_field_log.h" #include "dbServer.h" #include "rsrv.h" #define GLBLSOURCE #include "server.h" epicsThreadPrivateId rsrvCurrentClient; /* * * req_server() * * CA server task * * Waits for connections at the CA port and spawns a task to * handle each of them * */ static void req_server (void *pParm) { rsrv_iface_config *conf = pParm; SOCKET IOC_sock; taskwdInsert ( epicsThreadGetIdSelf (), NULL, NULL ); IOC_sock = conf->tcp; /* listen and accept new connections */ if ( listen ( IOC_sock, 20 ) < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAS: Listen error: %s\n", sockErrBuf ); epicsSocketDestroy (IOC_sock); epicsThreadSuspendSelf (); } epicsEventSignal(castcp_startStopEvent); while (TRUE) { SOCKET clientSock; osiSockAddr sockAddr; osiSocklen_t addLen = sizeof(sockAddr); while (castcp_ctl == ctlPause) { epicsThreadSleep(0.1); } clientSock = epicsSocketAccept ( IOC_sock, &sockAddr.sa, &addLen ); if ( clientSock == INVALID_SOCKET || sockAddr.sa.sa_family != AF_INET || addLen < sizeof(sockAddr.ia) ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf("CAS: Client accept error: %s (%d)\n", sockErrBuf, (int)addLen ); epicsThreadSleep(15.0); continue; } else { epicsThreadId id; struct client *pClient; /* socket passed in is closed if unsuccessful here */ pClient = create_tcp_client ( clientSock, &sockAddr ); if ( ! pClient ) { epicsThreadSleep ( 15.0 ); continue; } LOCK_CLIENTQ; ellAdd ( &clientQ, &pClient->node ); UNLOCK_CLIENTQ; id = epicsThreadCreate ( "CAS-client", epicsThreadPriorityCAServerLow, epicsThreadGetStackSize ( epicsThreadStackBig ), camsgtask, pClient ); if ( id == 0 ) { LOCK_CLIENTQ; ellDelete ( &clientQ, &pClient->node ); UNLOCK_CLIENTQ; destroy_tcp_client ( pClient ); errlogPrintf ( "CAS: task creation for new client failed\n" ); epicsThreadSleep ( 15.0 ); continue; } } } } static int tryBind(SOCKET sock, const osiSockAddr* addr, const char *name) { if(bind(sock, (struct sockaddr *) &addr->sa, sizeof(*addr))<0) { char sockErrBuf[64]; if(SOCKERRNO!=SOCK_EADDRINUSE) { epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAS: %s bind error: %s\n", name, sockErrBuf ); epicsThreadSuspendSelf (); } return -1; } else return 0; } /* need to collect a set of TCP sockets, one for each interface, * which are bound to the same TCP port number. * Needed to avoid the complications and confusion of different TCP * ports for each interface (name server and beacon sender would need * to know this). */ static SOCKET* rsrv_grab_tcp(unsigned short *port) { SOCKET *socks; osiSockAddr scratch; unsigned i; socks = mallocMustSucceed(ellCount(&casIntfAddrList)*sizeof(*socks), "rsrv_grab_tcp"); for(i=0; i0) { ELLNODE *cur, *next; unsigned ok = 1; for(i=0; iaddr; scratch.ia.sin_addr = ifaceAddr.ia.sin_addr; tcpsock = socks[i] = epicsSocketCreate (AF_INET, SOCK_STREAM, 0); if(tcpsock==INVALID_SOCKET) cantProceed("rsrv ran out of sockets during initialization"); epicsSocketEnableAddressReuseDuringTimeWaitState ( tcpsock ); if(bind(tcpsock, &scratch.sa, sizeof(scratch))==0) { if(scratch.ia.sin_port==0) { /* use first socket to pick a random port */ osiSocklen_t alen = sizeof(ifaceAddr); assert(i==0); if(getsockname(tcpsock, &ifaceAddr.sa, &alen)) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAS: getsockname error: %s\n", sockErrBuf ); epicsThreadSuspendSelf (); ok = 0; break; } scratch.ia.sin_port = ifaceAddr.ia.sin_port; assert(scratch.ia.sin_port!=0); } } else { int errcode = SOCKERRNO; /* bind fails. React harshly to unexpected errors to avoid an infinite loop */ if(errcode==SOCK_EADDRNOTAVAIL) { /* this is not a bind()able address. */ int j; char name[40]; ipAddrToDottedIP(&scratch.ia, name, sizeof(name)); printf("Skipping %s which is not an interface address\n", name); for(j=0; j<=i; j++) { epicsSocketDestroy(socks[j]); socks[j] = INVALID_SOCKET; } ellDelete(&casIntfAddrList, cur); free(cur); ok = 0; break; } /* if SOCK_EADDRINUSE or SOCK_EACCES try again with a different * port number, otherwise fail hard. */ if (errcode != SOCK_EADDRINUSE && errcode != SOCK_EACCES) { char name[40]; char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); ipAddrToDottedIP(&scratch.ia, name, sizeof(name)); cantProceed( "CAS: Socket bind %s error: %s\n", name, sockErrBuf ); } ok = 0; break; } } if (ok) { assert(scratch.ia.sin_port!=0); *port = ntohs(scratch.ia.sin_port); break; } else { for(i=0; inode) : NULL; pNode; pNode = pNext, pNext = pNext ? (osiSockAddrNode*)ellNext(&pNext->node) : NULL) { osiSockAddr match; epicsUInt32 top = ntohl(pNode->addr.ia.sin_addr.s_addr)>>24; if(pNode->addr.ia.sin_family==AF_INET && pNode->addr.ia.sin_addr.s_addr==htonl(INADDR_ANY)) { foundWildcard = 1; } else if(pNode->addr.ia.sin_family==AF_INET && top>=224 && top<=239) { /* This is a multi-cast address */ ellDelete(&casIntfAddrList, &pNode->node); ellAdd(&casMCastAddrList, &pNode->node); continue; } if(!doautobeacon) continue; /* when given a specific interface address, auto populate with the * corresponding broadcast address. */ autobeaconlist = 0; /* prevent later population from wildcard */ memset(&match, 0, sizeof(match)); match.ia.sin_family = AF_INET; match.ia.sin_addr.s_addr = pNode->addr.ia.sin_addr.s_addr; match.ia.sin_port = htons(ca_beacon_port); osiSockDiscoverBroadcastAddresses(&beaconAddrList, beaconSocket, &match); } if (foundWildcard && ellCount(&casIntfAddrList) != 1) { cantProceed("CAS interface address list can not contain 0.0.0.0 and other interface addresses.\n"); } } if (ellCount(&casIntfAddrList) == 0) { /* default to wildcard 0.0.0.0 when interface address list is empty */ osiSockAddrNode *pNode = (osiSockAddrNode *) callocMustSucceed( 1, sizeof(*pNode), "rsrv_init" ); pNode->addr.ia.sin_family = AF_INET; pNode->addr.ia.sin_addr.s_addr = htonl ( INADDR_ANY ); pNode->addr.ia.sin_port = 0; ellAdd ( &casIntfAddrList, &pNode->node ); } { ELLLIST temp = ELLLIST_INIT; osiSockAddrNode *pNode; ellConcat(&temp, &beaconAddrList); /* collect user specified beacon address list * prefer EPICS_CAS_BEACON_ADDR_LIST, fallback to EPICS_CA_ADDR_LIST */ addAddrToChannelAccessAddressList ( &temp, &EPICS_CAS_BEACON_ADDR_LIST, ca_beacon_port, 0 ); if (autobeaconlist) { /* auto populate with all broadcast addresses. * Note that autobeaconlist is zeroed above if an interface * address list is provided. */ osiSockAddr match; memset(&match, 0, sizeof(match)); match.ia.sin_family = AF_INET; match.ia.sin_addr.s_addr = htonl(INADDR_ANY); match.ia.sin_port = htons(ca_beacon_port); osiSockDiscoverBroadcastAddresses(&temp, beaconSocket, &match); } /* set the port for any automatically discovered destinations. */ for(pNode = (osiSockAddrNode*)ellFirst(&temp); pNode; pNode = (osiSockAddrNode*)ellNext(&pNode->node)) { if(pNode->addr.ia.sin_port==0) pNode->addr.ia.sin_port = htons(ca_beacon_port); } removeDuplicateAddresses(&beaconAddrList, &temp, 0); } if (ellCount(&beaconAddrList)==0) fprintf(stderr, "Warning: RSRV has empty beacon address list\n"); { osiSockAddrNode *node; ELLLIST temp = ELLLIST_INIT, temp2= ELLLIST_INIT; size_t idx = 0; addAddrToChannelAccessAddressList ( &temp, &EPICS_CAS_IGNORE_ADDR_LIST, 0, 0 ); removeDuplicateAddresses(&temp2, &temp, 0); /* Keep the list of addresses to ignore in an array on the assumption that * it is short enough that using a hash table would be slower. * 0.0.0.0 indicates end of list */ casIgnoreAddrs = callocMustSucceed(1+ellCount(&temp2), sizeof(casIgnoreAddrs[0]), "casIgnoreAddrs"); while((node=(osiSockAddrNode*)ellGet(&temp2))!=NULL) { casIgnoreAddrs[idx++] = node->addr.ia.sin_addr.s_addr; free(node); } casIgnoreAddrs[idx] = 0; } } /* * rsrv_init () */ static void rsrv_init (void) { long maxBytesAsALong; long status; SOCKET *socks; int autoMaxBytes; clientQlock = epicsMutexMustCreate(); freeListInitPvt ( &rsrvClientFreeList, sizeof(struct client), 8 ); freeListInitPvt ( &rsrvChanFreeList, sizeof(struct channel_in_use), 512 ); freeListInitPvt ( &rsrvEventFreeList, sizeof(struct event_ext), 512 ); freeListInitPvt ( &rsrvSmallBufFreeListTCP, MAX_TCP, 16 ); initializePutNotifyFreeList (); epicsSignalInstallSigPipeIgnore (); rsrvCurrentClient = epicsThreadPrivateCreate (); if ( envGetConfigParamPtr ( &EPICS_CAS_SERVER_PORT ) ) { ca_server_port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT, (unsigned short) CA_SERVER_PORT ); } else { ca_server_port = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT, (unsigned short) CA_SERVER_PORT ); } ca_udp_port = ca_server_port; if (envGetConfigParamPtr(&EPICS_CAS_BEACON_PORT)) { ca_beacon_port = envGetInetPortConfigParam (&EPICS_CAS_BEACON_PORT, (unsigned short) CA_REPEATER_PORT ); } else { ca_beacon_port = envGetInetPortConfigParam (&EPICS_CA_REPEATER_PORT, (unsigned short) CA_REPEATER_PORT ); } status = envGetLongConfigParam ( &EPICS_CA_MAX_ARRAY_BYTES, &maxBytesAsALong ); if ( status || maxBytesAsALong < 0 ) { errlogPrintf ( "CAS: EPICS_CA_MAX_ARRAY_BYTES was not a positive integer\n" ); rsrvSizeofLargeBufTCP = MAX_TCP; } else { /* allow room for the protocol header so that they get the array size they requested */ static const unsigned headerSize = sizeof ( caHdr ) + 2 * sizeof ( ca_uint32_t ); ca_uint32_t maxBytes = ( unsigned ) maxBytesAsALong; if ( maxBytes < 0xffffffff - headerSize ) { maxBytes += headerSize; } else { maxBytes = 0xffffffff; } if ( maxBytes < MAX_TCP ) { errlogPrintf ( "CAS: EPICS_CA_MAX_ARRAY_BYTES was rounded up to %u\n", MAX_TCP ); rsrvSizeofLargeBufTCP = MAX_TCP; } else { rsrvSizeofLargeBufTCP = maxBytes; } } if(envGetBoolConfigParam(&EPICS_CA_AUTO_ARRAY_BYTES, &autoMaxBytes)) autoMaxBytes = 1; if (!autoMaxBytes) freeListInitPvt ( &rsrvLargeBufFreeListTCP, rsrvSizeofLargeBufTCP, 1 ); else rsrvLargeBufFreeListTCP = NULL; pCaBucket = bucketCreate(CAS_HASH_TABLE_SIZE); if (!pCaBucket) cantProceed("RSRV failed to allocate ID lookup table\n"); rsrv_build_addr_lists(); castcp_startStopEvent = epicsEventMustCreate(epicsEventEmpty); casudp_startStopEvent = epicsEventMustCreate(epicsEventEmpty); beacon_startStopEvent = epicsEventMustCreate(epicsEventEmpty); castcp_ctl = ctlPause; /* Thread priorites * Now starting per interface * TCP Listener: epicsThreadPriorityCAServerLow-2 * Name receiver: epicsThreadPriorityCAServerLow-4 * Now starting global * Beacon sender: epicsThreadPriorityCAServerLow-3 * Started later per TCP client * TCP receiver: epicsThreadPriorityCAServerLow * TCP sender : epicsThreadPriorityCAServerLow-1 */ { unsigned i; threadPrios[0] = epicsThreadPriorityCAServerLow; for(i=1; itcpAddr = ((osiSockAddrNode *)cur)->addr; conf->tcpAddr.ia.sin_port = htons(ca_server_port); conf->tcp = socks[i]; socks[i] = INVALID_SOCKET; ipAddrToDottedIP (&conf->tcpAddr.ia, ifaceName, sizeof(ifaceName)); conf->udp = conf->udpbcast = INVALID_SOCKET; /* create and bind UDP name receiver socket(s) */ conf->udp = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); if(conf->udp==INVALID_SOCKET) cantProceed("rsrv_init ran out of udp sockets"); conf->udpAddr = conf->tcpAddr; conf->udpAddr.ia.sin_port = htons(ca_udp_port); epicsSocketEnableAddressUseForDatagramFanout ( conf->udp ); if(tryBind(conf->udp, &conf->udpAddr, "UDP unicast socket")) goto cleanup; #ifdef IP_ADD_MEMBERSHIP /* join UDP socket to any multicast groups */ { osiSockAddrNode *pNode; for(pNode = (osiSockAddrNode*)ellFirst(&casMCastAddrList); pNode; pNode = (osiSockAddrNode*)ellNext(&pNode->node)) { struct ip_mreq mreq; memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr = pNode->addr.ia.sin_addr; mreq.imr_interface.s_addr = conf->udpAddr.ia.sin_addr.s_addr; if (setsockopt(conf->udp, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq))!=0) { struct sockaddr_in temp; char name[40]; char sockErrBuf[64]; temp.sin_family = AF_INET; temp.sin_addr = mreq.imr_multiaddr; temp.sin_port = conf->udpAddr.ia.sin_port; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); ipAddrToDottedIP (&temp, name, sizeof(name)); errlogPrintf("CAS: Socket mcast join %s to %s failed: %s\n", ifaceName, name, sockErrBuf ); } } } #else if(ellCount(&casMCastAddrList)){ fprintf(stderr, "IPv4 Multicast name lookup not supported by this target\n"); } #endif #if !(defined(_WIN32) || defined(__CYGWIN__)) /* An oddness of BSD sockets (not winsock) is that binding to * INADDR_ANY will receive unicast and broadcast, but binding to * a specific interface address receives only unicast. The trick * is to bind a second socket to the interface broadcast address, * which will then receive only broadcasts. */ if(conf->udpAddr.ia.sin_addr.s_addr!=htonl(INADDR_ANY)) { /* find interface broadcast address */ ELLLIST bcastList = ELLLIST_INIT; osiSockAddrNode *pNode; osiSockDiscoverBroadcastAddresses (&bcastList, conf->udp, &conf->udpAddr); // match addr if(ellCount(&bcastList)==0) { fprintf(stderr, "Warning: Can't find broadcast address of interface %s\n" " Name lookup may not work on this interface\n", ifaceName); } else { if(ellCount(&bcastList)>1 && conf->udpAddr.ia.sin_addr.s_addr!=htonl(INADDR_ANY)) printf("Interface %s has more than one broadcast address?\n", ifaceName); pNode = (osiSockAddrNode*)ellFirst(&bcastList); conf->udpbcast = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); if(conf->udpbcast==INVALID_SOCKET) cantProceed("rsrv_init ran out of udp sockets for bcast"); epicsSocketEnableAddressUseForDatagramFanout ( conf->udpbcast ); conf->udpbcastAddr = conf->udpAddr; conf->udpbcastAddr.ia.sin_addr.s_addr = pNode->addr.ia.sin_addr.s_addr; if(tryBind(conf->udpbcast, &conf->udpbcastAddr, "UDP Socket bcast")) goto cleanup; } ellFree(&bcastList); } #endif /* !(defined(_WIN32) || defined(__CYGWIN__)) */ ellAdd(&servers, &conf->node); /* have all sockets, time to start some threads */ epicsThreadMustCreate("CAS-TCP", threadPrios[2], epicsThreadGetStackSize(epicsThreadStackMedium), &req_server, conf); epicsEventMustWait(castcp_startStopEvent); epicsThreadMustCreate("CAS-UDP", threadPrios[4], epicsThreadGetStackSize(epicsThreadStackMedium), &cast_server, conf); epicsEventMustWait(casudp_startStopEvent); #if !(defined(_WIN32) || defined(__CYGWIN__)) if(conf->udpbcast != INVALID_SOCKET) { conf->startbcast = 1; epicsThreadMustCreate("CAS-UDP2", threadPrios[4], epicsThreadGetStackSize(epicsThreadStackMedium), &cast_server, conf); epicsEventMustWait(casudp_startStopEvent); conf->startbcast = 0; } #endif /* !(defined(_WIN32) || defined(__CYGWIN__)) */ havesometcp = 1; continue; cleanup: epicsSocketDestroy(conf->tcp); if(conf->udp!=INVALID_SOCKET) epicsSocketDestroy(conf->udp); if(conf->udpbcast!=INVALID_SOCKET) epicsSocketDestroy(conf->udpbcast); free(conf); } if(!havesometcp) cantProceed("CAS: No TCP server started\n"); } /* servers list is considered read-only from this point */ epicsThreadMustCreate("CAS-beacon", threadPrios[3], epicsThreadGetStackSize(epicsThreadStackSmall), &rsrv_online_notify_task, NULL); epicsEventMustWait(beacon_startStopEvent); } static void rsrv_run (void) { castcp_ctl = ctlRun; casudp_ctl = ctlRun; beacon_ctl = ctlRun; } static void rsrv_pause (void) { beacon_ctl = ctlPause; casudp_ctl = ctlPause; castcp_ctl = ctlPause; } static unsigned countChanListBytes ( struct client *client, ELLLIST * pList ) { struct channel_in_use * pciu; unsigned bytes_reserved = 0; epicsMutexMustLock ( client->chanListLock ); pciu = ( struct channel_in_use * ) pList->node.next; while ( pciu ) { bytes_reserved += sizeof(struct channel_in_use); bytes_reserved += sizeof(struct event_ext)*ellCount( &pciu->eventq ); bytes_reserved += rsrvSizeOfPutNotify ( pciu->pPutNotify ); pciu = ( struct channel_in_use * ) ellNext( &pciu->node ); } epicsMutexUnlock ( client->chanListLock ); return bytes_reserved; } static void showChanList ( struct client * client, unsigned level, ELLLIST * pList ) { struct channel_in_use * pciu; epicsMutexMustLock ( client->chanListLock ); pciu = (struct channel_in_use *) pList->node.next; while ( pciu ){ dbChannelShow ( pciu->dbch, level, 8 ); if ( level >= 1u ) printf( "%12s# on eventq=%d, access=%c%c\n", "", ellCount ( &pciu->eventq ), asCheckGet ( pciu->asClientPVT ) ? 'r': '-', rsrvCheckPut ( pciu ) ? 'w': '-' ); pciu = ( struct channel_in_use * ) ellNext ( &pciu->node ); } epicsMutexUnlock ( client->chanListLock ); } /* * log_one_client () */ static void log_one_client (struct client *client, unsigned level) { char clientIP[40]; int n; ipAddrToDottedIP (&client->addr, clientIP, sizeof(clientIP)); if ( client->proto == IPPROTO_UDP ) { printf ( "\tLast name requested by %s:\n", clientIP ); } else if ( client->proto == IPPROTO_TCP ) { printf ( " TCP client at %s '%s':\n", clientIP, client->pHostName ? client->pHostName : "" ); } else { printf ( " Unknown client at %s '%s':\n", clientIP, client->pHostName ? client->pHostName : "" ); } n = ellCount(&client->chanList) + ellCount(&client->chanPendingUpdateARList); printf ( "\tUser '%s', V%u.%u, Priority = %u, %d Channel%s\n", client->pUserName ? client->pUserName : "", CA_MAJOR_PROTOCOL_REVISION, client->minor_version_number, client->priority, n, n == 1 ? "" : "s" ); if ( level >= 3u ) { double send_delay; double recv_delay; char *state[] = {"up", "down"}; epicsTimeStamp current; epicsTimeGetCurrent(¤t); send_delay = epicsTimeDiffInSeconds(¤t,&client->time_at_last_send); recv_delay = epicsTimeDiffInSeconds(¤t,&client->time_at_last_recv); printf ("\tTask Id = %p, Socket FD = %d\n", (void *) client->tid, (int)client->sock); printf( "\t%.2f secs since last send, %.2f secs since last receive\n", send_delay, recv_delay); printf( "\tUnprocessed request bytes = %u, Undelivered response bytes = %u\n", client->recv.cnt - client->recv.stk, client->send.stk ); printf( "\tState = %s%s%s\n", state[client->disconnect?1:0], client->send.type == mbtLargeTCP ? " jumbo-send-buf" : "", client->recv.type == mbtLargeTCP ? " jumbo-recv-buf" : ""); } if ( level >= 1u ) { showChanList ( client, level - 1u, & client->chanList ); showChanList ( client, level - 1u, & client->chanPendingUpdateARList ); } if ( level >= 4u ) { unsigned bytes_reserved = sizeof(struct client); bytes_reserved += countChanListBytes ( client, & client->chanList ); bytes_reserved += countChanListBytes ( client, & client->chanPendingUpdateARList ); printf( "\t%d bytes allocated\n", bytes_reserved); printf( "\tSend Lock:\n\t "); epicsMutexShow(client->lock,1); printf( "\tPut Notify Lock:\n\t "); epicsMutexShow (client->putNotifyLock,1); printf( "\tAddress Queue Lock:\n\t "); epicsMutexShow (client->chanListLock,1); printf( "\tEvent Queue Lock:\n\t "); epicsMutexShow (client->eventqLock,1); printf( "\tBlock Semaphore:\n\t "); epicsEventShow (client->blockSem,1); } } /* * casr() */ void casr (unsigned level) { size_t bytes_reserved; int n; if ( ! clientQlock ) { return; } printf ("Channel Access Server V%s\n", CA_VERSION_STRING ( CA_MINOR_PROTOCOL_REVISION ) ); LOCK_CLIENTQ n = ellCount ( &clientQ ); if (n == 0) { printf("No clients connected.\n"); } else if (level == 0) { printf("%d client%s connected.\n", n, n == 1 ? "" : "s" ); } else { struct client *client = (struct client *) ellFirst ( &clientQ ); printf("%d client%s connected:\n", n, n == 1 ? "" : "s" ); while (client) { log_one_client(client, level - 1); client = (struct client *) ellNext(&client->node); } } UNLOCK_CLIENTQ if (level>=1) { rsrv_iface_config *iface = (rsrv_iface_config *) ellFirst ( &servers ); while (iface) { char buf[40]; ipAddrToDottedIP (&iface->tcpAddr.ia, buf, sizeof(buf)); printf("CAS-TCP server on %s with\n", buf); ipAddrToDottedIP (&iface->udpAddr.ia, buf, sizeof(buf)); #if defined(_WIN32) printf(" CAS-UDP name server on %s\n", buf); if (level >= 2) log_one_client(iface->client, level - 2); #else if (iface->udpbcast==INVALID_SOCKET) { printf(" CAS-UDP name server on %s\n", buf); if (level >= 2) log_one_client(iface->client, level - 2); } else { printf(" CAS-UDP unicast name server on %s\n", buf); if (level >= 2) log_one_client(iface->client, level - 2); ipAddrToDottedIP (&iface->udpbcastAddr.ia, buf, sizeof(buf)); printf(" CAS-UDP broadcast name server on %s\n", buf); if (level >= 2) log_one_client(iface->bclient, level - 2); } #endif iface = (rsrv_iface_config *) ellNext(&iface->node); } } if (level>=1) { osiSockAddrNode * pAddr; char buf[40]; int n = ellCount(&casMCastAddrList); if (n) { printf("Monitoring %d multicast address%s:\n", n, n == 1 ? "" : "es"); for(pAddr = (osiSockAddrNode*)ellFirst(&casMCastAddrList); pAddr; pAddr = (osiSockAddrNode*)ellNext(&pAddr->node)) { ipAddrToDottedIP (&pAddr->addr.ia, buf, sizeof(buf)); printf(" %s\n", buf); } } n = ellCount(&beaconAddrList); printf("Sending CAS-beacons to %d address%s:\n", n, n == 1 ? "" : "es"); for(pAddr = (osiSockAddrNode*)ellFirst(&beaconAddrList); pAddr; pAddr = (osiSockAddrNode*)ellNext(&pAddr->node)) { ipAddrToDottedIP (&pAddr->addr.ia, buf, sizeof(buf)); printf(" %s\n", buf); } if (casIgnoreAddrs[0]) { /* 0 indicates end of array */ size_t i; printf("Ignoring UDP messages from address%s\n", n == 1 ? "" : "es"); for(i=0; casIgnoreAddrs[i]; i++) { struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = casIgnoreAddrs[i]; addr.sin_port = 0; ipAddrToDottedIP(&addr, buf, sizeof(buf)); printf(" %s\n", buf); } } } if (level>=4u) { bytes_reserved = 0u; bytes_reserved += sizeof (struct client) * freeListItemsAvail (rsrvClientFreeList); bytes_reserved += sizeof (struct channel_in_use) * freeListItemsAvail (rsrvChanFreeList); bytes_reserved += sizeof(struct event_ext) * freeListItemsAvail (rsrvEventFreeList); bytes_reserved += MAX_TCP * freeListItemsAvail ( rsrvSmallBufFreeListTCP ); if(rsrvLargeBufFreeListTCP) { bytes_reserved += rsrvSizeofLargeBufTCP * freeListItemsAvail ( rsrvLargeBufFreeListTCP ); } bytes_reserved += rsrvSizeOfPutNotify ( 0 ) * freeListItemsAvail ( rsrvPutNotifyFreeList ); printf( "Free-lists total %u bytes, comprising\n", (unsigned int) bytes_reserved); printf( " %u client(s), %u channel(s), %u monitor event(s), %u putNotify(s)\n", (unsigned int) freeListItemsAvail ( rsrvClientFreeList ), (unsigned int) freeListItemsAvail ( rsrvChanFreeList ), (unsigned int) freeListItemsAvail ( rsrvEventFreeList ), (unsigned int) freeListItemsAvail ( rsrvPutNotifyFreeList )); printf( " %u small (%u byte) buffers, %u jumbo (%u byte) buffers\n", (unsigned int) freeListItemsAvail ( rsrvSmallBufFreeListTCP ), MAX_TCP, (unsigned int)(rsrvLargeBufFreeListTCP ? freeListItemsAvail ( rsrvLargeBufFreeListTCP ) : -1), rsrvSizeofLargeBufTCP ); printf( "Server resource id table:\n"); LOCK_CLIENTQ; bucketShow (pCaBucket); UNLOCK_CLIENTQ; } } /* * destroy_client () */ void destroy_client ( struct client *client ) { if ( ! client ) { return; } if ( client->tid != 0 ) { taskwdRemove ( client->tid ); } if ( client->sock != INVALID_SOCKET ) { epicsSocketDestroy ( client->sock ); } if ( client->proto == IPPROTO_TCP ) { if ( client->send.buf ) { if ( client->send.type == mbtSmallTCP ) { freeListFree ( rsrvSmallBufFreeListTCP, client->send.buf ); } else if ( client->send.type == mbtLargeTCP ) { if(rsrvLargeBufFreeListTCP) freeListFree ( rsrvLargeBufFreeListTCP, client->send.buf ); else free(client->send.buf); } else { errlogPrintf ( "CAS: Corrupt send buffer free list type code=%u during client cleanup?\n", client->send.type ); } } if ( client->recv.buf ) { if ( client->recv.type == mbtSmallTCP ) { freeListFree ( rsrvSmallBufFreeListTCP, client->recv.buf ); } else if ( client->recv.type == mbtLargeTCP ) { if(rsrvLargeBufFreeListTCP) freeListFree ( rsrvLargeBufFreeListTCP, client->recv.buf ); else free(client->recv.buf); } else { errlogPrintf ( "CAS: Corrupt recv buffer free list type code=%u during client cleanup?\n", client->send.type ); } } } else if ( client->proto == IPPROTO_UDP ) { if ( client->send.buf ) { free ( client->send.buf ); } if ( client->recv.buf ) { free ( client->recv.buf ); } } if ( client->eventqLock ) { epicsMutexDestroy ( client->eventqLock ); } if ( client->chanListLock ) { epicsMutexDestroy ( client->chanListLock ); } if ( client->putNotifyLock ) { epicsMutexDestroy ( client->putNotifyLock ); } if ( client->lock ) { epicsMutexDestroy ( client->lock ); } if ( client->blockSem ) { epicsEventDestroy ( client->blockSem ); } if ( client->pUserName ) { free ( client->pUserName ); } if ( client->pHostName ) { free ( client->pHostName ); } freeListFree ( rsrvClientFreeList, client ); } static void destroyAllChannels ( struct client * client, ELLLIST * pList ) { if ( !client->chanListLock || !client->eventqLock ) { return; } while ( TRUE ) { struct event_ext *pevext; int status; struct channel_in_use *pciu; epicsMutexMustLock ( client->chanListLock ); pciu = (struct channel_in_use *) ellGet ( pList ); if(pciu) pciu->state = rsrvCS_shutdown; epicsMutexUnlock ( client->chanListLock ); if ( ! pciu ) { break; } while ( TRUE ) { /* * AS state change could be using this list */ epicsMutexMustLock ( client->eventqLock ); pevext = (struct event_ext *) ellGet ( &pciu->eventq ); epicsMutexUnlock ( client->eventqLock ); if ( ! pevext ) { break; } if ( pevext->pdbev ) { db_cancel_event (pevext->pdbev); } freeListFree (rsrvEventFreeList, pevext); } rsrvFreePutNotify ( client, pciu->pPutNotify ); LOCK_CLIENTQ; status = bucketRemoveItemUnsignedId ( pCaBucket, &pciu->sid); rsrvChannelCount--; UNLOCK_CLIENTQ; if ( status != S_bucket_success ) { errPrintf ( status, __FILE__, __LINE__, "Bad id=%d at close", pciu->sid); } status = asRemoveClient(&pciu->asClientPVT); if ( status && status != S_asLib_asNotActive ) { printf ( "bad asRemoveClient() status was %x \n", status ); errPrintf ( status, __FILE__, __LINE__, "asRemoveClient" ); } dbChannelDelete(pciu->dbch); freeListFree ( rsrvChanFreeList, pciu ); } } void destroy_tcp_client ( struct client *client ) { int status; if ( CASDEBUG > 0 ) { errlogPrintf ( "CAS: Connection %d Terminated\n", (int)client->sock ); } if ( client->evuser ) { /* * turn off extra labor callbacks from the event thread */ status = db_add_extra_labor_event ( client->evuser, NULL, NULL ); assert ( ! status ); /* * wait for extra labor in progress to comple */ db_flush_extra_labor_event ( client->evuser ); } destroyAllChannels ( client, & client->chanList ); destroyAllChannels ( client, & client->chanPendingUpdateARList ); if ( client->evuser ) { db_close_events (client->evuser); } destroy_client ( client ); } /* * create_client () */ struct client * create_client ( SOCKET sock, int proto ) { struct client *client; int spaceAvailOnFreeList; size_t spaceNeeded; /* * stop further use of server if memory becomes scarse */ spaceAvailOnFreeList = freeListItemsAvail ( rsrvClientFreeList ) > 0 && freeListItemsAvail ( rsrvSmallBufFreeListTCP ) > 0; spaceNeeded = sizeof (struct client) + MAX_TCP; if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { epicsSocketDestroy ( sock ); epicsPrintf ("CAS: no space in pool for a new client (below max block thresh)\n"); return NULL; } client = freeListCalloc ( rsrvClientFreeList ); if ( ! client ) { epicsSocketDestroy ( sock ); epicsPrintf ("CAS: no space in pool for a new client (alloc failed)\n"); return NULL; } client->sock = sock; client->proto = proto; client->blockSem = epicsEventCreate ( epicsEventEmpty ); client->lock = epicsMutexCreate(); client->putNotifyLock = epicsMutexCreate(); client->chanListLock = epicsMutexCreate(); client->eventqLock = epicsMutexCreate(); if ( ! client->blockSem || ! client->lock || ! client->putNotifyLock || ! client->chanListLock || ! client->eventqLock ) { destroy_client ( client ); return NULL; } client->pUserName = NULL; client->pHostName = NULL; ellInit ( & client->chanList ); ellInit ( & client->chanPendingUpdateARList ); ellInit ( & client->putNotifyQue ); memset ( (char *)&client->addr, 0, sizeof (client->addr) ); client->tid = 0; if ( proto == IPPROTO_TCP ) { client->send.buf = (char *) freeListCalloc ( rsrvSmallBufFreeListTCP ); client->send.maxstk = MAX_TCP; client->send.type = mbtSmallTCP; client->recv.buf = (char *) freeListCalloc ( rsrvSmallBufFreeListTCP ); client->recv.maxstk = MAX_TCP; client->recv.type = mbtSmallTCP; } else if ( proto == IPPROTO_UDP ) { client->send.buf = malloc ( MAX_UDP_SEND ); client->send.maxstk = MAX_UDP_SEND; client->send.type = mbtUDP; client->recv.buf = malloc ( MAX_UDP_RECV ); client->recv.maxstk = MAX_UDP_RECV; client->recv.type = mbtUDP; } if ( ! client->send.buf || ! client->recv.buf ) { destroy_client ( client ); return NULL; } client->send.stk = 0u; client->send.cnt = 0u; client->recv.stk = 0u; client->recv.cnt = 0u; client->evuser = NULL; client->priority = CA_PROTO_PRIORITY_MIN; client->disconnect = FALSE; epicsTimeGetCurrent ( &client->time_at_last_send ); epicsTimeGetCurrent ( &client->time_at_last_recv ); client->minor_version_number = CA_UKN_MINOR_VERSION; client->recvBytesToDrain = 0u; return client; } void casAttachThreadToClient ( struct client *pClient ) { epicsSignalInstallSigAlarmIgnore (); epicsSignalInstallSigPipeIgnore (); pClient->tid = epicsThreadGetIdSelf (); epicsThreadPrivateSet ( rsrvCurrentClient, pClient ); taskwdInsert ( pClient->tid, NULL, NULL ); } static void casExpandBuffer ( struct message_buffer *buf, ca_uint32_t size, int sendbuf ) { char *newbuf = NULL; unsigned newsize; enum messageBufferType newtype; assert (size > MAX_TCP); if ( size <= buf->maxstk || buf->type == mbtUDP ) return; /* try to alloc new buffer */ if (size <= MAX_TCP) { return; /* shouldn't happen */ } else if(!rsrvLargeBufFreeListTCP) { // round up to multiple of 4K size = ((size-1)|0xfff)+1; if (buf->type==mbtLargeTCP) { newbuf = realloc (buf->buf, size); if(newbuf) buf->buf = newbuf; } else { newbuf = malloc (size); } newtype = mbtLargeTCP; newsize = size; } else if (size <= rsrvSizeofLargeBufTCP) { newbuf = freeListCalloc ( rsrvLargeBufFreeListTCP ); newsize = rsrvSizeofLargeBufTCP; newtype = mbtLargeTCP; } if (newbuf) { /* copy existing buffer */ if (sendbuf) { /* send buffer uses [0, stk) */ if (!rsrvLargeBufFreeListTCP && buf->type==mbtLargeTCP) { /* realloc already copied */ } else { memcpy ( newbuf, buf->buf, buf->stk ); } } else { /* recv buffer uses [stk, cnt) */ unsigned used; assert ( buf->cnt >= buf->stk ); used = buf->cnt - buf->stk; /* buf->buf may be the same as newbuf if realloc() used */ memmove ( newbuf, &buf->buf[buf->stk], used ); buf->cnt = used; buf->stk = 0; } /* free existing buffer */ if(buf->type==mbtSmallTCP) { freeListFree ( rsrvSmallBufFreeListTCP, buf->buf ); } else if(rsrvLargeBufFreeListTCP && buf->type==mbtLargeTCP) { freeListFree ( rsrvLargeBufFreeListTCP, buf->buf ); } else { /* realloc() already free()'d if necessary */ } buf->buf = newbuf; buf->type = newtype; buf->maxstk = newsize; } } void casExpandSendBuffer ( struct client *pClient, ca_uint32_t size ) { casExpandBuffer (&pClient->send, size, 1); } void casExpandRecvBuffer ( struct client *pClient, ca_uint32_t size ) { casExpandBuffer (&pClient->recv, size, 0); } /* * create_tcp_client () */ struct client *create_tcp_client (SOCKET sock , const osiSockAddr *peerAddr) { int status; struct client *client; int intTrue = TRUE; unsigned priorityOfEvents; /* socket passed in is destroyed here if unsuccessful */ client = create_client ( sock, IPPROTO_TCP ); if ( ! client ) { return NULL; } client->addr = peerAddr->ia; if(asCheckClientIP) { epicsUInt32 ip = ntohl(client->addr.sin_addr.s_addr); client->pHostName = malloc(24); if(!client->pHostName) { destroy_client ( client ); return NULL; } epicsSnprintf(client->pHostName, 24, "%u.%u.%u.%u", (ip>>24)&0xff, (ip>>16)&0xff, (ip>>8)&0xff, (ip>>0)&0xff); } /* * see TCP(4P) this seems to make unsolicited single events much * faster. I take care of queue up as load increases. */ status = setsockopt ( sock, IPPROTO_TCP, TCP_NODELAY, (char *) &intTrue, sizeof (intTrue) ); if (status < 0) { errlogPrintf ( "CAS: TCP_NODELAY option set failed\n" ); destroy_client ( client ); return NULL; } /* * turn on KEEPALIVE so if the client crashes * this task will find out and exit */ status = setsockopt ( sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &intTrue, sizeof (intTrue) ); if ( status < 0 ) { errlogPrintf ( "CAS: SO_KEEPALIVE option set failed\n" ); destroy_client ( client ); return NULL; } /* * some concern that vxWorks will run out of mBuf's * if this change is made * * joh 11-10-98 */ #if 0 /* * set TCP buffer sizes to be synergistic * with CA internal buffering */ i = MAX_MSG_SIZE; status = setsockopt ( sock, SOL_SOCKET, SO_SNDBUF, (char *) &i, sizeof (i) ); if (status < 0) { errlogPrintf ( "CAS: SO_SNDBUF set failed\n" ); destroy_client ( client ); return NULL; } i = MAX_MSG_SIZE; status = setsockopt ( sock, SOL_SOCKET, SO_RCVBUF, (char *) &i, sizeof (i) ); if (status < 0) { errlogPrintf ( "CAS: SO_RCVBUF set failed\n" ); destroy_client ( client ); return NULL; } #endif client->evuser = (struct event_user *) db_init_events (); if ( ! client->evuser ) { errlogPrintf ("CAS: unable to init the event facility\n"); destroy_tcp_client (client); return NULL; } status = db_add_extra_labor_event ( client->evuser, rsrv_extra_labor, client ); if (status != DB_EVENT_OK) { errlogPrintf("CAS: unable to setup the event facility\n"); destroy_tcp_client (client); return NULL; } { epicsThreadBooleanStatus tbs; tbs = epicsThreadHighestPriorityLevelBelow ( epicsThreadPriorityCAServerLow, &priorityOfEvents ); if ( tbs != epicsThreadBooleanStatusSuccess ) { priorityOfEvents = epicsThreadPriorityCAServerLow; } } status = db_start_events ( client->evuser, "CAS-event", NULL, NULL, priorityOfEvents ); if ( status != DB_EVENT_OK ) { errlogPrintf ( "CAS: unable to start the event facility\n" ); destroy_tcp_client ( client ); return NULL; } /* * add first version message should it be needed */ rsrv_version_reply ( client ); if ( CASDEBUG > 0 ) { char buf[64]; ipAddrToDottedIP ( &client->addr, buf, sizeof(buf) ); errlogPrintf ( "CAS: conn req from %s\n", buf ); } return client; } void casStatsFetch ( unsigned *pChanCount, unsigned *pCircuitCount ) { LOCK_CLIENTQ; { int circuitCount = ellCount ( &clientQ ); if ( circuitCount < 0 ) { *pCircuitCount = 0; } else { *pCircuitCount = (unsigned) circuitCount; } *pChanCount = rsrvChannelCount; } UNLOCK_CLIENTQ; } static dbServer rsrv_server = { ELLNODE_INIT, "rsrv", casr, casStatsFetch, casClientInitiatingCurrentThread, rsrv_init, rsrv_run, rsrv_pause }; void rsrv_register_server(void) { dbRegisterServer(&rsrv_server); } base-7.0.3.1/modules/database/src/ioc/rsrv/cast_server.c0000664000577000060420000002056413557101274021657 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeffrey O. Hill * hill@luke.lanl.gov * (505) 665 1831 * Date: 5-88 * * Improvements * ------------ * .01 * Dont send channel found message unless there is memory, a task slot, * and a TCP socket available. Send a diagnostic instead. * Or ... make the timeout shorter? This is only a problem if * they persist in trying to make a connection after getting no * response. * * Notes: * ------ * .01 * Replies to broadcasts are not returned over * an existing TCP connection to avoid a TCP * pend which could lock up the cast server. */ #include #include #include #include #include #include "dbDefs.h" #include "envDefs.h" #include "epicsMutex.h" #include "epicsTime.h" #include "errlog.h" #include "freeList.h" #include "osiSock.h" #include "taskwd.h" #define epicsExportSharedSymbols #include "rsrv.h" #include "server.h" #define TIMEOUT 60.0 /* sec */ /* * clean_addrq */ static void clean_addrq(struct client *client) { struct channel_in_use * pciu; struct channel_in_use * pnextciu; epicsTimeStamp current; double delay; double maxdelay = 0; unsigned ndelete=0; double timeout = TIMEOUT; int s; epicsTimeGetCurrent ( ¤t ); epicsMutexMustLock ( client->chanListLock ); pnextciu = (struct channel_in_use *) client->chanList.node.next; while( (pciu = pnextciu) ) { pnextciu = (struct channel_in_use *)pciu->node.next; delay = epicsTimeDiffInSeconds(¤t,&pciu->time_at_creation); if (delay > timeout) { ellDelete(&client->chanList, &pciu->node); LOCK_CLIENTQ; s = bucketRemoveItemUnsignedId ( pCaBucket, &pciu->sid); if(s){ errMessage (s, "Bad id at close"); } else { rsrvChannelCount--; } UNLOCK_CLIENTQ; if ( ! s ) { freeListFree(rsrvChanFreeList, pciu); ndelete++; } if(delay>maxdelay) maxdelay = delay; } } epicsMutexUnlock ( client->chanListLock ); # ifdef DEBUG if(ndelete){ epicsPrintf ("CAS: %d CA channels have expired after %f sec\n", ndelete, maxdelay); } # endif } /* * CAST_SERVER * * service UDP messages * */ void cast_server(void *pParm) { rsrv_iface_config *conf = pParm; int status; int count=0; int mysocket=0; struct sockaddr_in new_recv_addr; osiSocklen_t recv_addr_size; osiSockIoctl_t nchars; SOCKET recv_sock, reply_sock; struct client *client; recv_addr_size = sizeof(new_recv_addr); reply_sock = conf->udp; /* * setup new client structure but reuse old structure if * possible * */ while ( TRUE ) { client = create_client ( reply_sock, IPPROTO_UDP ); if ( client ) { break; } epicsThreadSleep(300.0); } if (conf->startbcast) { recv_sock = conf->udpbcast; conf->bclient = client; } else { recv_sock = conf->udp; conf->client = client; } client->udpRecv = recv_sock; casAttachThreadToClient ( client ); /* * add placeholder for the first version message should it be needed */ rsrv_version_reply ( client ); /* these pointers become invalid after signaling casudp_startStopEvent */ conf = NULL; epicsEventSignal(casudp_startStopEvent); while (TRUE) { status = recvfrom ( recv_sock, client->recv.buf, client->recv.maxstk, 0, (struct sockaddr *)&new_recv_addr, &recv_addr_size); if (status < 0) { if (SOCKERRNO != SOCK_EINTR) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); epicsPrintf ("CAS: UDP recv error: %s\n", sockErrBuf); epicsThreadSleep(1.0); } } else { size_t idx; for(idx=0; casIgnoreAddrs[idx]; idx++) { if(new_recv_addr.sin_addr.s_addr==casIgnoreAddrs[idx]) { status = -1; /* ignore */ break; } } } if (status >= 0 && casudp_ctl == ctlRun) { client->recv.cnt = (unsigned) status; client->recv.stk = 0ul; epicsTimeGetCurrent(&client->time_at_last_recv); client->minor_version_number = CA_UKN_MINOR_VERSION; client->seqNoOfReq = 0; /* * If we are talking to a new client flush to the old one * in case we are holding UDP messages waiting to * see if the next message is for this same client. */ if (client->send.stk>sizeof(caHdr)) { status = memcmp(&client->addr, &new_recv_addr, recv_addr_size); if(status){ /* * if the address is different */ cas_send_dg_msg(client); client->addr = new_recv_addr; } } else { client->addr = new_recv_addr; } if (CASDEBUG>1) { char buf[40]; ipAddrToDottedIP (&client->addr, buf, sizeof(buf)); errlogPrintf ("CAS: cast server msg of %d bytes from addr %s\n", client->recv.cnt, buf); } if (CASDEBUG>2) count = ellCount (&client->chanList); status = camessage ( client ); if(status == RSRV_OK){ if(client->recv.cnt != client->recv.stk){ char buf[40]; ipAddrToDottedIP (&client->addr, buf, sizeof(buf)); epicsPrintf ("CAS: partial (damaged?) UDP msg of %d bytes from %s ?\n", client->recv.cnt - client->recv.stk, buf); epicsTimeToStrftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &client->time_at_last_recv); epicsPrintf ("CAS: message received at %s\n", buf); } } else if (CASDEBUG>0){ char buf[40]; ipAddrToDottedIP (&client->addr, buf, sizeof(buf)); epicsPrintf ("CAS: invalid (damaged?) UDP request from %s ?\n", buf); epicsTimeToStrftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &client->time_at_last_recv); epicsPrintf ("CAS: message received at %s\n", buf); } if (CASDEBUG>2) { if ( ellCount (&client->chanList) ) { errlogPrintf ("CAS: Fnd %d name matches (%d tot)\n", ellCount(&client->chanList)-count, ellCount(&client->chanList)); } } } /* * allow messages to batch up if more are comming */ nchars = 0; /* supress purify warning */ status = socket_ioctl(recv_sock, FIONREAD, &nchars); if (status<0) { errlogPrintf ("CA cast server: Unable to fetch N characters pending\n"); cas_send_dg_msg (client); clean_addrq (client); } else if (nchars == 0) { cas_send_dg_msg (client); clean_addrq (client); } } /* ATM never reached, just a placeholder */ if(!mysocket) client->sock = INVALID_SOCKET; /* only one cast_server should destroy the reply socket */ destroy_client(client); epicsSocketDestroy(recv_sock); } base-7.0.3.1/modules/database/src/ioc/rsrv/online_notify.c0000664000577000060420000001042213557101274022203 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * tell CA clients this a server has joined the network * * Author: Jeffrey O. Hill * hill@luke.lanl.gov * (505) 665 1831 * Date: 103090 * */ #include #include #include #include #include #include #include "addrList.h" #include "dbDefs.h" #include "envDefs.h" #include "errlog.h" #include "osiSock.h" #include "taskwd.h" #define epicsExportSharedSymbols #include "server.h" /* * RSRV_ONLINE_NOTIFY_TASK */ void rsrv_online_notify_task(void *pParm) { double delay; double maxdelay; long longStatus; double maxPeriod; caHdr msg; int status; ca_uint32_t beaconCounter = 0; int *lastError; taskwdInsert (epicsThreadGetIdSelf(),NULL,NULL); if ( envGetConfigParamPtr ( & EPICS_CAS_BEACON_PERIOD ) ) { longStatus = envGetDoubleConfigParam ( & EPICS_CAS_BEACON_PERIOD, & maxPeriod ); } else { longStatus = envGetDoubleConfigParam ( & EPICS_CA_BEACON_PERIOD, & maxPeriod ); } if (longStatus || maxPeriod<=0.0) { maxPeriod = 15.0; epicsPrintf ("EPICS \"%s\" float fetch failed\n", EPICS_CAS_BEACON_PERIOD.name); epicsPrintf ("Setting \"%s\" = %f\n", EPICS_CAS_BEACON_PERIOD.name, maxPeriod); } delay = 0.02; /* initial beacon period in sec */ maxdelay = maxPeriod; memset((char *)&msg, 0, sizeof msg); msg.m_cmmd = htons (CA_PROTO_RSRV_IS_UP); msg.m_count = htons (ca_server_port); msg.m_dataType = htons (CA_MINOR_PROTOCOL_REVISION); /* beaconAddrList should not change after rsrv_init(), which then starts this thread */ lastError = callocMustSucceed(ellCount(&beaconAddrList), sizeof(*lastError), "rsrv_online_notify_task lastError"); epicsEventSignal(beacon_startStopEvent); while (TRUE) { ELLNODE *cur; unsigned i; /* send beacon to each interface */ for(i=0, cur=ellFirst(&beaconAddrList); cur; i++, cur=ellNext(cur)) { osiSockAddrNode *pAddr = CONTAINER(cur, osiSockAddrNode, node); status = sendto (beaconSocket, (char *)&msg, sizeof(msg), 0, &pAddr->addr.sa, sizeof(pAddr->addr)); if (status < 0) { int err = SOCKERRNO; if(err != lastError[i]) { char sockErrBuf[64]; char sockDipBuf[22]; epicsSocketConvertErrorToString(sockErrBuf, sizeof(sockErrBuf), err); ipAddrToDottedIP(&pAddr->addr.ia, sockDipBuf, sizeof(sockDipBuf)); errlogPrintf ( "CAS: CA beacon send to %s error: %s\n", sockDipBuf, sockErrBuf); lastError[i] = err; } } else { assert (status == sizeof(msg)); if(lastError[i]) { char sockDipBuf[22]; ipAddrToDottedIP(&pAddr->addr.ia, sockDipBuf, sizeof(sockDipBuf)); errlogPrintf ( "CAS: CA beacon send to %s ok\n", sockDipBuf); } lastError[i] = 0; } } epicsThreadSleep(delay); if (delaymaxdelay) { delay = maxdelay; } } msg.m_cid = htonl ( beaconCounter++ ); /* expected to overflow */ while (beacon_ctl == ctlPause) { epicsThreadSleep(0.1); delay = 0.02; /* Restart beacon timing if paused */ } } free(lastError); } base-7.0.3.1/modules/database/src/ioc/rsrv/rsrv.dbd0000664000577000060420000000012013557101274020624 0ustar anjaesctl# This DBD file links the RSRV CA server into the IOC registrar(rsrvRegistrar) base-7.0.3.1/modules/database/src/ioc/rsrv/rsrv.h0000664000577000060420000000213413557101274020331 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeffrey O. Hill * hill@luke.lanl.gov * (505) 665 1831 * Date: 5-88 */ #ifndef rsrvh #define rsrvh #include #include "shareLib.h" #define RSRV_OK 0 #define RSRV_ERROR (-1) #ifdef __cplusplus extern "C" { #endif epicsShareFunc void rsrv_register_server(void); epicsShareFunc void casr (unsigned level); epicsShareFunc int casClientInitiatingCurrentThread ( char * pBuf, size_t bufSize ); epicsShareFunc void casStatsFetch ( unsigned *pChanCount, unsigned *pConnCount ); #ifdef __cplusplus } #endif #endif /*rsrvh */ base-7.0.3.1/modules/database/src/ioc/rsrv/rsrvIocRegister.c0000664000577000060420000000210213557101274022457 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "osiSock.h" #include "iocsh.h" #define epicsExportSharedSymbols #include "rsrv.h" #include "server.h" #include "epicsExport.h" /* casr */ static const iocshArg casrArg0 = { "level",iocshArgInt}; static const iocshArg * const casrArgs[1] = {&casrArg0}; static const iocshFuncDef casrFuncDef = {"casr",1,casrArgs}; static void casrCallFunc(const iocshArgBuf *args) { casr(args[0].ival); } static void rsrvRegistrar(void) { rsrv_register_server(); iocshRegister(&casrFuncDef,casrCallFunc); } epicsExportAddress(int, CASDEBUG); epicsExportRegistrar(rsrvRegistrar); base-7.0.3.1/modules/database/src/ioc/rsrv/server.h0000664000577000060420000002160113557101274020643 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeffrey O. Hill * */ #ifndef INCLserverh #define INCLserverh #ifdef epicsExportSharedSymbols # define rsrvRestore_epicsExportSharedSymbols # undef epicsExportSharedSymbols #endif /* ifdef epicsExportSharedSymbols */ #include "epicsThread.h" #include "epicsMutex.h" #include "epicsEvent.h" #include "bucketLib.h" #include "asLib.h" #include "dbChannel.h" #include "dbNotify.h" #define CA_MINOR_PROTOCOL_REVISION 13 #include "caProto.h" #include "ellLib.h" #include "epicsTime.h" #include "epicsAssert.h" #include "osiSock.h" #ifdef rsrvRestore_epicsExportSharedSymbols #define epicsExportSharedSymbols #endif /* a modified ca header with capacity for large arrays */ typedef struct caHdrLargeArray { ca_uint32_t m_postsize; /* size of message extension */ ca_uint32_t m_count; /* operation data count */ ca_uint32_t m_cid; /* channel identifier */ ca_uint32_t m_available; /* protocol stub dependent */ ca_uint16_t m_dataType; /* operation data type */ ca_uint16_t m_cmmd; /* operation to be performed */ } caHdrLargeArray; /* * !! buf must be the first item in this structure !! * This guarantees that buf will have 8 byte natural * alignment * * The terminating unsigned pad0 field is there to force the * length of the message_buffer to be a multiple of 8 bytes. * This is due to the sequential placing of two message_buffer * structures (trans, rec) within the client structure. * Eight-byte alignment is required by the Sparc 5 and other RISC * processors. */ enum messageBufferType { mbtUDP, mbtSmallTCP, mbtLargeTCP }; struct message_buffer { char *buf; /*! points to first filled byte in buffer */ unsigned stk; unsigned maxstk; /*! points to first unused byte in buffer (after filled bytes) */ unsigned cnt; enum messageBufferType type; }; extern epicsThreadPrivateId rsrvCurrentClient; typedef struct client { ELLNODE node; /*! guarded by SEND_LOCK() aka. client::lock */ struct message_buffer send; /*! accessed by receive thread w/o locks cf. camsgtask() */ struct message_buffer recv; epicsMutexId lock; epicsMutexId putNotifyLock; epicsMutexId chanListLock; epicsMutexId eventqLock; ELLLIST chanList; ELLLIST chanPendingUpdateARList; ELLLIST putNotifyQue; struct sockaddr_in addr; /* peer address, TCP only */ epicsTimeStamp time_at_last_send; epicsTimeStamp time_at_last_recv; void *evuser; char *pUserName; char *pHostName; epicsEventId blockSem; /* used whenever the client blocks */ SOCKET sock, udpRecv; int proto; epicsThreadId tid; unsigned minor_version_number; ca_uint32_t seqNoOfReq; /* for udp */ unsigned recvBytesToDrain; unsigned priority; char disconnect; /* disconnect detected */ } client; /* Channel state shows which struct client list a * channel_in_us::node is in. * * client::chanList * rsrvCS_pendConnectResp, rsrvCS_inService * client::chanPendingUpdateARList * rsrvCS_pendConnectRespUpdatePendAR, rsrvCS_inServiceUpdatePendAR * Not in any list * rsrvCS_shutdown * * rsrvCS_invalid is not used */ enum rsrvChanState { rsrvCS_invalid, rsrvCS_pendConnectResp, rsrvCS_inService, rsrvCS_pendConnectRespUpdatePendAR, rsrvCS_inServiceUpdatePendAR, rsrvCS_shutdown }; /* * per channel structure * (stored in chanList or chanPendingUpdateARList off of a client block) */ struct channel_in_use { ELLNODE node; ELLLIST eventq; struct client *client; struct rsrv_put_notify *pPutNotify; /* potential active put notify */ const unsigned cid; /* client id */ const unsigned sid; /* server id */ epicsTimeStamp time_at_creation; /* for UDP timeout */ struct dbChannel *dbch; ASCLIENTPVT asClientPVT; enum rsrvChanState state; }; /* * Event block extension for channel access * some things duplicated for speed */ struct event_ext { ELLNODE node; caHdrLargeArray msg; struct channel_in_use *pciu; struct event_block *pdbev; /* ptr to db event block */ unsigned size; /* for speed */ unsigned mask; char modified; /* mod & ev flw ctrl enbl */ }; typedef struct { ELLNODE node; osiSockAddr tcpAddr, /* TCP listener endpoint */ udpAddr, /* UDP name unicast receiver endpoint */ udpbcastAddr; /* UDP name broadcast receiver endpoint */ SOCKET tcp, udp, udpbcast; struct client *client, *bclient; unsigned int startbcast:1; } rsrv_iface_config; enum ctl {ctlInit, ctlRun, ctlPause, ctlExit}; /* NOTE: external used so they remember the state across loads */ #ifdef GLBLSOURCE # define GLBLTYPE # define GLBLTYPE_INIT(A) = A #else # define GLBLTYPE extern # define GLBLTYPE_INIT(A) #endif /* * for debug-level dependent messages: */ #ifdef DEBUG # define DLOG(LEVEL,ARGSINPAREN) \ if (CASDEBUG > LEVEL) errlogPrintf ARGSINPAREN #else # define DLOG(LEVEL,ARGSINPAREN) #endif GLBLTYPE int CASDEBUG; GLBLTYPE unsigned short ca_server_port, ca_udp_port, ca_beacon_port; GLBLTYPE ELLLIST clientQ GLBLTYPE_INIT(ELLLIST_INIT); GLBLTYPE ELLLIST servers; /* rsrv_iface_config::node, read-only after rsrv_init() */ GLBLTYPE ELLLIST beaconAddrList; GLBLTYPE SOCKET beaconSocket; GLBLTYPE ELLLIST casIntfAddrList, casMCastAddrList; GLBLTYPE epicsUInt32 *casIgnoreAddrs; GLBLTYPE epicsMutexId clientQlock; GLBLTYPE BUCKET *pCaBucket; /* locked by clientQlock */ GLBLTYPE void *rsrvClientFreeList; GLBLTYPE void *rsrvChanFreeList; GLBLTYPE void *rsrvEventFreeList; GLBLTYPE void *rsrvSmallBufFreeListTCP; GLBLTYPE void *rsrvLargeBufFreeListTCP; GLBLTYPE unsigned rsrvSizeofLargeBufTCP; GLBLTYPE void *rsrvPutNotifyFreeList; GLBLTYPE unsigned rsrvChannelCount; /* locked by clientQlock */ GLBLTYPE epicsEventId casudp_startStopEvent; GLBLTYPE epicsEventId beacon_startStopEvent; GLBLTYPE epicsEventId castcp_startStopEvent; GLBLTYPE volatile enum ctl casudp_ctl; GLBLTYPE volatile enum ctl beacon_ctl; GLBLTYPE volatile enum ctl castcp_ctl; GLBLTYPE unsigned int threadPrios[5]; #define CAS_HASH_TABLE_SIZE 4096 #define SEND_LOCK(CLIENT) epicsMutexMustLock((CLIENT)->lock) #define SEND_UNLOCK(CLIENT) epicsMutexUnlock((CLIENT)->lock) #define LOCK_CLIENTQ epicsMutexMustLock (clientQlock); #define UNLOCK_CLIENTQ epicsMutexUnlock (clientQlock); void camsgtask (void *client); void cas_send_bs_msg ( struct client *pclient, int lock_needed ); void cas_send_dg_msg ( struct client *pclient ); void rsrv_online_notify_task (void *); void cast_server (void *); struct client *create_client ( SOCKET sock, int proto ); void destroy_client ( struct client * ); struct client *create_tcp_client ( SOCKET sock, const osiSockAddr* peerAddr ); void destroy_tcp_client ( struct client * ); void casAttachThreadToClient ( struct client * ); int camessage ( struct client *client ); void rsrv_extra_labor ( void * pArg ); int rsrvCheckPut ( const struct channel_in_use *pciu ); int rsrv_version_reply ( struct client *client ); void rsrvFreePutNotify ( struct client *pClient, struct rsrv_put_notify *pNotify ); void initializePutNotifyFreeList (void); unsigned rsrvSizeOfPutNotify ( struct rsrv_put_notify *pNotify ); /* * inclming protocol maintetnance */ void casExpandRecvBuffer ( struct client *pClient, ca_uint32_t size ); /* * outgoing protocol maintenance */ void casExpandSendBuffer ( struct client *pClient, ca_uint32_t size ); int cas_copy_in_header ( struct client *pClient, ca_uint16_t response, ca_uint32_t payloadSize, ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, ca_uint32_t responseSpecific, void **pPayload ); void cas_set_header_cid ( struct client *pClient, ca_uint32_t ); void cas_set_header_count (struct client *pClient, ca_uint32_t count); void cas_commit_msg ( struct client *pClient, ca_uint32_t size ); #endif /*INCLserverh*/ base-7.0.3.1/modules/database/src/std/Makefile0000664000577000060420000000176213557101274017656 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* STDDIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) TOP = ../../../.. include $(TOP)/configure/CONFIG USR_CPPFLAGS += -DUSE_TYPED_RSET SHRLIB_VERSION = 3.17.0 LIBRARY_IOC += dbRecStd dbRecStd_LIBS = dbCore ca Com dbRecStd_RCS += dbRecStd.rc include $(STDDIR)/rec/Makefile include $(STDDIR)/dev/Makefile include $(STDDIR)/filters/Makefile include $(STDDIR)/link/Makefile include $(STDDIR)/softIoc/Makefile include $(TOP)/configure/RULES include $(STDDIR)/rec/RULES include $(STDDIR)/softIoc/RULES base-7.0.3.1/modules/database/src/std/dbRecStd.rc0000664000577000060420000000226213557101274020232 0ustar anjaesctl#include #include "epicsVersion.h" VS_VERSION_INFO VERSIONINFO FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_UNKNOWN FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "Comments","Record and Soft Device Support Library for EPICS\0" VALUE "CompanyName", "The EPICS collaboration\0" VALUE "FileDescription", "Record and Soft Device Support Library\0" VALUE "FileVersion", EPICS_VERSION_STRING "\0" VALUE "InternalName", "dbRecStd\0" VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" VALUE "OriginalFilename", "dbRecStd.dll\0" VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" VALUE "ProductVersion", EPICS_VERSION_STRING "\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END base-7.0.3.1/modules/database/src/std/dev/Makefile0000664000577000060420000000472713557101274020440 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/std/Makefile. SRC_DIRS += $(STDDIR)/dev DBD += devSoft.dbd dbRecStd_SRCS += devAaiSoft.c dbRecStd_SRCS += devAaoSoft.c dbRecStd_SRCS += devAiSoft.c dbRecStd_SRCS += devAiSoftRaw.c dbRecStd_SRCS += devAoSoft.c dbRecStd_SRCS += devAoSoftRaw.c dbRecStd_SRCS += devBiSoft.c dbRecStd_SRCS += devBiSoftRaw.c dbRecStd_SRCS += devBiDbState.c dbRecStd_SRCS += devBoSoft.c dbRecStd_SRCS += devBoSoftRaw.c dbRecStd_SRCS += devBoDbState.c dbRecStd_SRCS += devCalcoutSoft.c dbRecStd_SRCS += devEventSoft.c dbRecStd_SRCS += devHistogramSoft.c dbRecStd_SRCS += devI64inSoft.c dbRecStd_SRCS += devI64outSoft.c dbRecStd_SRCS += devLiSoft.c dbRecStd_SRCS += devLoSoft.c dbRecStd_SRCS += devLsiSoft.c dbRecStd_SRCS += devLsoSoft.c dbRecStd_SRCS += devMbbiDirectSoft.c dbRecStd_SRCS += devMbbiDirectSoftRaw.c dbRecStd_SRCS += devMbbiSoft.c dbRecStd_SRCS += devMbbiSoftRaw.c dbRecStd_SRCS += devMbboDirectSoft.c dbRecStd_SRCS += devMbboDirectSoftRaw.c dbRecStd_SRCS += devMbboSoft.c dbRecStd_SRCS += devMbboSoftRaw.c dbRecStd_SRCS += devPrintfSoft.c dbRecStd_SRCS += devSASoft.c dbRecStd_SRCS += devSiSoft.c dbRecStd_SRCS += devSoSoft.c dbRecStd_SRCS += devWfSoft.c dbRecStd_SRCS += devGeneralTime.c dbRecStd_SRCS += devAiSoftCallback.c dbRecStd_SRCS += devBiSoftCallback.c dbRecStd_SRCS += devI64inSoftCallback.c dbRecStd_SRCS += devLiSoftCallback.c dbRecStd_SRCS += devMbbiDirectSoftCallback.c dbRecStd_SRCS += devMbbiSoftCallback.c dbRecStd_SRCS += devSiSoftCallback.c dbRecStd_SRCS += devAoSoftCallback.c dbRecStd_SRCS += devBoSoftCallback.c dbRecStd_SRCS += devCalcoutSoftCallback.c dbRecStd_SRCS += devI64outSoftCallback.c dbRecStd_SRCS += devLoSoftCallback.c dbRecStd_SRCS += devLsoSoftCallback.c dbRecStd_SRCS += devMbboSoftCallback.c dbRecStd_SRCS += devMbboDirectSoftCallback.c dbRecStd_SRCS += devPrintfSoftCallback.c dbRecStd_SRCS += devSoSoftCallback.c dbRecStd_SRCS += devTimestamp.c dbRecStd_SRCS += devStdio.c dbRecStd_SRCS += devEnviron.c dbRecStd_SRCS += asSubRecordFunctions.c base-7.0.3.1/modules/database/src/std/dev/asSubRecordFunctions.c0000664000577000060420000000512513557101274023242 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* asSubRecordFunctions.c */ /* Author: Marty Kraimer Date: 01MAY2000 */ #include #include #include #include #include #include "dbAccess.h" #include "cantProceed.h" #include "callback.h" #include "alarm.h" #include "errlog.h" #include "dbEvent.h" #include "recSup.h" #include "recGbl.h" #include "registryFunction.h" #include "asLib.h" #include "asDbLib.h" #include "subRecord.h" #include "epicsExport.h" /* The following is provided for access security*/ /*It allows a CA client to force access security initialization*/ static void myCallback(epicsCallback *pcallback) { ASDBCALLBACK *pasdbcallback = (ASDBCALLBACK *)pcallback; subRecord *precord; rset *prset; callbackGetUser(precord,pcallback); prset=(rset *)(precord->rset); precord->val = 0.0; if(pasdbcallback->status) { recGblSetSevr(precord,READ_ALARM,precord->brsv); recGblRecordError(pasdbcallback->status,precord,"asInit Failed"); } dbScanLock((dbCommon *)precord); (*prset->process)((dbCommon *)precord); dbScanUnlock((dbCommon *)precord); } long asSubInit(subRecord *precord,void *process) { ASDBCALLBACK *pcallback; pcallback = (ASDBCALLBACK *)callocMustSucceed( 1,sizeof(ASDBCALLBACK),"asSubInit"); precord->dpvt = (void *)pcallback; callbackSetCallback(myCallback,&pcallback->callback); callbackSetUser(precord,&pcallback->callback); return(0); } long asSubProcess(subRecord *precord) { ASDBCALLBACK *pcallback = (ASDBCALLBACK *)precord->dpvt; if(!precord->pact && precord->val==1.0) { db_post_events(precord,&precord->val,DBE_VALUE); callbackSetPriority(precord->prio,&pcallback->callback); asInitAsyn(pcallback); precord->pact=TRUE; return(1); } db_post_events(precord,&precord->val,DBE_VALUE); return(0); } static registryFunctionRef asSubRef[] = { {"asSubInit",(REGISTRYFUNCTION)asSubInit}, {"asSubProcess",(REGISTRYFUNCTION)asSubProcess} }; static void asSub(void) { registryFunctionRefAdd(asSubRef,NELEMENTS(asSubRef)); } epicsExportRegistrar(asSub); base-7.0.3.1/modules/database/src/std/dev/devAaiSoft.c0000664000577000060420000000562613557101274021170 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * devAaiSoft.c - Device Support Routines for soft Waveform Records * * Original Author: Bob Dalesio * Current Author: Dirk Zimoch * Date: 27-MAY-2010 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "dbConstLink.h" #include "dbEvent.h" #include "recGbl.h" #include "devSup.h" #include "cantProceed.h" #include "menuYesNo.h" #include "aaiRecord.h" #include "epicsExport.h" /* Create the dset for devAaiSoft */ static long init_record(); static long read_aai(); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_aai; } devAaiSoft = { 5, NULL, NULL, init_record, NULL, read_aai }; epicsExportAddress(dset,devAaiSoft); static long init_record(aaiRecord *prec) { DBLINK *plink = &prec->inp; /* This is pass 0, link hasn't been initialized yet */ dbInitLink(plink, DBF_INLINK); if (dbLinkIsConstant(plink)) { long nRequest = prec->nelm; long status; /* Allocate a buffer, record support hasn't done that yet */ if (!prec->bptr) { prec->bptr = callocMustSucceed(nRequest, dbValueSize(prec->ftvl), "devAaiSoft: buffer calloc failed"); } status = dbLoadLinkArray(plink, prec->ftvl, prec->bptr, &nRequest); if (!status && nRequest > 0) { prec->nord = nRequest; prec->udf = FALSE; } } return 0; } static long readLocked(struct link *pinp, void *dummy) { aaiRecord *prec = (aaiRecord *) pinp->precord; long nRequest = prec->nelm; long status = dbGetLink(pinp, prec->ftvl, prec->bptr, 0, &nRequest); if (!status && nRequest > 0) { prec->nord = nRequest; prec->udf = FALSE; if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) dbGetTimeStamp(pinp, &prec->time); } return status; } static long read_aai(aaiRecord *prec) { epicsUInt32 nord = prec->nord; struct link *pinp = prec->simm == menuYesNoYES ? &prec->siol : &prec->inp; long status = dbLinkDoLocked(pinp, readLocked, NULL); if (status == S_db_noLSET) status = readLocked(pinp, NULL); if (!status && nord != prec->nord) db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); return status; } base-7.0.3.1/modules/database/src/std/dev/devAaoSoft.c0000664000577000060420000000316213557101274021167 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * devAaoSoft.c - Device Support Routines for soft Waveform Records * * Original Author: Bob Dalesio * Current Author: Dirk Zimoch * Date: 27-MAY-2010 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "devSup.h" #include "cantProceed.h" #include "menuYesNo.h" #include "aaoRecord.h" #include "epicsExport.h" /* Create the dset for devAaoSoft */ static long init_record(); static long write_aao(); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_aao; } devAaoSoft = { 5, NULL, NULL, init_record, NULL, write_aao }; epicsExportAddress(dset,devAaoSoft); static long init_record(aaoRecord *prec) { if (dbLinkIsConstant(&prec->out)) { prec->nord = 0; } return 0; } static long write_aao(aaoRecord *prec) { long nRequest = prec->nord; dbPutLink(prec->simm == menuYesNoYES ? &prec->siol : &prec->out, prec->ftvl, prec->bptr, nRequest); return 0; } base-7.0.3.1/modules/database/src/std/dev/devAiSoft.c0000664000577000060420000000472213557101274021023 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Authors: Bob Dalesio and Marty Kraimer * Date: 3/6/91 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "epicsMath.h" #include "recGbl.h" #include "devSup.h" #include "aiRecord.h" #include "epicsExport.h" /* Create the dset for devAiSoft */ static long init_record(aiRecord *prec); static long read_ai(aiRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_ai; DEVSUPFUN special_linconv; } devAiSoft = { 6, NULL, NULL, init_record, NULL, read_ai, NULL }; epicsExportAddress(dset, devAiSoft); static long init_record(aiRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBF_DOUBLE, &prec->val)) prec->udf = FALSE; return 0; } struct aivt { double val; epicsTimeStamp *ptime; }; static long readLocked(struct link *pinp, void *vvt) { struct aivt *pvt = (struct aivt *) vvt; long status = dbGetLink(pinp, DBR_DOUBLE, &pvt->val, 0, 0); if (!status && pvt->ptime) dbGetTimeStamp(pinp, pvt->ptime); return status; } static long read_ai(aiRecord *prec) { long status; struct aivt vt; if (dbLinkIsConstant(&prec->inp)) return 2; vt.ptime = (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) ? &prec->time : NULL; status = dbLinkDoLocked(&prec->inp, readLocked, &vt); if (status == S_db_noLSET) status = readLocked(&prec->inp, &vt); if (!status) { /* Apply smoothing algorithm */ if (prec->smoo != 0.0 && prec->dpvt && finite(prec->val)) prec->val = vt.val * (1.0 - prec->smoo) + (prec->val * prec->smoo); else prec->val = vt.val; prec->udf = FALSE; prec->dpvt = &devAiSoft; /* Any non-zero value */ } else prec->dpvt = NULL; return 2; } base-7.0.3.1/modules/database/src/std/dev/devAiSoftCallback.c0000664000577000060420000001300513557101274022432 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devAiSoftCallback.c */ /* * Authors: Marty Kraimer & Andrew Johnson */ #include #include #include "alarm.h" #include "callback.h" #include "cantProceed.h" #include "dbCommon.h" #include "dbDefs.h" #include "dbAccess.h" #include "dbChannel.h" #include "dbNotify.h" #include "epicsAssert.h" #include "epicsMath.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "link.h" #include "aiRecord.h" #include "epicsExport.h" #define GET_OPTIONS (DBR_STATUS | DBR_TIME) typedef struct devPvt { processNotify pn; epicsCallback callback; long options; int status; int smooth; struct { DBRstatus DBRtime epicsFloat64 value; } buffer; } devPvt; static void getCallback(processNotify *ppn, notifyGetType type) { aiRecord *prec = (aiRecord *)ppn->usrPvt; devPvt *pdevPvt = (devPvt *)prec->dpvt; long no_elements = 1; if (ppn->status == notifyCanceled) { printf("devAiSoftCallback::getCallback notifyCanceled\n"); return; } assert(type == getFieldType); pdevPvt->status = dbChannelGetField(ppn->chan, DBR_DOUBLE, &pdevPvt->buffer, &pdevPvt->options, &no_elements, 0); } static void doneCallback(processNotify *ppn) { aiRecord *prec = (aiRecord *)ppn->usrPvt; devPvt *pdevPvt = (devPvt *)prec->dpvt; callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec); } static long add_record(dbCommon *pcommon) { aiRecord *prec = (aiRecord *)pcommon; DBLINK *plink = &prec->inp; dbChannel *chan; devPvt *pdevPvt; processNotify *ppn; if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink)) return 0; if (plink->type != PV_LINK) { long status = S_db_badField; recGblRecordError(status, (void *)prec, "devAiSoftCallback (add_record) Illegal INP field"); return status; } chan = dbChannelCreate(plink->value.pv_link.pvname); if (!chan) { long status = S_db_notFound; recGblRecordError(status, (void *)prec, "devAiSoftCallback (add_record) link target not found"); return status; } pdevPvt = calloc(1, sizeof(*pdevPvt)); if (!pdevPvt) { long status = S_db_noMemory; recGblRecordError(status, (void *)prec, "devAiSoftCallback (add_record) out of memory, calloc() failed"); return status; } ppn = &pdevPvt->pn; plink->type = PN_LINK; plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */ ppn->usrPvt = prec; ppn->chan = chan; ppn->getCallback = getCallback; ppn->doneCallback = doneCallback; ppn->requestType = processGetRequest; pdevPvt->options = GET_OPTIONS; prec->dpvt = pdevPvt; return 0; } static long del_record(dbCommon *pcommon) { aiRecord *prec = (aiRecord *)pcommon; DBLINK *plink = &prec->inp; devPvt *pdevPvt = (devPvt *)prec->dpvt; if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink)) return 0; assert(plink->type == PN_LINK); dbNotifyCancel(&pdevPvt->pn); dbChannelDelete(pdevPvt->pn.chan); free(pdevPvt); plink->type = PV_LINK; return 0; } static struct dsxt dsxtSoftCallback = { add_record, del_record }; static long init(int pass) { if (pass == 0) devExtend(&dsxtSoftCallback); return 0; } static long init_record(aiRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBF_DOUBLE, &prec->val)) prec->udf = FALSE; return 0; } static long read_ai(aiRecord *prec) { devPvt *pdevPvt = (devPvt *)prec->dpvt; if (!prec->dpvt) return 2; if (!prec->pact) { dbProcessNotify(&pdevPvt->pn); prec->pact = TRUE; return 0; } if (pdevPvt->status) { recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); pdevPvt->smooth = FALSE; return 2; } /* Apply smoothing algorithm */ if (prec->smoo != 0.0 && pdevPvt->smooth && finite(prec->val)) prec->val = prec->val * prec->smoo + pdevPvt->buffer.value * (1.0 - prec->smoo); else prec->val = pdevPvt->buffer.value; prec->udf = FALSE; pdevPvt->smooth = TRUE; switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) { case pvlOptNMS: break; case pvlOptMSI: if (pdevPvt->buffer.severity < INVALID_ALARM) break; /* else fall through */ case pvlOptMS: recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity); break; case pvlOptMSS: recGblSetSevr(prec, pdevPvt->buffer.status, pdevPvt->buffer.severity); break; } if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) prec->time = pdevPvt->buffer.time; return 2; } /* Create the dset for devAiSoftCallback */ struct { dset common; DEVSUPFUN read_ai; DEVSUPFUN special_linconv; } devAiSoftCallback = { {6, NULL, init, init_record, NULL}, read_ai, NULL }; epicsExportAddress(dset, devAiSoftCallback); base-7.0.3.1/modules/database/src/std/dev/devAiSoftRaw.c0000664000577000060420000000357513557101274021502 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Authors: Bob Dalesio and Marty Kraimer * Date: 6-1-90 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "devSup.h" #include "aiRecord.h" #include "epicsExport.h" /* Create the dset for devAiSoftRaw */ static long init_record(aiRecord *prec); static long read_ai(aiRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_ai; DEVSUPFUN special_linconv; } devAiSoftRaw = { 6, NULL, NULL, init_record, NULL, read_ai, NULL }; epicsExportAddress(dset, devAiSoftRaw); static long init_record(aiRecord *prec) { recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->rval); return 0; } static long readLocked(struct link *pinp, void *dummy) { aiRecord *prec = (aiRecord *) pinp->precord; long status = dbGetLink(pinp, DBR_LONG, &prec->rval, 0, 0); if (status) return status; if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) dbGetTimeStamp(pinp, &prec->time); return status; } static long read_ai(aiRecord *prec) { long status = dbLinkDoLocked(&prec->inp, readLocked, NULL); if (status == S_db_noLSET) status = readLocked(&prec->inp, NULL); return status; } base-7.0.3.1/modules/database/src/std/dev/devAoSoft.c0000664000577000060420000000322613557101274021027 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* devAoSoft.c */ /* Device Support Routines for soft Analog Output Records*/ /* * Original Author: Bob Dalesio * Current Author: Marty Kraimer * Date: 6-1-90 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "link.h" #include "special.h" #include "aoRecord.h" #include "epicsExport.h" /* added for Channel Access Links */ static long init_record(aoRecord *prec); /* Create the dset for devAoSoft */ static long write_ao(aoRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_ao; DEVSUPFUN special_linconv; }devAoSoft={ 6, NULL, NULL, init_record, NULL, write_ao, NULL}; epicsExportAddress(dset,devAoSoft); static long init_record(aoRecord *prec) { long status=0; status = 2; return status; } /* end init_record() */ static long write_ao(aoRecord *prec) { long status; status = dbPutLink(&prec->out,DBR_DOUBLE, &prec->oval,1); return(status); } base-7.0.3.1/modules/database/src/std/dev/devAoSoftCallback.c0000664000577000060420000000315113557101274022441 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* devAoSoftCallbackCallback.c */ /* * Author: Marty Kraimer * Date: 04NOV2003 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "dbCa.h" #include "link.h" #include "special.h" #include "aoRecord.h" #include "epicsExport.h" /* Create the dset for devAoSoftCallback */ static long write_ao(aoRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_ao; DEVSUPFUN special_linconv; }devAoSoftCallback={ 6, NULL, NULL, NULL, NULL, write_ao, NULL}; epicsExportAddress(dset,devAoSoftCallback); static long write_ao(aoRecord *prec) { struct link *plink = &prec->out; long status; if (prec->pact) return 0; status = dbPutLinkAsync(plink, DBR_DOUBLE, &prec->oval, 1); if (!status) prec->pact = TRUE; else if (status == S_db_noLSET) status = dbPutLink(plink, DBR_DOUBLE, &prec->oval, 1); return status; } base-7.0.3.1/modules/database/src/std/dev/devAoSoftRaw.c0000664000577000060420000000270113557101274021476 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* devAoSoftRaw.c */ /* Device Support Routines for soft raw Analog Output Records*/ /* * Author: Janet Anderson * Date: 09-25-91 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "dbEvent.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "link.h" #include "special.h" #include "aoRecord.h" #include "epicsExport.h" /* Create the dset for devAoSoftRaw */ static long write_ao(aoRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_ao; DEVSUPFUN special_linconv; }devAoSoftRaw={ 6, NULL, NULL, NULL, NULL, write_ao, NULL }; epicsExportAddress(dset,devAoSoftRaw); static long write_ao(aoRecord *prec) { long status; status = dbPutLink(&prec->out,DBR_LONG,&prec->rval,1); return(status); } base-7.0.3.1/modules/database/src/std/dev/devBiDbState.c0000664000577000060420000000406713557101274021441 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include "errlog.h" #include "dbState.h" #include "devSup.h" #include "recGbl.h" #include "dbLink.h" #include "dbAccessDefs.h" #include "biRecord.h" #include "epicsExport.h" #define DEVSUPNAME "devBiDbState" static long add_record (struct dbCommon *pdbc) { biRecord *prec = (biRecord *) pdbc; if (INST_IO != prec->inp.type) { recGblRecordError(S_db_badField, (void *) prec, DEVSUPNAME ": Illegal INP field"); return(S_db_badField); } if (!(prec->dpvt = dbStateFind(prec->inp.value.instio.string)) && prec->inp.value.instio.string && '\0' != *prec->inp.value.instio.string) { errlogSevPrintf(errlogInfo, DEVSUPNAME ": Creating new db state '%s'\n", prec->inp.value.instio.string); prec->dpvt = dbStateCreate(prec->inp.value.instio.string); } return 0; } static long del_record (struct dbCommon *pdbc) { biRecord *prec = (biRecord *) pdbc; prec->dpvt = NULL; return 0; } static struct dsxt myDsxt = { add_record, del_record }; static long init(int pass) { if (pass == 0) devExtend(&myDsxt); return 0; } static long read_bi(biRecord *prec) { if (prec->dpvt) { prec->val = dbStateGet(prec->dpvt); prec->udf = FALSE; } return 2; } static struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_bi; } devBiDbState = { 5, NULL, init, NULL, NULL, read_bi }; epicsExportAddress(dset, devBiDbState); base-7.0.3.1/modules/database/src/std/dev/devBiSoft.c0000664000577000060420000000357313557101274021027 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Authors: Bob Dalesio and Marty Kraimer * Date: 6-1-90 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "devSup.h" #include "biRecord.h" #include "epicsExport.h" /* Create the dset for devBiSoft */ static long init_record(biRecord *prec); static long read_bi(biRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_bi; } devBiSoft = { 5, NULL, NULL, init_record, NULL, read_bi }; epicsExportAddress(dset, devBiSoft); static long init_record(biRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBF_ENUM, &prec->val)) prec->udf = FALSE; return 0; } static long readLocked(struct link *pinp, void *dummy) { biRecord *prec = (biRecord *) pinp->precord; long status = dbGetLink(pinp, DBR_USHORT, &prec->val, 0, 0); if (status) return status; prec->udf = FALSE; if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) dbGetTimeStamp(pinp, &prec->time); return 2; } static long read_bi(biRecord *prec) { long status = dbLinkDoLocked(&prec->inp, readLocked, NULL); if (status == S_db_noLSET) status = readLocked(&prec->inp, NULL); return status; } base-7.0.3.1/modules/database/src/std/dev/devBiSoftCallback.c0000664000577000060420000001223013557101274022432 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devBiSoftCallback.c */ /* * Authors: Marty Kraimer & Andrew Johnson */ #include #include #include "alarm.h" #include "callback.h" #include "cantProceed.h" #include "dbCommon.h" #include "dbDefs.h" #include "dbAccess.h" #include "dbChannel.h" #include "dbNotify.h" #include "epicsAssert.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "link.h" #include "biRecord.h" #include "epicsExport.h" #define GET_OPTIONS (DBR_STATUS | DBR_TIME) typedef struct devPvt { processNotify pn; epicsCallback callback; long options; int status; struct { DBRstatus DBRtime epicsEnum16 value; } buffer; } devPvt; static void getCallback(processNotify *ppn,notifyGetType type) { biRecord *prec = (biRecord *)ppn->usrPvt; devPvt *pdevPvt = (devPvt *)prec->dpvt; long no_elements = 1; if (ppn->status == notifyCanceled) { printf("devBiSoftCallback::getCallback notifyCanceled\n"); return; } assert(type == getFieldType); pdevPvt->status = dbChannelGetField(ppn->chan, DBR_ENUM, &pdevPvt->buffer, &pdevPvt->options, &no_elements, 0); } static void doneCallback(processNotify *ppn) { biRecord *prec = (biRecord *)ppn->usrPvt; devPvt *pdevPvt = (devPvt *)prec->dpvt; callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec); } static long add_record(dbCommon *pcommon) { biRecord *prec = (biRecord *)pcommon; DBLINK *plink = &prec->inp; dbChannel *chan; devPvt *pdevPvt; processNotify *ppn; if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink)) return 0; if (plink->type != PV_LINK) { long status = S_db_badField; recGblRecordError(status, (void *)prec, "devBiSoftCallback (add_record) Illegal INP field"); return status; } chan = dbChannelCreate(plink->value.pv_link.pvname); if (!chan) { long status = S_db_notFound; recGblRecordError(status, (void *)prec, "devBiSoftCallback (add_record) link target not found"); return status; } pdevPvt = calloc(1, sizeof(*pdevPvt)); if (!pdevPvt) { long status = S_db_noMemory; recGblRecordError(status, (void *)prec, "devBiSoftCallback (add_record) out of memory, calloc() failed"); return status; } ppn = &pdevPvt->pn; plink->type = PN_LINK; plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */ ppn->usrPvt = prec; ppn->chan = chan; ppn->getCallback = getCallback; ppn->doneCallback = doneCallback; ppn->requestType = processGetRequest; pdevPvt->options = GET_OPTIONS; prec->dpvt = pdevPvt; return 0; } static long del_record(dbCommon *pcommon) { biRecord *prec = (biRecord *)pcommon; DBLINK *plink = &prec->inp; devPvt *pdevPvt = (devPvt *)prec->dpvt; if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink)) return 0; assert(plink->type == PN_LINK); dbNotifyCancel(&pdevPvt->pn); dbChannelDelete(pdevPvt->pn.chan); free(pdevPvt); plink->type = PV_LINK; return 0; } static struct dsxt dsxtSoftCallback = { add_record, del_record }; static long init(int pass) { if (pass == 0) devExtend(&dsxtSoftCallback); return 0; } static long init_record(biRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBR_ENUM, &prec->val)) prec->udf = FALSE; return 0; } static long read_bi(biRecord *prec) { devPvt *pdevPvt = (devPvt *)prec->dpvt; if (!prec->dpvt) return 2; if (!prec->pact) { dbProcessNotify(&pdevPvt->pn); prec->pact = TRUE; return 0; } if (pdevPvt->status) { recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); return 2; } prec->val = pdevPvt->buffer.value; prec->udf = FALSE; switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) { case pvlOptNMS: break; case pvlOptMSI: if (pdevPvt->buffer.severity < INVALID_ALARM) break; /* else fall through */ case pvlOptMS: recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity); break; case pvlOptMSS: recGblSetSevr(prec, pdevPvt->buffer.status, pdevPvt->buffer.severity); break; } if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) prec->time = pdevPvt->buffer.time; return 2; } /* Create the dset for devBiSoftCallback */ struct { dset common; DEVSUPFUN read_bi; } devBiSoftCallback = { {5, NULL, init, init_record, NULL}, read_bi }; epicsExportAddress(dset, devBiSoftCallback); base-7.0.3.1/modules/database/src/std/dev/devBiSoftRaw.c0000664000577000060420000000352613557101274021477 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Authors: Bob Dalesio and Marty Kraimer * Date: 6-1-90 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "devSup.h" #include "biRecord.h" #include "epicsExport.h" /* Create the dset for devBiSoftRaw */ static long init_record(biRecord *prec); static long read_bi(biRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_bi; } devBiSoftRaw = { 5, NULL, NULL, init_record, NULL, read_bi }; epicsExportAddress(dset, devBiSoftRaw); static long init_record(biRecord *prec) { recGblInitConstantLink(&prec->inp, DBF_ULONG, &prec->rval); return 0; } static long readLocked(struct link *pinp, void *dummy) { biRecord *prec = (biRecord *) pinp->precord; long status = dbGetLink(pinp, DBR_ULONG, &prec->rval, 0, 0); if (status) return status; if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) dbGetTimeStamp(pinp, &prec->time); return status; } static long read_bi(biRecord *prec) { long status = dbLinkDoLocked(&prec->inp, readLocked, NULL); if (status == S_db_noLSET) status = readLocked(&prec->inp, NULL); return status; } base-7.0.3.1/modules/database/src/std/dev/devBoDbState.c0000664000577000060420000000404013557101274021436 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include "errlog.h" #include "dbState.h" #include "devSup.h" #include "recGbl.h" #include "dbAccessDefs.h" #include "boRecord.h" #include "epicsExport.h" #define DEVSUPNAME "devBoDbState" static long add_record (struct dbCommon *pdbc) { boRecord *prec = (boRecord *) pdbc; if (INST_IO != prec->out.type) { recGblRecordError(S_db_badField, (void *) prec, DEVSUPNAME ": Illegal OUT field"); return(S_db_badField); } if (!(prec->dpvt = dbStateFind(prec->out.value.instio.string)) && prec->out.value.instio.string && '\0' != *prec->out.value.instio.string) { errlogSevPrintf(errlogInfo, DEVSUPNAME ": Creating new db state '%s'\n", prec->out.value.instio.string); prec->dpvt = dbStateCreate(prec->out.value.instio.string); } return 0; } static long del_record (struct dbCommon *pdbc) { boRecord *prec = (boRecord *) pdbc; prec->dpvt = NULL; return 0; } static struct dsxt myDsxt = { add_record, del_record }; static long init(int pass) { if (pass == 0) devExtend(&myDsxt); return 0; } static long write_bo(boRecord *prec) { if (prec->val) dbStateSet(prec->dpvt); else dbStateClear(prec->dpvt); return 0; } static struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_bo; } devBoDbState = { 5, NULL, init, NULL, NULL, write_bo }; epicsExportAddress(dset, devBoDbState); base-7.0.3.1/modules/database/src/std/dev/devBoSoft.c0000664000577000060420000000306213557101274021026 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* devBoSoft.c - Device Support Routines for Soft Binary Output*/ /* * Original Author: Bob Dalesio * Current Author: Marty Kraimer * Date: 6-1-90 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "boRecord.h" #include "epicsExport.h" static long init_record(boRecord *prec); /* Create the dset for devBoSoft */ static long write_bo(boRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_bo; }devBoSoft={ 5, NULL, NULL, init_record, NULL, write_bo }; epicsExportAddress(dset,devBoSoft); static long init_record(boRecord *prec) { long status=0; /* dont convert */ status=2; return status; } /* end init_record() */ static long write_bo(boRecord *prec) { long status; status = dbPutLink(&prec->out,DBR_USHORT,&prec->val,1); return(status); } base-7.0.3.1/modules/database/src/std/dev/devBoSoftCallback.c0000664000577000060420000000277413557101274022454 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devBoCallbackSoft.c */ /* * Author: Marty Kraimer * Date: 04NOV2003 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbLock.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "boRecord.h" #include "epicsExport.h" /* Create the dset for devBoCallbackSoft */ static long write_bo(boRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_bo; }devBoSoftCallback={ 5, NULL, NULL, NULL, NULL, write_bo }; epicsExportAddress(dset,devBoSoftCallback); static long write_bo(boRecord *prec) { struct link *plink = &prec->out; long status; if (prec->pact) return 0; status = dbPutLinkAsync(plink, DBR_USHORT, &prec->val, 1); if (!status) prec->pact = TRUE; else if (status == S_db_noLSET) status = dbPutLink(plink, DBR_USHORT, &prec->val, 1); return status; } base-7.0.3.1/modules/database/src/std/dev/devBoSoftRaw.c0000664000577000060420000000306413557101274021502 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* devBoSoftRaw.c - Device Support Routines for SoftRaw Binary Output*/ /* * Author: Janet Anderson * Date: 3-28-92 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "boRecord.h" #include "epicsExport.h" /* added for Channel Access Links */ static long init_record(boRecord *prec); /* Create the dset for devBoSoftRaw */ static long write_bo(boRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_bo; }devBoSoftRaw={ 5, NULL, NULL, init_record, NULL, write_bo }; epicsExportAddress(dset,devBoSoftRaw); static long init_record(boRecord *prec) { long status; /*Don't convert*/ status = 2; return status; } /* end init_record() */ static long write_bo(boRecord *prec) { long status; status = dbPutLink(&prec->out,DBR_LONG, &prec->rval,1); return(status); } base-7.0.3.1/modules/database/src/std/dev/devCalcoutSoft.c0000664000577000060420000000242613557101274022063 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devCalcoutSoft.c */ /* * Author: Marty Kraimer * Date: 05DEC2003 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "link.h" #include "special.h" #include "postfix.h" #include "calcoutRecord.h" #include "epicsExport.h" static long write_calcout(calcoutRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write; } devCalcoutSoft = { 5, NULL, NULL, NULL, NULL, write_calcout }; epicsExportAddress(dset, devCalcoutSoft); static long write_calcout(calcoutRecord *prec) { return dbPutLink(&prec->out, DBR_DOUBLE, &prec->oval, 1); } base-7.0.3.1/modules/database/src/std/dev/devCalcoutSoftCallback.c0000664000577000060420000000305513557101274023477 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devCalcoutSoftCallback.c */ /* * Author: Marty Kraimer * Date: 05DEC2003 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "link.h" #include "special.h" #include "postfix.h" #include "calcoutRecord.h" #include "epicsExport.h" static long write_calcout(calcoutRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write; } devCalcoutSoftCallback = { 5, NULL, NULL, NULL, NULL, write_calcout }; epicsExportAddress(dset, devCalcoutSoftCallback); static long write_calcout(calcoutRecord *prec) { struct link *plink = &prec->out; long status; if (prec->pact) return 0; status = dbPutLinkAsync(plink, DBR_DOUBLE, &prec->oval, 1); if (!status) prec->pact = TRUE; else if (status == S_db_noLSET) status = dbPutLink(plink, DBR_DOUBLE, &prec->oval, 1); return 0; } base-7.0.3.1/modules/database/src/std/dev/devEnviron.c0000664000577000060420000000512513557101274021254 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devEnviron.c */ #include #include #include "alarm.h" #include "dbCommon.h" #include "devSup.h" #include "errlog.h" #include "recGbl.h" #include "recSup.h" #include "lsiRecord.h" #include "stringinRecord.h" #include "epicsExport.h" /* lsi device support */ static long add_lsi(dbCommon *pcommon) { lsiRecord *prec = (lsiRecord *) pcommon; if (prec->inp.type != INST_IO) return S_dev_badInpType; return 0; } static long del_lsi(dbCommon *pcommon) { return 0; } static struct dsxt dsxtLsiEnviron = { add_lsi, del_lsi }; static long init_lsi(int pass) { if (pass == 0) devExtend(&dsxtLsiEnviron); return 0; } static long read_lsi(lsiRecord *prec) { const char *val = getenv(prec->inp.value.instio.string); if (val) { strncpy(prec->val, val, prec->sizv); prec->val[prec->sizv - 1] = 0; prec->len = strlen(prec->val); prec->udf = FALSE; } else { prec->val[0] = 0; prec->len = 1; prec->udf = TRUE; recGblSetSevr(prec, UDF_ALARM, prec->udfs); } return 0; } lsidset devLsiEnviron = { 5, NULL, init_lsi, NULL, NULL, read_lsi }; epicsExportAddress(dset, devLsiEnviron); /* stringin device support */ static long add_stringin(dbCommon *pcommon) { stringinRecord *prec = (stringinRecord *) pcommon; if (prec->inp.type != INST_IO) return S_dev_badInpType; return 0; } static long del_stringin(dbCommon *pcommon) { return 0; } static struct dsxt dsxtSiEnviron = { add_stringin, del_stringin }; static long init_stringin(int pass) { if (pass == 0) devExtend(&dsxtSiEnviron); return 0; } static long read_stringin(stringinRecord *prec) { const char *val = getenv(prec->inp.value.instio.string); if (val) { strncpy(prec->val, val, MAX_STRING_SIZE); prec->val[MAX_STRING_SIZE - 1] = 0; prec->udf = FALSE; } else { prec->val[0] = 0; prec->udf = TRUE; recGblSetSevr(prec, UDF_ALARM, prec->udfs); } return 0; } static struct { dset common; DEVSUPFUN read; } devSiEnviron = { {5, NULL, init_stringin, NULL, NULL}, read_stringin }; epicsExportAddress(dset, devSiEnviron); base-7.0.3.1/modules/database/src/std/dev/devEventSoft.c0000664000577000060420000000443213557101274021551 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Janet Anderson * Date: 04-21-91 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "devSup.h" #include "eventRecord.h" #include "epicsExport.h" /* Create the dset for devEventSoft */ static long init_record(eventRecord *prec); static long read_event(eventRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_event; } devEventSoft = { 5, NULL, NULL, init_record, NULL, read_event }; epicsExportAddress(dset, devEventSoft); static long init_record(eventRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBF_STRING, prec->val)) prec->udf = FALSE; return 0; } struct eventvt { char newEvent[MAX_STRING_SIZE]; epicsTimeStamp *ptime; }; static long readLocked(struct link *pinp, void *vvt) { struct eventvt *pvt = (struct eventvt *) vvt; long status = dbGetLink(pinp, DBR_STRING, pvt->newEvent, 0, 0); if (!status && pvt->ptime) dbGetTimeStamp(pinp, pvt->ptime); return status; } static long read_event(eventRecord *prec) { long status; struct eventvt vt; if (dbLinkIsConstant(&prec->inp)) return 0; vt.ptime = (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) ? &prec->time : NULL; status = dbLinkDoLocked(&prec->inp, readLocked, &vt); if (status == S_db_noLSET) status = readLocked(&prec->inp, &vt); if (!status) { if (strcmp(vt.newEvent, prec->val) != 0) { strcpy(prec->val, vt.newEvent); prec->epvt = eventNameToHandle(prec->val); } prec->udf = FALSE; } return status; } base-7.0.3.1/modules/database/src/std/dev/devGeneralTime.c0000664000577000060420000001542313557101274022032 0ustar anjaesctl/*************************************************************************\ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Author: Sheng Peng, ORNL / SNS Project * Date: 07/2004 * * EPICS device support for general timestamp support * * Integrated into base by Peter Denison, Diamond Light Source */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "devSup.h" #include "epicsString.h" #include "epicsGeneralTime.h" #include "aiRecord.h" #include "boRecord.h" #include "longinRecord.h" #include "stringinRecord.h" #include "epicsExport.h" /********* ai record **********/ static int getCurrentTime(double * pseconds) { epicsTimeStamp ts; if (epicsTimeOK == epicsTimeGetCurrent(&ts)) { *pseconds = ts.secPastEpoch + ((double)(ts.nsec)) * 1e-9; return 0; } return -1; } static struct ai_channel { char *name; int (*get)(double *); } ai_channels[] = { {"TIME", getCurrentTime}, }; static long init_ai(aiRecord *prec) { int i; if (prec->inp.type != INST_IO) { recGblRecordError(S_db_badField, (void *)prec, "devAiGeneralTime::init_ai: Illegal INP field"); prec->pact = TRUE; return S_db_badField; } for (i = 0; i < NELEMENTS(ai_channels); i++) { struct ai_channel *pchan = &ai_channels[i]; if (!epicsStrCaseCmp(prec->inp.value.instio.string, pchan->name)) { prec->dpvt = pchan; return 0; } } recGblRecordError(S_db_badField, (void *)prec, "devAiGeneralTime::init_ai: Bad parm"); prec->pact = TRUE; prec->dpvt = NULL; return S_db_badField; } static long read_ai(aiRecord *prec) { struct ai_channel *pchan = (struct ai_channel *)prec->dpvt; if (!pchan) return -1; if (pchan->get(&prec->val) == 0) { prec->udf = FALSE; return 2; } prec->udf = TRUE; recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); return -1; } struct { dset common; DEVSUPFUN read_write; DEVSUPFUN special_linconv; } devAiGeneralTime = { {6, NULL, NULL, init_ai, NULL}, read_ai, NULL }; epicsExportAddress(dset, devAiGeneralTime); /********* bo record **********/ static void resetErrors(void) { generalTimeResetErrorCounts(); } static struct bo_channel { char *name; void (*put)(void); } bo_channels[] = { {"RSTERRCNT", resetErrors}, }; static long init_bo(boRecord *prec) { int i; if (prec->out.type != INST_IO) { recGblRecordError(S_db_badField, (void *)prec, "devAiGeneralTime::init_ai: Illegal INP field"); prec->pact = TRUE; return S_db_badField; } for (i = 0; i < NELEMENTS(bo_channels); i++) { struct bo_channel *pchan = &bo_channels[i]; if (!epicsStrCaseCmp(prec->out.value.instio.string, pchan->name)) { prec->dpvt = pchan; prec->mask = 0; return 2; } } recGblRecordError(S_db_badField, (void *)prec, "devBoGeneralTime::init_bo: Bad parm"); prec->pact = TRUE; prec->dpvt = NULL; return S_db_badField; } static long write_bo(boRecord *prec) { struct bo_channel *pchan = (struct bo_channel *)prec->dpvt; if (!pchan) return -1; pchan->put(); return 0; } struct { dset common; DEVSUPFUN read_write; } devBoGeneralTime = { {5, NULL, NULL, init_bo, NULL}, write_bo }; epicsExportAddress(dset, devBoGeneralTime); /******* longin record *************/ static int errorCount(void) { return generalTimeGetErrorCounts(); } static struct li_channel { char *name; int (*get)(void); } li_channels[] = { {"GETERRCNT", errorCount}, }; static long init_li(longinRecord *prec) { int i; if (prec->inp.type != INST_IO) { recGblRecordError(S_db_badField, (void *)prec, "devLiGeneralTime::init_li: Illegal INP field"); prec->pact = TRUE; return S_db_badField; } for (i = 0; i < NELEMENTS(li_channels); i++) { struct li_channel *pchan = &li_channels[i]; if (!epicsStrCaseCmp(prec->inp.value.instio.string, pchan->name)) { prec->dpvt = pchan; return 0; } } recGblRecordError(S_db_badField, (void *)prec, "devLiGeneralTime::init_li: Bad parm"); prec->pact = TRUE; prec->dpvt = NULL; return S_db_badField; } static long read_li(longinRecord *prec) { struct li_channel *pchan = (struct li_channel *)prec->dpvt; if (!pchan) return -1; prec->val = pchan->get(); return 0; } struct { dset common; DEVSUPFUN read_write; } devLiGeneralTime = { {5, NULL, NULL, init_li, NULL}, read_li }; epicsExportAddress(dset, devLiGeneralTime); /********** stringin record **********/ static const char * timeProvider(void) { return generalTimeCurrentProviderName(); } static const char * highestProvider(void) { return generalTimeHighestCurrentName(); } static const char * eventProvider(void) { return generalTimeEventProviderName(); } static struct si_channel { char *name; const char * (*get)(void); } si_channels[] = { {"BESTTCP", timeProvider}, {"TOPTCP", highestProvider}, {"BESTTEP", eventProvider}, }; static long init_si(stringinRecord *prec) { int i; if (prec->inp.type != INST_IO) { recGblRecordError(S_db_badField, (void *)prec, "devSiGeneralTime::init_si: Illegal INP field"); prec->pact = TRUE; return S_db_badField; } for (i = 0; i < NELEMENTS(si_channels); i++) { struct si_channel *pchan = &si_channels[i]; if (!epicsStrCaseCmp(prec->inp.value.instio.string, pchan->name)) { prec->dpvt = pchan; return 0; } } recGblRecordError(S_db_badField, (void *)prec, "devSiGeneralTime::init_si: Bad parm"); prec->pact = TRUE; prec->dpvt = NULL; return S_db_badField; } static long read_si(stringinRecord *prec) { struct si_channel *pchan = (struct si_channel *)prec->dpvt; const char *name; if (!pchan) return -1; name = pchan->get(); if (name) { strncpy(prec->val, name, sizeof(prec->val)); prec->val[sizeof(prec->val) - 1] = '\0'; } else { strcpy(prec->val, "No working providers"); recGblSetSevr(prec, READ_ALARM, MAJOR_ALARM); } prec->udf = FALSE; return 0; } struct { dset common; DEVSUPFUN read_write; } devSiGeneralTime = { {5, NULL, NULL, init_si, NULL}, read_si }; epicsExportAddress(dset, devSiGeneralTime); base-7.0.3.1/modules/database/src/std/dev/devHistogramSoft.c0000664000577000060420000000314413557101274022424 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* devHistogramSoft.c */ /* * Author: Janet Anderson * Date: 07/02/91 */ #include #include #include #include "alarm.h" #include "cvtTable.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "link.h" #include "histogramRecord.h" #include "epicsExport.h" /* Create the dset for devHistogramSoft */ static long init_record(histogramRecord *prec); static long read_histogram(histogramRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_histogram; DEVSUPFUN special_linconv; }devHistogramSoft={ 6, NULL, NULL, init_record, NULL, read_histogram, NULL }; epicsExportAddress(dset,devHistogramSoft); static long init_record(histogramRecord *prec) { if (recGblInitConstantLink(&prec->svl,DBF_DOUBLE,&prec->sgnl)) prec->udf = FALSE; return 0; } static long read_histogram(histogramRecord *prec) { dbGetLink(&prec->svl, DBR_DOUBLE, &prec->sgnl, 0, 0); return 0; /*add count*/ } base-7.0.3.1/modules/database/src/std/dev/devI64inSoft.c0000664000577000060420000000364213557101274021363 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Author: Janet Anderson * Date: 09-23-91 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "devSup.h" #include "int64inRecord.h" #include "epicsExport.h" /* Create the dset for devI64inSoft */ static long init_record(int64inRecord *prec); static long read_int64in(int64inRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_int64in; } devI64inSoft = { 5, NULL, NULL, init_record, NULL, read_int64in }; epicsExportAddress(dset, devI64inSoft); static long init_record(int64inRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBF_INT64, &prec->val)) prec->udf = FALSE; return 0; } static long readLocked(struct link *pinp, void *dummy) { int64inRecord *prec = (int64inRecord *) pinp->precord; long status = dbGetLink(&prec->inp, DBR_INT64, &prec->val, 0, 0); if (status) return status; if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) dbGetTimeStamp(pinp, &prec->time); return status; } static long read_int64in(int64inRecord *prec) { long status = dbLinkDoLocked(&prec->inp, readLocked, NULL); if (status == S_db_noLSET) status = readLocked(&prec->inp, NULL); return status; } base-7.0.3.1/modules/database/src/std/dev/devI64inSoftCallback.c0000664000577000060420000001240413557101274022774 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devI64inSoftCallback.c */ /* * Authors: Marty Kraimer & Andrew Johnson */ #include #include #include "alarm.h" #include "callback.h" #include "cantProceed.h" #include "dbCommon.h" #include "dbDefs.h" #include "dbAccess.h" #include "dbChannel.h" #include "dbNotify.h" #include "epicsAssert.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "link.h" #include "int64inRecord.h" #include "epicsExport.h" #define GET_OPTIONS (DBR_STATUS | DBR_TIME) typedef struct devPvt { processNotify pn; CALLBACK callback; long options; int status; struct { DBRstatus DBRtime epicsInt64 value; } buffer; } devPvt; static void getCallback(processNotify *ppn, notifyGetType type) { int64inRecord *prec = (int64inRecord *)ppn->usrPvt; devPvt *pdevPvt = (devPvt *)prec->dpvt; long no_elements = 1; if (ppn->status == notifyCanceled) { printf("devI64inSoftCallback::getCallback notifyCanceled\n"); return; } assert(type == getFieldType); pdevPvt->status = dbChannelGetField(ppn->chan, DBR_INT64, &pdevPvt->buffer, &pdevPvt->options, &no_elements, 0); } static void doneCallback(processNotify *ppn) { int64inRecord *prec = (int64inRecord *)ppn->usrPvt; devPvt *pdevPvt = (devPvt *)prec->dpvt; callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec); } static long add_record(dbCommon *pcommon) { int64inRecord *prec = (int64inRecord *)pcommon; DBLINK *plink = &prec->inp; dbChannel *chan; devPvt *pdevPvt; processNotify *ppn; if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink)) return 0; if (plink->type != PV_LINK) { long status = S_db_badField; recGblRecordError(status, (void *)prec, "devI64inSoftCallback (add_record) Illegal INP field"); return status; } chan = dbChannelCreate(plink->value.pv_link.pvname); if (!chan) { long status = S_db_notFound; recGblRecordError(status, (void *)prec, "devI64inSoftCallback (init_record) linked record not found"); return status; } pdevPvt = calloc(1, sizeof(*pdevPvt)); if (!pdevPvt) { long status = S_db_noMemory; recGblRecordError(status, (void *)prec, "devI64inSoftCallback (add_record) out of memory, calloc() failed"); return status; } ppn = &pdevPvt->pn; plink->type = PN_LINK; plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */ ppn->usrPvt = prec; ppn->chan = chan; ppn->getCallback = getCallback; ppn->doneCallback = doneCallback; ppn->requestType = processGetRequest; pdevPvt->options = GET_OPTIONS; prec->dpvt = pdevPvt; return 0; } static long del_record(dbCommon *pcommon) { int64inRecord *prec = (int64inRecord *)pcommon; DBLINK *plink = &prec->inp; devPvt *pdevPvt = (devPvt *)prec->dpvt; if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink)) return 0; assert(plink->type == PN_LINK); dbNotifyCancel(&pdevPvt->pn); dbChannelDelete(pdevPvt->pn.chan); free(pdevPvt); plink->type = PV_LINK; return 0; } static struct dsxt dsxtSoftCallback = { add_record, del_record }; static long init(int pass) { if (pass == 0) devExtend(&dsxtSoftCallback); return 0; } static long init_record(int64inRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBR_INT64, &prec->val)) prec->udf = FALSE; return 0; } static long read_int64in(int64inRecord *prec) { devPvt *pdevPvt = (devPvt *)prec->dpvt; if (!prec->dpvt) return 0; if (!prec->pact) { dbProcessNotify(&pdevPvt->pn); prec->pact = TRUE; return 0; } if (pdevPvt->status) { recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); return pdevPvt->status; } prec->val = pdevPvt->buffer.value; prec->udf = FALSE; switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) { case pvlOptNMS: break; case pvlOptMSI: if (pdevPvt->buffer.severity < INVALID_ALARM) break; /* else fall through */ case pvlOptMS: recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity); break; case pvlOptMSS: recGblSetSevr(prec, pdevPvt->buffer.status, pdevPvt->buffer.severity); break; } if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) prec->time = pdevPvt->buffer.time; return 0; } /* Create the dset for devI64inSoftCallback */ struct { dset common; DEVSUPFUN read_int64in; } devI64inSoftCallback = { {5, NULL, init, init_record, NULL}, read_int64in }; epicsExportAddress(dset, devI64inSoftCallback); base-7.0.3.1/modules/database/src/std/dev/devI64outSoft.c0000664000577000060420000000261413557101274021562 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Author: Janet Anderson * Date: 09-23-91 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "int64outRecord.h" #include "epicsExport.h" /* Create the dset for devI64outSoft */ static long init_record(int64outRecord *prec); static long write_int64out(int64outRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_int64out; } devI64outSoft = { 5, NULL, NULL, init_record, NULL, write_int64out }; epicsExportAddress(dset, devI64outSoft); static long init_record(int64outRecord *prec) { return 0; } static long write_int64out(int64outRecord *prec) { dbPutLink(&prec->out, DBR_INT64, &prec->val,1); return 0; } base-7.0.3.1/modules/database/src/std/dev/devI64outSoftCallback.c0000664000577000060420000000304713557101274023200 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Author: Marty Kraimer * Date: 04NOV2003 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "int64outRecord.h" #include "epicsExport.h" /* Create the dset for devI64outSoftCallback */ static long write_int64out(int64outRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_int64out; } devI64outSoftCallback = { 5, NULL, NULL, NULL, NULL, write_int64out }; epicsExportAddress(dset, devI64outSoftCallback); static long write_int64out(int64outRecord *prec) { struct link *plink = &prec->out; long status; if (prec->pact) return 0; status = dbPutLinkAsync(plink, DBR_INT64, &prec->val, 1); if (!status) prec->pact = TRUE; else if (status == S_db_noLSET) status = dbPutLink(plink, DBR_INT64, &prec->val, 1); return status; } base-7.0.3.1/modules/database/src/std/dev/devLiSoft.c0000664000577000060420000000357513557101274021043 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Janet Anderson * Date: 09-23-91 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "devSup.h" #include "longinRecord.h" #include "epicsExport.h" /* Create the dset for devLiSoft */ static long init_record(longinRecord *prec); static long read_longin(longinRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_longin; } devLiSoft = { 5, NULL, NULL, init_record, NULL, read_longin }; epicsExportAddress(dset, devLiSoft); static long init_record(longinRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val)) prec->udf = FALSE; return 0; } static long readLocked(struct link *pinp, void *dummy) { longinRecord *prec = (longinRecord *) pinp->precord; long status = dbGetLink(pinp, DBR_LONG, &prec->val, 0, 0); if (status) return status; if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) dbGetTimeStamp(pinp, &prec->time); return status; } static long read_longin(longinRecord *prec) { long status = dbLinkDoLocked(&prec->inp, readLocked, NULL); if (status == S_db_noLSET) status = readLocked(&prec->inp, NULL); return status; } base-7.0.3.1/modules/database/src/std/dev/devLiSoftCallback.c0000664000577000060420000001232513557101274022451 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devLiSoftCallback.c */ /* * Authors: Marty Kraimer & Andrew Johnson */ #include #include #include "alarm.h" #include "callback.h" #include "cantProceed.h" #include "dbCommon.h" #include "dbDefs.h" #include "dbAccess.h" #include "dbChannel.h" #include "dbNotify.h" #include "epicsAssert.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "link.h" #include "longinRecord.h" #include "epicsExport.h" #define GET_OPTIONS (DBR_STATUS | DBR_TIME) typedef struct devPvt { processNotify pn; epicsCallback callback; long options; int status; struct { DBRstatus DBRtime epicsInt32 value; } buffer; } devPvt; static void getCallback(processNotify *ppn, notifyGetType type) { longinRecord *prec = (longinRecord *)ppn->usrPvt; devPvt *pdevPvt = (devPvt *)prec->dpvt; long no_elements = 1; if (ppn->status == notifyCanceled) { printf("devLiSoftCallback::getCallback notifyCanceled\n"); return; } assert(type == getFieldType); pdevPvt->status = dbChannelGetField(ppn->chan, DBR_LONG, &pdevPvt->buffer, &pdevPvt->options, &no_elements, 0); } static void doneCallback(processNotify *ppn) { longinRecord *prec = (longinRecord *)ppn->usrPvt; devPvt *pdevPvt = (devPvt *)prec->dpvt; callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec); } static long add_record(dbCommon *pcommon) { longinRecord *prec = (longinRecord *)pcommon; DBLINK *plink = &prec->inp; dbChannel *chan; devPvt *pdevPvt; processNotify *ppn; if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink)) return 0; if (plink->type != PV_LINK) { long status = S_db_badField; recGblRecordError(status, (void *)prec, "devLiSoftCallback (add_record) Illegal INP field"); return status; } chan = dbChannelCreate(plink->value.pv_link.pvname); if (!chan) { long status = S_db_notFound; recGblRecordError(status, (void *)prec, "devLiSoftCallback (init_record) linked record not found"); return status; } pdevPvt = calloc(1, sizeof(*pdevPvt)); if (!pdevPvt) { long status = S_db_noMemory; recGblRecordError(status, (void *)prec, "devLiSoftCallback (add_record) out of memory, calloc() failed"); return status; } ppn = &pdevPvt->pn; plink->type = PN_LINK; plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */ ppn->usrPvt = prec; ppn->chan = chan; ppn->getCallback = getCallback; ppn->doneCallback = doneCallback; ppn->requestType = processGetRequest; pdevPvt->options = GET_OPTIONS; prec->dpvt = pdevPvt; return 0; } static long del_record(dbCommon *pcommon) { longinRecord *prec = (longinRecord *)pcommon; DBLINK *plink = &prec->inp; devPvt *pdevPvt = (devPvt *)prec->dpvt; if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink)) return 0; assert(plink->type == PN_LINK); dbNotifyCancel(&pdevPvt->pn); dbChannelDelete(pdevPvt->pn.chan); free(pdevPvt); plink->type = PV_LINK; return 0; } static struct dsxt dsxtSoftCallback = { add_record, del_record }; static long init(int pass) { if (pass == 0) devExtend(&dsxtSoftCallback); return 0; } static long init_record(longinRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBR_LONG, &prec->val)) prec->udf = FALSE; return 0; } static long read_li(longinRecord *prec) { devPvt *pdevPvt = (devPvt *)prec->dpvt; if (!prec->dpvt) return 0; if (!prec->pact) { dbProcessNotify(&pdevPvt->pn); prec->pact = TRUE; return 0; } if (pdevPvt->status) { recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); return pdevPvt->status; } prec->val = pdevPvt->buffer.value; prec->udf = FALSE; switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) { case pvlOptNMS: break; case pvlOptMSI: if (pdevPvt->buffer.severity < INVALID_ALARM) break; /* else fall through */ case pvlOptMS: recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity); break; case pvlOptMSS: recGblSetSevr(prec, pdevPvt->buffer.status, pdevPvt->buffer.severity); break; } if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) prec->time = pdevPvt->buffer.time; return 0; } /* Create the dset for devLiSoftCallback */ struct { dset common; DEVSUPFUN read_li; } devLiSoftCallback = { {5, NULL, init, init_record, NULL}, read_li }; epicsExportAddress(dset, devLiSoftCallback); base-7.0.3.1/modules/database/src/std/dev/devLoSoft.c0000664000577000060420000000262313557101274021042 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* devLoSoft.c */ /* * Author: Janet Anderson * Date: 09-23-91 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "longoutRecord.h" #include "epicsExport.h" /* Create the dset for devLoSoft */ static long init_record(longoutRecord *prec); static long write_longout(longoutRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_longout; }devLoSoft={ 5, NULL, NULL, init_record, NULL, write_longout }; epicsExportAddress(dset,devLoSoft); static long init_record(longoutRecord *prec) { return 0; } /* end init_record() */ static long write_longout(longoutRecord *prec) { dbPutLink(&prec->out,DBR_LONG, &prec->val,1); return 0; } base-7.0.3.1/modules/database/src/std/dev/devLoSoftCallback.c0000664000577000060420000000300613557101274022453 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devLoSoftCallback.c */ /* * Author: Marty Kraimer * Date: 04NOV2003 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "longoutRecord.h" #include "epicsExport.h" /* Create the dset for devLoSoftCallback */ static long write_longout(longoutRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_longout; }devLoSoftCallback={ 5, NULL, NULL, NULL, NULL, write_longout }; epicsExportAddress(dset,devLoSoftCallback); static long write_longout(longoutRecord *prec) { struct link *plink = &prec->out; long status; if (prec->pact) return 0; status = dbPutLinkAsync(plink, DBR_LONG, &prec->val, 1); if (!status) prec->pact = TRUE; else if (status == S_db_noLSET) status = dbPutLink(plink, DBR_LONG, &prec->val, 1); return status; } base-7.0.3.1/modules/database/src/std/dev/devLsiSoft.c0000664000577000060420000000257213557101274021222 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Long String Input soft device support * * Author: Andrew Johnson * Date: 2012-11-28 */ #include "dbAccess.h" #include "epicsTime.h" #include "link.h" #include "lsiRecord.h" #include "epicsExport.h" static long init_record(lsiRecord *prec) { dbLoadLinkLS(&prec->inp, prec->val, prec->sizv, &prec->len); return 0; } static long readLocked(struct link *pinp, void *dummy) { lsiRecord *prec = (lsiRecord *) pinp->precord; long status = dbGetLinkLS(pinp, prec->val, prec->sizv, &prec->len); if (status) return status; if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) dbGetTimeStamp(pinp, &prec->time); return status; } static long read_string(lsiRecord *prec) { long status = dbLinkDoLocked(&prec->inp, readLocked, NULL); if (status == S_db_noLSET) status = readLocked(&prec->inp, NULL); return status; } lsidset devLsiSoft = { 5, NULL, NULL, init_record, NULL, read_string }; epicsExportAddress(dset, devLsiSoft); base-7.0.3.1/modules/database/src/std/dev/devLsoSoft.c0000664000577000060420000000136713557101274021231 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Long String Output soft device support * * Author: Andrew Johnson * Date: 2012-11-29 */ #include "dbAccess.h" #include "lsoRecord.h" #include "epicsExport.h" static long write_string(lsoRecord *prec) { return dbPutLinkLS(&prec->out, prec->val, prec->len); } lsodset devLsoSoft = { 5, NULL, NULL, NULL, NULL, write_string }; epicsExportAddress(dset, devLsoSoft); base-7.0.3.1/modules/database/src/std/dev/devLsoSoftCallback.c0000664000577000060420000000227713557101274022647 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Andrew Johnson * Date: 30 Nov 2012 */ #include "alarm.h" #include "dbAccess.h" #include "recGbl.h" #include "lsoRecord.h" #include "epicsExport.h" static long write_string(lsoRecord *prec) { struct link *plink = &prec->out; int dtyp = dbGetLinkDBFtype(plink); long len = prec->len; long status; if (prec->pact || dtyp < 0) return 0; if (dtyp != DBR_CHAR && dtyp != DBF_UCHAR) { dtyp = DBR_STRING; len = 1; } status = dbPutLinkAsync(plink, dtyp, prec->val, len); if (!status) prec->pact = TRUE; else if (status == S_db_noLSET) status = dbPutLink(plink, dtyp, prec->val, len); return status; } lsodset devLsoSoftCallback = { 5, NULL, NULL, NULL, NULL, write_string }; epicsExportAddress(dset, devLsoSoftCallback); base-7.0.3.1/modules/database/src/std/dev/devMbbiDirectSoft.c0000664000577000060420000000372713557101274022502 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Authors: Bob Dalesio and Matthew Needes * Date: 10-08-93 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "devSup.h" #include "mbbiDirectRecord.h" #include "epicsExport.h" /* Create the dset for devMbbiDirectSoft */ static long init_record(mbbiDirectRecord *prec); static long read_mbbi(mbbiDirectRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_mbbi; } devMbbiDirectSoft = { 5, NULL, NULL, init_record, NULL, read_mbbi }; epicsExportAddress(dset, devMbbiDirectSoft); static long init_record(mbbiDirectRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBR_ULONG, &prec->val)) prec->udf = FALSE; return 0; } static long readLocked(struct link *pinp, void *dummy) { mbbiDirectRecord *prec = (mbbiDirectRecord *) pinp->precord; long status = dbGetLink(pinp, DBR_ULONG, &prec->val, 0, 0); if (status) return status; prec->udf = FALSE; if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) dbGetTimeStamp(pinp, &prec->time); return 2; } static long read_mbbi(mbbiDirectRecord *prec) { long status = dbLinkDoLocked(&prec->inp, readLocked, NULL); if (status == S_db_noLSET) status = readLocked(&prec->inp, NULL); return status; } base-7.0.3.1/modules/database/src/std/dev/devMbbiDirectSoftCallback.c0000664000577000060420000001251413557101274024111 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devMbbiDirectSoftCallback.c */ /* * Authors: Marty Kraimer & Andrew Johnson */ #include #include #include "alarm.h" #include "callback.h" #include "cantProceed.h" #include "dbCommon.h" #include "dbDefs.h" #include "dbAccess.h" #include "dbChannel.h" #include "dbNotify.h" #include "epicsAssert.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "link.h" #include "mbbiDirectRecord.h" #include "epicsExport.h" #define GET_OPTIONS (DBR_STATUS | DBR_TIME) typedef struct devPvt { processNotify pn; epicsCallback callback; long options; int status; struct { DBRstatus DBRtime epicsUInt32 value; } buffer; } devPvt; static void getCallback(processNotify *ppn, notifyGetType type) { mbbiDirectRecord *prec = (mbbiDirectRecord *)ppn->usrPvt; devPvt *pdevPvt = (devPvt *)prec->dpvt; long no_elements = 1; if (ppn->status == notifyCanceled) { printf("devMbbiDirectSoftCallback::getCallback notifyCanceled\n"); return; } assert(type == getFieldType); pdevPvt->status = dbChannelGetField(ppn->chan, DBR_ULONG, &pdevPvt->buffer, &pdevPvt->options, &no_elements, 0); } static void doneCallback(processNotify *ppn) { mbbiDirectRecord *prec = (mbbiDirectRecord *)ppn->usrPvt; devPvt *pdevPvt = (devPvt *)prec->dpvt; callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec); } static long add_record(dbCommon *pcommon) { mbbiDirectRecord *prec = (mbbiDirectRecord *)pcommon; DBLINK *plink = &prec->inp; dbChannel *chan; devPvt *pdevPvt; processNotify *ppn; if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink)) return 0; if (plink->type != PV_LINK) { long status = S_db_badField; recGblRecordError(status, (void *)prec, "devMbbiDirectSoftCallback (add_record) Illegal INP field"); return status; } chan = dbChannelCreate(plink->value.pv_link.pvname); if (!chan) { long status = S_db_notFound; recGblRecordError(status,(void *)prec, "devMbbiDirectSoftCallback (add_record) linked record not found"); return status; } pdevPvt = calloc(1, sizeof(*pdevPvt)); if (!pdevPvt) { long status = S_db_noMemory; recGblRecordError(status, (void *)prec, "devMbbiDirectSoftCallback (add_record) out of memory, calloc() failed"); return status; } ppn = &pdevPvt->pn; plink->type = PN_LINK; plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */ ppn->usrPvt = prec; ppn->chan = chan; ppn->getCallback = getCallback; ppn->doneCallback = doneCallback; ppn->requestType = processGetRequest; pdevPvt->options = GET_OPTIONS; prec->dpvt = pdevPvt; return 0; } static long del_record(dbCommon *pcommon) { mbbiDirectRecord *prec = (mbbiDirectRecord *)pcommon; DBLINK *plink = &prec->inp; devPvt *pdevPvt = (devPvt *)prec->dpvt; if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink)) return 0; assert(plink->type == PN_LINK); dbNotifyCancel(&pdevPvt->pn); dbChannelDelete(pdevPvt->pn.chan); free(pdevPvt); plink->type = PV_LINK; return 0; } static struct dsxt dsxtSoftCallback = { add_record, del_record }; static long init(int pass) { if (pass == 0) devExtend(&dsxtSoftCallback); return 0; } static long init_record(mbbiDirectRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBR_ULONG, &prec->val)) prec->udf = FALSE; return 0; } static long read_mbbiDirect(mbbiDirectRecord *prec) { devPvt *pdevPvt = (devPvt *)prec->dpvt; if (!prec->dpvt) return 2; if (!prec->pact) { dbProcessNotify(&pdevPvt->pn); prec->pact = TRUE; return 0; } if (pdevPvt->status) { recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); return 2; } prec->val = pdevPvt->buffer.value; prec->udf = FALSE; switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) { case pvlOptNMS: break; case pvlOptMSI: if (pdevPvt->buffer.severity < INVALID_ALARM) break; /* else fall through */ case pvlOptMS: recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity); break; case pvlOptMSS: recGblSetSevr(prec, pdevPvt->buffer.status, pdevPvt->buffer.severity); break; } if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) prec->time = pdevPvt->buffer.time; return 2; } /* Create the dset for devMbbiDirectSoftCallback */ struct { dset common; DEVSUPFUN read_mbbiDirect; } devMbbiDirectSoftCallback = { {5, NULL, init, init_record, NULL}, read_mbbiDirect }; epicsExportAddress(dset, devMbbiDirectSoftCallback); base-7.0.3.1/modules/database/src/std/dev/devMbbiDirectSoftRaw.c0000664000577000060420000000344213557101274023146 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Authors: Bob Dalesio and Matthew Needes * Date: 10-08-93 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "devSup.h" #include "mbbiDirectRecord.h" #include "epicsExport.h" /* Create the dset for devMbbiDirectSoftRaw */ static long init_record(mbbiDirectRecord *prec); static long read_mbbi(mbbiDirectRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_mbbi; } devMbbiDirectSoftRaw = { 5, NULL, NULL, init_record, NULL, read_mbbi }; epicsExportAddress(dset, devMbbiDirectSoftRaw); static long init_record(mbbiDirectRecord *prec) { recGblInitConstantLink(&prec->inp, DBF_ULONG, &prec->rval); /* Preserve old functionality */ if (prec->nobt == 0) prec->mask = 0xffffffff; prec->mask <<= prec->shft; return 0; } static long read_mbbi(mbbiDirectRecord *prec) { if (!dbGetLink(&prec->inp, DBR_ULONG, &prec->rval, 0, 0)) { prec->rval &= prec->mask; if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) dbGetTimeStamp(&prec->inp, &prec->time); } return 0; } base-7.0.3.1/modules/database/src/std/dev/devMbbiSoft.c0000664000577000060420000000363013557101274021340 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Authors: Bob Dalesio and Marty Kraimer * Date: 6-1-90 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "devSup.h" #include "mbbiRecord.h" #include "epicsExport.h" /* Create the dset for devMbbiSoft */ static long init_record(mbbiRecord *prec); static long read_mbbi(mbbiRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_mbbi; } devMbbiSoft = { 5, NULL, NULL, init_record, NULL, read_mbbi }; epicsExportAddress(dset, devMbbiSoft); static long init_record(mbbiRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBF_ENUM, &prec->val)) prec->udf = FALSE; return 0; } static long readLocked(struct link *pinp, void *dummy) { mbbiRecord *prec = (mbbiRecord *) pinp->precord; long status = dbGetLink(pinp, DBR_USHORT, &prec->val, 0, 0); if (status) return status; prec->udf = FALSE; if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) dbGetTimeStamp(pinp, &prec->time); return 2; } static long read_mbbi(mbbiRecord *prec) { long status = dbLinkDoLocked(&prec->inp, readLocked, NULL); if (status == S_db_noLSET) status = readLocked(&prec->inp, NULL); return status; } base-7.0.3.1/modules/database/src/std/dev/devMbbiSoftCallback.c0000664000577000060420000001227713557101274022764 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devMbbiSoftCallback.c */ /* * Authors: Marty Kraimer & Andrew Johnson */ #include #include #include "alarm.h" #include "callback.h" #include "cantProceed.h" #include "dbCommon.h" #include "dbDefs.h" #include "dbAccess.h" #include "dbChannel.h" #include "dbNotify.h" #include "epicsAssert.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "link.h" #include "mbbiRecord.h" #include "epicsExport.h" #define GET_OPTIONS (DBR_STATUS | DBR_TIME) typedef struct devPvt { processNotify pn; epicsCallback callback; long options; int status; struct { DBRstatus DBRtime epicsEnum16 value; } buffer; } devPvt; static void getCallback(processNotify *ppn, notifyGetType type) { mbbiRecord *prec = (mbbiRecord *)ppn->usrPvt; devPvt *pdevPvt = (devPvt *)prec->dpvt; long no_elements = 1; if (ppn->status == notifyCanceled) { printf("devMbbiSoftCallback::getCallback notifyCanceled\n"); return; } assert(type == getFieldType); pdevPvt->status = dbChannelGetField(ppn->chan, DBR_ENUM, &pdevPvt->buffer, &pdevPvt->options, &no_elements, 0); } static void doneCallback(processNotify *ppn) { mbbiRecord *prec = (mbbiRecord *)ppn->usrPvt; devPvt *pdevPvt = (devPvt *)prec->dpvt; callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec); } static long add_record(dbCommon *pcommon) { mbbiRecord *prec = (mbbiRecord *)pcommon; DBLINK *plink = &prec->inp; dbChannel *chan; devPvt *pdevPvt; processNotify *ppn; if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink)) return 0; if (plink->type != PV_LINK) { long status = S_db_badField; recGblRecordError(status, (void *)prec, "devMbbiSoftCallback (add_record) Illegal INP field"); return status; } chan = dbChannelCreate(plink->value.pv_link.pvname); if (!chan) { long status = S_db_notFound; recGblRecordError(status, (void *)prec, "devMbbiSoftCallback (add_record) linked record not found"); return status; } pdevPvt = calloc(1, sizeof(*pdevPvt)); if (!pdevPvt) { long status = S_db_noMemory; recGblRecordError(status, (void *)prec, "devMbbiSoftCallback (add_record) out of memory, calloc() failed"); return status; } ppn = &pdevPvt->pn; plink->type = PN_LINK; plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */ ppn->usrPvt = prec; ppn->chan = chan; ppn->getCallback = getCallback; ppn->doneCallback = doneCallback; ppn->requestType = processGetRequest; pdevPvt->options = GET_OPTIONS; prec->dpvt = pdevPvt; return 0; } static long del_record(dbCommon *pcommon) { mbbiRecord *prec = (mbbiRecord *)pcommon; DBLINK *plink = &prec->inp; devPvt *pdevPvt = (devPvt *)prec->dpvt; if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink)) return 0; assert(plink->type == PN_LINK); dbNotifyCancel(&pdevPvt->pn); dbChannelDelete(pdevPvt->pn.chan); free(pdevPvt); plink->type = PV_LINK; return 0; } static struct dsxt dsxtSoftCallback = { add_record, del_record }; static long init(int pass) { if (pass == 0) devExtend(&dsxtSoftCallback); return 0; } static long init_record(mbbiRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBR_ENUM, &prec->val)) prec->udf = FALSE; return 0; } static long read_mbbi(mbbiRecord *prec) { devPvt *pdevPvt = (devPvt *)prec->dpvt; if (!prec->dpvt) return 2; if (!prec->pact) { dbProcessNotify(&pdevPvt->pn); prec->pact = TRUE; return 0; } if (pdevPvt->status) { recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); return 2; } prec->val = pdevPvt->buffer.value; prec->udf = FALSE; switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) { case pvlOptNMS: break; case pvlOptMSI: if (pdevPvt->buffer.severity < INVALID_ALARM) break; /* else fall through */ case pvlOptMS: recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity); break; case pvlOptMSS: recGblSetSevr(prec, pdevPvt->buffer.status, pdevPvt->buffer.severity); break; } if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) prec->time = pdevPvt->buffer.time; return 2; } /* Create the dset for devMbbiSoftCallback */ struct { dset common; DEVSUPFUN read_mbbi; } devMbbiSoftCallback = { {5, NULL, init, init_record, NULL}, read_mbbi }; epicsExportAddress(dset,devMbbiSoftCallback); base-7.0.3.1/modules/database/src/std/dev/devMbbiSoftRaw.c0000664000577000060420000000404313557101274022011 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Authors: Bob Dalesio and Marty Kraimer * Date: 6-1-90 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "devSup.h" #include "mbbiRecord.h" #include "epicsExport.h" /* Create the dset for devMbbiSoftRaw */ static long init_record(mbbiRecord *prec); static long read_mbbi(mbbiRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_mbbi; } devMbbiSoftRaw = { 5, NULL, NULL, init_record, NULL, read_mbbi }; epicsExportAddress(dset, devMbbiSoftRaw); static long init_record(mbbiRecord *prec) { recGblInitConstantLink(&prec->inp, DBF_ULONG, &prec->rval); /* Preserve old functionality*/ if (prec->nobt == 0) prec->mask = 0xffffffff; prec->mask <<= prec->shft; return 0; } static long readLocked(struct link *pinp, void *dummy) { mbbiRecord *prec = (mbbiRecord *) pinp->precord; long status = dbGetLink(pinp, DBR_LONG, &prec->rval, 0, 0); if (status) return status; if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) dbGetTimeStamp(pinp, &prec->time); return status; } static long read_mbbi(mbbiRecord *prec) { long status = dbLinkDoLocked(&prec->inp, readLocked, NULL); if (status == S_db_noLSET) status = readLocked(&prec->inp, NULL); if (!status) prec->rval &= prec->mask; return status; } base-7.0.3.1/modules/database/src/std/dev/devMbboDirectSoft.c0000664000577000060420000000200513557101274022474 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devMbboDirectSoft.c */ /* * Original Author: Bob Dalesio * Date: 10-08-93 */ #include #include "dbAccess.h" #include "devSup.h" #include "mbboDirectRecord.h" #include "epicsExport.h" static long write_mbbo(mbboDirectRecord *prec) { dbPutLink(&prec->out, DBR_ULONG, &prec->val, 1); return 0; } /* Create the dset for devMbboDirectSoft */ struct { dset common; DEVSUPFUN write; } devMbboDirectSoft = { {5, NULL, NULL, NULL, NULL}, write_mbbo }; epicsExportAddress(dset, devMbboDirectSoft); base-7.0.3.1/modules/database/src/std/dev/devMbboDirectSoftCallback.c0000664000577000060420000000247313557101274024122 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devMbboDirectSoftCallback.c */ /* * Original Author: Marty Kraimer * Date: 04NOV2003 */ #include #include "alarm.h" #include "dbAccess.h" #include "recGbl.h" #include "devSup.h" #include "mbboDirectRecord.h" #include "epicsExport.h" static long write_mbbo(mbboDirectRecord *prec) { struct link *plink = &prec->out; long status; if (prec->pact) return 0; status = dbPutLinkAsync(plink, DBR_ULONG, &prec->val, 1); if (!status) prec->pact = TRUE; else if (status == S_db_noLSET) status = dbPutLink(plink, DBR_ULONG, &prec->val, 1); return status; } /* Create the dset for devMbboSoft */ struct { dset common; DEVSUPFUN write; } devMbboDirectSoftCallback = { {5, NULL, NULL, NULL, NULL}, write_mbbo }; epicsExportAddress(dset, devMbboDirectSoftCallback); base-7.0.3.1/modules/database/src/std/dev/devMbboDirectSoftRaw.c0000664000577000060420000000243313557101274023153 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devMbboDirectSoftRaw.c */ /* * Original Author: Janet Anderson * Date: 10-08-93 */ #include #include "dbAccess.h" #include "epicsTypes.h" #include "devSup.h" #include "mbboDirectRecord.h" #include "epicsExport.h" static long init_record(mbboDirectRecord *prec) { if (prec->nobt == 0) prec->mask = 0xffffffff; prec->mask <<= prec->shft; return 2; /* Don't convert */ } static long write_mbbo(mbboDirectRecord *prec) { epicsUInt32 data; data = prec->rval & prec->mask; dbPutLink(&prec->out, DBR_ULONG, &data, 1); return 0; } /* Create the dset for devMbboDirectSoftRaw */ struct { dset common; DEVSUPFUN write; } devMbboDirectSoftRaw = { {5, NULL, NULL, init_record, NULL}, write_mbbo }; epicsExportAddress(dset, devMbboDirectSoftRaw); base-7.0.3.1/modules/database/src/std/dev/devMbboSoft.c0000664000577000060420000000271413557101274021350 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* devMbboSoft.c */ /* * Original Author: Bob Dalesio * Current Author: Marty Kraimer * Date: 6-1-90 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "mbboRecord.h" #include "epicsExport.h" /* Create the dset for devMbboSoft */ static long init_record(mbboRecord *prec); static long write_mbbo(mbboRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_mbbo; }devMbboSoft={ 5, NULL, NULL, init_record, NULL, write_mbbo }; epicsExportAddress(dset,devMbboSoft); static long init_record(mbboRecord *prec) { /*dont convert*/ return 2; } /* end init_record() */ static long write_mbbo(mbboRecord *prec) { dbPutLink(&prec->out,DBR_USHORT, &prec->val,1); return 0; } base-7.0.3.1/modules/database/src/std/dev/devMbboSoftCallback.c0000664000577000060420000000277413557101274022773 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devMbboSoftCallback.c */ /* * Author: Marty Kraimer * Date: 04NOV2003 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "mbboRecord.h" #include "epicsExport.h" /* Create the dset for devMbboSoftCallback */ static long write_mbbo(mbboRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_mbbo; }devMbboSoftCallback={ 5, NULL, NULL, NULL, NULL, write_mbbo }; epicsExportAddress(dset,devMbboSoftCallback); static long write_mbbo(mbboRecord *prec) { struct link *plink = &prec->out; long status; if (prec->pact) return 0; status = dbPutLinkAsync(plink, DBR_USHORT, &prec->val, 1); if (!status) prec->pact = TRUE; else if (status == S_db_noLSET) status = dbPutLink(plink, DBR_USHORT, &prec->val, 1); return status; } base-7.0.3.1/modules/database/src/std/dev/devMbboSoftRaw.c0000664000577000060420000000236013557101274022017 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devMbboSoftRaw.c */ /* * Original Author: Janet Anderson * Date: 3-28-92 */ #include #include "dbAccess.h" #include "epicsTypes.h" #include "devSup.h" #include "mbboRecord.h" #include "epicsExport.h" static long init_record(mbboRecord *prec) { if (prec->nobt == 0) prec->mask = 0xffffffff; prec->mask <<= prec->shft; return 2; /* Don't convert */ } static long write_mbbo(mbboRecord *prec) { epicsUInt32 data; data = prec->rval & prec->mask; dbPutLink(&prec->out, DBR_ULONG, &data, 1); return 0; } /* Create the dset for devMbboSoftRaw */ struct { dset common; DEVSUPFUN write; } devMbboSoftRaw = { {5, NULL, NULL, init_record, NULL}, write_mbbo }; epicsExportAddress(dset, devMbboSoftRaw); base-7.0.3.1/modules/database/src/std/dev/devPrintfSoft.c0000664000577000060420000000135313557101274021731 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Andrew Johnson * Date: 28 Sept 2012 */ #include "dbAccess.h" #include "printfRecord.h" #include "epicsExport.h" static long write_string(printfRecord *prec) { return dbPutLinkLS(&prec->out, prec->val, prec->len); } printfdset devPrintfSoft = { 5, NULL, NULL, NULL, NULL, write_string }; epicsExportAddress(dset, devPrintfSoft); base-7.0.3.1/modules/database/src/std/dev/devPrintfSoftCallback.c0000664000577000060420000000231613557101274023346 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Andrew Johnson * Date: 28 Sept 2012 */ #include "alarm.h" #include "dbAccess.h" #include "recGbl.h" #include "printfRecord.h" #include "epicsExport.h" static long write_string(printfRecord *prec) { struct link *plink = &prec->out; int dtyp = dbGetLinkDBFtype(plink); long len = prec->len; long status; if (prec->pact || dtyp < 0) return 0; if (dtyp != DBR_CHAR && dtyp != DBF_UCHAR) { dtyp = DBR_STRING; len = 1; } status = dbPutLinkAsync(plink, dtyp, prec->val, len); if (!status) prec->pact = TRUE; else if (status == S_db_noLSET) status = dbPutLink(plink, dtyp, prec->val, len); return status; } printfdset devPrintfSoftCallback = { 5, NULL, NULL, NULL, NULL, write_string }; epicsExportAddress(dset, devPrintfSoftCallback); base-7.0.3.1/modules/database/src/std/dev/devSASoft.c0000664000577000060420000000640113557101274020771 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 Lawrence Berkeley Laboratory,The Control Systems * Group, Systems Engineering Department * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Carl Lionberger * Date: 9-2-93 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "dbEvent.h" #include "recGbl.h" #include "devSup.h" #include "subArrayRecord.h" #include "epicsExport.h" /* Create the dset for devSASoft */ static long init_record(subArrayRecord *prec); static long read_sa(subArrayRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_sa; } devSASoft = { 5, NULL, NULL, init_record, NULL, read_sa }; epicsExportAddress(dset, devSASoft); static void subset(subArrayRecord *prec, long nRequest) { long ecount = nRequest - prec->indx; if (ecount > 0) { int esize = dbValueSize(prec->ftvl); if (ecount > prec->nelm) ecount = prec->nelm; memmove(prec->bptr, (char *)prec->bptr + prec->indx * esize, ecount * esize); } else ecount = 0; prec->nord = ecount; prec->udf = FALSE; } static long init_record(subArrayRecord *prec) { long nRequest = prec->indx + prec->nelm; long status; if (nRequest > prec->malm) nRequest = prec->malm; status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nRequest); if (!status && nRequest > 0) subset(prec, nRequest); return status; } struct sart { long nRequest; epicsTimeStamp *ptime; }; static long readLocked(struct link *pinp, void *vrt) { subArrayRecord *prec = (subArrayRecord *) pinp->precord; struct sart *prt = (struct sart *) vrt; long status = dbGetLink(pinp, prec->ftvl, prec->bptr, 0, &prt->nRequest); if (!status && prt->ptime) dbGetTimeStamp(pinp, prt->ptime); return status; } static long read_sa(subArrayRecord *prec) { long status; struct sart rt; epicsUInt32 nord = prec->nord; rt.nRequest = prec->indx + prec->nelm; if (rt.nRequest > prec->malm) rt.nRequest = prec->malm; rt.ptime = (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) ? &prec->time : NULL; if (dbLinkIsConstant(&prec->inp)) { status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &rt.nRequest); if (status == S_db_badField) { /* INP was empty */ rt.nRequest = prec->nord; status = 0; } } else { status = dbLinkDoLocked(&prec->inp, readLocked, &rt); if (status == S_db_noLSET) status = readLocked(&prec->inp, &rt); } if (!status && rt.nRequest > 0) { subset(prec, rt.nRequest); if (nord != prec->nord) db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); } return status; } base-7.0.3.1/modules/database/src/std/dev/devSiSoft.c0000664000577000060420000000401413557101274021037 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Janet Anderson * Date: 04-21-91 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "epicsTime.h" #include "recGbl.h" #include "devSup.h" #include "link.h" #include "stringinRecord.h" #include "epicsExport.h" /* Create the dset for devSiSoft */ static long init_record(stringinRecord *prec); static long read_stringin(stringinRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_stringin; } devSiSoft = { 5, NULL, NULL, init_record, NULL, read_stringin }; epicsExportAddress(dset, devSiSoft); static long init_record(stringinRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBF_STRING, prec->val)) prec->udf = FALSE; return 0; } static long readLocked(struct link *pinp, void *dummy) { stringinRecord *prec = (stringinRecord *) pinp->precord; long status = dbGetLink(pinp, DBR_STRING, prec->val, 0, 0); if (status) return status; if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) dbGetTimeStamp(pinp, &prec->time); return status; } static long read_stringin(stringinRecord *prec) { long status = dbLinkDoLocked(&prec->inp, readLocked, NULL); if (status == S_db_noLSET) status = readLocked(&prec->inp, NULL); if (!status && !dbLinkIsConstant(&prec->inp)) prec->udf = FALSE; return status; } base-7.0.3.1/modules/database/src/std/dev/devSiSoftCallback.c0000664000577000060420000001253413557101274022462 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devSiSoftCallback.c */ /* * Authors: Marty Kraimer & Andrew Johnson */ #include #include #include #include "alarm.h" #include "callback.h" #include "cantProceed.h" #include "dbCommon.h" #include "dbDefs.h" #include "dbAccess.h" #include "dbChannel.h" #include "dbNotify.h" #include "epicsAssert.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "link.h" #include "stringinRecord.h" #include "epicsExport.h" #define GET_OPTIONS (DBR_STATUS | DBR_TIME) typedef struct devPvt { DBADDR dbaddr; processNotify pn; epicsCallback callback; long options; int status; struct { DBRstatus DBRtime char value[MAX_STRING_SIZE]; } buffer; } devPvt; static void getCallback(processNotify *ppn, notifyGetType type) { stringinRecord *prec = (stringinRecord *)ppn->usrPvt; devPvt *pdevPvt = (devPvt *)prec->dpvt; long no_elements = 1; if (ppn->status==notifyCanceled) { printf("devSiSoftCallback::getCallback notifyCanceled\n"); return; } assert(type == getFieldType); pdevPvt->status = dbChannelGetField(ppn->chan, DBR_STRING, &pdevPvt->buffer, &pdevPvt->options, &no_elements, 0); } static void doneCallback(processNotify *ppn) { stringinRecord *prec = (stringinRecord *)ppn->usrPvt; devPvt *pdevPvt = (devPvt *)prec->dpvt; callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec); } static long add_record(dbCommon *pcommon) { stringinRecord *prec = (stringinRecord *)pcommon; DBLINK *plink = &prec->inp; dbChannel *chan; devPvt *pdevPvt; processNotify *ppn; if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink)) return 0; if (plink->type != PV_LINK) { long status = S_db_badField; recGblRecordError(status, (void *)prec, "devSiSoftCallback (add_record) Illegal INP field"); return status; } pdevPvt = calloc(1, sizeof(*pdevPvt)); if (!pdevPvt) { long status = S_db_noMemory; recGblRecordError(status, (void *)prec, "devSiSoftCallback (add_record) out of memory, calloc() failed"); return status; } ppn = &pdevPvt->pn; chan = dbChannelCreate(plink->value.pv_link.pvname); if (!chan) { long status = S_db_notFound; recGblRecordError(status, (void *)prec, "devSiSoftCallback (add_record) linked record not found"); return status; } plink->type = PN_LINK; plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */ ppn->usrPvt = prec; ppn->chan = chan; ppn->getCallback = getCallback; ppn->doneCallback = doneCallback; ppn->requestType = processGetRequest; pdevPvt->options = GET_OPTIONS; prec->dpvt = pdevPvt; return 0; } static long del_record(dbCommon *pcommon) { stringinRecord *prec = (stringinRecord *)pcommon; DBLINK *plink = &prec->inp; devPvt *pdevPvt = (devPvt *)prec->dpvt; if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink)) return 0; assert(plink->type == PN_LINK); dbNotifyCancel(&pdevPvt->pn); dbChannelDelete(pdevPvt->pn.chan); free(pdevPvt); plink->type = PV_LINK; return 0; } static struct dsxt dsxtSoftCallback = { add_record, del_record }; static long init(int pass) { if (pass == 0) devExtend(&dsxtSoftCallback); return 0; } static long init_record(stringinRecord *prec) { if (recGblInitConstantLink(&prec->inp, DBR_STRING, &prec->val)) prec->udf = FALSE; return 0; } static long read_si(stringinRecord *prec) { devPvt *pdevPvt = (devPvt *)prec->dpvt; if (!prec->dpvt) return 0; if (!prec->pact) { dbProcessNotify(&pdevPvt->pn); prec->pact = TRUE; return 0; } if (pdevPvt->status) { recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); return pdevPvt->status; } strncpy(prec->val, pdevPvt->buffer.value, MAX_STRING_SIZE); prec->val[MAX_STRING_SIZE-1] = 0; prec->udf = FALSE; switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) { case pvlOptNMS: break; case pvlOptMSI: if (pdevPvt->buffer.severity < INVALID_ALARM) break; /* else fall through */ case pvlOptMS: recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity); break; case pvlOptMSS: recGblSetSevr(prec, pdevPvt->buffer.status, pdevPvt->buffer.severity); break; } if (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) prec->time = pdevPvt->buffer.time; return 0; } /* Create the dset for devSiSoftCallback */ struct { dset common; DEVSUPFUN read_li; } devSiSoftCallback = { {5, NULL, init, init_record, NULL}, read_si }; epicsExportAddress(dset,devSiSoftCallback); base-7.0.3.1/modules/database/src/std/dev/devSoSoft.c0000664000577000060420000000253513557101274021053 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Janet Anderson * Date: 21APR1991 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "stringoutRecord.h" #include "epicsExport.h" /* Create the dset for devSoSoft */ static long write_stringout(stringoutRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_stringout; } devSoSoft = { 5, NULL, NULL, NULL, NULL, write_stringout }; epicsExportAddress(dset, devSoSoft); static long write_stringout(stringoutRecord *prec) { long status; status = dbPutLink(&prec->out, DBR_STRING, prec->val, 1); return status; } base-7.0.3.1/modules/database/src/std/dev/devSoSoftCallback.c0000664000577000060420000000306313557101274022465 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Marty Kraimer * Date: 04NOV2003 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "stringoutRecord.h" #include "epicsExport.h" /* Create the dset for devSoSoftCallback */ static long write_stringout(stringoutRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_stringout; } devSoSoftCallback = { 5, NULL, NULL, NULL, NULL, write_stringout }; epicsExportAddress(dset, devSoSoftCallback); static long write_stringout(stringoutRecord *prec) { struct link *plink = &prec->out; long status; if (prec->pact) return 0; status = dbPutLinkAsync(plink, DBR_STRING, &prec->val, 1); if (!status) prec->pact = TRUE; else if (status == S_db_noLSET) status = dbPutLink(plink, DBR_STRING, &prec->val, 1); return status; } base-7.0.3.1/modules/database/src/std/dev/devSoft.dbd0000664000577000060420000000656313557101274021065 0ustar anjaesctl# devSoft.dbd device(aai,CONSTANT,devAaiSoft,"Soft Channel") device(aao,CONSTANT,devAaoSoft,"Soft Channel") device(ai,CONSTANT,devAiSoft,"Soft Channel") device(ao,CONSTANT,devAoSoft,"Soft Channel") device(bi,CONSTANT,devBiSoft,"Soft Channel") device(bo,CONSTANT,devBoSoft,"Soft Channel") device(calcout,CONSTANT,devCalcoutSoft,"Soft Channel") device(event,CONSTANT,devEventSoft,"Soft Channel") device(histogram,CONSTANT,devHistogramSoft,"Soft Channel") device(int64in,CONSTANT,devI64inSoft,"Soft Channel") device(int64out,CONSTANT,devI64outSoft,"Soft Channel") device(longin,CONSTANT,devLiSoft,"Soft Channel") device(longout,CONSTANT,devLoSoft,"Soft Channel") device(lsi,CONSTANT,devLsiSoft,"Soft Channel") device(lso,CONSTANT,devLsoSoft,"Soft Channel") device(mbbi,CONSTANT,devMbbiSoft,"Soft Channel") device(mbbiDirect,CONSTANT,devMbbiDirectSoft,"Soft Channel") device(mbbo,CONSTANT,devMbboSoft,"Soft Channel") device(mbboDirect,CONSTANT,devMbboDirectSoft,"Soft Channel") device(printf,CONSTANT,devPrintfSoft,"Soft Channel") device(stringin,CONSTANT,devSiSoft,"Soft Channel") device(stringout,CONSTANT,devSoSoft,"Soft Channel") device(subArray,CONSTANT,devSASoft,"Soft Channel") device(waveform,CONSTANT,devWfSoft,"Soft Channel") device(ai,CONSTANT,devAiSoftRaw,"Raw Soft Channel") device(ao,CONSTANT,devAoSoftRaw,"Raw Soft Channel") device(bi,CONSTANT,devBiSoftRaw,"Raw Soft Channel") device(bo,CONSTANT,devBoSoftRaw,"Raw Soft Channel") device(mbbi,CONSTANT,devMbbiSoftRaw,"Raw Soft Channel") device(mbbiDirect,CONSTANT,devMbbiDirectSoftRaw,"Raw Soft Channel") device(mbbo,CONSTANT,devMbboSoftRaw,"Raw Soft Channel") device(mbboDirect,CONSTANT,devMbboDirectSoftRaw,"Raw Soft Channel") device(ai,CONSTANT,devAiSoftCallback,"Async Soft Channel") device(ao,CONSTANT,devAoSoftCallback,"Async Soft Channel") device(bi,CONSTANT,devBiSoftCallback,"Async Soft Channel") device(bo,CONSTANT,devBoSoftCallback,"Async Soft Channel") device(calcout,CONSTANT,devCalcoutSoftCallback,"Async Soft Channel") device(int64in,CONSTANT,devI64inSoftCallback,"Async Soft Channel") device(int64out,CONSTANT,devI64outSoftCallback,"Async Soft Channel") device(longin,CONSTANT,devLiSoftCallback,"Async Soft Channel") device(longout,CONSTANT,devLoSoftCallback,"Async Soft Channel") device(lso,CONSTANT,devLsoSoftCallback,"Async Soft Channel") device(mbbi,CONSTANT,devMbbiSoftCallback,"Async Soft Channel") device(mbbiDirect,CONSTANT,devMbbiDirectSoftCallback,"Async Soft Channel") device(mbbo,CONSTANT,devMbboSoftCallback,"Async Soft Channel") device(mbboDirect,CONSTANT,devMbboDirectSoftCallback,"Async Soft Channel") device(printf,CONSTANT,devPrintfSoftCallback,"Async Soft Channel") device(stringin,CONSTANT,devSiSoftCallback,"Async Soft Channel") device(stringout,CONSTANT,devSoSoftCallback,"Async Soft Channel") device(ai, INST_IO,devTimestampAI,"Soft Timestamp") device(stringin,INST_IO,devTimestampSI,"Soft Timestamp") device(ai, INST_IO,devAiGeneralTime,"General Time") device(bo, INST_IO,devBoGeneralTime,"General Time") device(longin, INST_IO,devLiGeneralTime,"General Time") device(stringin,INST_IO,devSiGeneralTime,"General Time") device(lso,INST_IO,devLsoStdio,"stdio") device(printf,INST_IO,devPrintfStdio,"stdio") device(stringout,INST_IO,devSoStdio,"stdio") device(lsi,INST_IO,devLsiEnviron,"getenv") device(stringin,INST_IO,devSiEnviron,"getenv") device(bi, INST_IO, devBiDbState, "Db State") device(bo, INST_IO, devBoDbState, "Db State") base-7.0.3.1/modules/database/src/std/dev/devStdio.c0000664000577000060420000001103113557101274020707 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include "dbCommon.h" #include "devSup.h" #include "errlog.h" #include "recGbl.h" #include "recSup.h" #include "lsoRecord.h" #include "printfRecord.h" #include "stringoutRecord.h" #include "epicsExport.h" typedef int (*PRINTFFUNC)(const char *fmt, ...); static int stderrPrintf(const char *fmt, ...); static int logPrintf(const char *fmt, ...); static struct outStream { const char *name; PRINTFFUNC print; } outStreams[] = { {"stdout", printf}, {"stderr", stderrPrintf}, {"errlog", logPrintf}, {NULL, NULL} }; static int stderrPrintf(const char *fmt, ...) { va_list pvar; int retval; va_start(pvar, fmt); retval = vfprintf(stderr, fmt, pvar); va_end (pvar); return retval; } static int logPrintf(const char *fmt, ...) { va_list pvar; int retval; va_start(pvar, fmt); retval = errlogVprintf(fmt, pvar); va_end (pvar); return retval; } /* lso device support */ static long add_lso(dbCommon *pcommon) { lsoRecord *prec = (lsoRecord *) pcommon; struct outStream *pstream; if (prec->out.type != INST_IO) return S_dev_badOutType; for (pstream = outStreams; pstream->name; ++pstream) { if (strcmp(prec->out.value.instio.string, pstream->name) == 0) { prec->dpvt = pstream; return 0; } } prec->dpvt = NULL; return -1; } static long del_lso(dbCommon *pcommon) { lsoRecord *prec = (lsoRecord *) pcommon; prec->dpvt = NULL; return 0; } static struct dsxt dsxtLsoStdio = { add_lso, del_lso }; static long init_lso(int pass) { if (pass == 0) devExtend(&dsxtLsoStdio); return 0; } static long write_lso(lsoRecord *prec) { struct outStream *pstream = (struct outStream *)prec->dpvt; if (pstream) pstream->print("%s\n", prec->val); return 0; } lsodset devLsoStdio = { 5, NULL, init_lso, NULL, NULL, write_lso }; epicsExportAddress(dset, devLsoStdio); /* printf device support */ static long add_printf(dbCommon *pcommon) { printfRecord *prec = (printfRecord *) pcommon; struct outStream *pstream; if (prec->out.type != INST_IO) return S_dev_badOutType; for (pstream = outStreams; pstream->name; ++pstream) { if (strcmp(prec->out.value.instio.string, pstream->name) == 0) { prec->dpvt = pstream; return 0; } } prec->dpvt = NULL; return -1; } static long del_printf(dbCommon *pcommon) { printfRecord *prec = (printfRecord *) pcommon; prec->dpvt = NULL; return 0; } static struct dsxt dsxtPrintfStdio = { add_printf, del_printf }; static long init_printf(int pass) { if (pass == 0) devExtend(&dsxtPrintfStdio); return 0; } static long write_printf(printfRecord *prec) { struct outStream *pstream = (struct outStream *)prec->dpvt; if (pstream) pstream->print("%s\n", prec->val); return 0; } printfdset devPrintfStdio = { 5, NULL, init_printf, NULL, NULL, write_printf }; epicsExportAddress(dset, devPrintfStdio); /* stringout device support */ static long add_stringout(dbCommon *pcommon) { stringoutRecord *prec = (stringoutRecord *) pcommon; struct outStream *pstream; if (prec->out.type != INST_IO) return S_dev_badOutType; for (pstream = outStreams; pstream->name; ++pstream) { if (strcmp(prec->out.value.instio.string, pstream->name) == 0) { prec->dpvt = pstream; return 0; } } prec->dpvt = NULL; return -1; } static long del_stringout(dbCommon *pcommon) { stringoutRecord *prec = (stringoutRecord *) pcommon; prec->dpvt = NULL; return 0; } static struct dsxt dsxtSoStdio = { add_stringout, del_stringout }; static long init_stringout(int pass) { if (pass == 0) devExtend(&dsxtSoStdio); return 0; } static long write_stringout(stringoutRecord *prec) { struct outStream *pstream = (struct outStream *)prec->dpvt; if (pstream) pstream->print("%s\n", prec->val); return 0; } static struct { dset common; DEVSUPFUN write; } devSoStdio = { {5, NULL, init_stringout, NULL, NULL}, write_stringout }; epicsExportAddress(dset, devSoStdio); base-7.0.3.1/modules/database/src/std/dev/devTimestamp.c0000664000577000060420000000342013557101274021573 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in the file LICENSE that is included with this distribution. \*************************************************************************/ /* * Device support for EPICS time stamps * * Original Author: Eric Norum */ #include "dbDefs.h" #include "epicsTime.h" #include "alarm.h" #include "devSup.h" #include "recGbl.h" #include "aiRecord.h" #include "stringinRecord.h" #include "epicsExport.h" /* Extended device support to allow INP field changes */ static long initAllow(int pass) { if (pass == 0) devExtend(&devSoft_DSXT); return 0; } /* ai record */ static long read_ai(aiRecord *prec) { recGblGetTimeStamp(prec); prec->val = prec->time.secPastEpoch + (double)prec->time.nsec * 1e-9; prec->udf = FALSE; return 2; } struct { dset common; DEVSUPFUN read_write; DEVSUPFUN special_linconv; } devTimestampAI = { {6, NULL, initAllow, NULL, NULL}, read_ai, NULL }; epicsExportAddress(dset, devTimestampAI); /* stringin record */ static long read_stringin (stringinRecord *prec) { int len; recGblGetTimeStamp(prec); len = epicsTimeToStrftime(prec->val, sizeof prec->val, prec->inp.value.instio.string, &prec->time); if (len >= sizeof prec->val) { prec->udf = TRUE; recGblSetSevr(prec, UDF_ALARM, prec->udfs); return -1; } prec->udf = FALSE; return 0; } struct { dset common; DEVSUPFUN read_stringin; } devTimestampSI = { {5, NULL, initAllow, NULL, NULL}, read_stringin }; epicsExportAddress(dset, devTimestampSI); base-7.0.3.1/modules/database/src/std/dev/devWfSoft.c0000664000577000060420000000476713557101274021057 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Authors: Bob Dalesio and Marty Kraimer * Date: 6-1-90 */ #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "dbEvent.h" #include "recGbl.h" #include "devSup.h" #include "waveformRecord.h" #include "epicsExport.h" /* Create the dset for devWfSoft */ static long init_record(waveformRecord *prec); static long read_wf(waveformRecord *prec); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_wf; } devWfSoft = { 5, NULL, NULL, init_record, NULL, read_wf }; epicsExportAddress(dset, devWfSoft); static long init_record(waveformRecord *prec) { long nelm = prec->nelm; long status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nelm); if (!status && nelm > 0) { prec->nord = nelm; prec->udf = FALSE; } else prec->nord = 0; return status; } struct wfrt { long nRequest; epicsTimeStamp *ptime; }; static long readLocked(struct link *pinp, void *vrt) { waveformRecord *prec = (waveformRecord *) pinp->precord; struct wfrt *prt = (struct wfrt *) vrt; long status = dbGetLink(pinp, prec->ftvl, prec->bptr, 0, &prt->nRequest); if (!status && prt->ptime) dbGetTimeStamp(pinp, prt->ptime); return status; } static long read_wf(waveformRecord *prec) { long status; struct wfrt rt; epicsUInt32 nord = prec->nord; rt.nRequest = prec->nelm; rt.ptime = (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) ? &prec->time : NULL; status = dbLinkDoLocked(&prec->inp, readLocked, &rt); if (status == S_db_noLSET) status = readLocked(&prec->inp, &rt); if (!status && rt.nRequest > 0) { prec->nord = rt.nRequest; prec->udf = FALSE; if (nord != prec->nord) db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); } return status; } base-7.0.3.1/modules/database/src/std/dev/softDevIoc.rc0000664000577000060420000000224013557101274021357 0ustar anjaesctl#include #include "epicsVersion.h" VS_VERSION_INFO VERSIONINFO FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_UNKNOWN FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "Comments","Soft Device Support Library for EPICS\0" VALUE "CompanyName", "The EPICS collaboration\0" VALUE "FileDescription", "Soft Device Support Library\0" VALUE "FileVersion", EPICS_VERSION_STRING "\0" VALUE "InternalName", "softDevIoc\0" VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" VALUE "OriginalFilename", "softDevIoc.dll\0" VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" VALUE "ProductVersion", EPICS_VERSION_STRING "\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END base-7.0.3.1/modules/database/src/std/filters/Makefile0000664000577000060420000000116313557101274021321 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/std/Makefile. SRC_DIRS += $(STDDIR)/filters DBD += filters.dbd dbRecStd_SRCS += ts.c dbRecStd_SRCS += dbnd.c dbRecStd_SRCS += arr.c dbRecStd_SRCS += sync.c dbRecStd_SRCS += decimate.c HTMLS += filters.html base-7.0.3.1/modules/database/src/std/filters/arr.c0000664000577000060420000001430613557101274020614 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #include #include #include #include #include #include #include #include #include #include typedef struct myStruct { epicsInt32 start; epicsInt32 incr; epicsInt32 end; void *arrayFreeList; long no_elements; } myStruct; static void *myStructFreeList; static const chfPluginArgDef opts[] = { chfInt32 (myStruct, start, "s", 0, 1), chfInt32 (myStruct, incr, "i", 0, 1), chfInt32 (myStruct, end, "e", 0, 1), chfPluginArgEnd }; static void * allocPvt(void) { myStruct *my = (myStruct*) freeListCalloc(myStructFreeList); if (!my) return NULL; my->incr = 1; my->end = -1; return (void *) my; } static void freePvt(void *pvt) { myStruct *my = (myStruct*) pvt; if (my->arrayFreeList) freeListCleanup(my->arrayFreeList); freeListFree(myStructFreeList, pvt); } static int parse_ok(void *pvt) { myStruct *my = (myStruct*) pvt; if (my->incr <= 0) my->incr = 1; return 0; } static void freeArray(db_field_log *pfl) { if (pfl->type == dbfl_type_ref) { freeListFree(pfl->u.r.pvt, pfl->u.r.field); } } static long wrapArrayIndices(long *start, const long increment, long *end, const long no_elements) { long len = 0; if (*start < 0) *start = no_elements + *start; if (*start < 0) *start = 0; if (*start > no_elements) *start = no_elements; if (*end < 0) *end = no_elements + *end; if (*end < 0) *end = 0; if (*end >= no_elements) *end = no_elements - 1; if (*end - *start >= 0) len = 1 + (*end - *start) / increment; return len; } static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { myStruct *my = (myStruct*) pvt; struct dbCommon *prec; rset *prset; long start = my->start; long end = my->end; long nTarget = 0; long offset = 0; long nSource = chan->addr.no_elements; long capacity = nSource; void *pdst; switch (pfl->type) { case dbfl_type_val: /* Only filter arrays */ break; case dbfl_type_rec: /* Extract from record */ if (chan->addr.special == SPC_DBADDR && nSource > 1 && (prset = dbGetRset(&chan->addr)) && prset->get_array_info) { void *pfieldsave = chan->addr.pfield; prec = dbChannelRecord(chan); dbScanLock(prec); prset->get_array_info(&chan->addr, &nSource, &offset); nTarget = wrapArrayIndices(&start, my->incr, &end, nSource); pfl->type = dbfl_type_ref; pfl->stat = prec->stat; pfl->sevr = prec->sevr; pfl->time = prec->time; pfl->field_type = chan->addr.field_type; pfl->field_size = chan->addr.field_size; pfl->no_elements = nTarget; if (nTarget) { pdst = freeListCalloc(my->arrayFreeList); if (pdst) { pfl->u.r.dtor = freeArray; pfl->u.r.pvt = my->arrayFreeList; offset = (offset + start) % chan->addr.no_elements; dbExtractArrayFromRec(&chan->addr, pdst, nTarget, capacity, offset, my->incr); pfl->u.r.field = pdst; } } dbScanUnlock(prec); chan->addr.pfield = pfieldsave; } break; /* Extract from buffer */ case dbfl_type_ref: pdst = NULL; nSource = pfl->no_elements; nTarget = wrapArrayIndices(&start, my->incr, &end, nSource); pfl->no_elements = nTarget; if (nTarget) { /* Copy the data out */ void *psrc = pfl->u.r.field; pdst = freeListCalloc(my->arrayFreeList); if (!pdst) break; offset = start; dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type, nTarget, nSource, offset, my->incr); } if (pfl->u.r.dtor) pfl->u.r.dtor(pfl); if (nTarget) { pfl->u.r.dtor = freeArray; pfl->u.r.pvt = my->arrayFreeList; pfl->u.r.field = pdst; } break; } return pfl; } static void channelRegisterPost(dbChannel *chan, void *pvt, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { myStruct *my = (myStruct*) pvt; long start = my->start; long end = my->end; long max = 0; if (probe->no_elements <= 1) return; /* array data only */ max = wrapArrayIndices(&start, my->incr, &end, probe->no_elements); if (max) { if (!my->arrayFreeList) freeListInitPvt(&my->arrayFreeList, max * probe->field_size, 2); if (!my->arrayFreeList) return; } probe->no_elements = my->no_elements = max; *cb_out = filter; *arg_out = pvt; } static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) { myStruct *my = (myStruct*) pvt; printf("%*sArray (arr): start=%d, incr=%d, end=%d\n", indent, "", my->start, my->incr, my->end); } static chfPluginIf pif = { allocPvt, freePvt, NULL, /* parse_error, */ parse_ok, NULL, /* channel_open, */ NULL, /* channelRegisterPre, */ channelRegisterPost, channel_report, NULL /* channel_close */ }; static void arrShutdown(void* ignore) { if(myStructFreeList) freeListCleanup(myStructFreeList); myStructFreeList = NULL; } static void arrInitialize(void) { if (!myStructFreeList) freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64); chfPluginRegister("arr", &pif, opts); epicsAtExit(arrShutdown, NULL); } epicsExportRegistrar(arrInitialize); base-7.0.3.1/modules/database/src/std/filters/dbnd.c0000664000577000060420000000720513557101274020737 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * für Materialien und Energie GmbH. * Copyright (c) 2014 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #include #include #include #include #include #include #include #include typedef struct myStruct { int mode; double cval; double hyst; double last; } myStruct; static void *myStructFreeList; static const chfPluginEnumType modeEnum[] = { {"abs", 0}, {"rel", 1}, {NULL,0} }; static const chfPluginArgDef opts[] = { chfDouble (myStruct, cval, "d", 0, 1), chfEnum (myStruct, mode, "m", 0, 1, modeEnum), chfTagDouble (myStruct, cval, "abs", mode, 0, 0, 1), chfTagDouble (myStruct, cval, "rel", mode, 1, 0, 1), chfPluginArgEnd }; static void * allocPvt(void) { return freeListCalloc(myStructFreeList); } static void freePvt(void *pvt) { freeListFree(myStructFreeList, pvt); } static int parse_ok(void *pvt) { myStruct *my = (myStruct*) pvt; my->hyst = my->cval; my->last = epicsNAN; return 0; } static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { myStruct *my = (myStruct*) pvt; long status; double val; unsigned send = 1; /* * Only scalar values supported - strings, arrays, and conversion errors * are just passed on */ if (pfl->type == dbfl_type_val) { DBADDR localAddr = chan->addr; /* Structure copy */ localAddr.field_type = pfl->field_type; localAddr.field_size = pfl->field_size; localAddr.no_elements = pfl->no_elements; localAddr.pfield = (char *) &pfl->u.v.field; status = dbFastGetConvertRoutine[pfl->field_type][DBR_DOUBLE] (localAddr.pfield, (void*) &val, &localAddr); if (!status) { send = 0; recGblCheckDeadband(&my->last, val, my->hyst, &send, 1); if (send && my->mode == 1) { my->hyst = val * my->cval/100.; } } } if (!send) { db_delete_field_log(pfl); return NULL; } else return pfl; } static void channelRegisterPre(dbChannel *chan, void *pvt, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { *cb_out = filter; *arg_out = pvt; } static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) { myStruct *my = (myStruct*) pvt; printf("%*sDeadband (dbnd): mode=%s, delta=%g%s\n", indent, "", chfPluginEnumString(modeEnum, my->mode, "n/a"), my->cval, my->mode == 1 ? "%" : ""); } static chfPluginIf pif = { allocPvt, freePvt, NULL, /* parse_error, */ parse_ok, NULL, /* channel_open, */ channelRegisterPre, NULL, /* channelRegisterPost, */ channel_report, NULL /* channel_close */ }; static void dbndShutdown(void* ignore) { if(myStructFreeList) freeListCleanup(myStructFreeList); myStructFreeList = NULL; } static void dbndInitialize(void) { if (!myStructFreeList) freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64); chfPluginRegister("dbnd", &pif, opts); epicsAtExit(dbndShutdown, NULL); } epicsExportRegistrar(dbndInitialize); base-7.0.3.1/modules/database/src/std/filters/decimate.c0000664000577000060420000000504513557101274021603 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2019 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Authors: Ralph Lange , * Andrew Johnson */ #include #include "freeList.h" #include "db_field_log.h" #include "chfPlugin.h" #include "epicsExport.h" typedef struct myStruct { epicsInt32 n, i; } myStruct; static void *myStructFreeList; static const chfPluginArgDef opts[] = { chfInt32(myStruct, n, "n", 1, 0), chfPluginArgEnd }; static void * allocPvt(void) { myStruct *my = (myStruct*) freeListCalloc(myStructFreeList); return (void *) my; } static void freePvt(void *pvt) { freeListFree(myStructFreeList, pvt); } static int parse_ok(void *pvt) { myStruct *my = (myStruct*) pvt; if (my->n < 1) return -1; return 0; } static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { db_field_log *passfl = NULL; myStruct *my = (myStruct*) pvt; epicsInt32 i = my->i; if (pfl->ctx == dbfl_context_read) return pfl; if (i++ == 0) passfl = pfl; else db_delete_field_log(pfl); if (i >= my->n) i = 0; my->i = i; return passfl; } static void channelRegisterPre(dbChannel *chan, void *pvt, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { *cb_out = filter; *arg_out = pvt; } static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) { myStruct *my = (myStruct*) pvt; printf("%*sDecimate (dec): n=%d, i=%d\n", indent, "", my->n, my->i); } static chfPluginIf pif = { allocPvt, freePvt, NULL, /* parse_error, */ parse_ok, NULL, /* channel_open, */ channelRegisterPre, NULL, /* channelRegisterPost, */ channel_report, NULL /* channel_close */ }; static void decInitialize(void) { static int firstTime = 1; if (!firstTime) return; firstTime = 0; if (!myStructFreeList) freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64); chfPluginRegister("dec", &pif, opts); } epicsExportRegistrar(decInitialize); base-7.0.3.1/modules/database/src/std/filters/filters.dbd.pod0000664000577000060420000002154113557101274022567 0ustar anjaesctl=head1 Channel Filters Channel Filters can be applied to Channel Access channels by a client, using a JSON Field Modifier to select the filter and any parameters. The following filters are available in this release: =over =item * L =item * L =item * L =item * L =item * L =back =head2 Using Filters Channel filters can be added to any Channel Access channel name. There can be more than one filter applied to the same channel, in which case the order that they are specified will control the order in which they are applied to the resulting data-stream. The filter specification must appear after the field name, or if the default (VAL) field is used after a dot C<.> appended to the record name. With the exception of the array short-hand which is described below, all filters must appear inside a pair of braces C< {} > after the dot expressed as a JSON (L) object, which allows filter parameters to be included as needed. Each filter is given as a name/value pair. The filter name (given in parentheses in the titles below) is a string, and must be enclosed inside double-quotes C<"> characters as per the JSON specification. Parameters to that filter are provided as the value part of the name/value pair, and will normally appear as a child JSON object consisting of name/value pairs inside a nested pair of braces C< {} >. =head4 Example Filter Given a record called C the following would apply a filter C to the VAL field of that record, giving the filter two numeric parameters named C and C: test:channel.{"f":{"lo":0,"hi":10}} Note that due to the required presence of the double-quote characters in the JSON strings in the name string, it will usually be necessary to enclose a filtered name within single-quotes C<< ' ... ' >> when typing it as an argument to a Unix shell command. =head2 Filter Reference =cut registrar(tsInitialize) =head3 TimeStamp Filter C<"ts"> This filter is used to set the timestamp of the value fetched through the channel to the time the value was fetched (or an update was sent), rather than the time the record last processed which could have been days or even weeks ago for some records, or set to the EPICS epoch if the record has never processed. =head4 Parameters None, use an empty pair of braces. =head4 Example Hal$ caget -a 'test:channel.{"ts":{}}' test:channel.{"ts":{}} 2012-08-28 22:10:31.192547 0 UDF INVALID Hal$ caget -a 'test:channel' test:channel 0 UDF INVALID =cut registrar(dbndInitialize) =head3 Deadband Filter C<"dbnd"> This filter implements a channel-specific monitor deadband, which is applied after any deadbands implemented by the record itself (it can only drop updates that the unfiltered channel generates, never add additional updates). The deadband can be specified as an absolute value change, or as a relative percentage. =head4 Parameters =over =item Mode+Deadband C<"abs">/C<"rel"> (shorthand) Mode and deadband can be specified in one definition (shorthand). The desired mode is given as parameter name (C<"abs"> or C<"rel">), with the numeric size of the deadband (absolute value or numeric percentage) as value. =item Deadband C<"d"> The size of the deadband to use. Relative deadband values are given as a numeric percentage, but without any trailing percent character. =item Mode C<"m"> (optional) A string (enclosed in double-quotes C<">), which should contain either C or C. The default mode is C if no mode parameter is included. =back =head4 Example Hal$ camonitor 'test:channel' test:channel 2012-09-01 22:10:19.600595 1 LOLO MAJOR test:channel 2012-09-01 22:10:20.600661 2 LOLO MAJOR test:channel 2012-09-01 22:10:21.600819 3 LOW MINOR test:channel 2012-09-01 22:10:22.600905 4 LOW MINOR test:channel 2012-09-01 22:10:23.601023 5 test:channel 2012-09-01 22:10:24.601136 6 HIGH MINOR ^C Hal$ camonitor 'test:channel.{"dbnd":{"abs":1.5}}' test:channel.{"dbnd":{"d":1.5}} 2012-09-01 22:11:49.613341 1 LOLO MAJOR test:channel.{"dbnd":{"d":1.5}} 2012-09-01 22:11:51.613615 3 LOW MINOR test:channel.{"dbnd":{"d":1.5}} 2012-09-01 22:11:53.613804 5 test:channel.{"dbnd":{"d":1.5}} 2012-09-01 22:11:55.614074 7 HIGH MINOR test:channel.{"dbnd":{"d":1.5}} 2012-09-01 22:11:57.614305 9 HIHI MAJOR ^C =cut registrar(arrInitialize) =head3 Array Filter C<"arr"> This filter is used to retrieve parts of an array (subarrays and strided subarrays). =head4 Parameters Note: Negative index numbers address from the end of the array, with C<-1> being the last element. =over =item Square bracket notation C<[start:increment:end]> (shorthand) The common square bracket notation which can be used in place of JSON. Any parameter may be omitted (keeping the colons) to use the default value. If only one colon is included, this means C<[start:end]> with an increment of 1. If only a single parameter is used C<[index]> the filter returns one element. =item Start index C<"s"> Index of the first original array element to retrieve. =item Increment C<"i"> Index increment between retrieved elements of the original array; must be a positive number. =item End index C<"e"> Index of the last original array element to retrieve. =back Defaults (when parameters are omitted) are: C (first element), C (fetch all elements), C (last element) =head4 Example Hal$ caget test:channel 'test:channel.{"arr":{"s":2,"i":2,"e":8}}' test:channel.[3:5] test:channel.[3:2:-3] test:channel 10 0 1 2 3 4 5 6 7 8 9 test:channel.{"arr":{"s":2,"i":2,"e":8}} 4 2 4 6 8 test:channel.[3:5] 3 3 4 5 test:channel.[3:2:-3] 3 3 5 7 =cut registrar(syncInitialize) =head3 Synchronize Filter C<"sync"> This filter is used to dynamically enable or disable monitors according to a condition and a state variable declared by the IOC. State variables have a boolean value and can be set by a binary output record, an iocsh command or by other software running in the IOC calling C. =head4 Parameters =over =item Mode+State Mode and state can be specified in one definition (shorthand). The desired mode is given as parameter name (C<"before"> / C<"first"> / C<"while"> / C<"last"> / C<"after"> / C<"unless">), with the state name (enclosed in double quotes C<">) as value. =item Mode C<"m"> A single word from the list below, enclosed in double quotes C<">. This controls how the state value should affect the monitor stream. =over =item C<"before"> E only the last value received before the state changes from false to true is forwarded to the client. =item C<"first"> E only the first value received after the state changes from true to false is forwarded to the client. =item C<"while"> E values are forwarded to the client as long as the state is true. =item C<"last"> E only the last value received before the state changes from true to false is forwarded to the client. =item C<"after"> E only the first value received after the state changes from true to false is forwarded to the client. =item C<"unless"> E values are forwarded to the client as long as the state is false. =back =item State C<"s"> The name of a state variable, enclosed in double quotes C<">. =back =head4 Example Assuming there is a system state called "blue", that is being controlled by some other facility such as a timing system, updates could be restricted to periods only when "blue" is true by using Hal$ camonitor 'test:channel' 'test:channel.{"sync":{"while":"blue"}}' ... =cut registrar(decInitialize) =head3 Decimation Filter C<"dec"> This filter is used to reduce the number or rate of monitor updates from a channel by an integer factor C that is provided as a filter argument, discarding the other updates. A true decimation following the original meaning of the word would be achieved by giving C as 10, to only allow every tenth update through. =head4 Parameters =over =item Number C<"n"> The decimation factor, a positive integer. Giving n=1 is equivalent to a no-op that allows all updates to be passed to the client. =back This filter is intentionally very simplistic. It passes on the first monitor event that it sees after the channel connects, then discards the next N-1 events before sending the next event. If several clients connect to a channel using the same filter settings they may see completely different data streams since each client gets its own instance of the filter whose event counter starts when that client connects. =head4 Example To sample a 60Hz channel at 1Hz, a 10Hz channel every 6 seconds or a 1Hz channel once every minute: Hal$ camonitor 'test:channel' 'test:channel.{"dec":{"n":60}}' ... =cut base-7.0.3.1/modules/database/src/std/filters/sync.c0000664000577000060420000001150713557101274021004 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #include "freeList.h" #include "db_field_log.h" #include "chfPlugin.h" #include "dbState.h" #include "epicsExit.h" #include "epicsAssert.h" #include "epicsExport.h" #define STATE_NAME_LENGTH 20 typedef enum syncMode { syncModeBefore=0, syncModeFirst=1, syncModeLast=2, syncModeAfter=3, syncModeWhile=4, syncModeUnless=5 } syncMode; static const chfPluginEnumType modeEnum[] = { {"before", syncModeBefore}, {"first", syncModeFirst}, {"last", syncModeLast}, {"after", syncModeAfter}, {"while", syncModeWhile}, {"unless", syncModeUnless}, {NULL, 0} }; typedef struct myStruct { syncMode mode; char state[STATE_NAME_LENGTH]; dbStateId id; db_field_log *lastfl; int laststate:1; } myStruct; static void *myStructFreeList; static const chfPluginArgDef opts[] = { chfEnum (myStruct, mode, "m", 1, 1, modeEnum), chfString (myStruct, state, "s", 1, 0), chfTagString (myStruct, state, "before", mode, 0, 1, 0), chfTagString (myStruct, state, "first", mode, 1, 1, 0), chfTagString (myStruct, state, "last", mode, 2, 1, 0), chfTagString (myStruct, state, "after", mode, 3, 1, 0), chfTagString (myStruct, state, "while", mode, 4, 1, 0), chfTagString (myStruct, state, "unless", mode, 5, 1, 0), chfPluginArgEnd }; static void * allocPvt(void) { myStruct *my = (myStruct*) freeListCalloc(myStructFreeList); return (void *) my; } static void freePvt(void *pvt) { myStruct *my = (myStruct*) pvt; db_delete_field_log(my->lastfl); freeListFree(myStructFreeList, pvt); } static int parse_ok(void *pvt) { myStruct *my = (myStruct*) pvt; if (!(my->id = dbStateFind(my->state))) return -1; return 0; } static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { db_field_log *passfl = NULL; myStruct *my = (myStruct*) pvt; int actstate; if (pfl->ctx == dbfl_context_read) return pfl; actstate = dbStateGet(my->id); switch (my->mode) { case syncModeBefore: if (actstate && !my->laststate) { passfl = my->lastfl; my->lastfl = NULL; } break; case syncModeFirst: if (actstate && !my->laststate) { passfl = pfl; pfl = NULL; } else db_delete_field_log(pfl); goto save_state; case syncModeLast: if (!actstate && my->laststate) { passfl = my->lastfl; my->lastfl = NULL; } break; case syncModeAfter: if (!actstate && my->laststate) { passfl = pfl; pfl = NULL; } else db_delete_field_log(pfl); goto save_state; case syncModeWhile: if (actstate) passfl = pfl; else db_delete_field_log(pfl); goto no_shift; case syncModeUnless: if (!actstate) passfl = pfl; else db_delete_field_log(pfl); goto no_shift; } if (my->lastfl) db_delete_field_log(my->lastfl); my->lastfl = pfl; /* since no copy is made we can't keep a reference to the returned fl */ assert(my->lastfl != passfl); save_state: my->laststate = actstate; no_shift: return passfl; } static void channelRegisterPre(dbChannel *chan, void *pvt, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { *cb_out = filter; *arg_out = pvt; } static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) { myStruct *my = (myStruct*) pvt; printf("%*sSynchronize (sync): mode=%s, state=%s\n", indent, "", chfPluginEnumString(modeEnum, my->mode, "n/a"), my->state); } static chfPluginIf pif = { allocPvt, freePvt, NULL, /* parse_error, */ parse_ok, NULL, /* channel_open, */ channelRegisterPre, NULL, /* channelRegisterPost, */ channel_report, NULL /* channel_close */ }; static void syncShutdown(void* ignore) { if(myStructFreeList) freeListCleanup(myStructFreeList); myStructFreeList = NULL; } static void syncInitialize(void) { if (!myStructFreeList) freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64); chfPluginRegister("sync", &pif, opts); epicsAtExit(syncShutdown, NULL); } epicsExportRegistrar(syncInitialize); base-7.0.3.1/modules/database/src/std/filters/ts.c0000664000577000060420000000334013557101274020452 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #include #include #include #include static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { epicsTimeStamp now; epicsTimeGetCurrent(&now); /* If string or array, must make a copy (to ensure coherence between time and data) */ if (pfl->type == dbfl_type_rec) { dbScanLock(dbChannelRecord(chan)); dbChannelMakeArrayCopy(pvt, pfl, chan); dbScanUnlock(dbChannelRecord(chan)); } pfl->time = now; return pfl; } static void channelRegisterPre(dbChannel *chan, void *pvt, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { *cb_out = filter; } static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) { printf("%*sTimestamp (ts)\n", indent, ""); } static chfPluginIf pif = { NULL, /* allocPvt, */ NULL, /* freePvt, */ NULL, /* parse_error, */ NULL, /* parse_ok, */ NULL, /* channel_open, */ channelRegisterPre, NULL, /* channelRegisterPost, */ channel_report, NULL /* channel_close */ }; static void tsInitialize(void) { chfPluginRegister("ts", &pif, NULL); } epicsExportRegistrar(tsInitialize); base-7.0.3.1/modules/database/src/std/link/Makefile0000664000577000060420000000114113557101274020602 0ustar anjaesctl#************************************************************************* # Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/std/Makefile. SRC_DIRS += $(STDDIR)/link DBD += links.dbd dbRecStd_SRCS += lnkConst.c dbRecStd_SRCS += lnkCalc.c dbRecStd_SRCS += lnkState.c dbRecStd_SRCS += lnkDebug.c HTMLS += links.html base-7.0.3.1/modules/database/src/std/link/links.dbd.pod0000664000577000060420000001767013557101274021534 0ustar anjaesctl=head1 Extensible Links The extensible link mechanism allows new kinds of record links to be created, using JSON for the link address syntax. The IOC continues to support the older link types that do not use JSON to specify their link addresses. The following additional link types are available in this release: =over =item * L =item * L =item * L =item * L =item * L =back =head2 Using JSON Links When setting a record link field to a JSON link address, the link specification must appear inside a pair of braces C< {} > expressed as a JSON (L) object, which allows link parameters to be defined as needed by the particular link type. When link fields are set from an IOC database file at initialization time, the field definitions may take advantage of a "relaxed JSON" syntax that reduces the number of double-quote characters required and maintains backwards compatibility with the older database file syntax. =head2 Link Type Reference =cut link(const, lnkConstIf) =head3 Constant Link C<"const"> Constant links are input links that provide literal values at link initalization time, but do not return any data when their C routine is called. Most record types support the use of constant links on their input links by calling C at record initialization, which results in the constant value being loaded into the target field at that time. Note that for most record types (the C and C records are the main exceptions) it is pointless to set an input link to a constant link at runtime since the link initialization that loads the field value usually only happens when a record is initialized. A constant link that is embedded inside another input link type such as a calculation link should be OK though since the link initialization will take place when the record's field gets set. =head4 Parameters A const link takes a parameter which may be an integer, double or string, or an array of those types. If an array contains both integers and double values the integers will be promoted to doubles. Mixing strings and numbers in an array results in an error. =head4 Examples {const: 3.14159265358979} {const: "Pi"} {const: [1, 2.718281828459, 3.14159265358979]} {const: ["One", "e", "Pi"]} The JSON syntax does not support Infinity or NaN values when parsing numbers, but (for scalars) it is possible to provide these in a string which will be converted to the desired double value at initialization, for example: field(INP, {const:"Inf"}) =cut link(calc, lnkCalcIf) =head3 Calculation Link C<"calc"> A calculation link is an input link that can evaluate mathematical expressions on scalar (double-precision floating-point) values obtained from up to 12 child input links, and returns a double-precision floating-point result. The expression is evaluated by the EPICS Calc engine, and the result is returned as the value of the link. Two additional expressions may also be provided and are evaluated to determine whether the record owning the link should be placed in alarm state. In both cases the result of the main calculation is available to these expressions as C (attempts to assign to C inside either expression will have no lasting effect). If the C expression evaluates to a non-zero value the record will be placed in C alarm. If not and the C expression evaluates to non-zero the record will be placed in C alarm state. A calculation link can also be an output link, with the scalar output value being converted to a double and provided to the expression as C. Up to 12 additional input links can also be read and provided to the expression as above. The result of the calculation is forwarded to a child output link specified in the link's C parameter. For an output link the main expression is actually optional; if not provided the converted value will be forwarded to the output link unchanged. The two alarm expressions may still be used to put the output link into alarm state as described above. =head4 Parameters The link address is a JSON map with the following keys: =over =item expr The primary expression to be evaluated, given as a string. This is optional for output links, required for input links. =item major An optional expression that returns non-zero to raise a major alarm. =item minor An optional expression that returns non-zero to raise a minor alarm. =item args A JSON list of up to 12 input arguments for the expression, which are assigned to the inputs C
, C, C, ... C. Each input argument may be either a numeric literal or an embedded JSON link inside C<{}> braces. The same input values are provided to the two alarm expressions as to the primary expression. =item out A JSON link inside C<{}> braces which specifies the destination of C operations after any expressions have been evaluated. This key is required for output links, not used by input links. =item units An optional string specifying the engineering units for the result of the expression. Equivalent to the C field of a record. =item prec An optional integer specifying the numeric precision with which the calculation result should be displayed. Equivalent to the C field of a record. =item time An optional string containing a single upper or lower-case letter C ... C which must correspond to an input provided in the c parameter. When the record containing such a link has C set to -2 (epicsTimeEventDeviceTime) the record's timestamp field C
for a description of the various hardware address formats supported. =head3 Units Conversion These fields control if and how the raw input value gets converted into engineering units: =fields RVAL, ROFF, ASLO, AOFF, LINR, ESLO, EOFF, EGUL, EGUF These fields are not used if the device support layer reads its value in engineering units and puts it directly into the VAL field. This applies to Soft Channel and Async Soft Channel device support, and is also fairly common for GPIB and similar high-level device interfaces. If the device support sets the RVAL field, the LINR field controls how this gets converted into engineering units and placed in the VAL field as follows: =over =item 1. RVAL is converted to a double and ROFF is added to it. =item 2. If ASLO is non-zero the value is multiplied by ASLO. =item 3. AOFF is added. =item 4. If LINR is C the units conversion is finished after the above steps. =item 5. If LINR is C or C, the value from step 3 above is multiplied by ESLO and EOFF is added to complete the units conversion process. =item 6. Any other value for LINR selects a particular breakpoint table to be used on the value from step 3 above. =back The distinction between the C and C settings for the LINR field are in how the conversion parameters are calculated: =over =item * With C conversion the user must set EGUL and EGUF to the lowest and highest possible engineering units values respectively that can be converted by the hardware. The device support knows the range of the raw data and calculates ESLO and EOFF from them. =item * C conversion requires the user to calculate the appropriate scaling and offset factors and put them directly in ESLO and EOFF. =back =head3 Smoothing Filter This filter is usually only used if the device support sets the RVAL field and the Units Conversion process is used. Device support that directly sets the VAL field may implement the filter if desired. The filter is controlled with a single parameter field: =fields SMOO The SMOO field should be set to a number between 0 and 1. If set to zero the filter is not used (no smoothing), while if set to one the result is infinite smoothing (the VAL field will never change). The calculation performed is: =over VAL = VAL * SMOO + (1 - SMOO) * New Data =back where C was the result from the Units Conversion above. This implements a first-order infinite impulse response (IIR) digital filter with z-plane pole at SMOO. The equivalent continuous-time filter time constant E is given by =over E = ET / ln(SMOO) =back where T is the time between record processing. =head3 Undefined Check If after applying the smoothing filter the VAL field contains a NaN (Not-a-Number) value, the UDF field is set to a non-zero value, indicating that the record value is undefined, which will trigger a C with severity C. =fields UDF =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. They do not affect the functioning of the record at all. =over =item * DESC is a string that is usually used to briefly describe the record. =item * EGU is a string of up to 16 characters naming the engineering units that the VAL field represents. =item * The HOPR and LOPR fields set the upper and lower display limits for the VAL, HIHI, HIGH, LOW, and LOLO fields. =item * The PREC field determines the floating point precision (i.e. the number of digits to show after the decimal point) with which to display VAL and the other DOUBLE fields. =back =fields DESC, EGU, HOPR, LOPR, PREC =head3 Alarm Limits The user configures limit alarms by putting numerical values into the HIHI, HIGH, LOW and LOLO fields, and by setting the associated alarm severity in the corresponding HHSV, HSV, LSV and LLSV menu fields. The HYST field controls hysteresis to prevent alarm chattering from an input signal that is close to one of the limits and suffers from significant readout noise. The AFTC field sets the time constant on a low-pass filter that delays the reporting of limit alarms until the signal has been within the alarm range for that number of seconds (the default AFTC value of zero retains the previous behavior). =fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, AFTC, LALM =head3 Monitor Parameters These parameters are used to determine when to send monitors placed on the VAL field. The monitors are sent when the current value exceeds the last transmitted value by the appropriate deadband. If these fields are set to zero, a monitor will be triggered every time the value changes; if set to -1, a monitor will be sent every time the record is processed. The ADEL field sets the deadband for archive monitors (C events), while the MDEL field controls value monitors (C events). The remaining fields are used by the record at run-time to implement the record monitoring functionality. =fields ADEL, MDEL, ALST, MLST, ORAW =cut include "dbCommon.dbd" field(VAL,DBF_DOUBLE) { prompt("Current EGU Value") promptgroup("40 - Input") asl(ASL0) pp(TRUE) } field(INP,DBF_INLINK) { prompt("Input Specification") promptgroup("40 - Input") interest(1) } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) prop(YES) } field(LINR,DBF_MENU) { prompt("Linearization") promptgroup("60 - Convert") special(SPC_LINCONV) pp(TRUE) interest(1) menu(menuConvert) } field(EGUF,DBF_DOUBLE) { prompt("Engineer Units Full") promptgroup("60 - Convert") special(SPC_LINCONV) pp(TRUE) interest(1) } field(EGUL,DBF_DOUBLE) { prompt("Engineer Units Low") promptgroup("60 - Convert") special(SPC_LINCONV) pp(TRUE) interest(1) } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) prop(YES) } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(AOFF,DBF_DOUBLE) { prompt("Adjustment Offset") promptgroup("60 - Convert") pp(TRUE) interest(1) } field(ASLO,DBF_DOUBLE) { prompt("Adjustment Slope") promptgroup("60 - Convert") pp(TRUE) interest(1) initial("1") } field(SMOO,DBF_DOUBLE) { prompt("Smoothing") promptgroup("60 - Convert") interest(1) } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { prompt("Lolo Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(HSV,DBF_MENU) { prompt("High Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(LSV,DBF_MENU) { prompt("Low Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { prompt("Alarm Deadband") promptgroup("70 - Alarm") interest(1) } field(AFTC,DBF_DOUBLE) { prompt("Alarm Filter Time Constant") promptgroup("70 - Alarm") interest(1) } field(ADEL,DBF_DOUBLE) { prompt("Archive Deadband") promptgroup("80 - Display") interest(1) } field(MDEL,DBF_DOUBLE) { prompt("Monitor Deadband") promptgroup("80 - Display") interest(1) } field(LALM,DBF_DOUBLE) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(AFVL,DBF_DOUBLE) { prompt("Alarm Filter Value") special(SPC_NOMOD) interest(3) } field(ALST,DBF_DOUBLE) { prompt("Last Value Archived") special(SPC_NOMOD) interest(3) } field(MLST,DBF_DOUBLE) { prompt("Last Val Monitored") special(SPC_NOMOD) interest(3) } field(ESLO,DBF_DOUBLE) { prompt("Raw to EGU Slope") promptgroup("60 - Convert") pp(TRUE) interest(2) initial("1") } field(EOFF,DBF_DOUBLE) { prompt("Raw to EGU Offset") promptgroup("60 - Convert") pp(TRUE) interest(2) } field(ROFF,DBF_ULONG) { prompt("Raw Offset") pp(TRUE) interest(2) } field(PBRK,DBF_NOACCESS) { prompt("Ptrto brkTable") special(SPC_NOMOD) interest(4) extra("void * pbrk") } field(INIT,DBF_SHORT) { prompt("Initialized?") special(SPC_NOMOD) interest(3) } field(LBRK,DBF_SHORT) { prompt("LastBreak Point") special(SPC_NOMOD) interest(3) } field(RVAL,DBF_LONG) { prompt("Current Raw Value") pp(TRUE) } field(ORAW,DBF_LONG) { prompt("Previous Raw Value") special(SPC_NOMOD) interest(3) } =head3 Simulation Mode The record provides several fields to support simulation of absent hardware. If the SIML field is set it is used to read a value into the SIMM field, which controls whether simulation is used or not: =over =item * SIMM must be zero (C) for the record to request a value from the device support. =item * If SIMM is C and the SIOL link field is set, a simlated value in engineering units is read using the link into the SVAL field, from where it will subsequently be copied into the VAL field. =item * If SIMM is C the SIOL link is still read into SVAL, but is then truncated and copied into the RVAL field. The L process described above is then followed to transform the simulated raw value into engineering units. =back The SIMS field can be set to give the record an alarm severity while it is in simulation mode. =fields SIML, SIMM, SIOL, SVAL, SIMS =cut field(SIOL,DBF_INLINK) { prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } field(SVAL,DBF_DOUBLE) { prompt("Simulation Value") } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuSimm) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } } =head2 Device Support Interface The record requires device support to provide an entry table (dset) which defines the following members: typedef struct { long number; long (*report)(int level); long (*init)(int after); long (*init_record)(aiRecord *prec); long (*get_ioint_info)(int cmd, aiRecord *prec, IOSCANPVT *piosl); long (*read_ai)(aiRecord *prec); long (*special_linconv)(aiRecord *prec, int after); } aidset; The module must set C to at least 6, and provide a pointer to its C routine; the other function pointers may be C if their associated functionality is not required for this support layer. Most device supports also provide an C routine to configure the record instance and connect it to the hardware or driver support layer, and if using the record's L features they set C as well. The individual routines are described below. =head3 Device Support Routines long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. long init_record(aiRecord *prec) This optional routine is called by the record initialization code for each ai record instance that has its DTYP field set to use this device support. It is normally used to check that the INP address is the expected type and that it points to a valid device; to allocate any record-specific buffer space and other memory; and to connect any communication channels needed for the C routine to work properly. If the record type's unit conversion features are used, the C routine should calculate appropriate values for the ESLO and EOFF fields from the EGUL and EGUF field values. This calculation only has to be performed if the record's LINR field is set to C, but it is not necessary to check that condition first. This same calculation takes place in the C routine, so the implementation can usually just call that routine to perform the task. long get_ioint_info(int cmd, aiRecord *prec, IOSCANPVT *piosl) This optional routine is called whenever the record's SCAN field is being changed to or from the value C to find out which I/O Interrupt Scan list the record should be added to or deleted from. If this routine is not provided, it will not be possible to set the SCAN field to the value C at all. The C parameter is zero when the record is being added to the scan list, and one when it is being removed from the list. The routine must determine which interrupt source the record should be connected to, which it indicates by the scan list that it points the location at C<*piosl> to before returning. It can prevent the SCAN field from being changed at all by returning a non-zero value to its caller. In most cases the device support will create the I/O Interrupt Scan lists that it returns for itself, by calling C once for each separate interrupt source. That routine allocates memory and inializes the list, then passes back a pointer to the new list in the location at C<*piosl>. When the device support receives notification that the interrupt has occurred, it announces that to the IOC by calling C which will arrange for the appropriate records to be processed in a suitable thread. The C routine is safe to call from an interrupt service routine on embedded architectures (vxWorks and RTEMS). long read_ai(aiRecord *prec) This essential routine is called when the record wants a new value from the addressed device. It is responsible for performing (or at least initiating) a read operation, and (eventually) returning its value to the record. ... PACT and asynchronous processing ... ... return value ... long special_linconv(aiRecord *prec, int after) This optional routine should be provided if the record type's unit conversion features are used by the device support's C routine returning a status value of zero. It is called by the record code whenever any of the the fields LINR, EGUL or EGUF are modified and LINR has the value C. The routine must calculate and set the fields EOFF and ESLO appropriately based on the new values of EGUL and EGUF. These calculations can be expressed in terms of the minimum and maximum raw values that the C routine can put in the RVAL field. When RVAL is set to I the VAL field will be set to EGUF, and when RVAL is set to I the VAL field will become EGUL. The formulae to use are: =over EOFF = (I * EGUL E I * EGUF) / (I E I) ESLO = (EGUF E EGUL) / (I E I) =back Note that the record support sets EOFF to EGUL before calling this routine, which is a very common case (when I is zero). =head3 Extended Device Support ... =cut base-7.0.3.1/modules/database/src/std/rec/aoRecord.c0000664000577000060420000004015413557101274020667 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* aoRecord.c - Record Support Routines for Analog Output records */ /* * Original Author: Bob Dalesio * Date: 7-14-89 * */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "epicsMath.h" #include "alarm.h" #include "callback.h" #include "cvtTable.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "special.h" #include "recSup.h" #include "recGbl.h" #include "menuConvert.h" #include "menuOmsl.h" #include "menuYesNo.h" #include "menuIvoa.h" #define GEN_SIZE_OFFSET #include "aoRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL static long get_units(DBADDR *, char *); static long get_precision(const DBADDR *, long *); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *, struct dbr_grDouble *); static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); static long get_alarm_double(DBADDR *, struct dbr_alDouble *); rset aoRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; struct aodset { /* analog input dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (0,2)=>(success,success no convert)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN write_ao;/*(0)=>(success ) */ DEVSUPFUN special_linconv; }; epicsExportAddress(rset,aoRSET); static void checkAlarms(aoRecord *); static long fetch_value(aoRecord *, double *); static void convert(aoRecord *, double); static void monitor(aoRecord *); static long writeValue(aoRecord *); static long init_record(struct dbCommon *pcommon, int pass) { struct aoRecord *prec = (struct aoRecord *)pcommon; struct aodset *pdset; double eoff = prec->eoff, eslo = prec->eslo; double value; long status = 0; if (pass == 0) return 0; recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if(!(pdset = (struct aodset *)(prec->dset))) { recGblRecordError(S_dev_noDSET,(void *)prec,"ao: init_record"); return(S_dev_noDSET); } /* get the initial value if dol is a constant*/ if (recGblInitConstantLink(&prec->dol,DBF_DOUBLE,&prec->val)) prec->udf = isnan(prec->val); /* must have write_ao function defined */ if ((pdset->number < 6) || (pdset->write_ao ==NULL)) { recGblRecordError(S_dev_missingSup,(void *)prec,"ao: init_record"); return(S_dev_missingSup); } prec->init = TRUE; /*The following is for old device support that doesnt know about eoff*/ if ((prec->eslo==1.0) && (prec->eoff==0.0)) { prec->eoff = prec->egul; } if (pdset->init_record) { status = (*pdset->init_record)(prec); if (prec->linr == menuConvertSLOPE) { prec->eoff = eoff; prec->eslo = eslo; } switch(status){ case(0): /* convert */ value = (double)prec->rval + (double)prec->roff; if(prec->aslo!=0.0) value *= prec->aslo; value += prec->aoff; if (prec->linr == menuConvertNO_CONVERSION){ ; /*do nothing*/ } else if ((prec->linr == menuConvertLINEAR) || (prec->linr == menuConvertSLOPE)) { value = value*prec->eslo + prec->eoff; }else{ if(cvtRawToEngBpt(&value,prec->linr,prec->init, (void *)&prec->pbrk,&prec->lbrk)!=0) break; } prec->val = value; prec->udf = isnan(value); break; case(2): /* no convert */ break; default: recGblRecordError(S_dev_badInitRet,(void *)prec,"ao: init_record"); return(S_dev_badInitRet); } } prec->oval = prec->pval = prec->val; prec->mlst = prec->val; prec->alst = prec->val; prec->lalm = prec->val; prec->oraw = prec->rval; prec->orbv = prec->rbv; return(0); } static long process(struct dbCommon *pcommon) { struct aoRecord *prec = (struct aoRecord *)pcommon; struct aodset *pdset = (struct aodset *)(prec->dset); long status=0; unsigned char pact=prec->pact; double value; if ((pdset==NULL) || (pdset->write_ao==NULL)) { prec->pact=TRUE; recGblRecordError(S_dev_missingSup,(void *)prec,"write_ao"); return(S_dev_missingSup); } /* fetch value and convert*/ if (prec->pact == FALSE) { if (!dbLinkIsConstant(&prec->dol) && prec->omsl == menuOmslclosed_loop) { status = fetch_value(prec, &value); } else { value = prec->val; } if(!status) convert(prec, value); prec->udf = isnan(prec->val); } /* check for alarms */ checkAlarms(prec); if (prec->nsev < INVALID_ALARM ) status=writeValue(prec); /* write the new value */ else { switch (prec->ivoa) { case (menuIvoaContinue_normally) : status=writeValue(prec); /* write the new value */ break; case (menuIvoaDon_t_drive_outputs) : break; case (menuIvoaSet_output_to_IVOV) : if(prec->pact == FALSE){ prec->val=prec->ivov; value=prec->ivov; convert(prec,value); } status=writeValue(prec); /* write the new value */ break; default : status=-1; recGblRecordError(S_db_badField,(void *)prec, "ao:process Illegal IVOA field"); } } /* check if device support set pact */ if ( !pact && prec->pact ) return(0); prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, NULL); /* check event list */ monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->init=FALSE; prec->pact=FALSE; return(status); } static long special(DBADDR *paddr, int after) { aoRecord *prec = (aoRecord *)(paddr->precord); struct aodset *pdset = (struct aodset *) (prec->dset); int special_type = paddr->special; switch(special_type) { case(SPC_LINCONV): if(pdset->number<6 ) { recGblDbaddrError(S_db_noMod,paddr,"ao: special"); return(S_db_noMod); } prec->init=TRUE; if ((prec->linr == menuConvertLINEAR) && pdset->special_linconv) { double eoff = prec->eoff; double eslo = prec->eslo; long status; prec->eoff = prec->egul; status = (*pdset->special_linconv)(prec,after); if (eoff != prec->eoff) db_post_events(prec, &prec->eoff, DBE_VALUE|DBE_LOG); if (eslo != prec->eslo) db_post_events(prec, &prec->eslo, DBE_VALUE|DBE_LOG); return (status); } return (0); case(SPC_MOD): if (dbGetFieldIndex(paddr) == aoRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return(0); } default: recGblDbaddrError(S_db_badChoice,paddr,"ao: special"); return(S_db_badChoice); } } #define indexof(field) aoRecord##field static long get_units(DBADDR * paddr,char *units) { aoRecord *prec=(aoRecord *)paddr->precord; if(paddr->pfldDes->field_type == DBF_DOUBLE) { switch (dbGetFieldIndex(paddr)) { case indexof(ASLO): case indexof(AOFF): break; default: strncpy(units,prec->egu,DB_UNITS_SIZE); } } return(0); } static long get_precision(const DBADDR *paddr,long *precision) { aoRecord *prec=(aoRecord *)paddr->precord; *precision = prec->prec; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(OVAL): case indexof(PVAL): break; default: recGblGetPrec(paddr,precision); } return(0); } static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd) { aoRecord *prec=(aoRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(OVAL): case indexof(PVAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): case indexof(IVOV): pgd->upper_disp_limit = prec->hopr; pgd->lower_disp_limit = prec->lopr; break; default: recGblGetGraphicDouble(paddr,pgd); } return(0); } static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) { aoRecord *prec=(aoRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(OVAL): case indexof(PVAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): pcd->upper_ctrl_limit = prec->drvh; pcd->lower_ctrl_limit = prec->drvl; break; default: recGblGetControlDouble(paddr,pcd); } return(0); } static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) { aoRecord *prec=(aoRecord *)paddr->precord; if(dbGetFieldIndex(paddr) == indexof(VAL)){ pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN; pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN; pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN; pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN; } else recGblGetAlarmDouble(paddr,pad); return(0); } static void checkAlarms(aoRecord *prec) { double val, hyst, lalm; double alev; epicsEnum16 asev; if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, prec->udfs); return; } val = prec->val; hyst = prec->hyst; lalm = prec->lalm; /* alarm condition hihi */ asev = prec->hhsv; alev = prec->hihi; if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { if (recGblSetSevr(prec, HIHI_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition lolo */ asev = prec->llsv; alev = prec->lolo; if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { if (recGblSetSevr(prec, LOLO_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition high */ asev = prec->hsv; alev = prec->high; if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { if (recGblSetSevr(prec, HIGH_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition low */ asev = prec->lsv; alev = prec->low; if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { if (recGblSetSevr(prec, LOW_ALARM, asev)) prec->lalm = alev; return; } /* we get here only if val is out of alarm by at least hyst */ prec->lalm = val; return; } static long fetch_value(aoRecord *prec,double *pvalue) { short save_pact; long status; save_pact = prec->pact; prec->pact = TRUE; /* don't allow dbputs to val field */ prec->val=prec->pval; status = dbGetLink(&prec->dol,DBR_DOUBLE,pvalue,0,0); prec->pact = save_pact; if (status) { recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM); return(status); } if (prec->oif == aoOIF_Incremental) *pvalue += prec->val; return(0); } static void convert(aoRecord *prec, double value) { /* check drive limits */ if (prec->drvh > prec->drvl) { if (value > prec->drvh) value = prec->drvh; else if (value < prec->drvl) value = prec->drvl; } prec->val = value; prec->pval = value; /* now set value equal to desired output value */ /* apply the output rate of change */ if (prec->oroc != 0){/*must be defined and >0*/ double diff; diff = value - prec->oval; if (diff < 0) { if (prec->oroc < -diff) value = prec->oval - prec->oroc; } else if (prec->oroc < diff) value = prec->oval + prec->oroc; } prec->omod = (prec->oval!=value); prec->oval = value; /* convert */ switch (prec->linr) { case menuConvertNO_CONVERSION: break; /* do nothing*/ case menuConvertLINEAR: case menuConvertSLOPE: if (prec->eslo == 0.0) value = 0; else value = (value - prec->eoff) / prec->eslo; break; default: if (cvtEngToRawBpt(&value, prec->linr, prec->init, (void *)&prec->pbrk, &prec->lbrk) != 0) { recGblSetSevr(prec, SOFT_ALARM, MAJOR_ALARM); return; } } value -= prec->aoff; if (prec->aslo != 0) value /= prec->aslo; /* Apply raw offset and limits, round to 32-bit integer */ value -= prec->roff; if (value >= 0.0) { if (value >= (0x7fffffff - 0.5)) prec->rval = 0x7fffffff; else prec->rval = (epicsInt32)(value + 0.5); } else { if (value > (0.5 - 0x80000000)) prec->rval = (epicsInt32)(value - 0.5); else prec->rval = 0x80000000; } } static void monitor(aoRecord *prec) { unsigned monitor_mask = recGblResetAlarms(prec); /* check for value change */ recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE); /* check for archive change */ recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE); /* send out monitors connected to the value field */ if (monitor_mask){ db_post_events(prec,&prec->val,monitor_mask); } if(prec->omod) monitor_mask |= (DBE_VALUE|DBE_LOG); if(monitor_mask) { prec->omod = FALSE; db_post_events(prec,&prec->oval,monitor_mask); if(prec->oraw != prec->rval) { db_post_events(prec,&prec->rval, monitor_mask|DBE_VALUE|DBE_LOG); prec->oraw = prec->rval; } if(prec->orbv != prec->rbv) { db_post_events(prec,&prec->rbv, monitor_mask|DBE_VALUE|DBE_LOG); prec->orbv = prec->rbv; } } return; } static long writeValue(aoRecord *prec) { struct aodset *pdset = (struct aodset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: status = pdset->write_ao(prec); break; case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbPutLink(&prec->siol, DBR_DOUBLE, &prec->oval, 1); prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } base-7.0.3.1/modules/database/src/std/rec/aoRecord.dbd.pod0000664000577000060420000007026513557101274021765 0ustar anjaesctl#************************************************************************* # Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Analog Output Record (ao) This record type is normally used to send an analog value to an output device, converting it from engineering units into an integer value if necessary. The record supports alarm and drive limits, rate-of-change limiting, output value integration, linear and break-point conversion from engineering units, and graphics and control limits. =head2 Record-specific Menus =head3 Menu aoOIF The OIF field which uses this menu controls whether the record acts as an integrator (C) or not (C). =menu aoOIF =head2 Parameter Fields The record-specific fields are described below. =recordtype ao =cut menu(aoOIF) { choice(aoOIF_Full,"Full") choice(aoOIF_Incremental,"Incremental") } recordtype(ao) { =head3 Output Value Determination These fields control how the record determines the value to be output when it gets processed: =fields OMSL, DOL, OIF, PVAL, DRVH, DRVL, VAL, OROC, OVAL The following steps are performed in order during record processing. =head4 Fetch Value, Integrate The OMSL menu field is used to determine whether the DOL link and OIF menu fields should be used during processing or not: =over =item * If OMSL is C the DOL and OIF fields are not used. The new output value is taken from the VAL field, which may have been set from elsewhere. =item * If OMSL is C the DOL link field is read to obtain a value; if OIF is C and the DOL link was read successfully, the record's previous output value PVAL is added to it. =back =head4 Drive Limits The output value is now clipped to the range DRVL to DRVH inclusive, provided that DRVH E DRVL. The result is copied into both the VAL and PVAL fields. =head4 Limit Rate of Change If the OROC field is not zero, the VAL field is now adjusted so it is no more than OROC different to the previous output value given in OVAL. OROC thus determines the maximum change in the output value that can occur each time the record gets processed. The result is copied into the OVAL field, which is used as the input to the following Units Conversion processing stage. =head3 Units Conversion ... For analog output records that do not use the Soft Channel device support routine, the specified conversions (if any) are performed on the OVAL field and the resulting value in the RVAL field is sent to the address contained in the output link after it is adjusted by the values in the AOFF and ASLO fields. =fields LINR, RVAL, ROFF, EGUF, EGUL, AOFF, ASLO, ESLO, EOFF =head4 Conversion Related Fields and the Conversion Process Except for analog outputs that use Soft Channel device support, the LINR field determines if a conversion is performed and which conversion algorithm is used to convert OVAL to RVAL. The LINR field can specify C or C for linear conversions, C for no conversions at all, or the name of a breakpoint table such as C for breakpoint conversions. Note that the ESLO, EOFF, EGUF, and EGUL fields are only used for linear conversions. Also note that none of these fields have any significance for records that use the Soft Channel device support module. =over =item EGUF, EGUF The user must calculate these fields when configuring the database for records that use C conversions. They are used to calculate the values for ESLO and EOFF. See Conversion Specification for more information on how to calculate these fields. =item AOFF, ASLO These fields are adjustment parameters for the raw output values. They are applied to the raw output value after conversion from engineering units. =item ESLO, EOFF Computed by device support using EGUF and EGUL when LINR specifies C. These values must be supplied by the user when LINR specifies C. Used only when LINR is C or C. =item ROFF This field can be used to offset the raw value generated by the conversion process, which is needed for some kinds of hardware. =back Conversion proceeds as follows: =over =item 1. If LINR==LINEAR or LINR==SLOPE, then X = (VAL - EOFF) / ESLO, else if LINR==NO_CONVERSION, then X = VAL, else X is obtained via breakpoint table. =item 2. X = (X - AOFF) / ASLO =item 3. RVAL = round(X) - ROFF =back To see how the Raw Soft Channel device support routine uses these fields, see L below for more information. =head3 Output Specification The analog output record sends its desired output to the address in the OUT field. For analog outputs that write their values to devices, the OUT field must specify the address of the I/O card. In addition, the DTYP field must contain the name of the device support module. Be aware that the address format differs according to the I/O bus used. See Address Specification for information on the format of hardware addresses. For soft records the output link can be a database link, a channel access link, or a constant value. If the link is a constant, no output is sent. See Address Specification for information on the format of database and channel access addresses. =fields DTYP, OUT =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. They display the value and other parameters of the analog output either textually or graphically. EGU is a string of up to 16 characters describing the units that the analog output measures. It is retrieved by the get_units record support routine. The HOPR and LOPR fields set the upper and lower display limits for the VAL, OVAL, PVAL, HIHI, HIGH, LOW, and LOLO fields. Both the get_graphic_double and get_control_double record support routines retrieve these fields. If these values are defined, they must be in the range: DRVL E= LOPR E= HOPR E= DRVH. The PREC field determines the floating point precision with which to display VAL, OVAL and PVAL. It is used whenever the get_precision record support routine is called. See Fields Common to All Record Types for more on the record name (NAME) and description (DESC) fields. =fields EGU, HOPR, LOPR, PREC, NAME, DESC =head3 Alarm Parameters The possible alarm conditions for analog outputs are the SCAN, READ, INVALID and limit alarms. The SCAN, READ, and INVALID alarms are called by the record or device support routines. The limit alarms are configured by the user in the HIHI, LOLO, HIGH, and LOW fields, which must be floating-point values. For each of these fields, there is a corresponding severity field which can be either NO_ALARM, MINOR, or MAJOR. See Alarm Specification for a complete explanation of alarms and these fields. See Invalid Alarm Output Action for more information on the IVOA and IVOV fields. Alarm Fields lists other fields related to a alarms that are common to all record types. =fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, IVOA, IVOV =head3 Monitor Parameters These parameters are used to specify deadbands for monitors on the VAL field. The monitors are sent when the value field exceeds the last monitored field by the specified deadband. If these fields have a value of zero, everytime the value changes, a monitor will be triggered; if they have a value of -1, everytime the record is processed, monitors are triggered. ADEL is the deadband for archive monitors, and MDEL the deadband for all other types of monitors. See Monitor Specification for a complete explanation of monitors. =fields ADEL, MDEL =head3 Run-time and Simulation Mode Parameters These parameters are used by the run-time code for processing the analog output. They are not configurable. They represent the current state of the record. The record support routines use some of them for more efficient processing. The ORAW field is used to decide if monitors should be triggered for RVAL when monitors are triggered for VAL. The RBV field is the actual read back value obtained from the hardware itself or from the associated device driver. It is the responsibility of the device support routine to give this field a value. ORBV is used to decide if monitors should be triggered for RBV at the same time monitors are triggered for changes in VAL. The LALM, MLST, and ALST fields are used to implement the hysteresis factors for monitor callbacks. The INIT field is used to initialize the LBRK field and for smoothing. The PBRK field contains a pointer to the current breakpoint table (if any), and LBRK contains a pointer to the last breakpoint table used. The OMOD field indicates whether OVAL differs from VAL. It will be different if VAL or OVAL have changed since the last time the record was processed, or if VAL has been adjusted by OROC during the current processing. =fields ORAW, RBV, ORBV, LALM, ALST, MLST, INIT, PBRK, LBRK, PVAL, OMOD The following fields are used to operate the analog output in the simulation mode. See Fields Common to Many Record Types for more information on these fields. =fields SIOL, SIML, SIMM, SIMS =cut include "dbCommon.dbd" field(VAL,DBF_DOUBLE) { prompt("Desired Output") promptgroup("50 - Output") asl(ASL0) pp(TRUE) } field(OVAL,DBF_DOUBLE) { prompt("Output Value") } field(OUT,DBF_OUTLINK) { prompt("Output Specification") promptgroup("50 - Output") interest(1) } field(OROC,DBF_DOUBLE) { prompt("Output Rate of Change") promptgroup("50 - Output") interest(1) } field(DOL,DBF_INLINK) { prompt("Desired Output Loc") promptgroup("40 - Input") interest(1) } field(OMSL,DBF_MENU) { prompt("Output Mode Select") promptgroup("50 - Output") interest(1) menu(menuOmsl) } field(OIF,DBF_MENU) { prompt("Out Full/Incremental") promptgroup("50 - Output") interest(1) menu(aoOIF) } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) prop(YES) } field(LINR,DBF_MENU) { prompt("Linearization") promptgroup("60 - Convert") special(SPC_LINCONV) pp(TRUE) interest(1) menu(menuConvert) } field(EGUF,DBF_DOUBLE) { prompt("Eng Units Full") promptgroup("60 - Convert") special(SPC_LINCONV) pp(TRUE) interest(1) } field(EGUL,DBF_DOUBLE) { prompt("Eng Units Low") promptgroup("60 - Convert") special(SPC_LINCONV) pp(TRUE) interest(1) } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) prop(YES) } field(ROFF,DBF_ULONG) { prompt("Raw Offset") pp(TRUE) interest(2) } field(EOFF,DBF_DOUBLE) { prompt("EGU to Raw Offset") promptgroup("60 - Convert") pp(TRUE) interest(2) } field(ESLO,DBF_DOUBLE) { prompt("EGU to Raw Slope") promptgroup("60 - Convert") pp(TRUE) interest(2) initial("1") } field(DRVH,DBF_DOUBLE) { prompt("Drive High Limit") promptgroup("30 - Action") pp(TRUE) interest(1) prop(YES) } field(DRVL,DBF_DOUBLE) { prompt("Drive Low Limit") promptgroup("30 - Action") pp(TRUE) interest(1) prop(YES) } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(AOFF,DBF_DOUBLE) { prompt("Adjustment Offset") promptgroup("60 - Convert") pp(TRUE) interest(1) } field(ASLO,DBF_DOUBLE) { prompt("Adjustment Slope") promptgroup("60 - Convert") pp(TRUE) interest(1) } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { prompt("Lolo Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(HSV,DBF_MENU) { prompt("High Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(LSV,DBF_MENU) { prompt("Low Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { prompt("Alarm Deadband") promptgroup("70 - Alarm") interest(1) } field(ADEL,DBF_DOUBLE) { prompt("Archive Deadband") promptgroup("80 - Display") interest(1) } field(MDEL,DBF_DOUBLE) { prompt("Monitor Deadband") promptgroup("80 - Display") interest(1) } field(RVAL,DBF_LONG) { prompt("Current Raw Value") pp(TRUE) } field(ORAW,DBF_LONG) { prompt("Previous Raw Value") special(SPC_NOMOD) interest(3) } field(RBV,DBF_LONG) { prompt("Readback Value") special(SPC_NOMOD) } field(ORBV,DBF_LONG) { prompt("Prev Readback Value") special(SPC_NOMOD) interest(3) } field(PVAL,DBF_DOUBLE) { prompt("Previous value") special(SPC_NOMOD) interest(3) } field(LALM,DBF_DOUBLE) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(ALST,DBF_DOUBLE) { prompt("Last Value Archived") special(SPC_NOMOD) interest(3) } field(MLST,DBF_DOUBLE) { prompt("Last Val Monitored") special(SPC_NOMOD) interest(3) } field(PBRK,DBF_NOACCESS) { prompt("Ptrto brkTable") special(SPC_NOMOD) interest(4) extra("void * pbrk") } field(INIT,DBF_SHORT) { prompt("Initialized?") special(SPC_NOMOD) interest(3) } field(LBRK,DBF_SHORT) { prompt("LastBreak Point") special(SPC_NOMOD) interest(3) } field(SIOL,DBF_OUTLINK) { prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } field(IVOA,DBF_MENU) { prompt("INVALID output action") promptgroup("50 - Output") interest(2) menu(menuIvoa) } field(IVOV,DBF_DOUBLE) { prompt("INVALID output value") promptgroup("50 - Output") interest(2) } field(OMOD,DBF_UCHAR) { prompt("Was OVAL modified?") special(SPC_NOMOD) } } =head2 Record Support =head3 Record Support Routines The following are the record support routines that would be of interest to an application developer. Other routines are the get_units, get_precision, get_graphic_double, and get_control_double routines. =over =item init_record This routine initializes SIMM if SIML is a constant or creates a channel access link if SIML is PV_LINK. If SIOL is PV_LINK a channel access link is created. This routine next checks to see that device support is available. If DOL is a constant, then VAL is initialized with its value and UDF is set to FALSE. The routine next checks to see if the device support write routine is defined. If either device support or the device support write routine does not exist, an error message is issued and processing is terminated. For compatibility with old device supports that don't know EOFF, if both EOFF and ESLO have their default value, EOFF is set to EGUL. If device support includes C, it is called. INIT is set TRUE. This causes PBRK, LBRK, and smoothing to be re-initialized. If "backwards" linear conversion is requested, then VAL is computed from RVAL using the algorithm: VAL = ((RVAL+ROFF) * ASLO + AOFF) * ESLO + EOFF and UDF is set to FALSE. For breakpoint conversion, a call is made to cvtEngToRawBpt and UDF is then set to FALSE. PVAL is set to VAL. =item process See next section. =item special The only special processing for analog output records is SPC_LINCONV which is invoked whenever either of the fields LINR, EGUF, EGUL or ROFF is changed If the device support routine special_linconv exists it is called. INIT is set TRUE. This causes PBRK, LBRK, and smoothing to be re-initialized. =item get_alarm_double Sets the following values: upper_alarm_limit = HIHI upper_warning_limit = HIGH lower_warning_limit = LOW lower_alarm_limit = LOLO =back =head3 Record Processing Routine process implements the following algorithm: =over =item 1. Check to see that the appropriate device support module exists. If it doesn't, an error message is issued and processing is terminated with the PACT field set to TRUE. This ensures that processes will no longer be called for this record. Thus error storms will not occur. =item 2. Check PACT: If PACT is FALSE call fetch_values and convert which perform the following steps: =over =item * fetch_values: =over =item * if DOL is DB_LINK and OMSL is CLOSED_LOOP then get value from DOL =item * if OIF is INCREMENTAL then set value = value + VAL else value = VAL =back =item * convert: =over =item * If Drive limits are defined force value to be within limits =item * Set VAL equal to value =item * Set UDF to FALSE. =item * If OVAL is undefined set it equal to value =item * If OROC is defined and not 0 make |value-OVAL| E=OROC =item * Set OVAL equal to value =item * Compute RVAL from OVAL. using linear or break point table conversion. For linear conversions the algorithm is RVAL = (OVAL-EOFF)/ESLO. =item * For break point table conversion a call is made to cvtEngToRawBpt. =item * After that, for all conversion types AOFF, ASLO, and ROFF are calculated in, using the formula RVAL = (RVAL -AOFF) / ASLO - ROFF. =back =back =item 3. Check alarms: This routine checks to see if the new VAL causes the alarm status and severity to change. If so, NSEV, NSTA and y are set. It also honors the alarm hysteresis factor (HYST). Thus the value must change by at least HYST before the alarm status and severity is reduced. =item 4. Check severity and write the new value. See Invalid Alarm Output Action for details on how invalid alarms affect output records. =item 5. If PACT has been changed to TRUE, the device support write output routine has started but has not completed writing the new value. In this case, the processing routine merely returns, leaving PACT TRUE. =item 6. Check to see if monitors should be invoked: =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if ADEL and MDEL conditions are met. =item * Monitors for RVAL and for RBV are checked whenever other monitors are invoked. =item * NSEV and NSTA are reset to 0. =back =item 7. Scan forward link if necessary, set PACT and INIT FALSE, and return. =back =head2 Device Support =head3 Fields Of Interest To Device Support Each analog output record must have an associated set of device support routines. The primary responsibility of the device support routines is to output a new value whenever write_ao is called. The device support routines are primarily interested in the following fields: =over =item * PACT E Process Active, used to indicate asynchronous completion =item * DPVT E Device Private, reserved for device support to use =item * OUT E Output Link, provides addressing information =item * EGUF E Engineering Units Full =item * EGUL E Engineering Units Low =item * ESLO E Engineering Unit Slope =item * EOFF E Engineering Unit Offset =item * OVAL E Output Value, in Engineering units =item * RVAL E Raw Output Value, after conversion =back =head3 Device Support routines Device support consists of the following routines: =over =item C This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. =item C This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. =item C This optional routine is called by the record initialization code for each ao record instance that has its DTYP field set to use this device support. It is normally used to check that the OUT address has the expected type and points to a valid device; to allocate any record-specific buffer space and other memory; and to connect any communication channels needed for the C routine to work properly. If the record type's unit conversion features are used, the C routine should calculate appropriate values for the ESLO and EOFF fields from the EGUL and EGUF field values. This calculation only has to be performed if the record's LINR field is set to C, but it is not necessary to check that condition first. This same calculation takes place in the C routine, so the implementation can usually just call that routine to perform the task. If the the last output value can be read back from the hardware, this routine should also fetch that value and put it into the record's RVAL or VAL field. The return value should be zero if the RVAL field has been set, or 2 if either the VAL field has been set or if the last output value cannot be retrieved. =item C This optional routine is called whenever the record's SCAN field is being changed to or from the value C to find out which I/O Interrupt Scan list the record should be added to or deleted from. If this routine is not provided, it will not be possible to set the SCAN field to the value C at all. The C parameter is zero when the record is being added to the scan list, and one when it is being removed from the list. The routine must determine which interrupt source the record should be connected to, which it indicates by the scan list that it points the location at C<*piosl> to before returning. It can prevent the SCAN field from being changed at all by returning a non-zero value to its caller. In most cases the device support will create the I/O Interrupt Scan lists that it returns for itself, by calling C once for each separate interrupt source. That API allocates memory and inializes the list, then passes back a pointer to the new list in the location at C<*piosl>. When the device support receives notification that the interrupt has occurred, it announces that to the IOC by calling C which will arrange for the appropriate records to be processed in a suitable thread. The C routine is safe to call from an interrupt service routine on embedded architectures (vxWorks and RTEMS). =item C This essential routine is called whenever the record has a new output value to send to the device. It is responsible for performing the write operation, using either the engineering units value found in the record's OVAL field, or the raw value from the record's RVAL field if the record type's unit conversion facilities are used. A return value of zero indicates success, any other value indicates that an error occurred. This routine must not block (busy-wait) if the device takes more than a few microseconds to accept the new value. In that case the routine must use asynchronous completion to tell the record when the write operation eventually completes. It signals that this is an asynchronous operation by setting the record's PACT field to TRUE before it returns, having arranged for the record's C routine to be called later once the write operation is over. When that happens the C routine will be called again with PACT still set to TRUE; it should then set it to FALSE to indicate the write has completed, and return. =item C This optional routine should be provided if the record type's unit conversion features are used by the device support's C routine utilizing the RVAL field rather than OVAL or VAL. It is called by the record code whenever any of the the fields LINR, EGUL or EGUF are modified and LINR has the value C. The routine must calculate and set the fields EOFF and ESLO appropriately based on the new values of EGUL and EGUF. These calculations can be expressed in terms of the minimum and maximum raw values that the C routine can accept in the RVAL field. When VAL is EGUF the RVAL field will be set to I, and when VAL is EGUL the RVAL field will become I. The fomulae to use are: =over EOFF = (I * EGUL E I * EGUF) / (I E I) ESLO = (EGUF E EGUL) / (I E I) =back Note that the record support sets EOFF to EGUL before calling this routine, which is a very common case (I is zero). =back =head2 Device Support For Soft Records Two soft device support modules Soft Channel and Raw Soft Channel are provided for output records not related to actual hardware devices. The OUT link type must be either a CONSTANT, DB_LINK, or CA_LINK. =head3 Soft Channel This module writes the current value of OVAL. If the OUT link type is PV_LINK, then dbCaAddInlink is called by C. C always returns a value of 2, which means that no conversion will ever be attempted. write_ao calls recGblPutLinkValue to write the current value of VAL. See Soft Output for details. =head3 Raw Soft Channel This module is like the previous except that it writes the current value of RVAL. =cut base-7.0.3.1/modules/database/src/std/rec/biRecord.c0000664000577000060420000002155113557101274020662 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* recBi.c - Record Support Routines for Binary Input records */ /* * Original Author: Bob Dalesio * Date: 7-14-89 * */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbFldTypes.h" #include "dbEvent.h" #include "devSup.h" #include "errMdef.h" #include "menuSimm.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #define GEN_SIZE_OFFSET #include "biRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL #define get_units NULL #define get_precision NULL static long get_enum_str(const DBADDR *, char *); static long get_enum_strs(const DBADDR *, struct dbr_enumStrs *); static long put_enum_str(const DBADDR *, const char *); #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset biRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; struct bidset { /* binary input dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN read_bi;/*(0,2)=> success and convert, don't convert)*/ /* if convert then raw value stored in rval */ }; epicsExportAddress(rset,biRSET); static void checkAlarms(biRecord *); static void monitor(biRecord *); static long readValue(biRecord *); static long init_record(struct dbCommon *pcommon, int pass) { struct biRecord *prec = (struct biRecord *)pcommon; struct bidset *pdset; long status; if (pass == 0) return 0; recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); recGblInitConstantLink(&prec->siol, DBF_USHORT, &prec->sval); if(!(pdset = (struct bidset *)(prec->dset))) { recGblRecordError(S_dev_noDSET,(void *)prec,"bi: init_record"); return(S_dev_noDSET); } /* must have read_bi function defined */ if( (pdset->number < 5) || (pdset->read_bi == NULL) ) { recGblRecordError(S_dev_missingSup,(void *)prec,"bi: init_record"); return(S_dev_missingSup); } if( pdset->init_record ) { if((status=(*pdset->init_record)(prec))) return(status); } prec->mlst = prec->val; prec->lalm = prec->val; prec->oraw = prec->rval; return(0); } static long process(struct dbCommon *pcommon) { struct biRecord *prec = (struct biRecord *)pcommon; struct bidset *pdset = (struct bidset *)(prec->dset); long status; unsigned char pact=prec->pact; if( (pdset==NULL) || (pdset->read_bi==NULL) ) { prec->pact=TRUE; recGblRecordError(S_dev_missingSup,(void *)prec,"read_bi"); return(S_dev_missingSup); } status=readValue(prec); /* read the new value */ /* check if device support set pact */ if ( !pact && prec->pact ) return(0); prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); if(status==0) { /* convert rval to val */ if(prec->rval==0) prec->val =0; else prec->val = 1; prec->udf = FALSE; } else if(status==2) status=0; /* check for alarms */ checkAlarms(prec); /* check event list */ monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact=FALSE; return(status); } static long special(DBADDR *paddr, int after) { biRecord *prec = (biRecord *)(paddr->precord); int special_type = paddr->special; switch(special_type) { case(SPC_MOD): if (dbGetFieldIndex(paddr) == biRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return(0); } default: recGblDbaddrError(S_db_badChoice, paddr, "bi: special"); return(S_db_badChoice); } } static long get_enum_str(const DBADDR *paddr, char *pstring) { biRecord *prec=(biRecord *)paddr->precord; int index; unsigned short *pfield = (unsigned short *)paddr->pfield; index = dbGetFieldIndex(paddr); if(index!=biRecordVAL) { strcpy(pstring,"Illegal_Value"); } else if(*pfield==0) { strncpy(pstring,prec->znam,sizeof(prec->znam)); pstring[sizeof(prec->znam)] = 0; } else if(*pfield==1) { strncpy(pstring,prec->onam,sizeof(prec->onam)); pstring[sizeof(prec->onam)] = 0; } else { strcpy(pstring,"Illegal_Value"); } return(0); } static long get_enum_strs(const DBADDR *paddr,struct dbr_enumStrs *pes) { biRecord *prec=(biRecord *)paddr->precord; pes->no_str = 2; memset(pes->strs,'\0',sizeof(pes->strs)); strncpy(pes->strs[0],prec->znam,sizeof(prec->znam)); if(*prec->znam!=0) pes->no_str=1; strncpy(pes->strs[1],prec->onam,sizeof(prec->onam)); if(*prec->onam!=0) pes->no_str=2; return(0); } static long put_enum_str(const DBADDR *paddr, const char *pstring) { biRecord *prec=(biRecord *)paddr->precord; if(strncmp(pstring,prec->znam,sizeof(prec->znam))==0) prec->val = 0; else if(strncmp(pstring,prec->onam,sizeof(prec->onam))==0) prec->val = 1; else return(S_db_badChoice); prec->udf=FALSE; return(0); } static void checkAlarms(biRecord *prec) { unsigned short val = prec->val; if(prec->udf == TRUE){ recGblSetSevr(prec,UDF_ALARM,prec->udfs); return; } if(val>1)return; /* check for state alarm */ if (val == 0){ recGblSetSevr(prec,STATE_ALARM,prec->zsv); }else{ recGblSetSevr(prec,STATE_ALARM,prec->osv); } /* check for cos alarm */ if(val == prec->lalm) return; recGblSetSevr(prec,COS_ALARM,prec->cosv); prec->lalm = val; return; } static void monitor(biRecord *prec) { unsigned short monitor_mask; monitor_mask = recGblResetAlarms(prec); /* check for value change */ if (prec->mlst != prec->val){ /* post events for value change and archive change */ monitor_mask |= (DBE_VALUE | DBE_LOG); /* update last value monitored */ prec->mlst = prec->val; } /* send out monitors connected to the value field */ if (monitor_mask){ db_post_events(prec,&prec->val,monitor_mask); } if(prec->oraw!=prec->rval) { db_post_events(prec,&prec->rval, monitor_mask|DBE_VALUE|DBE_LOG); prec->oraw = prec->rval; } return; } static long readValue(biRecord *prec) { struct bidset *pdset = (struct bidset *)prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuSimmNO: status = pdset->read_bi(prec); break; case menuSimmYES: case menuSimmRAW: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbGetLink(&prec->siol, DBR_ULONG, &prec->sval, 0, 0); if (status == 0) { if (prec->simm == menuSimmYES) { prec->val = prec->sval; status = 2; /* Don't convert */ } else { prec->rval = (unsigned short) prec->sval; } prec->udf = FALSE; } prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } base-7.0.3.1/modules/database/src/std/rec/biRecord.dbd.pod0000664000577000060420000003403313557101274021751 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Binary Input Record (bi) This record type is normally used to obtain a binary value of 0 or 1. Most device support modules obtain values from hardware and place the value in RVAL. For these devices, record processing sets VAL = (0,1) if RVAL is (0, not 0). Device support modules may optionally read a value directly from VAL. Soft device modules are provided to obtain input via database or channel access links via dbPutField or dbPutLink requests. Two soft device support modules are provided: C and C. The first allows VAL to be an arbitrary unsigned short integer. The second reads the value into RVAL just like normal hardware modules. =head2 Parameter Fields The binary input's fields fall into the following categories: =over =item * scan Parameters =item * read and convert parameters =item * operator display parameters =item * alarm parameters =item * run-time parameters =back =recordtype bi =cut recordtype(bi) { =head3 Scan Parameters The binary input record has the standard fields for specifying under what circumstances the record will be processed. These fields are listed in L. In addition, L explains how these fields are used. Note that I/O event scanning is only supported for those card types that interrupt. =fields SCAN =head3 Read and Convert Parameters The read and convert fields determine where the binary input gets its input from and how to convert the raw signal to engineering units. The INP field contains the address from where device support retrieves the value. If the binary input record gets its value from hardware, the address of the card must be entered in the INP field, and the name of the device support module must be entered in the DTYP field. See L
for information on the format of the hardware address. Be aware that the format differs between types of cards. For records that specify C or C device support routines, the INP field can be a channel or a database link, or a constant. If a constant, VAL can be changed directly by dbPuts. See L
for information on the format of database and channel access addresses. Also, see L in this chapter for information on soft device support. If the record gets its values from hardware or uses the C device support, the device support routines place the value in the RVAL field which is then converted using the process described in the next section. =fields INP, DTYP, ZNAM, ONAM, RVAL, VAL =head3 Conversion Fields The VAL field is set equal to (0,1) if the RVAL field is (0, not 0), unless the device support module reads a value directly into VAL or the C device support is used. The value can also be fetched as one of the strings specified in the ZNAM or ONAM fields. The ZNAM field has a string that corresponds to the 0 state, so when the value is fetched as this string, C will return a 0. The ONAM field hold the string that corresponds to the 1 state, so when the value is fetched as this string, C returns a 1. =fields ZNAM, ONAM =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. The C record support routine can retrieve the state string corresponding to the VAL's state. If the value is 1, C will return the string in the ONAM field; and if 0, C will return the ZNAM string. See L for more on the record name (NAME) and description (DESC) fields. =fields ZNAM, ONAM, NAME, DESC =head3 Alarm Parameters These parameters are used to determine if the binary input is in alarm condition and to determine the severity of that condition. The possible alarm conditions for binary inputs are the SCAN, READ state alarms, and the change of state alarm. The SCAN and READ alarms are called by the device supprt routines. The user can choose the severity of each state in the ZSV and OSV fields. The possible values for these fields are C, C, and C. The ZSV field holds the severity for the zero state; OSV, for the one state. COSV causes an alarm whenever the state changes between 0 and 1 and the severity is configured as MINOR or MAJOR. See L for a complete explanation of the discrete alarm states. L lists other fields related to alarms that are common to all record types. =fields ZSV, OSV, COSV =head3 Run-time Parameters and Simulation Mode Parameters These parameters are used by the run-time code for processing the binary input. They are not configured using a database configuration tool. ORAW is used to determine if monitors should be triggered for RVAL at the same time they are triggered for VAL. MASK is given a value by ithe device support routines. This value is used to manipulate the record's value, but is only the concern of the hardware device support routines. The LALM fields holds the value of the last occurence of the change of state alarm. It is used to implement the change of state alarm, and thus only has meaning if COSV is MAJOR or MINOR. The MSLT field is used by the C record support routine to determine if archive and value change monitors are invoked. They are if MSLT is not equal to VAL. =fields ORAW, MASK, LALM, MLST The following fields are used to operate the binary input in simulation mode. See L for more information on these fields. =fields SIOL, SVAL, SIML, SIMM, SIMS =cut include "dbCommon.dbd" field(INP,DBF_INLINK) { prompt("Input Specification") promptgroup("40 - Input") interest(1) } field(VAL,DBF_ENUM) { prompt("Current Value") promptgroup("40 - Input") asl(ASL0) pp(TRUE) } field(ZSV,DBF_MENU) { prompt("Zero Error Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(OSV,DBF_MENU) { prompt("One Error Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(COSV,DBF_MENU) { prompt("Change of State Svr") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(ZNAM,DBF_STRING) { prompt("Zero Name") promptgroup("80 - Display") pp(TRUE) interest(1) size(26) prop(YES) } field(ONAM,DBF_STRING) { prompt("One Name") promptgroup("80 - Display") pp(TRUE) interest(1) size(26) prop(YES) } field(RVAL,DBF_ULONG) { prompt("Raw Value") pp(TRUE) } field(ORAW,DBF_ULONG) { prompt("prev Raw Value") special(SPC_NOMOD) interest(3) } field(MASK,DBF_ULONG) { prompt("Hardware Mask") special(SPC_NOMOD) interest(1) } field(LALM,DBF_USHORT) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(MLST,DBF_USHORT) { prompt("Last Value Monitored") special(SPC_NOMOD) interest(3) } field(SIOL,DBF_INLINK) { prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } field(SVAL,DBF_ULONG) { prompt("Simulation Value") } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuSimm) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } =head2 Record Support =head3 Record Support Routines long init_record(struct dbCommon *precord, int pass); This routine initializes SIMM with the value of SIML if SIML type is a CONSTANT link or creates a channel access link if SIML type is PV_LINK. SVAL is likewise initialized if SIOL is a CONSTANT or PV_LINK. This routine next checks to see that device support is available and a device support routine is defined. If neither exist, an error is issued and processing is terminated. If device support includes C, it is called. long process(struct dbCommon *precord); See L below. long get_enum_str(const struct dbAddr *paddr, char *pbuffer); Retrieves ASCII string corresponding to VAL. long get_enum_strs(const struct dbAddr *paddr, struct dbr_enumStrs *p); Retrieves ASCII strings for ZNAM and ONAM. long put_enum_str(const struct dbAddr *paddr, const char *pbuffer); Check if string matches ZNAM or ONAM, and if it does, sets VAL. =head2 Record Processing Routine process implements the following algorithm: =over =item 1. Check to see that the appropriate device support module exists. If it doesn't, an error message is issued and processing is terminated with the PACT field still set to TRUE. This ensures that processes will no longer be called for this record. Thus error storms will not occur. =item 2. C is called. See L for details. =item 3. If PACT has been changed to TRUE, the device support read routine has started but has not completed reading a new input value. In this case, the processing routine merely returns, leaving PACT TRUE. =item 4. Convert. =back =over =item * status = read_bi =item * PACT = TRUE =item * C is called. =item * if status is 0, then set VAL=(0,1) if RVAL is (0, not 0) and UDF = False. =item * if status is 2, set status = 0 =back =over =item 5. Check alarms: This routine checks to see if the new VAL causes the alarm status and severity to change. If so, NSEV, NSTA and LALM are set. Note that if VAL is greater than 1, no checking is performed. =item 6. Check if monitors should be invoked: =back =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if MSLT is not equal to VAL. =item * Monitors for RVAL are checked whenever other monitors are invoked. =item * NSEV and NSTA are reset to 0. =back =over =item 7. Scan forward link if necessary, set PACT FALSE, and return. =back =head2 Device Support =head3 Fields of Interest to Device Support Each binary input record must have an associated set of device support routines. The primary resposibility of the device support routines is to obtain a new raw input value whenever C is called. The device support routines are primarily interested in the following fields: =fields PACT, DPVT, UDF, NSEV, NSTA, VAL, INP, RVAL, MASK =head3 Device Support routines Device support consists of the following routines: long report(int level); This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. long init(int after); This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. long init_record(struct dbCommon *precord); This routine is optional. If provided, it is called by the record support C routine. long get_ioint_info(int cmd, struct dbCommon *precord, IOSCANPVT *ppvt); This routine is called by the ioEventScan system each time the record is added or deleted from an I/O event scan list. C has the value (0,1) if the record is being (added to, deleted from) and I/O event list. It must be provided for any device type that can use the ioEvent scanner. long read_bi(struct dbCommon *precord); This routine must provide a new input value. It returns the following values: =over =item 0: Success. A new raw value is placed in RVAL. The record support module forces VAL to be (0,1) if RVAL is (0, not 0). =item 2: Success, but don't modify VAL. =item Other: Error. =back =head3 Device Support for Soft Records Two soft device support modules, Soft Channel and Raw Soft Channel, are provided for input records not related to actual hardware devices. The INP link type must be either CONSTANT, DB_LINK, or CA_LINK. =head3 Soft Channel C always returns a value of 2, which means that no conversion is performed. If the INP link type is CONSTANT, then the constant value is stored in VAL by C, and the UDF is set to FALSE. VAL can be changed via C requests. If the INP link type is PV_LINK, the C is called by C. C calls C to read the current value of VAL. See L for details. If the return status of C is zero, then C sets UDF to FALSE. The status of C is returned. =head3 Raw Soft Channel This module is like the previous except that values are read into RVAL. C returns a value of 0. Thus the record processing routine will force VAL to be 0 or 1. =cut } base-7.0.3.1/modules/database/src/std/rec/boRecord.c0000664000577000060420000003130313557101274020664 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* recBo.c - Record Support Routines for Binary Output records */ /* * Original Author: Bob Dalesio * Date: 7-14-89 */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #include "menuIvoa.h" #include "menuOmsl.h" #include "menuYesNo.h" #define GEN_SIZE_OFFSET #include "boRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL static long get_units(DBADDR *, char *); static long get_precision(const DBADDR *, long *); static long get_enum_str(const DBADDR *, char *); static long get_enum_strs(const DBADDR *, struct dbr_enumStrs *); static long put_enum_str(const DBADDR *, const char *); #define get_graphic_double NULL static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); #define get_alarm_double NULL rset boRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,boRSET); int boHIGHprecision = 2; epicsExportAddress(int, boHIGHprecision); double boHIGHlimit = 100000; epicsExportAddress(double, boHIGHlimit); struct bodset { /* binary output dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns:(0,2)=>(success,success no convert*/ DEVSUPFUN get_ioint_info; DEVSUPFUN write_bo;/*returns: (-1,0)=>(failure,success)*/ }; /* control block for callback*/ typedef struct myCallback { epicsCallback callback; struct dbCommon *precord; }myCallback; static void checkAlarms(boRecord *); static void monitor(boRecord *); static long writeValue(boRecord *); static void myCallbackFunc(epicsCallback *arg) { myCallback *pcallback; boRecord *prec; callbackGetUser(pcallback,arg); prec=(boRecord *)pcallback->precord; dbScanLock((struct dbCommon *)prec); if(prec->pact) { if((prec->val==1) && (prec->high>0)){ myCallback *pcallback; pcallback = (myCallback *)(prec->rpvt); callbackSetPriority(prec->prio, &pcallback->callback); callbackRequestDelayed(&pcallback->callback,(double)prec->high); } } else { prec->val = 0; dbProcess((struct dbCommon *)prec); } dbScanUnlock((struct dbCommon *)prec); } static long init_record(struct dbCommon *pcommon,int pass) { struct boRecord *prec = (struct boRecord *)pcommon; struct bodset *pdset = (struct bodset *) prec->dset; unsigned short ival = 0; long status = 0; myCallback *pcallback; if (pass == 0) return 0; recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "bo: init_record"); return S_dev_noDSET; } /* must have write_bo functions defined */ if ((pdset->number < 5) || (pdset->write_bo == NULL)) { recGblRecordError(S_dev_missingSup, prec, "bo: init_record"); return S_dev_missingSup; } /* get the initial value */ if (recGblInitConstantLink(&prec->dol, DBF_USHORT, &ival)) { prec->val = !!ival; prec->udf = FALSE; } pcallback = (myCallback *) calloc(1, sizeof(myCallback)); prec->rpvt = pcallback; callbackSetCallback(myCallbackFunc, &pcallback->callback); callbackSetUser(pcallback, &pcallback->callback); pcallback->precord = (struct dbCommon *) prec; if (pdset->init_record) { status=(*pdset->init_record)(prec); if(status==0) { if(prec->rval==0) prec->val = 0; else prec->val = 1; prec->udf = FALSE; } else if (status==2) status=0; } prec->mlst = prec->val; /* convert val to rval */ if ( prec->mask != 0 ) { if(prec->val==0) prec->rval = 0; else prec->rval = prec->mask; } else prec->rval = (epicsUInt32)prec->val; prec->mlst = prec->val; prec->lalm = prec->val; prec->oraw = prec->rval; prec->orbv = prec->rbv; return(status); } static long process(struct dbCommon *pcommon) { struct boRecord *prec = (struct boRecord *)pcommon; struct bodset *pdset = (struct bodset *)(prec->dset); long status=0; unsigned char pact=prec->pact; if( (pdset==NULL) || (pdset->write_bo==NULL) ) { prec->pact=TRUE; recGblRecordError(S_dev_missingSup,(void *)prec,"write_bo"); return(S_dev_missingSup); } if (!prec->pact) { if (!dbLinkIsConstant(&prec->dol) && prec->omsl == menuOmslclosed_loop) { unsigned short val; prec->pact = TRUE; status=dbGetLink(&prec->dol,DBR_USHORT, &val,0,0); prec->pact = FALSE; if(status==0){ prec->val = val; prec->udf = FALSE; }else { recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM); } } /* convert val to rval */ if ( prec->mask != 0 ) { if(prec->val==0) prec->rval = 0; else prec->rval = prec->mask; } else prec->rval = (epicsUInt32)prec->val; } /* check for alarms */ checkAlarms(prec); if (prec->nsev < INVALID_ALARM ) status=writeValue(prec); /* write the new value */ else { switch (prec->ivoa) { case (menuIvoaContinue_normally) : status=writeValue(prec); /* write the new value */ break; case (menuIvoaDon_t_drive_outputs) : break; case (menuIvoaSet_output_to_IVOV) : if(prec->pact == FALSE){ /* convert val to rval */ prec->val=prec->ivov; if ( prec->mask != 0 ) { if(prec->val==0) prec->rval = 0; else prec->rval = prec->mask; } else prec->rval = (epicsUInt32)prec->val; } status=writeValue(prec); /* write the new value */ break; default : status=-1; recGblRecordError(S_db_badField,(void *)prec, "bo:process Illegal IVOA field"); } } /* check if device support set pact */ if ( !pact && prec->pact ) return(0); prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, NULL); if((prec->val==1) && (prec->high>0)){ myCallback *pcallback; pcallback = (myCallback *)(prec->rpvt); callbackSetPriority(prec->prio, &pcallback->callback); callbackRequestDelayed(&pcallback->callback,(double)prec->high); } /* check event list */ monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact=FALSE; return(status); } static long special(DBADDR *paddr, int after) { boRecord *prec = (boRecord *)(paddr->precord); int special_type = paddr->special; switch(special_type) { case(SPC_MOD): if (dbGetFieldIndex(paddr) == boRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return(0); } default: recGblDbaddrError(S_db_badChoice, paddr, "bo: special"); return(S_db_badChoice); } } #define indexof(field) boRecord##field static long get_units(DBADDR *paddr, char *units) { if(dbGetFieldIndex(paddr) == indexof(HIGH)) strcpy(units, "s"); return(0); } static long get_precision(const DBADDR *paddr, long *precision) { if(dbGetFieldIndex(paddr) == indexof(HIGH)) *precision = boHIGHprecision; else recGblGetPrec(paddr,precision); return(0); } static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd) { if(dbGetFieldIndex(paddr) == indexof(HIGH)) { pcd->lower_ctrl_limit = 0.0; pcd->upper_ctrl_limit = boHIGHlimit; } else recGblGetControlDouble(paddr,pcd); return(0); } static long get_enum_str(const DBADDR *paddr, char *pstring) { boRecord *prec=(boRecord *)paddr->precord; int index; unsigned short *pfield = (unsigned short *)paddr->pfield; index = dbGetFieldIndex(paddr); if(index!=indexof(VAL)) { strcpy(pstring,"Illegal_Value"); } else if(*pfield==0) { strncpy(pstring,prec->znam,sizeof(prec->znam)); pstring[sizeof(prec->znam)] = 0; } else if(*pfield==1) { strncpy(pstring,prec->onam,sizeof(prec->onam)); pstring[sizeof(prec->onam)] = 0; } else { strcpy(pstring,"Illegal_Value"); } return(0); } static long get_enum_strs(const DBADDR *paddr,struct dbr_enumStrs *pes) { boRecord *prec=(boRecord *)paddr->precord; /*SETTING no_str=0 breaks channel access clients*/ pes->no_str = 2; memset(pes->strs,'\0',sizeof(pes->strs)); strncpy(pes->strs[0],prec->znam,sizeof(pes->strs[0])); if(*prec->znam!=0) pes->no_str=1; strncpy(pes->strs[1],prec->onam,sizeof(pes->strs[1])); if(*prec->onam!=0) pes->no_str=2; return(0); } static long put_enum_str(const DBADDR *paddr, const char *pstring) { boRecord *prec=(boRecord *)paddr->precord; if(strncmp(pstring,prec->znam,sizeof(prec->znam))==0) prec->val = 0; else if(strncmp(pstring,prec->onam,sizeof(prec->onam))==0) prec->val = 1; else return(S_db_badChoice); return(0); } static void checkAlarms(boRecord *prec) { unsigned short val = prec->val; /* check for udf alarm */ if(prec->udf == TRUE ){ recGblSetSevr(prec,UDF_ALARM,prec->udfs); } /* check for state alarm */ if (val == 0){ recGblSetSevr(prec,STATE_ALARM,prec->zsv); }else{ recGblSetSevr(prec,STATE_ALARM,prec->osv); } /* check for cos alarm */ if(val == prec->lalm) return; recGblSetSevr(prec,COS_ALARM,prec->cosv); prec->lalm = val; return; } static void monitor(boRecord *prec) { unsigned short monitor_mask; monitor_mask = recGblResetAlarms(prec); /* check for value change */ if (prec->mlst != prec->val){ /* post events for value change and archive change */ monitor_mask |= (DBE_VALUE | DBE_LOG); /* update last value monitored */ prec->mlst = prec->val; } /* send out monitors connected to the value field */ if (monitor_mask){ db_post_events(prec,&prec->val,monitor_mask); } if(prec->oraw!=prec->rval) { db_post_events(prec,&prec->rval, monitor_mask|DBE_VALUE|DBE_LOG); prec->oraw = prec->rval; } if(prec->orbv!=prec->rbv) { db_post_events(prec,&prec->rbv, monitor_mask|DBE_VALUE|DBE_LOG); prec->orbv = prec->rbv; } return; } static long writeValue(boRecord *prec) { struct bodset *pdset = (struct bodset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: status = pdset->write_bo(prec); break; case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbPutLink(&prec->siol, DBR_USHORT, &prec->val, 1); prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } base-7.0.3.1/modules/database/src/std/rec/boRecord.dbd.pod0000664000577000060420000004241313557101274021760 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Binary Output Record (bo) The normal use for this record type is to store a simple bit (0 or 1) value to be sent to a Digital Output module. It can also be used to write binary values into other records via database or channel access links. This record can implement both latched and momentary binary outputs depending on how the HIGH field is configured. =head2 Parameter Fields The binary output's fields fall into the following categories: =over 1 =item * scan parameters =item * convert and write parameters =item * operator display parameters =item * alarm parameters =item * run-time parameters =back =recordtype bo =cut recordtype(bo) { =head3 Scan Parameters The binary output record has the standard fields for specifying under what circumstances the record will be processed. The fields are listed in L. In addition, L explains how these fields are used. Note that I/O event scanning is only supported for those card types that interrupt. =fields SCAN =head3 Desired Output Parameters The binary output record must specify where its desired output originates. The desired output needs to be in engineering units. The first field that determines where the desired output originates is the output mode select (OMSL) field, which can have two possible values: C or C. If C is specified, the value in the VAL field can be set externally via dbPuts at run-time. If C is specified, the VAL field's value is obtained from the address specified in the desired output location (DOL) field which can be a database link or a channel access link, but not a constant. To achieve continuous control, a database link to a control algorithm record should be entered in the DOL field. L
presents more information on database addresses and links. L explaines the effect of database linkage on scanning. =fields DOL, OMSL =head3 Convert and Write Parameters These parameters are used to determine where the binary output writes to and how to convert the engineering units to a raw signal. After VAL is set and forced to be either 1 or 0, as the result of either a dbPut or a new value being retrieved from the link in the DOL field, then what happens next depends on which device support routine is used and how the HIGH field is configured. If the C device support routine is specified, then the device support routine writes the VAL field's value to the address specified in the OUT field. Otherwise, RVAL is the value written by the device support routines after being converted. If VAL is equal to 0, then the record processing routine sets RVAL equal to zero. When VAL is not equal to 0, then RVAL is set equal to the value contained in the MASK field. (MASK is set by the device support routines and is of no concern to the user.) Also, when VAL is not 0 and after RVAL is set equal to MASK, the record processing routine checks to see if the HIGH field is greater than 0. If it is, then the routine will process the record again with VAL set to 0 after the number of seconds specified by HIGH. Thus, HIGH implements a momentary output which changes the state of the device back to 0 after I number of seconds. =fields DTYP, OUT, VAL, RVAL, HIGH, ZNAM, ONAM =head3 Conversion Parameters The ZNAM field has the string that corresponds to the 0 state, and the ONAM field holds the string that corresponds to the 1 state. These fields, other than being used to tell the operator what each state represents, are used to perform conversions if the value fetched by DOL is a string. If it is, VAL is set to the state which corresponds to that string. For instance, if the value fetched is the string "Off" and the ZNAM string is "Off," then VAL is set to 0. After VAL is set, if VAL is equal to 0, then the record processing routine sets RVAL equal to zero. When VAL is not equal to 0, then RVAL is set equal to the value contained in the MASK field. (Mask is set by the device support routines and is of no concern to the user.) Also when VAL is equal to 1 and after RVAL is set equal to MASK, the record processing routine checks to see if the HIGH field is greater than 0. If it is, then the routine processes the record again with VAL=0 after the number of seconds specified by HIGH. Thus, HIGH implements a latched output which changes the state of the device or link to 1, then changes it back to 0 after I number of seconds. =fields ZNAM, ONAM, HIGH =head3 Output Specification The OUT field specifies where the binary output record writes its output. It must specify the address of an I/O card if the record sends its output to hardware, and the DTYP field must contain the corresponding device support module. Be aware that the address format differs according to the I/O bus used. See L
for information on the format of hardware addresses. Otherwise, if the record is configured to use the soft device support modules, then it can be either a database link, a channel access link, or a constant. Be aware that nothing will be written when OUT is a constant. See L
for information on the format of the database and channel access addresses. Also, see L in this chapter for more on output to other records. =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator, The C record support routine can retrieve the state string corresponding to the VAL's state. So, if the value is 1, C will return the string in the ONAM field: and if 0, C will return the ZNAM string. See L for more on the record name (NAME) and description (DESC) fields. =fields ZNAM, ONAM, NAME, DESC =head3 Alarm Parameters These parameters are used to determine the binary output's alarm condition and to determine the severity of that condition. The possible alarm conditions for binary outputs are the SCAN, READ, INVALID and state alarms. The user can configure the state alarm conditions using these fields. The possible values for these fields are C, C, and C. The ZSV holds the severity for the zero state; OSV for the one state. COSV is used to cause an alarm whenever the state changes between states (0-1, 1-0) and its severity is configured as MINOR or MAJOR. See L for more information on the IVOA and IVOV fields. L lists other fields related to alarms that are common to all record types. =fields ZSV, OSV, COSV, IVOA, IVOV =head3 Run-Time and Simulation Mode Parameters These parameters are used by the run-time code for processiong the binary output. They are not configurable using a configuration tool. They represent the current state of the binary output. ORAW is used to determine if monitors should be triggered for RVAL at the same time they are triggered for VAL. MASK is given a value by the device support routines and should not concern the user. The RBV field is also set by device support. It is the actual read back value obtained from the hardware itself or from the associated device driver. The ORBV field is used to decide if monitors should be triggered for RBV at the same time monitors are triggered for changes in VAL. The LALM field holds the value of the last occurrence of the change of state alarm. It is used to implement the change of state alarm, and thus only has meaning if COSV is MINOR or MAJOR. The MLST is used by the C record support routine to determine if archive and value change monitors are invoked. They are if MLST is not equal to VAL. The WPDT field is a private field for honoring seconds to hold HIGH. =fields ORAW, MASK, RBV, ORBV, LALM, MLST, RPVT, WDPT The following fields are used to operate the binary output in the simulation mode. See L for more information on these fields. =fields SIOL, SIML, SIMM, SIMS =cut include "dbCommon.dbd" field(VAL,DBF_ENUM) { prompt("Current Value") promptgroup("50 - Output") asl(ASL0) pp(TRUE) } field(OMSL,DBF_MENU) { prompt("Output Mode Select") promptgroup("50 - Output") interest(1) menu(menuOmsl) } field(DOL,DBF_INLINK) { prompt("Desired Output Loc") promptgroup("40 - Input") interest(1) } field(OUT,DBF_OUTLINK) { prompt("Output Specification") promptgroup("50 - Output") interest(1) } field(HIGH,DBF_DOUBLE) { prompt("Seconds to Hold High") promptgroup("30 - Action") interest(1) } field(ZNAM,DBF_STRING) { prompt("Zero Name") promptgroup("80 - Display") pp(TRUE) interest(1) size(26) prop(YES) } field(ONAM,DBF_STRING) { prompt("One Name") promptgroup("80 - Display") pp(TRUE) interest(1) size(26) prop(YES) } field(RVAL,DBF_ULONG) { prompt("Raw Value") pp(TRUE) } field(ORAW,DBF_ULONG) { prompt("prev Raw Value") special(SPC_NOMOD) interest(3) } field(MASK,DBF_ULONG) { prompt("Hardware Mask") special(SPC_NOMOD) interest(1) } field(RPVT,DBF_NOACCESS) { prompt("Record Private") special(SPC_NOMOD) interest(4) extra("void * rpvt") } field(WDPT,DBF_NOACCESS) { prompt("Watch Dog Timer ID") special(SPC_NOMOD) interest(4) extra("void * wdpt") } field(ZSV,DBF_MENU) { prompt("Zero Error Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(OSV,DBF_MENU) { prompt("One Error Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(COSV,DBF_MENU) { prompt("Change of State Sevr") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(RBV,DBF_ULONG) { prompt("Readback Value") special(SPC_NOMOD) } field(ORBV,DBF_ULONG) { prompt("Prev Readback Value") special(SPC_NOMOD) interest(3) } field(MLST,DBF_USHORT) { prompt("Last Value Monitored") special(SPC_NOMOD) interest(3) } field(LALM,DBF_USHORT) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(SIOL,DBF_OUTLINK) { prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } field(IVOA,DBF_MENU) { prompt("INVALID outpt action") promptgroup("50 - Output") interest(2) menu(menuIvoa) } field(IVOV,DBF_USHORT) { prompt("INVALID output value") promptgroup("50 - Output") interest(2) } =head2 Record Support =head3 Record Support Routines =head2 C This routine initializes SIMM if SIML is a constant or creates a channel access link if SIML is PV_LINK. If SIOL is a PV_LINK a channel access link is created. This routine next checks to see that device support is available. The routine next checks to see if the device support write routine is defined. If either device support or the device support write routine does not exist, and error message is issued and processing is terminated. If DOL is a constant, then VAL is initialized to 1 if its value is nonzero or initialzed to 0 if DOL is zero, and UDF is set to FALSE. If device support includes C, it is called. VAL is set using RVAL, and UDF is set to FALSE. =head2 C See next section. =head2 C Retrieves ASCII string corresponding to VAL. =head2 C Retrieves ASCII strings for ZNAM and ONAM. =head2 C Checks if string matches ZNAM or ONAM, and if it does, sets VAL. =head2 Record Processing Routine process implements the following algorithm: =over 1 =item 1. Check to see that the appropriate device support module exists. If it doesn't, an error message is issued and processing is terminated with the PACT field still set to TRUE. This ensures that processes will no longer be called for this record. Thus error storms will not occur. =item 2. If PACT is FALSE =back =over =item * If DOL holds a link and OMSL is C =over =item * get values from DOL =item * check for link alarm =item * force VAL to be 0 or 1 =item * if MASK is defined =over =item * if VAL is 0 set RVAL = 0 =back =item * else set RVAL = MASK =back =back =over =item 3. Check alarms: This routine checks to see if the new VAL causes the alarm status and severity to change. If so, NSEV, NSTA, and LALM are set. =item 4. Check severity and write the new value. See L for more information on how INVALID alarms affect output. =item 5. If PACT has been changed to TRUE, the device support write output routine has started but has not completed writing the new value. in this case, the processing routine merely returns, leaving PACT TRUE. =item 6. Check WAIT. If VAL is 1 and WAIT is greater than 0, process again with a VAL=0 after WAIT seconds. =item 7. Check to see if monitors should be invoked. =back =over 1 =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if MLST is not equal to VAL. =item * Monitors for RVAL and for RBV are checked whenever other monitors are invoked. =item * NSEV and NSTA are reset to 0. =back =over =item 8 Scan forward link if necessary, set PACT FALSE, and return =back =head2 Device support =head3 Fields Of Interest To Device Support Each binary output record must have an associated set of device support routines. The primary responsibility of the device support routines is to write a new value whenever C is called. The device support routines are primarily interested in the following fields: =fields PACT, DPVT, NSEV, NSTA, VAL, OUT, RVAL, MASK, RBV =head3 Device Support Routines Device support consists of the following routines: =head4 long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. =head4 long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. =head2 C This routine is optional. If provided, it is called by record support C routine. It should determine MASK if it is needed. =over =item * 0: Success. RVAL modified (VAL will be set accordingly) =item * 2: Success. VAL modified =item * other: Error =back =head2 C This routine is called by the ioEventScan system each time the record is added or deleted from an I/O event scan list. C has the value (0,1) if the record is being (added to, deleted from) an I/O event list. It must be provided for any device type that can use the ioEvent scanner. =head2 C This routine must output a new value. It returns the following values: =over =item * 0: Success =item * other: Error. =back =head2 Device Support For Soft Records Two soft device support modules C and C are provided for output records not related to actual hardware devices. The OUT link type must be either CONSTANT, DB_LINK, or CA_LINK. =head3 Soft Channel This module writes the current value of VAL. If the OUT link type is PV_LINK, then C is called by C. C always returns a value of 2, which means that no conversion will ever be attempted. C calls C to write the current value of VAL. See L for details. =head3 Raw Soft Channel This module is like the previous except that it writes the current value of RVAL =cut } variable(boHIGHprecision, int) variable(boHIGHlimit, double) base-7.0.3.1/modules/database/src/std/rec/calcRecord.c0000664000577000060420000003053013557101274021167 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Record Support Routines for Calculation records */ /* * Original Author: Julie Sander and Bob Dalesio * Date: 7-27-87 */ #include #include #include #include #include #include "dbDefs.h" #include "errlog.h" #include "alarm.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "epicsMath.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #define GEN_SIZE_OFFSET #include "calcRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Hysterisis for alarm filtering: 1-1/e */ #define THRESHOLD 0.6321 /* Create RSET - Record Support Entry Table */ #define report NULL #define initialize NULL static long init_record(struct dbCommon *prec, int pass); static long process(struct dbCommon *prec); static long special(DBADDR *paddr, int after); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL static long get_units(DBADDR *paddr, char *units); static long get_precision(const DBADDR *paddr, long *precision); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd); static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd); static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad); rset calcRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset, calcRSET); static void checkAlarms(calcRecord *prec, epicsTimeStamp *timeLast); static void monitor(calcRecord *prec); static int fetch_values(calcRecord *prec); static long init_record(struct dbCommon *pcommon, int pass) { struct calcRecord *prec = (struct calcRecord *)pcommon; struct link *plink; double *pvalue; int i; short error_number; if (pass==0) return(0); plink = &prec->inpa; pvalue = &prec->a; for (i = 0; i < CALCPERFORM_NARGS; i++, plink++, pvalue++) { recGblInitConstantLink(plink, DBF_DOUBLE, pvalue); } if (postfix(prec->calc, prec->rpcl, &error_number)) { recGblRecordError(S_db_badField, (void *)prec, "calc: init_record: Illegal CALC field"); errlogPrintf("%s.CALC: %s in expression \"%s\"\n", prec->name, calcErrorStr(error_number), prec->calc); } return 0; } static long process(struct dbCommon *pcommon) { struct calcRecord *prec = (struct calcRecord *)pcommon; epicsTimeStamp timeLast; prec->pact = TRUE; if (fetch_values(prec) == 0) { if (calcPerform(&prec->a, &prec->val, prec->rpcl)) { recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM); } else prec->udf = isnan(prec->val); } timeLast = prec->time; recGblGetTimeStamp(prec); /* check for alarms */ checkAlarms(prec, &timeLast); /* check event list */ monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact = FALSE; return 0; } static long special(DBADDR *paddr, int after) { calcRecord *prec = (calcRecord *)paddr->precord; short error_number; if (!after) return 0; if (paddr->special == SPC_CALC) { if (postfix(prec->calc, prec->rpcl, &error_number)) { recGblRecordError(S_db_badField, (void *)prec, "calc: Illegal CALC field"); errlogPrintf("%s.CALC: %s in expression \"%s\"\n", prec->name, calcErrorStr(error_number), prec->calc); return S_db_badField; } return 0; } recGblDbaddrError(S_db_badChoice, paddr, "calc::special - bad special value!"); return S_db_badChoice; } #define indexof(field) calcRecord##field static long get_linkNumber(int fieldIndex) { if (fieldIndex >= indexof(A) && fieldIndex <= indexof(L)) return fieldIndex - indexof(A); if (fieldIndex >= indexof(LA) && fieldIndex <= indexof(LL)) return fieldIndex - indexof(LA); return -1; } static long get_units(DBADDR *paddr, char *units) { calcRecord *prec = (calcRecord *)paddr->precord; int linkNumber; if(paddr->pfldDes->field_type == DBF_DOUBLE) { linkNumber = get_linkNumber(dbGetFieldIndex(paddr)); if (linkNumber >= 0) dbGetUnits(&prec->inpa + linkNumber, units, DB_UNITS_SIZE); else strncpy(units,prec->egu,DB_UNITS_SIZE); } return 0; } static long get_precision(const DBADDR *paddr, long *pprecision) { calcRecord *prec = (calcRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); int linkNumber; *pprecision = prec->prec; if (fieldIndex == indexof(VAL)) return 0; linkNumber = get_linkNumber(fieldIndex); if (linkNumber >= 0) { short precision; if (dbGetPrecision(&prec->inpa + linkNumber, &precision) == 0) *pprecision = precision; } else recGblGetPrec(paddr, pprecision); return 0; } static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) { calcRecord *prec = (calcRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); int linkNumber; switch (fieldIndex) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): pgd->lower_disp_limit = prec->lopr; pgd->upper_disp_limit = prec->hopr; break; default: linkNumber = get_linkNumber(fieldIndex); if (linkNumber >= 0) { dbGetGraphicLimits(&prec->inpa + linkNumber, &pgd->lower_disp_limit, &pgd->upper_disp_limit); } else recGblGetGraphicDouble(paddr,pgd); } return 0; } static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) { calcRecord *prec = (calcRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): pcd->lower_ctrl_limit = prec->lopr; pcd->upper_ctrl_limit = prec->hopr; break; default: recGblGetControlDouble(paddr,pcd); } return 0; } static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) { calcRecord *prec = (calcRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); int linkNumber; if (fieldIndex == indexof(VAL)) { pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN; pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN; pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN; pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN; } else { linkNumber = get_linkNumber(fieldIndex); if (linkNumber >= 0) { dbGetAlarmLimits(&prec->inpa + linkNumber, &pad->lower_alarm_limit, &pad->lower_warning_limit, &pad->upper_warning_limit, &pad->upper_alarm_limit); } else recGblGetAlarmDouble(paddr, pad); } return 0; } static void checkAlarms(calcRecord *prec, epicsTimeStamp *timeLast) { enum { range_Lolo = 1, range_Low, range_Normal, range_High, range_Hihi } alarmRange; static const epicsEnum16 range_stat[] = { SOFT_ALARM, LOLO_ALARM, LOW_ALARM, NO_ALARM, HIGH_ALARM, HIHI_ALARM }; double val, hyst, lalm, alev, aftc, afvl; epicsEnum16 asev; if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, prec->udfs); prec->afvl = 0; return; } val = prec->val; hyst = prec->hyst; lalm = prec->lalm; /* check VAL against alarm limits */ if ((asev = prec->hhsv) && (val >= (alev = prec->hihi) || ((lalm == alev) && (val >= alev - hyst)))) alarmRange = range_Hihi; else if ((asev = prec->llsv) && (val <= (alev = prec->lolo) || ((lalm == alev) && (val <= alev + hyst)))) alarmRange = range_Lolo; else if ((asev = prec->hsv) && (val >= (alev = prec->high) || ((lalm == alev) && (val >= alev - hyst)))) alarmRange = range_High; else if ((asev = prec->lsv) && (val <= (alev = prec->low) || ((lalm == alev) && (val <= alev + hyst)))) alarmRange = range_Low; else { alev = val; asev = NO_ALARM; alarmRange = range_Normal; } aftc = prec->aftc; afvl = 0; if (aftc > 0) { /* Apply level filtering */ afvl = prec->afvl; if (afvl == 0) { afvl = (double)alarmRange; } else { double t = epicsTimeDiffInSeconds(&prec->time, timeLast); double alpha = aftc / (t + aftc); /* The sign of afvl indicates whether the result should be * rounded up or down. This gives the filter hysteresis. * If afvl > 0 the floor() function rounds to a lower alarm * level, otherwise to a higher. */ afvl = alpha * afvl + ((afvl > 0) ? (1 - alpha) : (alpha - 1)) * alarmRange; if (afvl - floor(afvl) > THRESHOLD) afvl = -afvl; /* reverse rounding */ alarmRange = abs((int)floor(afvl)); switch (alarmRange) { case range_Hihi: asev = prec->hhsv; alev = prec->hihi; break; case range_High: asev = prec->hsv; alev = prec->high; break; case range_Normal: asev = NO_ALARM; break; case range_Low: asev = prec->lsv; alev = prec->low; break; case range_Lolo: asev = prec->llsv; alev = prec->lolo; break; } } } prec->afvl = afvl; if (asev) { /* Report alarm condition, store LALM for future HYST calculations */ if (recGblSetSevr(prec, range_stat[alarmRange], asev)) prec->lalm = alev; } else { /* No alarm condition, reset LALM */ prec->lalm = val; } } static void monitor(calcRecord *prec) { unsigned monitor_mask; double *pnew, *pprev; int i; monitor_mask = recGblResetAlarms(prec); /* check for value change */ recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE); /* check for archive change */ recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE); /* send out monitors connected to the value field */ if (monitor_mask){ db_post_events(prec, &prec->val, monitor_mask); } /* check all input fields for changes*/ pnew = &prec->a; pprev = &prec->la; for (i = 0; i < CALCPERFORM_NARGS; i++, pnew++, pprev++) { if (*pnew != *pprev || monitor_mask & DBE_ALARM) { db_post_events(prec, pnew, monitor_mask | DBE_VALUE | DBE_LOG); *pprev = *pnew; } } return; } static int fetch_values(calcRecord *prec) { struct link *plink; double *pvalue; long status = 0; int i; plink = &prec->inpa; pvalue = &prec->a; for(i = 0; i < CALCPERFORM_NARGS; i++, plink++, pvalue++) { int newStatus; newStatus = dbGetLink(plink, DBR_DOUBLE, pvalue, 0, 0); if (status == 0) status = newStatus; } return status; } base-7.0.3.1/modules/database/src/std/rec/calcRecord.dbd.pod0000664000577000060420000004631113557101274022263 0ustar anjaesctl#************************************************************************* # Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Calculation Record (calc) The calculation or "Calc" record is used to perform algebraic, relational, and logical operations on values retrieved from other records. The result of its operations can then be accessed by another record so that it can then be used. =head2 Parameter Fields The fields in the record fall into the following categories: =over 1 =item * scan parameters =item * read parameters =item * expression parameters =item * operator display parameters =item * alarm parameters =item * monitor parameters =item * run-time parameters =back =recordtype calc =cut recordtype(calc) { =head3 Scan Parameters The Calc record has the standard fields for specifying under what circumstances the record will be processed. These fields are listed in L. In addition, L explains how these fields are used. Since the Calc record supports no direct interfaces to hardware, it cannot be scanned on I/O interrupt, so its SCAN field cannot be C. =fields SCAN =head3 Read Parameters The read parameters for the Calc record consist of 12 input links INPA, INPB, ... INPL. The fields can be database links, channel access links, or constants. If they are links, they must specify another record's field or a channel access link. If they are constants, they will be initialized with the value they are configured with and can be changed via C. They cannot be hardware addresses. See L
for information on how to specify database links. =fields INPA, INPB, INPC, INPD, INPE, INPF, INPG, INPH, INPI, INPJ, INPK, INPL =head3 Expression At the core of the Calc record lies the CALC and RPCL fields. The CALC field contains the infix expresion which the record routine will use when it processes the record. The resulting value is placed in the VAL field and can be accessed from there. The CALC expression is actually converted to opcode and stored as Reverse Polish Notation in the RPCL field. It is this expression which is actually used to calculate VAL. The Reverse Polish expression is evaluated more efficiently during run-time than an infix expression. CALC can be changed at run-time, and a special record routine calls a function to convert it to Reverse Polish Notation. The infix expressions that can be used are very similar to the C expression syntax, but with some additions and subtle differences in operator meaning and precedence. The string may contain a series of expressions separated by a semi-colon character ";" any one of which may actually provide the calculation result; however, all of the other expressions included must assign their result to a variable. All alphabetic elements described below are case independent, so upper and lower case letters may be used and mixed in the variable and function names as desired. Spaces may be used anywhere within an expression except between characters that make up a single expression element. The range of expressions supported by the calculation record are separated into literals, constants, operands, algebraic operators, trigonometric operators, relational operators, logical operators, the assignment operator, parentheses and commas, and the question mark or '?:' operator. =fields CALC, RPCL =head3 Literals =over 1 =item * Standard double precision floating point numbers =item * Inf: Infinity =item * Nan: Not a Number =back =head3 Constants =over 1 =item * PI: returns the mathematical constant E =item * D2R: evaluates to E/180 which, when used as a multiplier, converts an angle from degrees to radians =item * R2D: evaluates to 180/E which as a multiplier converts an angle from radians to degrees =back =head3 Operands The expression uses the values retrieved from the INPx links as operands, though constants can be used as operands too. These values retrieved from the input links are stored in the A-L fields. The values to be used in the expression are simply referenced by the field letter. For instance, the value obtained from INPA link is stored in the field A, and the value obtained from INPB is stored in field B. The field names can be included in the expression which will operate on their respective values, as in A+B. Also, the RNDM nullary function can be included as an operand in the expression in order to generate a random number between 0 and 1. =fields A, B, C, D, E, F, G, H, I, J, K, L The keyword VAL returns the current contents of the VAL field (which can be written to by a CA put, so it might I be the result from the last time the expression was evaluated). =head3 Algebraic Operators =over 1 =item * ABS: Absolute value (unary) =item * SQR: Square root (unary) =item * MIN: Minimum (any number of args) =item * MAX: Maximum (any number of args) =item * FINITE: returns non-zero if none of the arguments are NaN or Inf (any number of args) =item * ISNAN: returns non-zero if any of the arguments is NaN or Inf (any number of args) =item * CEIL: Ceiling (unary) =item * FLOOR: Floor (unary) =item * LOG: Log base 10 (unary) =item * LOGE: Natural log (unary) =item * LN: Natural log (unary) =item * EXP: Exponential function (unary) =item * ^ : Exponential (binary) =item * ** : Exponential (binary) =item * + : Addition (binary) =item * - : Subtraction (binary) =item * * : Multiplication (binary) =item * / : Division (binary) =item * % : Modulo (binary) =item * NOT: Negate (unary) =back =head3 Trigonometric Operators =over 1 =item * SIN: Sine =item * SINH: Hyperbolic sine =item * ASIN: Arc sine =item * COS: Cosine =item * COSH: Hyperbolic cosine =item * ACOS: Arc cosine =item * TAN: Tangent =item * TANH: Hyperbolic tangent =item * ATAN: Arc tangent =back =head3 Relational Operators =over 1 =item * C<<< >= >>> : Greater than or equal to =item * C<<< > >>> : Greater than =item * C<<< <= >>> : Less than or equal to =item * C<<< < >>> : Less than =item * C<<< # >>> : Not equal to =item * C<<< = >>> : Equal to =back =head3 Logical Operators =over 1 =item * C<&&> : And =item * C<||> : Or =item * C : Not =back =head3 Bitwise Operators =over 1 =item * C<|> : Bitwise Or =item * C<&> : Bitwise And =item * OR : Bitwise Or =item * AND : Bitwise And =item * XOR : Bitwise Exclusive Or =item * C<~> : One's Complement =item * C<<< << >>> : Left shift =item * C<<< >> >>> : Right shift =back =head3 Assignment Operator =over 1 =item * C<:=> : assigns a value (right hand side) to a variable (i.e. field) =back =head3 Parantheses, Comma, and Semicolon The open and close parentheses are supported. Nested parentheses are supported. The comma is supported when used to separate the arguments of a binary function. The semicolon is used to separate expressions. Although only one traditional calculation expression is allowed, multiple assignment expressions are allowed. =head3 Conditional Expression The C language's question mark operator is supported. The format is: C =head3 Expression Examples =head3 Algebraic C =over 1 =item * Result is C =back =head3 Relational C<<< (A + B) < (C + D) >>> =over 1 =item * Result is 1 if C<<< (A + B) < (C + D) >>> =item * Result is 0 if C<<< (A + B) >= (C + D) >>> =back =head3 Question Mark C<<< (A + B) < (C + D) ? E : F + L + 10 >>> =over 1 =item * Result is C if C<<< (A + B) < (C + D) >>> =item * Result is C if C<<< (A + B) >= (C + D) >>> =back Prior to Base 3.14.9 it was legal to omit the : and the second (else) part of the conditional, like this: C<(A + B)<(C + D) ? E> =over 1 =item Result is E if (A + B)<(C + D) =item Result is unchanged if (A + B)>=(C + D) From 3.14.9 onwards, this expresion must be written as C<(A + B) < (C + D) ? E : VAL> =back =head3 Logical C =over 1 =item * Causes the following to occur: =over 1 =item * Convert A to integer =item * Convert B to integer =item * Bitwise And A and B =item * Convert result to floating point =back =back =head3 Assignment C =over 1 =item * Causes the Calc record to output the successive values of a sine curve in 1 degree intervals. =back =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. These fields are used to display VAL and other parameters of the calculation record either textually or graphically. The EGU field contains a string of up to 16 characters which is supplied by the user and which describes the values being operated upon. The string is retrieved whenever the routine C is called. The EGU string is solely for an operator's sake and does not have to be used. The HOPR and LOPR fields only refer to the limits of the VAL, HIHI, HIGH, LOW and LOLO fields. PREC controls the precision of the VAL field. See L for more on the record name (NAME) and description (DESC) fields. =fields EGU, PREC, HOPR, LOPR, NAME, DESC =head3 Alarm Parameters The possible alarm conditions for the Calc record are the SCAN, READ, Calculation, and limit alarms. The SCAN and READ alarms are called by the record support routines. The Calculation alarm is called by the record processing routine when the CALC expression is an invalid one, upon which an error message is generated. The following alarm parameters which are configured by the user, define the limit alarms for the VAL field and the severity corresponding to those conditions. The HYST field defines an alarm deadband for each limit. See L for a complete explanation of alarms of these fields. L lists other fields related to alarms that are common to all record types. =fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST =head3 Monitor Parameters These paramaeters are used to determine when to send monitors for the value fields. These monitors are sent when the value field exceeds the last monitored field by the appropriate deadband, the ADEL for archiver monitors and the MDEL field for all other types of monitors. If these fields have a value of zero, everytime the value changes, monitors are triggered; if they have a value of -1, everytime the record is scanned, monitors are triggered. See L for a complete explanation of monitors. =fields ADEL, MDEL =head3 Run-time Parameters These fields are not configurable using a configuration tool and none are modifiable at run-time. They are used to process the record. The LALM field is used to implement the hysteresis factor for the alarm limits. The LA-LL fields are used to decide when to trigger monitors for the corresponding fields. For instance, if LA does not equal the value A, monitors for A are triggered. The MLST and ALST fields are used in the same manner for the VAL field. =fields LALM, ALST, MLST, LA, LB, LC, LD, LE, LF, LG, LH, LI, LJ, LK, LL =cut include "dbCommon.dbd" field(VAL,DBF_DOUBLE) { prompt("Result") promptgroup("50 - Output") asl(ASL0) } field(CALC,DBF_STRING) { prompt("Calculation") promptgroup("30 - Action") special(SPC_CALC) pp(TRUE) size(80) initial("0") } field(INPA,DBF_INLINK) { prompt("Input A") promptgroup("41 - Input A-F") interest(1) } field(INPB,DBF_INLINK) { prompt("Input B") promptgroup("41 - Input A-F") interest(1) } field(INPC,DBF_INLINK) { prompt("Input C") promptgroup("41 - Input A-F") interest(1) } field(INPD,DBF_INLINK) { prompt("Input D") promptgroup("41 - Input A-F") interest(1) } field(INPE,DBF_INLINK) { prompt("Input E") promptgroup("41 - Input A-F") interest(1) } field(INPF,DBF_INLINK) { prompt("Input F") promptgroup("41 - Input A-F") interest(1) } field(INPG,DBF_INLINK) { prompt("Input G") promptgroup("42 - Input G-L") interest(1) } field(INPH,DBF_INLINK) { prompt("Input H") promptgroup("42 - Input G-L") interest(1) } field(INPI,DBF_INLINK) { prompt("Input I") promptgroup("42 - Input G-L") interest(1) } field(INPJ,DBF_INLINK) { prompt("Input J") promptgroup("42 - Input G-L") interest(1) } field(INPK,DBF_INLINK) { prompt("Input K") promptgroup("42 - Input G-L") interest(1) } field(INPL,DBF_INLINK) { prompt("Input L") promptgroup("42 - Input G-L") interest(1) } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) prop(YES) } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) prop(YES) } field(HOPR,DBF_DOUBLE) { prompt("High Operating Rng") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { prompt("Lolo Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(HSV,DBF_MENU) { prompt("High Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(LSV,DBF_MENU) { prompt("Low Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(AFTC, DBF_DOUBLE) { prompt("Alarm Filter Time Constant") promptgroup("70 - Alarm") interest(1) } field(AFVL, DBF_DOUBLE) { prompt("Alarm Filter Value") special(SPC_NOMOD) interest(3) } field(HYST,DBF_DOUBLE) { prompt("Alarm Deadband") promptgroup("70 - Alarm") interest(1) } field(ADEL,DBF_DOUBLE) { prompt("Archive Deadband") promptgroup("80 - Display") interest(1) } field(MDEL,DBF_DOUBLE) { prompt("Monitor Deadband") promptgroup("80 - Display") interest(1) } field(A,DBF_DOUBLE) { prompt("Value of Input A") pp(TRUE) } field(B,DBF_DOUBLE) { prompt("Value of Input B") pp(TRUE) } field(C,DBF_DOUBLE) { prompt("Value of Input C") pp(TRUE) } field(D,DBF_DOUBLE) { prompt("Value of Input D") pp(TRUE) } field(E,DBF_DOUBLE) { prompt("Value of Input E") pp(TRUE) } field(F,DBF_DOUBLE) { prompt("Value of Input F") pp(TRUE) } field(G,DBF_DOUBLE) { prompt("Value of Input G") pp(TRUE) } field(H,DBF_DOUBLE) { prompt("Value of Input H") pp(TRUE) } field(I,DBF_DOUBLE) { prompt("Value of Input I") pp(TRUE) } field(J,DBF_DOUBLE) { prompt("Value of Input J") pp(TRUE) } field(K,DBF_DOUBLE) { prompt("Value of Input K") pp(TRUE) } field(L,DBF_DOUBLE) { prompt("Value of Input L") pp(TRUE) } field(LA,DBF_DOUBLE) { prompt("Prev Value of A") special(SPC_NOMOD) interest(3) } field(LB,DBF_DOUBLE) { prompt("Prev Value of B") special(SPC_NOMOD) interest(3) } field(LC,DBF_DOUBLE) { prompt("Prev Value of C") special(SPC_NOMOD) interest(3) } field(LD,DBF_DOUBLE) { prompt("Prev Value of D") special(SPC_NOMOD) interest(3) } field(LE,DBF_DOUBLE) { prompt("Prev Value of E") special(SPC_NOMOD) interest(3) } field(LF,DBF_DOUBLE) { prompt("Prev Value of F") special(SPC_NOMOD) interest(3) } field(LG,DBF_DOUBLE) { prompt("Prev Value of G") special(SPC_NOMOD) interest(3) } field(LH,DBF_DOUBLE) { prompt("Prev Value of H") special(SPC_NOMOD) interest(3) } field(LI,DBF_DOUBLE) { prompt("Prev Value of I") special(SPC_NOMOD) interest(3) } field(LJ,DBF_DOUBLE) { prompt("Prev Value of J") special(SPC_NOMOD) interest(3) } field(LK,DBF_DOUBLE) { prompt("Prev Value of K") special(SPC_NOMOD) interest(3) } field(LL,DBF_DOUBLE) { prompt("Prev Value of L") special(SPC_NOMOD) interest(3) } field(LALM,DBF_DOUBLE) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(ALST,DBF_DOUBLE) { prompt("Last Value Archived") special(SPC_NOMOD) interest(3) } field(MLST,DBF_DOUBLE) { prompt("Last Val Monitored") special(SPC_NOMOD) interest(3) } %#include "postfix.h" field(RPCL,DBF_NOACCESS) { prompt("Reverse Polish Calc") special(SPC_NOMOD) interest(4) extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") } =head2 Record Support =head3 Record Support Routines =head2 C For each constant input link, the corresponding value field is initialized with the constant value if the input link is CONSTANT or a channel access link is created if the input link is a PV_LINK. A routine postfix is called to convert the infix expression in CALC to Reverse Polish Notation. The result is stored in RPCL. =head2 C See next section. =head2 C This is called if CALC is changed. C calls postfix. =head2 C Retrieves EGU. =head2 C Retrieves PREC. =head2 C Sets the upper display and lower display limits for a field. If the field is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field will be used. =head2 C Sets the upper control and the lower control limits for a field. If the field is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. =head2 C Sets the following values: =over 1 upper_alarm_limit = HIHI upper_warning_limit = HIGH lower_warning_limit = LOW lower_alarm_limit = LOLO =back =head3 Record Processing Routine process implements the following algorithm: =over 1 =item 1. Fetch all arguments. =item 2. Call routine C, which calculates VAL from the postfix version of the expression given in CALC. If C returns success UDF is set to FALSE. =item 3. Check alarms. This routine checks to see if the new VAL causes the alarm status and severity to change. If so, NSEV, NSTA, and LALM are set. It also honors the alarm hysteresis factor (HYST). Thus the value must change by at least HYST before the alarm status and severity changes. =item 4. Check to see if monitors should be invoked. =back =over 1 =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and values change monitors are invoked if ADEL and MDEL conditions are met. =item * Monitors for A-L are checked whenever other monitors are invoked. =item * NSEV and NSTA are reset to 0. =back =over =item 5. Scan forward link if necessary, set PACT FALSE, and return. =back =cut } base-7.0.3.1/modules/database/src/std/rec/calcoutRecord.c0000664000577000060420000005637413557101274021735 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* calcout.c - Record Support Routines for calc with output records */ /* * Author : Ned Arnold * Based on recCalc.c by Julie Sander and Bob Dalesio * Date: 7-27-87 */ #include #include #include #include #include #include #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbLink.h" #include "dbScan.h" #include "cantProceed.h" #include "epicsMath.h" #include "errMdef.h" #include "errlog.h" #include "recSup.h" #include "devSup.h" #include "recGbl.h" #include "special.h" #include "callback.h" #include "taskwd.h" #include "menuIvoa.h" #define GEN_SIZE_OFFSET #include "calcoutRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL static long get_units(DBADDR *, char *); static long get_precision(const DBADDR *, long *); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *, struct dbr_grDouble *); static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); static long get_alarm_double(DBADDR *, struct dbr_alDouble *); rset calcoutRSET = { RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset, calcoutRSET); int calcoutODLYprecision = 2; epicsExportAddress(int, calcoutODLYprecision); double calcoutODLYlimit = 100000; epicsExportAddress(double, calcoutODLYlimit); typedef struct calcoutDSET { long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write; }calcoutDSET; /* To provide feedback to the user as to the connection status of the * links (.INxV and .OUTV), the following algorithm has been implemented ... * * A new PV_LINK is checked [in both init() and special()] to see if the * target is local -- if so it is marked as such. If not, a checkLinkCb * callback is scheduled to check the connection status later by calling * dbIsLinkConnected(). Anytime there are unconnected CA_LINKs, another * callback is scheduled. Once all connections are established, the CA_LINKs * are checked whenever the record processes. * */ #define NO_CA_LINKS 0 #define CA_LINKS_ALL_OK 1 #define CA_LINKS_NOT_OK 2 typedef struct rpvtStruct { epicsCallback doOutCb; epicsCallback checkLinkCb; short cbScheduled; short caLinkStat; /* NO_CA_LINKS, CA_LINKS_ALL_OK, CA_LINKS_NOT_OK */ } rpvtStruct; static void checkAlarms(calcoutRecord *prec); static void monitor(calcoutRecord *prec); static int fetch_values(calcoutRecord *prec); static void execOutput(calcoutRecord *prec); static void checkLinks(calcoutRecord *prec); static void checkLinksCallback(epicsCallback *arg); static long writeValue(calcoutRecord *prec); int calcoutRecDebug; static long init_record(struct dbCommon *pcommon, int pass) { struct calcoutRecord *prec = (struct calcoutRecord *)pcommon; DBLINK *plink; int i; double *pvalue; epicsEnum16 *plinkValid; short error_number; calcoutDSET *pcalcoutDSET; rpvtStruct *prpvt; if (pass == 0) { prec->rpvt = (rpvtStruct *) callocMustSucceed(1, sizeof(rpvtStruct), "calcoutRecord"); return 0; } if (!(pcalcoutDSET = (calcoutDSET *)prec->dset)) { recGblRecordError(S_dev_noDSET, (void *)prec, "calcout:init_record"); return S_dev_noDSET; } /* must have write defined */ if ((pcalcoutDSET->number < 5) || (pcalcoutDSET->write ==NULL)) { recGblRecordError(S_dev_missingSup, (void *)prec, "calcout:init_record"); return S_dev_missingSup; } prpvt = prec->rpvt; plink = &prec->inpa; pvalue = &prec->a; plinkValid = &prec->inav; for (i = 0; i <= CALCPERFORM_NARGS; i++, plink++, pvalue++, plinkValid++) { /* Don't InitConstantLink the .OUT link */ if (i < CALCPERFORM_NARGS) { recGblInitConstantLink(plink, DBF_DOUBLE, pvalue); } if (dbLinkIsConstant(plink)) { *plinkValid = calcoutINAV_CON; } else if (dbLinkIsVolatile(plink)) { int conn = dbIsLinkConnected(plink); if (conn) *plinkValid = calcoutINAV_EXT; else { /* Monitor for connection */ *plinkValid = calcoutINAV_EXT_NC; prpvt->caLinkStat = CA_LINKS_NOT_OK; } } else { /* PV must reside on this ioc */ *plinkValid = calcoutINAV_LOC; if (!dbIsLinkConnected(plink)) { errlogPrintf("calcout: %s.INP%c in no-vo disco state\n", prec->name, i+'A'); } } } prec->clcv = postfix(prec->calc, prec->rpcl, &error_number); if (prec->clcv){ recGblRecordError(S_db_badField, (void *)prec, "calcout: init_record: Illegal CALC field"); errlogPrintf("%s.CALC: %s in expression \"%s\"\n", prec->name, calcErrorStr(error_number), prec->calc); } prec->oclv = postfix(prec->ocal, prec->orpc, &error_number); if (prec->dopt == calcoutDOPT_Use_OVAL && prec->oclv){ recGblRecordError(S_db_badField, (void *)prec, "calcout: init_record: Illegal OCAL field"); errlogPrintf("%s.OCAL: %s in expression \"%s\"\n", prec->name, calcErrorStr(error_number), prec->ocal); } prpvt = prec->rpvt; callbackSetCallback(checkLinksCallback, &prpvt->checkLinkCb); callbackSetPriority(0, &prpvt->checkLinkCb); callbackSetUser(prec, &prpvt->checkLinkCb); prpvt->cbScheduled = 0; prec->epvt = eventNameToHandle(prec->oevt); if (pcalcoutDSET->init_record) pcalcoutDSET->init_record(prec); prec->pval = prec->val; prec->mlst = prec->val; prec->alst = prec->val; prec->lalm = prec->val; prec->povl = prec->oval; return 0; } static long process(struct dbCommon *pcommon) { struct calcoutRecord *prec = (struct calcoutRecord *)pcommon; rpvtStruct *prpvt = prec->rpvt; int doOutput; if (!prec->pact) { prec->pact = TRUE; /* if some links are CA, check connections */ if (prpvt->caLinkStat != NO_CA_LINKS) { checkLinks(prec); } if (fetch_values(prec) == 0) { if (calcPerform(&prec->a, &prec->val, prec->rpcl)) { recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM); } else { prec->udf = isnan(prec->val); } } checkAlarms(prec); /* check for output link execution */ switch (prec->oopt) { case calcoutOOPT_Every_Time: doOutput = 1; break; case calcoutOOPT_On_Change: doOutput = ! (fabs(prec->pval - prec->val) <= prec->mdel); break; case calcoutOOPT_Transition_To_Zero: doOutput = ((prec->pval != 0.0) && (prec->val == 0.0)); break; case calcoutOOPT_Transition_To_Non_zero: doOutput = ((prec->pval == 0.0) && (prec->val != 0.0)); break; case calcoutOOPT_When_Zero: doOutput = (prec->val == 0.0); break; case calcoutOOPT_When_Non_zero: doOutput = (prec->val != 0.0); break; default: doOutput = 0; break; } prec->pval = prec->val; if (doOutput) { if (prec->odly > 0.0) { prec->dlya = 1; recGblGetTimeStamp(prec); db_post_events(prec, &prec->dlya, DBE_VALUE); callbackRequestProcessCallbackDelayed(&prpvt->doOutCb, prec->prio, prec, (double)prec->odly); return 0; } else { prec->pact = FALSE; execOutput(prec); if (prec->pact) return 0; prec->pact = TRUE; } } recGblGetTimeStamp(prec); } else { /* pact == TRUE */ if (prec->dlya) { prec->dlya = 0; recGblGetTimeStamp(prec); db_post_events(prec, &prec->dlya, DBE_VALUE); /* Make pact FALSE for asynchronous device support*/ prec->pact = FALSE; execOutput(prec); if (prec->pact) return 0; prec->pact = TRUE; } else {/*Device Support is asynchronous*/ writeValue(prec); recGblGetTimeStamp(prec); } } monitor(prec); recGblFwdLink(prec); prec->pact = FALSE; return 0; } static long special(DBADDR *paddr, int after) { calcoutRecord *prec = (calcoutRecord *)paddr->precord; rpvtStruct *prpvt = prec->rpvt; short error_number; int fieldIndex = dbGetFieldIndex(paddr); int lnkIndex; DBLINK *plink; double *pvalue; epicsEnum16 *plinkValid; if (!after) return 0; switch(fieldIndex) { case(calcoutRecordCALC): prec->clcv = postfix(prec->calc, prec->rpcl, &error_number); if (prec->clcv){ recGblRecordError(S_db_badField, (void *)prec, "calcout: special(): Illegal CALC field"); errlogPrintf("%s.CALC: %s in expression \"%s\"\n", prec->name, calcErrorStr(error_number), prec->calc); } db_post_events(prec, &prec->clcv, DBE_VALUE); return 0; case(calcoutRecordOCAL): prec->oclv = postfix(prec->ocal, prec->orpc, &error_number); if (prec->dopt == calcoutDOPT_Use_OVAL && prec->oclv){ recGblRecordError(S_db_badField, (void *)prec, "calcout: special(): Illegal OCAL field"); errlogPrintf("%s.OCAL: %s in expression \"%s\"\n", prec->name, calcErrorStr(error_number), prec->ocal); } db_post_events(prec, &prec->oclv, DBE_VALUE); return 0; case(calcoutRecordINPA): case(calcoutRecordINPB): case(calcoutRecordINPC): case(calcoutRecordINPD): case(calcoutRecordINPE): case(calcoutRecordINPF): case(calcoutRecordINPG): case(calcoutRecordINPH): case(calcoutRecordINPI): case(calcoutRecordINPJ): case(calcoutRecordINPK): case(calcoutRecordINPL): case(calcoutRecordOUT): lnkIndex = fieldIndex - calcoutRecordINPA; plink = &prec->inpa + lnkIndex; pvalue = &prec->a + lnkIndex; plinkValid = &prec->inav + lnkIndex; if (fieldIndex != calcoutRecordOUT) recGblInitConstantLink(plink, DBF_DOUBLE, pvalue); if (dbLinkIsConstant(plink)) { db_post_events(prec, pvalue, DBE_VALUE); *plinkValid = calcoutINAV_CON; } else if (dbLinkIsVolatile(plink)) { int conn = dbIsLinkConnected(plink); if (conn) *plinkValid = calcoutINAV_EXT; else { /* Monitor for connection */ *plinkValid = calcoutINAV_EXT_NC; /* DO_CALLBACK, if not already scheduled */ if (!prpvt->cbScheduled) { callbackRequestDelayed(&prpvt->checkLinkCb, .5); prpvt->cbScheduled = 1; prpvt->caLinkStat = CA_LINKS_NOT_OK; } } } else { /* PV must reside on this ioc */ *plinkValid = calcoutINAV_LOC; if (!dbIsLinkConnected(plink)) { errlogPrintf("calcout: %s.INP%c in no-vo diso state\n", prec->name, lnkIndex); } } db_post_events(prec, plinkValid, DBE_VALUE); return 0; case(calcoutRecordOEVT): prec->epvt = eventNameToHandle(prec->oevt); return 0; default: recGblDbaddrError(S_db_badChoice, paddr, "calc: special"); return(S_db_badChoice); } } #define indexof(field) calcoutRecord##field static long get_linkNumber(int fieldIndex) { if (fieldIndex >= indexof(A) && fieldIndex <= indexof(L)) return fieldIndex - indexof(A); if (fieldIndex >= indexof(LA) && fieldIndex <= indexof(LL)) return fieldIndex - indexof(LA); return -1; } static long get_units(DBADDR *paddr, char *units) { calcoutRecord *prec = (calcoutRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); int linkNumber; if(fieldIndex == indexof(ODLY)) { strcpy(units, "s"); return 0; } if(paddr->pfldDes->field_type == DBF_DOUBLE) { linkNumber = get_linkNumber(dbGetFieldIndex(paddr)); if (linkNumber >= 0) dbGetUnits(&prec->inpa + linkNumber, units, DB_UNITS_SIZE); else strncpy(units,prec->egu,DB_UNITS_SIZE); } return 0; } static long get_precision(const DBADDR *paddr, long *pprecision) { calcoutRecord *prec = (calcoutRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); int linkNumber; if (fieldIndex == indexof(ODLY)) { *pprecision = calcoutODLYprecision; return 0; } *pprecision = prec->prec; if (fieldIndex == indexof(VAL)) return 0; linkNumber = get_linkNumber(fieldIndex); if (linkNumber >= 0) { short precision; if (dbGetPrecision(&prec->inpa + linkNumber, &precision) == 0) *pprecision = precision; } else recGblGetPrec(paddr, pprecision); return 0; } static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) { calcoutRecord *prec = (calcoutRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); int linkNumber; switch (fieldIndex) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): pgd->lower_disp_limit = prec->lopr; pgd->upper_disp_limit = prec->hopr; break; case indexof(ODLY): recGblGetGraphicDouble(paddr,pgd); pgd->lower_disp_limit = 0.0; break; default: linkNumber = get_linkNumber(fieldIndex); if (linkNumber >= 0) { dbGetGraphicLimits(&prec->inpa + linkNumber, &pgd->lower_disp_limit, &pgd->upper_disp_limit); } else recGblGetGraphicDouble(paddr,pgd); } return 0; } static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) { calcoutRecord *prec = (calcoutRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): pcd->lower_ctrl_limit = prec->lopr; pcd->upper_ctrl_limit = prec->hopr; break; case indexof(ODLY): pcd->lower_ctrl_limit = 0.0; pcd->upper_ctrl_limit = calcoutODLYlimit; break; default: recGblGetControlDouble(paddr,pcd); } return 0; } static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) { calcoutRecord *prec = (calcoutRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); int linkNumber; if (fieldIndex == indexof(VAL)) { pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN; pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN; pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN; pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN; } else { linkNumber = get_linkNumber(fieldIndex); if (linkNumber >= 0) { dbGetAlarmLimits(&prec->inpa + linkNumber, &pad->lower_alarm_limit, &pad->lower_warning_limit, &pad->upper_warning_limit, &pad->upper_alarm_limit); } else recGblGetAlarmDouble(paddr, pad); } return 0; } static void checkAlarms(calcoutRecord *prec) { double val, hyst, lalm; double alev; epicsEnum16 asev; if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, prec->udfs); return; } val = prec->val; hyst = prec->hyst; lalm = prec->lalm; /* alarm condition hihi */ asev = prec->hhsv; alev = prec->hihi; if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { if (recGblSetSevr(prec, HIHI_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition lolo */ asev = prec->llsv; alev = prec->lolo; if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { if (recGblSetSevr(prec, LOLO_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition high */ asev = prec->hsv; alev = prec->high; if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { if (recGblSetSevr(prec, HIGH_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition low */ asev = prec->lsv; alev = prec->low; if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { if (recGblSetSevr(prec, LOW_ALARM, asev)) prec->lalm = alev; return; } /* we get here only if val is out of alarm by at least hyst */ prec->lalm = val; return; } static void execOutput(calcoutRecord *prec) { /* Determine output data */ switch(prec->dopt) { case calcoutDOPT_Use_VAL: prec->oval = prec->val; break; case calcoutDOPT_Use_OVAL: if (calcPerform(&prec->a, &prec->oval, prec->orpc)) { recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM); } else { prec->udf = isnan(prec->oval); } break; } if (prec->udf){ recGblSetSevr(prec, UDF_ALARM, prec->udfs); } /* Check to see what to do if INVALID */ if (prec->nsev < INVALID_ALARM ) { /* Output the value */ writeValue(prec); /* post output event if set */ if (prec->epvt) postEvent(prec->epvt); } else switch (prec->ivoa) { case menuIvoaContinue_normally: writeValue(prec); /* post output event if set */ if (prec->epvt) postEvent(prec->epvt); break; case menuIvoaDon_t_drive_outputs: break; case menuIvoaSet_output_to_IVOV: prec->oval = prec->ivov; writeValue(prec); /* post output event if set */ if (prec->epvt) postEvent(prec->epvt); break; default: recGblRecordError(S_db_badField, (void *)prec, "calcout:process Illegal IVOA field"); } } static void monitor(calcoutRecord *prec) { unsigned monitor_mask; double *pnew; double *pprev; int i; monitor_mask = recGblResetAlarms(prec); /* check for value change */ recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE); /* check for archive change */ recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE); /* send out monitors connected to the value field */ if (monitor_mask){ db_post_events(prec, &prec->val, monitor_mask); } /* check all input fields for changes*/ for (i = 0, pnew = &prec->a, pprev = &prec->la; ipovl != prec->oval) { db_post_events(prec, &prec->oval, monitor_mask|DBE_VALUE|DBE_LOG); prec->povl = prec->oval; } return; } static int fetch_values(calcoutRecord *prec) { DBLINK *plink; /* structure of the link field */ double *pvalue; long status = 0; int i; for (i = 0, plink = &prec->inpa, pvalue = &prec->a; irpvt; dbScanLock((dbCommon *)prec); prpvt->cbScheduled = 0; checkLinks(prec); dbScanUnlock((dbCommon *)prec); } static void checkLinks(calcoutRecord *prec) { DBLINK *plink; rpvtStruct *prpvt = prec->rpvt; int i; int stat; int caLink = 0; int caLinkNc = 0; epicsEnum16 *plinkValid; if (calcoutRecDebug) printf("checkLinks() for %p\n", prec); plink = &prec->inpa; plinkValid = &prec->inav; for (i = 0; icaLinkStat = CA_LINKS_NOT_OK; else if (caLink) prpvt->caLinkStat = CA_LINKS_ALL_OK; else prpvt->caLinkStat = NO_CA_LINKS; if (!prpvt->cbScheduled && caLinkNc) { /* Schedule another epicsCallback */ prpvt->cbScheduled = 1; callbackRequestDelayed(&prpvt->checkLinkCb, .5); } } static long writeValue(calcoutRecord *prec) { calcoutDSET *pcalcoutDSET = (calcoutDSET *)prec->dset; if (!pcalcoutDSET || !pcalcoutDSET->write) { errlogPrintf("%s DSET write does not exist\n", prec->name); recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); prec->pact = TRUE; return(-1); } return pcalcoutDSET->write(prec); } base-7.0.3.1/modules/database/src/std/rec/calcoutRecord.dbd.pod0000664000577000060420000007121613557101274023015 0ustar anjaesctl#************************************************************************* # Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Calculation Output Record (calcout) The Calculation Output or "Calcout" record is similar to the Calc record with the added feature of having outputs (an "output link" and an "output event") which are conditionally executed based on the result of the calculation. This feature allows conditional branching to be implemented within an EPICS database (e.g. process Record_A only if Record_B has a value of 0). The Calcout record is also similar to the Wait record (with additional features) but uses EPICS standard INLINK and OUTLINK fields rather than the DBF_STRING fields used in the Wait record. For new databases, it is recommended that the Calcout record be used instead of the Wait record. =head2 Parameter Fields The fields in this record fall into these categories: =over 1 =item * scan parameters =item * read parameters =item * expression parameters =item * output parameters =item * operator display parameters =item * alarm parameters =item * monitor parameters =item * run-time parameters =back =recordtype calcout =cut menu(calcoutOOPT) { choice(calcoutOOPT_Every_Time,"Every Time") choice(calcoutOOPT_On_Change,"On Change") choice(calcoutOOPT_When_Zero,"When Zero") choice(calcoutOOPT_When_Non_zero,"When Non-zero") choice(calcoutOOPT_Transition_To_Zero,"Transition To Zero") choice(calcoutOOPT_Transition_To_Non_zero,"Transition To Non-zero") } menu(calcoutDOPT) { choice(calcoutDOPT_Use_VAL,"Use CALC") choice(calcoutDOPT_Use_OVAL,"Use OCAL") } menu(calcoutINAV) { choice(calcoutINAV_EXT_NC,"Ext PV NC") choice(calcoutINAV_EXT,"Ext PV OK") choice(calcoutINAV_LOC,"Local PV") choice(calcoutINAV_CON,"Constant") } recordtype(calcout) { =head3 Scan Parameters The Calcout record has the standard fields for specifying under what circumstances the record will be processed. The fields are listed in L. In addition, L explains how these fields are used. Since the Calcout record supports no direct interfaces to hardware, it cannot be scanned on I/O interrupt, so its SCAN field cannot be C. =head3 Read Parameters The read parameters for the Calcout record consists of 12 input links INPA, INPB, ... INPL. The fields can be database links, channel access links, or constants. If they are links, they must specify another record's field. If they are constants, they will be initialized with the value they are configured with and can be changed via C. These fields cannot be hardware addresses. In addition, the Calcout record contains the INAV, INBV, ... INLV fields which indicate the status of the link fields, for example, whether or not the specified PV was found and a link to it established. See L for an explanation of these fields. See L
for information on how to specify database links. =fields INPA, INPB, INPC, INPD, INPE, INPF, INPG, INPH, INPI, INPJ, INPK, INPL =head3 Expression Like the Calc record, the Calcout record has a CALC field in which the developer can enter an infix expression which the record routine will evaluate when it processes the record. The resulting value is placed in the VAL field. This value can then be used by the OOPT field (see L) to determine whether or not to write to the output link or post an output event. It can also be the value that is written to the output link. The CALC expression is actually converted to opcode and stored in Reverse Polish Notation in the RPCL field. It is this expression which is actually used to calculate VAL. The Reverse Polish expression is evaluated more efficiently during run-time than an infix expression. CALC can be changes at run-time, and a special record routine will call a function to convert it to Reverse Polish Notation. The infix expressions that can be used are very similar to the C expression syntax, but with some additions and subtle differences in operator meaning and precedence. The string may contain a series of expressions separated by a semi-colon character ';' any one of which may actually provide the calculation result; however all of the other expressions included must assign their result to a variable. All alphabetic elements described below are case independent, so upper and lower case letters may be used and mixed in the variable and function names as desired. Spaces may be used anywhere within an expression except between the characters that make up a single expression element. The range of expressions supported by the calculation record are separated into literals, constants, operands, algebraic operators, trigonometric operators, relational operators, logical operator, the assignment operator, parentheses and commas, and the question mark or '?:' operator. =fields CALC, VAL, RPCL =head3 Literals =over 1 =item * Standard double precision floating point numbers =item * Inf: Infinity =item * Nan: Not a Number =back =head3 Constants =over =item * PI: returns the mathematical constant E =item * D2R: evaluates to E/180 which, when used as a multiplier, converts an angle from degrees to radians =item * R2D: evaluates to 180/E which, when used as a multiplier, converts an angle from radians to degrees =back =head3 Operands The expression can use the values retrieved from the INPx links as operands, though constants can be used as operands too. These values retrieved from the input links are stored in the A-L fields. The values to be used in the expression are simple references by the field letter. For instance, the value obtained from the INPA link is stored in field A, and the values obtained from the INPB link is stored in the field B. The names can be included in the expression will operate on their respective values, as in A+B. =fields A, B, C, D, E, F, G, H, I, J, K, L The keyword VAL returns the current contents of the expression's result field, i.e. the VAL field for the CALC expression and the OVAL field for the OCAL expression. (These fields can be written to by CA put, so it might I be the result from the last time the expression was evaluated). =head3 Algebraic Operations =over 1 =item * ABS: Absolute value (unary) =item * SQR: Square root (unary) =item * MIN: Minimum (any number of args) =item * MAX: Maximum (any number of args) =item * FINITE: returns non-zero if none of the arguments are NaN or Inf (any number of args) =item * ISNAN: returns non-zero if any of the arguments is NaN or Inf (any number of args) =item * CEIL: Ceiling (unary) =item * FLOOR: Floor (unary) =item * LOG: Log base 10 (unary) =item * LOGE: Natural log (unary) =item * LN: Natural log (unary) =item * EXP: Exponential function (unary) =item * ^ : Exponential (binary) =item * ** : Exponential (binary) =item * + : Addition (binary) =item * - : Subtraction (binary) =item * * : Multiplication (binary) =item * / : Division (binary) =item * % : Modulo (binary) =item * NOT: Negate (unary) =back =head3 Trigonometric Operators =over 1 =item * SIN: Sine =item * SINH: Hyperbolic sine =item * ASIN: Arc sine =item * COS: Cosine =item * COSH: Hyperbolic cosine =item * ACOS: Arc cosine =item * TAN: Tangent =item * TANH: Hyperbolic tangent =item * ATAN: Arc tangent =back =head3 Relational Operators =over 1 =item * C<<< >= >>> : Greater than or equal to =item * C<<< > >>> : Greater than =item * C<<< <= >>> : Less than or equal to =item * C<<< < >>> : Less than =item * C<<< # >>> : Not equal to =item * C<<< = >>> : Equal to =back =head3 Logical Operators =over 1 =item * && : And =item * || : Or =item * ! : Not =back =head3 Bitwise Operators =over 1 =item * C<|> : Bitwise Or =item * C<&> : Bitwise And =item * OR : Bitwise Or =item * AND : Bitwise And =item * XOR : Bitwise Exclusive Or =item * C<~> : One's Complement =item * C<<< << >>> : Left shift =item * C<<< >> >>> : Right shift =back =head3 Assignment Operator =over 1 =item * C<:=> : assigns a value (right hand side) to a variable (i.e. field) =back =head3 Parantheses, Comma, and Semicolon The open and close parentheses are supported. Nested parentheses are supported. The comma is supported when used to separate the arguments of a binary function. The semicolon is used to separate expressions. Although only one traditional calculation expression is allowed, multiple assignment expressions are allowed. =head3 Conditional Expression The C language's question mark operator is supported. The format is: C =head3 Expression Examples =head3 Algebraic C =over 1 =item * Result is C =back =head3 Relational C<<< (A + B) < (C + D) >>> =over 1 =item * Result is 1 if C<<< (A + B) < (C + D) >>> =item * Result is 0 if C<<< (A + B) >= (C + D) >>> =back =head3 Question Mark C<<< (A + B) < (C + D) ? E : F + L + 10 >>> =over 1 =item * Result is C if C<<< (A + B) < (C + D) >>> =item * Result is C if C<<< (A + B) >= (C + D) >>> =back Prior to Base 3.14.9 it was legal to omit the : and the second (else) part of the conditional, like this: C<(A + B)<(C + D) ? E> =over 1 =item Result is E if (A + B)<(C + D) =item Result is unchanged if (A + B)>=(C + D) From 3.14.9 onwards, this expresion must be written as C<(A + B) < (C + D) ? E : VAL> =back =head3 Logical C =over 1 =item * Causes the following to occur: =over 1 =item * Convert A to integer =item * Convert B to integer =item * Bitwise And A and B =item * Convert result to floating point =back =back =head3 Assignment C =over 1 =item * Causes the Calc record to output the successive values of a sine curve in 1 degree intervals. =back =head3 Output Parameters These parameters specify and control the output capabilities of the Calcout record. They determine when to write the output, where to write it, and what the output will be. The OUT link specifies the Process Variable to which the result will be written. =head4 Menu calcoutOOPT The OOPT field determines the condition that causes the output link to be written to. It's a menu field that has six choices: =menu calcoutOOPT =over =item * C -- write output every time record is processed. =item * C -- write output every time VAL changes, i.e., every time the result of the expression changes. =item * C -- when record is preocessed, write output if VAL is zero. =item * C -- when record is processed, write output if VAL is non-zero. =item * C -- when record is processed, write output only if VAL is zero and the last value was non-zero. =item * C -- when record is processed, write output only if VAL is non-zero and last value was zero. =back =head4 Menu calcoutDOPT The DOPT field determines what data is written to the output link when the output is executed. The field is a menu field with two options: =menu calcoutDOPT If C is specified, when the record writes its output it will write the result of the expression in the CALC field, that is, it will write the value of the VAL field. If C is specified, the record will instead write the result of the expresion in the OCAL field, which is contained in the OVAL field. The OCAL field is exactly like the CALC field and has the same fuctionality it can contain the string representation of an expression which is evaluated at run-time. Thus, if necessary, the record can use the result of the CALC expression to determine if data should be written and can use the result of the OCAL expression as the data to write. If the OEVT field specifies a non-zero integer and the condition in the OOPT field is met, the record will post a corresponding event. If the ODLY field is non-zero, the record pauses for the specified number of seconds before executing the OUT link or posting the output event. During this waiting period the record is "active" and will not be processed again until the wait is over. The field DLYA is equal to 1 during the delay period. The resolution of the delay entry system dependent. The IVOA field specifies what action to take with the OUT link if the Calcout record eneters an INVALID alarm status. The options are C, C, and C. If the IVOA field is C, the data entered into the IVOV field is written to the OUT link if the record alarm severity is INVALID. =fields OUT, OOPT, DOPT, OCAL, OVAL, OEVT, ODLY, IVOA, IVOV =head3 Operator Display Parameter These parameters are used to present meaningful data to the operator. Some are also meant to represent the status of the record at run-time. The EGU field contains a string of up to 16 characters which is supplied by the user and which describes the values being operated upon. The string is retrieved whenever the routine C is called. The EGU string is solely for an operator's sake and does not have to be used. The HOPR and LOPR fields on;y refer to the limits if the VAL, HIHI, HIGH, LOW, and LOLO fields. PREC controls the precision of the VAL field. =head4 Menu calcoutINAV The INAV-INLV fields indicate the status of the link to the PVs specified in the INPA-INPL fields, respectfully. These field can have four possible values: =menu calcoutINAV =over 1 =item * C -- the PV wasn't found on this IOC and a Channel Access link hasn't been established. =item * C -- the PV wasn't found on this IOC and a Channel Access link has been established. =item * C -- the PV was found on this IOC. =item * C -- the corresponding link field is a constant. =back The OUTV field indicates the status of the OUT link. If has the same possible values as the INAV-INLV fields. The CLCV and OLCV fields indicate the validity of the expression in the CALC and OCAL fields respectfully. If the expression in invalid, the field is set to one. The DYLA field is set to one during the delay specified in ODLY. See L for more information on the record name (NAME) and description (DESC) fields. =fields EGU, PREC, HOPR, LOPR, INAV, INBV, INCV, INDV, INEV, INFV, INGV, INHV, INIV, INJV, INKV, INLV, OUTV, CLCV, OCLV, DLYA, NAME, DESC =head3 Alarm Parameters The possible alarm conditions for the Calcout record are the SCAN, READ, Calculation, and limit alarms. The SCAN and READ alarms are called by the record support routines. The Calculation alarm is called by the record processing routine when the CALC expression is an invalid one, upon which an error message is generated. The following alarm parametersi, which are configured by the user, define the limit alarms for the VAL field and the severity corresponding to those conditions. The HYST field defines an alarm deadband for each limit. See L for a complete explanation of alarms and these fields. C lists other fields related to alarms that are common to all record types. =fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST =head3 Monitor Parameters These parameters are used to determine when to send monitors for the value fields. These monitors are sent when the value field exceeds the last monitored field by the appropriate deadband, the ADEL for archiver monitors and the MDEL field for all aother types of monitors. If these fields have a value of zero, every time the value changes, monitors are triggered; if they have a value of -1, every time the record is scanned, monitors are triggered. See L for a complete explanation of monitors. =fields ADEL, MDEL =head3 Run-time Parameters These fields are not configurable using a configuration tool and none are modifiable at run-time. They are used to process the record. The LALM field is used to implement the hysteresis factor for the alarm limits. The LA-LL fields are used to decide when to trigger monitors for the corresponding fields. For instance, if LA does not equal the value for A, monitors for A are triggered. The MLST and ALST fields are used in the same manner for the VAL field. =fields LALM, ALST, MLST, LA, LB, LC, LD, LE, LF, LG, LH, LI, LJ, LK, LL =cut include "dbCommon.dbd" field(RPVT,DBF_NOACCESS) { prompt("Record Private") special(SPC_NOMOD) interest(4) extra("struct rpvtStruct *rpvt") } field(VAL,DBF_DOUBLE) { prompt("Result") promptgroup("50 - Output") asl(ASL0) } field(PVAL,DBF_DOUBLE) { prompt("Previous Value") } field(CALC,DBF_STRING) { prompt("Calculation") promptgroup("30 - Action") special(SPC_CALC) pp(TRUE) size(80) initial("0") } field(CLCV,DBF_LONG) { prompt("CALC Valid") interest(1) } field(INPA,DBF_INLINK) { prompt("Input A") special(SPC_MOD) promptgroup("41 - Input A-F") interest(1) } field(INPB,DBF_INLINK) { prompt("Input B") special(SPC_MOD) promptgroup("41 - Input A-F") interest(1) } field(INPC,DBF_INLINK) { prompt("Input C") special(SPC_MOD) promptgroup("41 - Input A-F") interest(1) } field(INPD,DBF_INLINK) { prompt("Input D") special(SPC_MOD) promptgroup("41 - Input A-F") interest(1) } field(INPE,DBF_INLINK) { prompt("Input E") special(SPC_MOD) promptgroup("41 - Input A-F") interest(1) } field(INPF,DBF_INLINK) { prompt("Input F") special(SPC_MOD) promptgroup("41 - Input A-F") interest(1) } field(INPG,DBF_INLINK) { prompt("Input G") special(SPC_MOD) promptgroup("42 - Input G-L") interest(1) } field(INPH,DBF_INLINK) { prompt("Input H") special(SPC_MOD) promptgroup("42 - Input G-L") interest(1) } field(INPI,DBF_INLINK) { prompt("Input I") special(SPC_MOD) promptgroup("42 - Input G-L") interest(1) } field(INPJ,DBF_INLINK) { prompt("Input J") special(SPC_MOD) promptgroup("42 - Input G-L") interest(1) } field(INPK,DBF_INLINK) { prompt("Input K") special(SPC_MOD) promptgroup("42 - Input G-L") interest(1) } field(INPL,DBF_INLINK) { prompt("Input L") special(SPC_MOD) promptgroup("42 - Input G-L") interest(1) } field(OUT,DBF_OUTLINK) { prompt("Output Specification") special(SPC_MOD) promptgroup("50 - Output") interest(1) } field(INAV,DBF_MENU) { prompt("INPA PV Status") special(SPC_NOMOD) interest(1) menu(calcoutINAV) initial("1") } field(INBV,DBF_MENU) { prompt("INPB PV Status") special(SPC_NOMOD) interest(1) menu(calcoutINAV) initial("1") } field(INCV,DBF_MENU) { prompt("INPC PV Status") special(SPC_NOMOD) interest(1) menu(calcoutINAV) initial("1") } field(INDV,DBF_MENU) { prompt("INPD PV Status") special(SPC_NOMOD) interest(1) menu(calcoutINAV) initial("1") } field(INEV,DBF_MENU) { prompt("INPE PV Status") special(SPC_NOMOD) interest(1) menu(calcoutINAV) initial("1") } field(INFV,DBF_MENU) { prompt("INPF PV Status") special(SPC_NOMOD) interest(1) menu(calcoutINAV) initial("1") } field(INGV,DBF_MENU) { prompt("INPG PV Status") special(SPC_NOMOD) interest(1) menu(calcoutINAV) initial("1") } field(INHV,DBF_MENU) { prompt("INPH PV Status") special(SPC_NOMOD) interest(1) menu(calcoutINAV) initial("1") } field(INIV,DBF_MENU) { prompt("INPI PV Status") special(SPC_NOMOD) interest(1) menu(calcoutINAV) initial("1") } field(INJV,DBF_MENU) { prompt("INPJ PV Status") special(SPC_NOMOD) interest(1) menu(calcoutINAV) initial("1") } field(INKV,DBF_MENU) { prompt("INPK PV Status") special(SPC_NOMOD) interest(1) menu(calcoutINAV) initial("1") } field(INLV,DBF_MENU) { prompt("INPL PV Status") special(SPC_NOMOD) interest(1) menu(calcoutINAV) initial("1") } field(OUTV,DBF_MENU) { prompt("OUT PV Status") special(SPC_NOMOD) interest(1) menu(calcoutINAV) } field(OOPT,DBF_MENU) { prompt("Output Execute Opt") promptgroup("50 - Output") interest(1) menu(calcoutOOPT) } field(ODLY,DBF_DOUBLE) { prompt("Output Execute Delay") promptgroup("50 - Output") asl(ASL0) interest(1) } field(DLYA,DBF_USHORT) { prompt("Output Delay Active") special(SPC_NOMOD) asl(ASL0) } field(DOPT,DBF_MENU) { prompt("Output Data Opt") promptgroup("30 - Action") interest(1) menu(calcoutDOPT) } field(OCAL,DBF_STRING) { prompt("Output Calculation") promptgroup("30 - Action") special(SPC_CALC) pp(TRUE) size(80) initial("0") } field(OCLV,DBF_LONG) { prompt("OCAL Valid") interest(1) } field(OEVT,DBF_STRING) { prompt("Event To Issue") promptgroup("30 - Action") special(SPC_MOD) asl(ASL0) size(40) } %#include "dbScan.h" field(EPVT, DBF_NOACCESS) { prompt("Event private") special(SPC_NOMOD) interest(4) extra("EVENTPVT epvt") } field(IVOA,DBF_MENU) { prompt("INVALID output action") promptgroup("50 - Output") interest(2) menu(menuIvoa) } field(IVOV,DBF_DOUBLE) { prompt("INVALID output value") promptgroup("50 - Output") interest(2) } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) prop(YES) } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) prop(YES) } field(HOPR,DBF_DOUBLE) { prompt("High Operating Rng") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { prompt("Lolo Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(HSV,DBF_MENU) { prompt("High Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(LSV,DBF_MENU) { prompt("Low Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { prompt("Alarm Deadband") promptgroup("70 - Alarm") interest(1) } field(ADEL,DBF_DOUBLE) { prompt("Archive Deadband") promptgroup("80 - Display") interest(1) } field(MDEL,DBF_DOUBLE) { prompt("Monitor Deadband") promptgroup("80 - Display") interest(1) } field(A,DBF_DOUBLE) { prompt("Value of Input A") pp(TRUE) } field(B,DBF_DOUBLE) { prompt("Value of Input B") pp(TRUE) } field(C,DBF_DOUBLE) { prompt("Value of Input C") pp(TRUE) } field(D,DBF_DOUBLE) { prompt("Value of Input D") pp(TRUE) } field(E,DBF_DOUBLE) { prompt("Value of Input E") pp(TRUE) } field(F,DBF_DOUBLE) { prompt("Value of Input F") pp(TRUE) } field(G,DBF_DOUBLE) { prompt("Value of Input G") pp(TRUE) } field(H,DBF_DOUBLE) { prompt("Value of Input H") pp(TRUE) } field(I,DBF_DOUBLE) { prompt("Value of Input I") pp(TRUE) } field(J,DBF_DOUBLE) { prompt("Value of Input J") pp(TRUE) } field(K,DBF_DOUBLE) { prompt("Value of Input K") pp(TRUE) } field(L,DBF_DOUBLE) { prompt("Value of Input L") pp(TRUE) } field(OVAL,DBF_DOUBLE) { prompt("Output Value") asl(ASL0) } field(LA,DBF_DOUBLE) { prompt("Prev Value of A") special(SPC_NOMOD) interest(3) } field(LB,DBF_DOUBLE) { prompt("Prev Value of B") special(SPC_NOMOD) interest(3) } field(LC,DBF_DOUBLE) { prompt("Prev Value of C") special(SPC_NOMOD) interest(3) } field(LD,DBF_DOUBLE) { prompt("Prev Value of D") special(SPC_NOMOD) interest(3) } field(LE,DBF_DOUBLE) { prompt("Prev Value of E") special(SPC_NOMOD) interest(3) } field(LF,DBF_DOUBLE) { prompt("Prev Value of F") special(SPC_NOMOD) interest(3) } field(LG,DBF_DOUBLE) { prompt("Prev Value of G") special(SPC_NOMOD) interest(3) } field(LH,DBF_DOUBLE) { prompt("Prev Value of H") special(SPC_NOMOD) interest(3) } field(LI,DBF_DOUBLE) { prompt("Prev Value of I") special(SPC_NOMOD) interest(3) } field(LJ,DBF_DOUBLE) { prompt("Prev Value of J") special(SPC_NOMOD) interest(3) } field(LK,DBF_DOUBLE) { prompt("Prev Value of K") special(SPC_NOMOD) interest(3) } field(LL,DBF_DOUBLE) { prompt("Prev Value of L") special(SPC_NOMOD) interest(3) } field(POVL,DBF_DOUBLE) { prompt("Prev Value of OVAL") asl(ASL0) } field(LALM,DBF_DOUBLE) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(ALST,DBF_DOUBLE) { prompt("Last Value Archived") special(SPC_NOMOD) interest(3) } field(MLST,DBF_DOUBLE) { prompt("Last Val Monitored") special(SPC_NOMOD) interest(3) } %#include "postfix.h" field(RPCL,DBF_NOACCESS) { prompt("Reverse Polish Calc") special(SPC_NOMOD) interest(4) extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") } field(ORPC,DBF_NOACCESS) { prompt("Reverse Polish OCalc") special(SPC_NOMOD) interest(4) extra("char orpc[INFIX_TO_POSTFIX_SIZE(80)]") } =head2 Record Support =head3 Record Support Routines =head2 C For each constant input link, the corresponding value field is initialized with the constant value if the input link is CONSTANT or a channel access link is created if the input link is PV_LINK. A routine postfix is called to convert the infix expression in CALC and OCAL to Reverse Polish Notation. The result is stored in RPCL and ORPC, respectively. =head2 C See next section. =head2 C This is called id CALC or OCAL is changed. C calls postfix. =head2 C Retrieves EGU. =head2 C Retrieves PREC. =head2 C Sets the upper display and lower display limits for a field. If the field is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower macimum values for the field type will be used. =head2 C Sets the upper control and lower control limits for a field. If the VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defimed they will be used, else the upper and lower maximum values for the field will be used. =head2 C Sets the following values: =over upper_alarm_limit = HIHI upper_warning_limit = HIGH lower warning_limit = LOW lower_alarm_limit = LOLO =back =head3 Record Processing =head2 C The C routine implements the following algorithm: =over =item 1. Fetch all arguments. =item 2. Call routine C, which calculates VAL from the prefix version of the expression given in CALC. If C returns success, UDF is set to FALSE. =item 3. Check alarms. This routine checks to see if the new VAL causes the alarm status and severity to change. If so, NSEV, NSTA and LALM are set. If also honors the alarm hysteresis factor (HYST). Thus the value must change by at least HYST before the alarm status and severity changes. =item 4. Determin if the Output Execution Option (OOPT) is met. If it met, either execute the output link (and output event) immediately (if ODLY = 0), or schedule a callback after the specified interval. See the explanation for the C routine below. =item 5. Check to see if monitors should be invoked. =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if ADEL and MDEL conditions are met. =item * Monitors for A-L are checked whenever other monitors are invoked. =item * NSEV and NSTA are reset to 0 =back =item 6. If no output delay was specified, scan forwark link if necessaru, set PACT FALSE, and return. =back =head2 C =over =item 1. If DOPT field specifies the use of OCAL, call the routine C for the postfix version of the expression in OCAL. Otherwise, use VAL. =item 2. If the Alarm Severity is INVALID, follow the option as designated by the field IVOA. =item 3. The Alarm Severity is not INVALID or IVOA specifies "Continue Normally", put the value of OVAL to the OUT link and post the event in OEVT (if non-zero). =item 4. If an output delay was implemented, process the forwark link. =back =cut } variable(calcoutODLYprecision, int) variable(calcoutODLYlimit, double) base-7.0.3.1/modules/database/src/std/rec/compressRecord.c0000664000577000060420000003236113557101274022124 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Author: Bob Dalesio * Date: 7-14-89 */ #include #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" #include "dbStaticLib.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "errMdef.h" #include "special.h" #include "recSup.h" #include "recGbl.h" #define GEN_SIZE_OFFSET #include "compressRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" #define indexof(field) compressRecord##field /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL static long cvt_dbaddr(DBADDR *); static long get_array_info(DBADDR *, long *, long *); static long put_array_info(DBADDR *, long); static long get_units(DBADDR *, char *); static long get_precision(const DBADDR *, long *); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *, struct dbr_grDouble *); static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); #define get_alarm_double NULL rset compressRSET = { RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,compressRSET); static void reset(compressRecord *prec) { prec->nuse = 0; prec->off = 0; prec->inx = 0; prec->cvb = 0.0; prec->res = 0; /* allocate memory for the summing buffer for conversions requiring it */ if (prec->alg == compressALG_Average && prec->sptr == NULL) { prec->sptr = calloc(prec->nsam, sizeof(double)); } if (prec->bptr && prec->nsam) memset(prec->bptr, 0, prec->nsam * sizeof(double)); } static void monitor(compressRecord *prec) { unsigned short alarm_mask = recGblResetAlarms(prec); unsigned short monitor_mask = alarm_mask | DBE_LOG | DBE_VALUE; if (alarm_mask || prec->nuse != prec->ouse) { db_post_events(prec, &prec->nuse, monitor_mask); prec->ouse = prec->nuse; } db_post_events(prec, prec->bptr, monitor_mask); } static void put_value(compressRecord *prec, double *psource, int n) { int fifo = (prec->balg == bufferingALG_FIFO); epicsUInt32 offset = prec->off; epicsUInt32 nuse = prec->nuse; epicsUInt32 nsam = prec->nsam; nuse += n; if (nuse > nsam) nuse = nsam; while (n--) { /* for LIFO, pre-decrement modulo nsam */ if (!fifo) offset = (offset + nsam - 1) % nsam; prec->bptr[offset] = *psource++; /* for FIFO, post-increment modulo nsam */ if (fifo) offset = (offset + 1) % nsam; } prec->off = offset; prec->nuse = nuse; } /* qsort comparison function (for median calculation) */ static int compare(const void *arg1, const void *arg2) { double a = *(double *)arg1; double b = *(double *)arg2; if ( a < b ) return -1; else if ( a == b ) return 0; else return 1; } static int compress_array(compressRecord *prec, double *psource, int no_elements) { epicsInt32 i,j; epicsInt32 n, nnew; epicsInt32 nsam = prec->nsam; double value; /* skip out of limit data */ if (prec->ilil < prec->ihil) { while (((*psource < prec->ilil) || (*psource > prec->ihil)) && (no_elements > 0)) { no_elements--; psource++; } } if (prec->n <= 0) prec->n = 1; n = prec->n; if (no_elements < n) return 1; /*dont do anything*/ /* determine number of samples to take */ if (no_elements < nsam * n) nnew = (no_elements / n); else nnew = nsam; /* compress according to specified algorithm */ switch (prec->alg){ case compressALG_N_to_1_Low_Value: /* compress N to 1 keeping the lowest value */ for (i = 0; i < nnew; i++) { value = *psource++; for (j = 1; j < n; j++, psource++) { if (value > *psource) value = *psource; } put_value(prec, &value, 1); } break; case compressALG_N_to_1_High_Value: /* compress N to 1 keeping the highest value */ for (i = 0; i < nnew; i++){ value = *psource++; for (j = 1; j < n; j++, psource++) { if (value < *psource) value = *psource; } put_value(prec, &value, 1); } break; case compressALG_N_to_1_Average: /* compress N to 1 keeping the average value */ for (i = 0; i < nnew; i++) { value = 0; for (j = 0; j < n; j++, psource++) value += *psource; value /= n; put_value(prec, &value, 1); } break; case compressALG_N_to_1_Median: /* compress N to 1 keeping the median value */ /* note: sorts source array (OK; it's a work pointer) */ for (i = 0; i < nnew; i++, psource += nnew) { qsort(psource, n, sizeof(double), compare); value = psource[n / 2]; put_value(prec, &value, 1); } break; } return 0; } static int array_average(compressRecord *prec, double *psource, epicsInt32 no_elements) { epicsInt32 i; epicsInt32 nnow; epicsInt32 nsam=prec->nsam; double *psum; double multiplier; epicsInt32 inx = prec->inx; epicsInt32 nuse, n; nuse = nsam; if (nuse > no_elements) nuse = no_elements; nnow = nuse; if (nnow > no_elements) nnow=no_elements; psum = (double *)prec->sptr; /* add in the new waveform */ if (inx == 0) { for (i = 0; i < nnow; i++) *psum++ = *psource++; for (i = nnow; i < nuse; i++) *psum++ = 0; } else { for (i = 0; i < nnow; i++) *psum++ += *psource++; } /* do we need to calculate the result */ inx++; if (prec->n <= 0) prec->n = 1; n = prec->n; if (inx < n) { prec->inx = inx; return 1; } if (n > 1) { psum = (double *)prec->sptr; multiplier = 1.0 / n; for (i = 0; i < nuse; i++, psum++) *psum = *psum * multiplier; } put_value(prec, prec->sptr, nuse); prec->inx = 0; return 0; } static int compress_scalar(struct compressRecord *prec,double *psource) { double value = *psource; double *pdest=&prec->cvb; epicsInt32 inx = prec->inx; /* compress according to specified algorithm */ switch (prec->alg) { case (compressALG_N_to_1_Low_Value): if ((value < *pdest) || (inx == 0)) *pdest = value; break; case (compressALG_N_to_1_High_Value): if ((value > *pdest) || (inx == 0)) *pdest = value; break; /* for scalars, Median not implemented => use average */ case (compressALG_N_to_1_Average): case (compressALG_N_to_1_Median): if (inx == 0) *pdest = value; else { *pdest += value; if (inx + 1 >= prec->n) *pdest = *pdest / (inx + 1); } break; } inx++; if (inx >= prec->n) { put_value(prec,pdest,1); prec->inx = 0; return 0; } else { prec->inx = inx; return 1; } } /*Beginning of record support routines*/ static long init_record(struct dbCommon *pcommon, int pass) { struct compressRecord *prec = (struct compressRecord *)pcommon; if (pass == 0) { if (prec->nsam < 1) prec->nsam = 1; prec->bptr = calloc(prec->nsam, sizeof(double)); reset(prec); } return 0; } static long process(struct dbCommon *pcommon) { struct compressRecord *prec = (struct compressRecord *)pcommon; long status = 0; long nelements = 0; int alg = prec->alg; prec->pact = TRUE; if (!dbIsLinkConnected(&prec->inp) || dbGetNelements(&prec->inp, &nelements) || nelements <= 0) { recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM); } else { if (!prec->wptr || nelements != prec->inpn) { if (prec->wptr) { free(prec->wptr); reset(prec); } prec->wptr = dbCalloc(nelements, sizeof(double)); prec->inpn = nelements; } status = dbGetLink(&prec->inp, DBF_DOUBLE, prec->wptr, 0, &nelements); if (status || nelements <= 0) { recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM); status = 0; } else if (alg == compressALG_Average) { status = array_average(prec, prec->wptr, nelements); } else if (alg == compressALG_Circular_Buffer) { put_value(prec, prec->wptr, nelements); status = 0; } else if (nelements > 1) { status = compress_array(prec, prec->wptr, nelements); } else if (nelements == 1){ status = compress_scalar(prec, prec->wptr); } else status = 1; } /* check event list */ if (status != 1) { prec->udf = FALSE; recGblGetTimeStamp(prec); monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); } prec->pact = FALSE; return 0; } static long special(DBADDR *paddr, int after) { compressRecord *prec = (compressRecord *) paddr->precord; int special_type = paddr->special; if (!after) return 0; if (special_type == SPC_RESET) { reset(prec); return 0; } recGblDbaddrError(S_db_badChoice, paddr, "compress: special"); return S_db_badChoice; } static long cvt_dbaddr(DBADDR *paddr) { compressRecord *prec = (compressRecord *) paddr->precord; paddr->pfield = prec->bptr; paddr->no_elements = prec->nsam; paddr->field_type = DBF_DOUBLE; paddr->field_size = sizeof(double); paddr->dbr_field_type = DBF_DOUBLE; if (prec->balg == bufferingALG_LIFO) paddr->special = SPC_NOMOD; return 0; } static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) { /* offset indicates the next element which would be written. * In FIFO mode offset-1 is the last valid element * In LIFO mode offset is the first valid element * (*offset) should be set to the index of the first valid element */ compressRecord *prec = (compressRecord *) paddr->precord; epicsUInt32 off = prec->off; epicsUInt32 nuse = prec->nuse; if (prec->balg == bufferingALG_FIFO) { epicsUInt32 nsam = prec->nsam; off = (off + nsam - nuse) % nsam; } *no_elements = nuse; *offset = off; return 0; } static long put_array_info(DBADDR *paddr, long nNew) { compressRecord *prec = (compressRecord *) paddr->precord; epicsUInt32 nuse = prec->nuse; if (prec->balg == bufferingALG_FIFO) prec->off = (prec->off + nNew) % prec->nsam; prec->nuse += nNew; if (prec->nuse > prec->nsam) prec->nuse = prec->nsam; if (nuse != prec->nuse) db_post_events(prec, &prec->nuse, DBE_VALUE | DBE_LOG); return 0; } static long get_units(DBADDR *paddr, char *units) { compressRecord *prec = (compressRecord *) paddr->precord; if (paddr->pfldDes->field_type == DBF_DOUBLE || dbGetFieldIndex(paddr) == indexof(VAL)) { strncpy(units, prec->egu, DB_UNITS_SIZE); } return 0; } static long get_precision(const DBADDR *paddr, long *precision) { compressRecord *prec = (compressRecord *) paddr->precord; *precision = prec->prec; if (dbGetFieldIndex(paddr) != indexof(VAL)) recGblGetPrec(paddr,precision); return 0; } static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) { compressRecord *prec = (compressRecord *) paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(IHIL): case indexof(ILIL): pgd->upper_disp_limit = prec->hopr; pgd->lower_disp_limit = prec->lopr; break; default: recGblGetGraphicDouble(paddr,pgd); } return 0; } static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) { compressRecord *prec = (compressRecord *) paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(IHIL): case indexof(ILIL): pcd->upper_ctrl_limit = prec->hopr; pcd->lower_ctrl_limit = prec->lopr; break; default: recGblGetControlDouble(paddr, pcd); } return 0; } base-7.0.3.1/modules/database/src/std/rec/compressRecord.dbd.pod0000664000577000060420000003300713557101274023212 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Compression Record (compress) The data compression record is used to collect and compress data from arrays. When the INP field references a data array field, it immediately compresses the entire array into an element of an array using one of several algorithms, overwriting the previous element. If the INP field obtains its value from a scalar-value field, the compression record will collect a new sample each time the record is processed and add it to the compressed data array as a circular buffer. The INP link can also specify a constant; however, if this is the case, the compression algorithms are ignored, and the record support routines merely return after checking the FLNK field. =head2 Record-specific Menus =head3 Menu compressALG The ALG field which uses this menu controls the compression algorithm used by the record. =menu compressALG =head3 Menu bufferingALG The BALG field which uses this menu controls whether new values are inserted at the beginning or the end of the VAL array. =menu bufferingALG =head2 Parameter Fields The record-specific fields are described below. =recordtype compress ... =cut menu(compressALG) { choice(compressALG_N_to_1_Low_Value,"N to 1 Low Value") choice(compressALG_N_to_1_High_Value,"N to 1 High Value") choice(compressALG_N_to_1_Average,"N to 1 Average") choice(compressALG_Average,"Average") choice(compressALG_Circular_Buffer,"Circular Buffer") choice(compressALG_N_to_1_Median,"N to 1 Median") } menu(bufferingALG) { choice(bufferingALG_FIFO, "FIFO Buffer") choice(bufferingALG_LIFO, "LIFO Buffer") } recordtype(compress) { =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scanning Parameters The compression record has the standard fields for specifying under what circumstances the record will be processed. These fields are listed in L. In addition, L explains how these fields are used. Since the compression record supports no direct interfaces to hardware, its SCAN field cannot specify C<<< I/O Intr >>>. =head3 Algorithms and Related Parameters The user specifies the algorithm to be used in the ALG field. There are six possible algorithms which can be specified as follows: =head4 Menu compressALG =menu compressALG The following fields determine what channel to read and how to compress the data: =fields ALG, INP, NSAM, N, ILIL, IHIL, OFF, RES As stated above, the ALG field specifies which algorithm to be performed on the data. The INP should be a database or channel access link. Though INP can be a constant, the data compression algorithms are supported only when INP is a database link. See L
for information on specifying links. IHIL and ILIL can be set to provide an initial value filter on the input array. If ILIL E IHIL, the input elements will be skipped until a value is found that is in the range of ILIL to IHIL. Note that ILIL and IHIL are used only in C<<< N to 1 >>> algorithms. OFF provides the offset to the current beginning of the array data. Note that OFF is used only in C<<< N to 1 >>> algorithms. The RES field can be accessed at run time to cause the algorithm to reset itself before the maximum number of samples are reached. =head4 Algorithms B algorithm keeps a circular buffer of length NSAM. Each time the record is processed, it gets the data referenced by INP and puts it into the circular buffer referenced by VAL. The INP can refer to both scalar or array data and VAL is just a time ordered circular buffer of values obtained from INP. Note that N, ILIL, IHIL and OFF are not used in C<<< Circular Buffer >>> algorithm. B takes an average of every element of the array obtained from INP over time; that is, the entire array referenced by INP is retrieved, and for each element, the new average is calculated and placed in the corresponding element of the value buffer. The retrieved array is truncated to be of length NSAM. N successive arrays are averaged and placed in the buffer. Thus, VAL[0] holds the average of the first element of INP over N samples, VAL[1] holds the average of the next element of INP over N samples, and so on. The following shows the equation: =for comment Latex form of equation bellow : VAL[i] \leftarrow \frac{1}{N}\sum_{n=1}^NINP_{n}[i] =begin html =end html B If any of the C<<< N to 1 >>> algorithms are chosen, then VAL is a circular buffer of NSAM samples. The actual algorithm depends on whether INP references a scalar or an array. If INP refers to a scalar, then N successive time ordered samples of INP are taken. After the Nth sample is obtained, a new value determined by the algorithm (Lowest, Highest, or Average), is written to the circular buffer referenced by VAL. If C<<< Low Value >>> the lowest value of all the samples is written; if C<<< High Value >>> the highest value is written; and if C<<< Average >>>, the average of all the samples are written. The C<<< Median >>> setting behaves like C<<< Average >>> with scalar input data. If INP refers to an array, then the following applies: =over =item C<<< N to 1 Low Value >>> Compress N to 1 samples, keeping the lowest value. =item C<<< N to 1 High Value >>> Compress N to 1 samples, keeping the highest value. =item C<<< N to 1 Average >>> Compress N to 1 samples, taking the average value. =item C<<< N to 1 Median >>> Compress N to 1 samples, taking the median value. =back The compression record keeps NSAM data samples. The field N determines the number of elements to compress into each result. Thus, if NSAM was 3, and N was also equal to 3, then the algorithms would work as in the following diagram: =begin html =end html =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. They display the value and other parameters of the record either textually or graphically. =fields EGU, HOPR, LOPR, PREC, NAME, DESC The EGU field should be given a string that describes the value of VAL, but is used whenever the C<<< get_units >>> record support routine is called. The HOPR and LOPR fields only specify the upper and lower display limits for VAL, HIHI, HIGH, LOLO and LOW fields. PREC controls the floating-point precision whenever C<<< get_precision >>> is called, and the field being referenced is the VAL field (i.e., one of the values contained in the circular buffer). See L for more on the record name (NAME) and description (DESC) fields. =head3 Alarm Parameters The compression record has the alarm parameters common to all record types described in L. =head3 Run-time Parameters These parameters are used by the run-time code for processing the data compression algorithm. They are not configurable by the user, though some are accessible at run-time. They can represent the current state of the waveform or of the record whose field is referenced by the INP field. =fields NUSE, OUSE, BPTR, SPTR, WPTR, CVB, INPN, INX NUSE and OUSE hold the current and previous number of elements stored in VAL. BPTR is a pointer that refers to the buffer referenced by VAL. SPTR points to an array that is used for array averages. WPTR is used by the dbGetlinks routines. =head2 Record Support =head3 Record Support Routines long init_record(struct dbCommon *precord, int pass) Space for all necessary arrays is allocated. The addresses are stored in the appropriate fields in the record. long process(struct dbCommon *precord) See L below. long special(struct dbAddr *paddr, int after) This routine is called when RSET, ALG, or N are set. It performs a reset. long cvt_dbaddr(struct dbAddr *paddr) This is called by dbNameToAddr. It makes the dbAddr structure refer to the actual buffer holding the result. long get_array_info(struct dbAddr *paddr, long *no_elements, long *offset) Obtains values from the circular buffer referenced by VAL. long put_array_info(struct dbAddr *paddr, long nNew); Writes values into the circular buffer referenced by VAL. long get_units(struct dbAddr *paddr, char *units); Retrieves EGU. long get_precision(const struct dbAddr *paddr, long *precision); Retrieves PREC. long get_graphic_double(struct dbAddr *paddr, struct dbr_grDouble *p); Sets the upper display and lower display limits for a field. If the field is VAL, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. long get_control_double(struct dbAddr *paddr, struct dbr_ctrlDouble *p); Sets the upper control and the lower control limits for a field. If the field is VAL, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. =head3 Record Processing Routine process implements the following algorithm: =over =item 1. If INP is not a database link, check monitors and the forward link and return. =item 2. Get the current data referenced by INP. =item 3. Perform the appropriate algorithm: =over =item * Average: Read N successive instances of INP and perform an element by element average. Until N instances have been obtained it just return without checking monitors or the forward link. When N instances have been obtained complete the algorithm, store the result in the VAL array, check monitors and the forward link, and return. =item * Circular Buffer: Write the values obtained from INP into the VAL array as a circular buffer, check monitors and the forward link, and return. =item * N to 1 xxx when INP refers to a scalar: Obtain N successive values from INP and apply the N to 1 xxx algorithm to these values. Until N values are obtained monitors and forward links are not triggered. When N successive values have been obtained, complete the algorithm, check monitors and trigger the forward link, and return. =item * N to 1 xxx when INP refers to an array: The ILIL and IHIL are honored if ILIL E IHIL. The input array is divided into subarrays of length N. The specified N to 1 xxx compression algorithm is applied to each sub-array and the result stored in the array referenced by VAL. The monitors and forward link are checked. =back =item 4. If success, set UDF to FALSE. =item 5. Check to see if monitors should be invoked: =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * NSEV and NSTA are reset to 0. =back =item 6. Scan forward link if necessary, set PACT FALSE, and return. =back =cut include "dbCommon.dbd" field(VAL,DBF_NOACCESS) { prompt("Value") asl(ASL0) special(SPC_DBADDR) pp(TRUE) extra("void * val") #=type DOUBLE[] #=read Yes #=write Yes } field(INP,DBF_INLINK) { prompt("Input Specification") promptgroup("40 - Input") interest(1) } field(RES,DBF_SHORT) { prompt("Reset") asl(ASL0) special(SPC_RESET) interest(3) } field(ALG,DBF_MENU) { prompt("Compression Algorithm") promptgroup("30 - Action") special(SPC_RESET) interest(1) menu(compressALG) } field(BALG,DBF_MENU) { prompt("Buffering Algorithm") promptgroup("30 - Action") special(SPC_RESET) interest(1) menu(bufferingALG) } field(NSAM,DBF_ULONG) { prompt("Number of Values") promptgroup("30 - Action") special(SPC_NOMOD) interest(1) initial("1") } field(N,DBF_ULONG) { prompt("N to 1 Compression") promptgroup("30 - Action") special(SPC_RESET) interest(1) initial("1") } field(IHIL,DBF_DOUBLE) { prompt("Init High Interest Lim") promptgroup("30 - Action") interest(1) } field(ILIL,DBF_DOUBLE) { prompt("Init Low Interest Lim") promptgroup("30 - Action") interest(1) } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) prop(YES) } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) prop(YES) } field(OFF,DBF_ULONG) { prompt("Offset") special(SPC_NOMOD) } field(NUSE,DBF_ULONG) { prompt("Number Used") special(SPC_NOMOD) } field(OUSE,DBF_ULONG) { prompt("Old Number Used") special(SPC_NOMOD) } field(BPTR,DBF_NOACCESS) { prompt("Buffer Pointer") special(SPC_NOMOD) interest(4) extra("double *bptr") } field(SPTR,DBF_NOACCESS) { prompt("Summing Buffer Ptr") special(SPC_NOMOD) interest(4) extra("double *sptr") } field(WPTR,DBF_NOACCESS) { prompt("Working Buffer Ptr") special(SPC_NOMOD) interest(4) extra("double *wptr") } field(INPN,DBF_LONG) { prompt("Number of elements in Working Buffer") special(SPC_NOMOD) interest(4) } field(CVB,DBF_DOUBLE) { prompt("Compress Value Buffer") special(SPC_NOMOD) interest(3) } field(INX,DBF_ULONG) { prompt("Compressed Array Inx") special(SPC_NOMOD) interest(3) } } base-7.0.3.1/modules/database/src/std/rec/dfanoutRecord.c0000664000577000060420000002125413557101274021730 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 Southeastern Universities Research Association, as * Operator of Thomas Jefferson National Accelerator Facility. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* recDfanout.c - Record Support Routines for Dfanout records */ /* * Original Author: Matt Bickley (Sometime in 1994) * * Modification Log: * ----------------- * .01 1994 mhb Started with longout record to make the data fanout * .02 May 10, 96 jt Bug Fix * .03 11SEP2000 mrk LONG=>DOUBLE, add SELL,SELN,SELM */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "epicsMath.h" #include "alarm.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #include "menuOmsl.h" #define GEN_SIZE_OFFSET #include "dfanoutRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); #define special NULL #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL static long get_units(DBADDR *, char *); static long get_precision(const DBADDR *, long *); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *,struct dbr_grDouble *); static long get_control_double(DBADDR *,struct dbr_ctrlDouble *); static long get_alarm_double(DBADDR *,struct dbr_alDouble *); rset dfanoutRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,dfanoutRSET); static void checkAlarms(dfanoutRecord *); static void monitor(dfanoutRecord *); static void push_values(dfanoutRecord *); #define OUT_ARG_MAX 8 static long init_record(struct dbCommon *pcommon, int pass) { struct dfanoutRecord *prec = (struct dfanoutRecord *)pcommon; if (pass==0) return 0; recGblInitConstantLink(&prec->sell, DBF_USHORT, &prec->seln); /* get the initial value dol is a constant*/ if (recGblInitConstantLink(&prec->dol, DBF_DOUBLE, &prec->val)) prec->udf = isnan(prec->val); return 0; } static long process(struct dbCommon *pcommon) { struct dfanoutRecord *prec = (struct dfanoutRecord *)pcommon; long status=0; if (!prec->pact && !dbLinkIsConstant(&prec->dol) && prec->omsl == menuOmslclosed_loop) { status = dbGetLink(&prec->dol, DBR_DOUBLE, &prec->val, 0, 0); if (!dbLinkIsConstant(&prec->dol) && !status) prec->udf = isnan(prec->val); } prec->pact = TRUE; recGblGetTimeStamp(prec); /* Push out the data to all the forward links */ dbGetLink(&(prec->sell),DBR_USHORT,&(prec->seln),0,0); checkAlarms(prec); push_values(prec); monitor(prec); recGblFwdLink(prec); prec->pact=FALSE; return(status); } #define indexof(field) dfanoutRecord##field static long get_units(DBADDR *paddr,char *units) { dfanoutRecord *prec=(dfanoutRecord *)paddr->precord; if(paddr->pfldDes->field_type == DBF_DOUBLE) { strncpy(units,prec->egu,DB_UNITS_SIZE); } return(0); } static long get_precision(const DBADDR *paddr,long *precision) { dfanoutRecord *prec=(dfanoutRecord *)paddr->precord; *precision = prec->prec; if (dbGetFieldIndex(paddr) == indexof(VAL)) return(0); recGblGetPrec(paddr,precision); return(0); } static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd) { dfanoutRecord *prec=(dfanoutRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): pgd->upper_disp_limit = prec->hopr; pgd->lower_disp_limit = prec->lopr; break; default: recGblGetGraphicDouble(paddr,pgd); } return(0); } static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd) { dfanoutRecord *prec=(dfanoutRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(LALM): case indexof(ALST): case indexof(MLST): pcd->upper_ctrl_limit = prec->hopr; pcd->lower_ctrl_limit = prec->lopr; break; default: recGblGetControlDouble(paddr,pcd); } return(0); } static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad) { dfanoutRecord *prec=(dfanoutRecord *)paddr->precord; if(dbGetFieldIndex(paddr) == indexof(VAL)) { pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN; pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN; pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN; pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN; } else recGblGetAlarmDouble(paddr,pad); return(0); } static void checkAlarms(dfanoutRecord *prec) { double val, hyst, lalm; double alev; epicsEnum16 asev; if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, prec->udfs); return; } val = prec->val; hyst = prec->hyst; lalm = prec->lalm; /* alarm condition hihi */ asev = prec->hhsv; alev = prec->hihi; if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { if (recGblSetSevr(prec, HIHI_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition lolo */ asev = prec->llsv; alev = prec->lolo; if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { if (recGblSetSevr(prec, LOLO_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition high */ asev = prec->hsv; alev = prec->high; if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { if (recGblSetSevr(prec, HIGH_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition low */ asev = prec->lsv; alev = prec->low; if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { if (recGblSetSevr(prec, LOW_ALARM, asev)) prec->lalm = alev; return; } /* we get here only if val is out of alarm by at least hyst */ prec->lalm = val; return; } static void monitor(dfanoutRecord *prec) { unsigned monitor_mask = recGblResetAlarms(prec); /* check for value change */ recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE); /* check for archive change */ recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE); /* send out monitors connected to the value field */ if (monitor_mask){ db_post_events(prec,&prec->val,monitor_mask); } return; } static void push_values(dfanoutRecord *prec) { struct link *plink; /* structure of the link field */ int i; long status; unsigned short state; switch (prec->selm){ case (dfanoutSELM_All): for(i=0, plink=&(prec->outa); ival),1); if(status) recGblSetSevr(prec,LINK_ALARM,MAJOR_ALARM); } break; case (dfanoutSELM_Specified): if(prec->seln>OUT_ARG_MAX) { recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); break; } if(prec->seln==0) break; plink=&(prec->outa); plink += (prec->seln -1); status=dbPutLink(plink,DBR_DOUBLE,&(prec->val),1); if(status) recGblSetSevr(prec,LINK_ALARM,MAJOR_ALARM); break; case (dfanoutSELM_Mask): if(prec->seln==0) break; for(i=0, plink=&(prec->outa), state=prec->seln; i>=1) { if(state&1) { status=dbPutLink(plink,DBR_DOUBLE,&(prec->val),1); if(status) recGblSetSevr(prec,LINK_ALARM,MAJOR_ALARM); } } break; default: recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); } } base-7.0.3.1/modules/database/src/std/rec/dfanoutRecord.dbd.pod0000664000577000060420000002714713557101274023027 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Data Fanout Record (dfanout) The Data Fanout or "dfanout" record is used to forward data to up to eight other records. It's similar to the fanout record except that the capability to forward data has been added to it. If has no associated device support. =head2 Parameter Fields The fields in this record can be classified into the following categories: =over =item * scan parameters =item * desired output parameters =item * write parameters =item * operator display parameters =item * alarm parameters =item * monitor parameters =item * run-time and simulation mode parameters =back =recordtype dfanout =cut menu(dfanoutSELM) { choice(dfanoutSELM_All,"All") choice(dfanoutSELM_Specified,"Specified") choice(dfanoutSELM_Mask,"Mask") } recordtype(dfanout) { =head3 Scan Parameters The data fanout record has the standard fields for specifying under what circumstances it will be processed. These fields are listed in L. In addition, L explains how these fields are used. Since the data fanout record supports no direct interfaces to hardware, it cannot be scanned on I/O interrupt, so its SCAN field cannot be C. =head3 Desired Output Parameters The data fanout record must specify where the desired output value originates, i.e., the data which is to be fowarded to the records in its output links. The output mode select (OMSL) field determines whether the output originates from another record or from run-time database access. When set to C, the desired output is retrieved from the link specified in the desired output (DOL) field, which can specify either a database or a channel access link, and placed into the VAL field. When set to C, the desired output can be written to the VAL field via dbPuts at run-time. The DOL field can also be a constant in which case the VAL field is initialized to the constant value. Note that there are no conversion parameters, so the desired output value undergoes no conversions before it is sent out to the output links. =fields DOL, OMSL, VAL =head3 Write Parameters The OUTA-OUTH fields specify where VAL is to be sent. Each field that is to forward data must specify an address to another record. See L
for information on specifying links. The SELL, SELM, and SELN fields specify which output links are to be used. =head4 Menu dfanoutSELM SELM is a menu, with three choices: =menu dfanoutSELM If SELM=="All", then all output links are used, and the values of SELL and SELN are ignored. If SELM=="Specified", then the value of SELN is used to specify a single link which will be used. If SELN==0, then no link will be used; if SELN==1, then OUTA will be used, and so on. SELN can either have its value set directly, or have its values retrieved from another EPICS PV. If SELL is a valid PV link, then SELN will be set to the values of the linked PV. If SELM=="Mask", then SELN will be treated as a bit mask. If bit one of SELN is set, then OUTA will be used, if bit two is set, OUTB will be used. Thus if SELN==5, OUTC and OUTA will be used. =fields OUTA, OUTB, OUTC, OUTD, OUTE, OUTF, OUTG, OUTH =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. They display the value and other parameters of the data fanout record either textually or graphically. The EGU field can contain a string of up to 16 characters describing the value on the VAL field. The HOPR and LOPR fields determine the upper and lower display limits for graphic displays and the upper and lower control limits for control displays. They apply to the VAL, HIHI, HIGH, LOW, and LOLO fields. The record support routines C and C retrieve HOPR and LOPR. See L for more on the record name (NAME) and description (DESC) fields. =fields EGU, HOPR, LOPR, NAME, DESC =head3 Alarm Parameters The possible alarm conditions for data fanouts are the SCAN, READ, INVALID, and limit alarms. The SCAN and READ alarms are called by the record routines. The limit alarms are configured by the user in the HIHI, LOLO, HIGH, and LOW fields using floating point values. The limit alarms apply only to the VAL field. The severity for each of these limits is specified in the corresponding field (HHSV, LLSV, HSV, LSV) and can be either NO_ALARM, MINOR, or MAJOR. In the hysteresis field (HYST) can be entered a number which serves as the deadband on the limit alarms. See L for a complete explanation of alarms and these fields. L lists other fields related to alarms that are common to all record types. =fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST =head3 Monitor Parameters These parameters are used to determine when to send monitors placed on the VAL field. These monitors are sent when the value field exceeds the last monitored fields by the specified deadband, ADEL for archivers monitors and MDEL for all other types of monitors. If these fields have a value of zero, everytime the value changes, a monitor will be triggered; if they have a value of -1, everytime the record is scanned, monitors are triggered. See L for a complete explanation of monitors. =fields ADEL, MDEL =head3 Run-Time Parameters and Simulation Mode Parameters These parameters are used by the run-time code for processing the data fanout record. Ther are not configurable. They are used to implement the hysteresis factors for monitor callbacks. =fields LALM, ALST, MLST =cut include "dbCommon.dbd" field(VAL,DBF_DOUBLE) { prompt("Desired Output") promptgroup("40 - Input") asl(ASL0) pp(TRUE) } field(SELM,DBF_MENU) { prompt("Select Mechanism") promptgroup("30 - Action") interest(1) menu(dfanoutSELM) } field(SELN,DBF_USHORT) { prompt("Link Selection") interest(1) initial("1") } field(SELL,DBF_INLINK) { prompt("Link Selection Loc") promptgroup("30 - Action") interest(1) } field(OUTA,DBF_OUTLINK) { prompt("Output Spec A") promptgroup("50 - Output") interest(1) } field(OUTB,DBF_OUTLINK) { prompt("Output Spec B") promptgroup("50 - Output") interest(1) } field(OUTC,DBF_OUTLINK) { prompt("Output Spec C") promptgroup("50 - Output") interest(1) } field(OUTD,DBF_OUTLINK) { prompt("Output Spec D") promptgroup("50 - Output") interest(1) } field(OUTE,DBF_OUTLINK) { prompt("Output Spec E") promptgroup("50 - Output") interest(1) } field(OUTF,DBF_OUTLINK) { prompt("Output Spec F") promptgroup("50 - Output") interest(1) } field(OUTG,DBF_OUTLINK) { prompt("Output Spec G") promptgroup("50 - Output") interest(1) } field(OUTH,DBF_OUTLINK) { prompt("Output Spec H") promptgroup("50 - Output") interest(1) } field(DOL,DBF_INLINK) { prompt("Desired Output Loc") promptgroup("40 - Input") interest(1) } field(OMSL,DBF_MENU) { prompt("Output Mode Select") promptgroup("50 - Output") interest(1) menu(menuOmsl) } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) prop(YES) } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) prop(YES) } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { prompt("Lolo Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(HSV,DBF_MENU) { prompt("High Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(LSV,DBF_MENU) { prompt("Low Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { prompt("Alarm Deadband") promptgroup("70 - Alarm") interest(1) } field(ADEL,DBF_DOUBLE) { prompt("Archive Deadband") promptgroup("80 - Display") interest(1) } field(MDEL,DBF_DOUBLE) { prompt("Monitor Deadband") promptgroup("80 - Display") interest(1) } field(LALM,DBF_DOUBLE) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(ALST,DBF_DOUBLE) { prompt("Last Value Archived") special(SPC_NOMOD) interest(3) } field(MLST,DBF_DOUBLE) { prompt("Last Val Monitored") special(SPC_NOMOD) interest(3) } =head2 Record Support =head3 Record Support Routines =head2 C This routine initializes all output links that are defined. Then it initializes DOL if DOL is a constant or a PV_LINK. When initializing the output links and the DOL link, a non-zero value is returned if an error occurs. =head2 C See next section. =head2 C The routine copies the string specified in the EGU field to the location specified by a pointer which is passed to the routine. =head2 C If the referenced field is VAL, HIHI, HIGH, LOW, or LOLO, this routine sets the C member of the C structure to the HOPR and the C member to the LOPR. If the referenced field is not one of the above fields, then C routine is called. =head2 C Same as the C routine except that it uses the C structure. =head2 C This sets the members of the C structure to the specified alarm limits when the referenced field is VAL: =over upper_alarm_limit = HIHI upper_warning_limit = HIGH lower_warning_limit = LOW lower_alarm_limit = LOLO =back If the referenced field is not VAL, the C routine is called. =head3 Record Processing =over =item 1. The C routine first checks that DOL is not a constant link and that OMSL is set to "closed_loop". If so, it retrieves a value through DOL and places it into VAL. If no errors occur, UDF is set to FALSE. =item 2. PACT is set TRUE, and the record's timestamp is set. =item 3. A value is fetched from SELL and placed into SELN. =item 4. Alarms ranges are checked against the contents of the VAL field. =item 5. VAL is then sent through the OUTA-OUTH links by calling C for each link, conditional on the setting of SELM and the value in SELN. =item 6. Value and archive monitors are posted on the VAL field if appropriate based on the settings of MDEL and ADEL respectively. =item 7. The data fanout's forward link FLNK is processed. =item 6. PACT is set FALSE, and the C routine returns. =back =cut } base-7.0.3.1/modules/database/src/std/rec/eventRecord.c0000664000577000060420000001323113557101274021405 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* recEvent.c - Record Support Routines for Event records */ /* * Author: Janet Anderson * Date: 12-13-91 */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbScan.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #include "menuYesNo.h" #define GEN_SIZE_OFFSET #include "eventRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL #define get_units NULL #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset eventRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,eventRSET); struct eventdset { /* event input dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN read_event;/*(0)=> success */ }; static void monitor(eventRecord *); static long readValue(eventRecord *); static long init_record(struct dbCommon *pcommon, int pass) { struct eventRecord *prec = (struct eventRecord *)pcommon; struct eventdset *pdset; long status=0; if (pass == 0) return 0; recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); recGblInitConstantLink(&prec->siol, DBF_STRING, &prec->sval); if( (pdset=(struct eventdset *)(prec->dset)) && (pdset->init_record) ) status=(*pdset->init_record)(prec); prec->epvt = eventNameToHandle(prec->val); return(status); } static long process(struct dbCommon *pcommon) { struct eventRecord *prec = (struct eventRecord *)pcommon; struct eventdset *pdset = (struct eventdset *)(prec->dset); long status=0; unsigned char pact=prec->pact; if((pdset!=NULL) && (pdset->number >= 5) && pdset->read_event ) status=readValue(prec); /* read the new value */ /* check if device support set pact */ if ( !pact && prec->pact ) return(0); prec->pact = TRUE; postEvent(prec->epvt); recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); /* check event list */ monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact=FALSE; return(status); } static long special(DBADDR *paddr, int after) { eventRecord *prec = (eventRecord *)paddr->precord; if (dbGetFieldIndex(paddr) == eventRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return 0; } if (!after) return 0; if (dbGetFieldIndex(paddr) == eventRecordVAL) { prec->epvt = eventNameToHandle(prec->val); } return 0; } static void monitor(eventRecord *prec) { unsigned short monitor_mask; /* get previous stat and sevr and new stat and sevr*/ monitor_mask = recGblResetAlarms(prec); db_post_events(prec,&prec->val,monitor_mask|DBE_VALUE); return; } static long readValue(eventRecord *prec) { struct eventdset *pdset = (struct eventdset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: status = pdset->read_event(prec); break; case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbGetLink(&prec->siol, DBR_STRING, &prec->sval, 0, 0); if (status == 0) { if (strcmp(prec->sval, prec->val) != 0) { strcpy(prec->val, prec->sval); prec->epvt = eventNameToHandle(prec->val); } prec->udf = FALSE; } prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } base-7.0.3.1/modules/database/src/std/rec/eventRecord.dbd.pod0000664000577000060420000001707213557101274022504 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Event Record (event) The normal use for this record type is to post an event and/or process a forward link. Device support for this record can provide a hardware interrupt handler routine for I/O Event-scanned records. =head2 Parameter Fields The records in this field fall into the following groups of parameters: =over =item * scan parameters =item * read parameters =item * event number parameters =item * simulation mode parameters =back =recordtype event =cut recordtype(event) { include "dbCommon.dbd" =head3 Scan Parameters The event record has the standard fields for specifying under what circumstances it will be processed. If the SCAN field specifies C, then device support will provide an interrupt handler, posting an event number when an I/O interrupt occurs. These fields are listed in L. In addition, L explains how the scanning fields work. Note that I/O event scanning is only supported for those card types that interrupt. =head3 Event Number Parameters The VAL field contains the event number read by the device support routines. It is this number which is posted. For records that use C device support, it can be configured before run-time or set via dbPuts. =fields VAL =cut field(VAL,DBF_STRING) { prompt("Event Name To Post") promptgroup("40 - Input") special(SPC_MOD) asl(ASL0) size(40) } %#include "dbScan.h" field(EPVT, DBF_NOACCESS) { prompt("Event private") special(SPC_NOMOD) interest(4) extra("EVENTPVT epvt") } =head3 Input Specification The device support routines use the address in this record to obtain input. For records that provide an interrupt handler, the INP field should specify the address of the I/O card, and the DTYP field should specify a valid device support module. Be aware that the address format differs according to the card type used. See L
for information on the format of hardware addresses and specifying links. For soft records, the INP field can be a constant, a database link, or a channel access link. For soft records, the DTYP field should specify C. =fields INP, DTYP =cut field(INP,DBF_INLINK) { prompt("Input Specification") promptgroup("40 - Input") interest(1) } =head3 Operator Display Parameters See L for more on the record name (NAME) and description (DESC) fields. =fields NAME, DESC =head3 Alarm Parameters The Event record has the alarm parameters common to all record types. L lists other fields related to alarms that are common to all record types. =head3 Simulation Mode Parameters The following fields are used to operate the event record in the simulation mode. See L for more information on these fields. =fields SIOL, SVAL, SIML, SIMM, SIMS =cut field(SIOL,DBF_INLINK) { prompt("Sim Input Specifctn") promptgroup("90 - Simulate") interest(1) } field(SVAL,DBF_STRING) { prompt("Simulation Value") size(40) } field(SIML,DBF_INLINK) { prompt("Sim Mode Location") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Sim mode Alarm Svrty") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } =head2 Record Support =head3 Record Support Routines =head4 init_record This routine initializes SIMM with the value of SIML if SIML type is a CONSTANT link or creates a channel access link if SIML type is PV_LINK. SVAL is likewise initialized if SIOL is CONSTANT or PV_LINK. If device support includes C, it is called. =head4 process See next section. =head3 Record Processing Routine process implements the following algorithm: =over =item 1. readValue is called. See L for more information. =item 2. If PACT has been changed to TRUE, the device support read routine has started but has not completed reading a new input value. In this case, the processing routine merely returns, leaving PACT TRUE. =item 3. If VAL E 0, post event number VAL. =item 4. Check to see if monitors should be invoked. Alarm monitors are invoked if the alarm status or severity has chanet to 0. =item 5. Scan forward link if necessary, set PACT FALSE, and return. =back =head2 Device Support =head3 Fields of Interest To Device Support Each record must have an associated set of device support routines. The device support routines are primarily interested in the following fields: =fields PACT, DPVT, UDF, NSEV, NSTA, INP, PRIO =head3 Device Support Routines Device support consists of the following routines: =head4 long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. =head4 long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. =head4 init_record init_record(precord) This routine is optional. If provided, it is called by the record support C routine. =head4 get_ioint_info get_ioint_info(int cmd, struct dbCommon *precord, IOSCANPVT *ppvt) This routine is called by the ioEventScan system each time the record is added or deleted from an I/O event scan list. cmd has the value (0,1) if the record is being (added to, deleted from) an I/O event list. It must be provided for any device type that can use the ioEvent scanner. =head4 read_event read_event(precord) This routine returns the following values: =over =item * 0: Success. =item * Other: Error. =back =head3 Device Support For Soft Records The C device support module is available. The INP link type must be either CONSTANT, DB_LINK, or CA_LINK. If the INP link type is CONSTANT, then the constant value is stored into VAL by C, and UDF is set to FALSE. If the INP link type is PV_LINK, then dbCaAddInlink is called by C. C calls recGblGetLinkValue to read the current value of VAL. See L for details on soft input. =cut } base-7.0.3.1/modules/database/src/std/rec/fanoutRecord.c0000664000577000060420000000765013557101274021570 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Author: Bob Dalesio * Date: 12-20-88 */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "errMdef.h" #include "epicsTypes.h" #include "recSup.h" #include "recGbl.h" #include "dbCommon.h" #define GEN_SIZE_OFFSET #include "fanoutRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" #define NLINKS 16 /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); #define special NULL #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL #define get_units NULL #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset fanoutRSET = { RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,fanoutRSET); static long init_record(struct dbCommon *pcommon, int pass) { struct fanoutRecord *prec = (struct fanoutRecord *)pcommon; if (pass == 0) return 0; recGblInitConstantLink(&prec->sell, DBF_USHORT, &prec->seln); return 0; } static long process(struct dbCommon *pcommon) { struct fanoutRecord *prec = (struct fanoutRecord *)pcommon; struct link *plink; epicsUInt16 seln, events; int i; epicsUInt16 oldn = prec->seln; prec->pact = TRUE; /* fetch link selection */ dbGetLink(&prec->sell, DBR_USHORT, &prec->seln, 0, 0); seln = prec->seln; switch (prec->selm) { case fanoutSELM_All: plink = &prec->lnk0; for (i = 0; i < NLINKS; i++, plink++) { dbScanFwdLink(plink); } break; case fanoutSELM_Specified: i = seln + prec->offs; if (i < 0 || i >= NLINKS) { recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); break; } plink = &prec->lnk0 + i; dbScanFwdLink(plink); break; case fanoutSELM_Mask: i = prec->shft; if (i < -15 || i > 15) { /* Shifting by more than the number of bits in the * value produces undefined behavior in C */ recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); break; } seln = (i >= 0) ? seln >> i : seln << -i; if (seln == 0) break; plink = &prec->lnk0; for (i = 0; i < NLINKS; i++, seln >>= 1, plink++) { if (seln & 1) dbScanFwdLink(plink); } break; default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); } prec->udf = FALSE; recGblGetTimeStamp(prec); /* post monitors */ events = recGblResetAlarms(prec); if (events) db_post_events(prec, &prec->val, events); if (prec->seln != oldn) db_post_events(prec, &prec->seln, events | DBE_VALUE | DBE_LOG); /* finish off */ recGblFwdLink(prec); prec->pact = FALSE; return 0; } base-7.0.3.1/modules/database/src/std/rec/fanoutRecord.dbd.pod0000664000577000060420000001624313557101274022656 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Fanout Record (fanout) The fanout record uses several forward processing links to force multiple passive records to scan. When more than one record needs to be scanned as the result of a record being processed, the forward link of that record can specify a fanout record. The fanout record can specify up to sixteen other records to process. If more than sixteen are needed, one of the forward links in the fanout record (or its FLNK field) can point to another fanout record. B The dfanout or data fanout record can, on the other hand, send data to other records. =head2 Parameter Fields The fanout record's fields fall into the following categories: =over =item * scan parameters =item * operator display parameters =item * run-time parameters. =back =recordtype fanout =cut menu(fanoutSELM) { choice(fanoutSELM_All,"All") choice(fanoutSELM_Specified,"Specified") choice(fanoutSELM_Mask,"Mask") } recordtype(fanout) { include "dbCommon.dbd" =head3 Scan Parameters The forward link fields of the fanout record (LNK0-LNK9, LNKA-LNKF) specify records to be scanned. The records to be processed must specify C in their SCAN fields; otherwise the forward link will not cause them to process. Also when specifying database links for the fanout record, the user needs only to specify the record name. As no value is being sent or retrieved, a field name is only required when the link will be over Channel Access, in which case the field PROC must be named. The SELM, SELN, and SELL fields specify the order of processing for the forward links. The select mechanism menu field (SELM) has three choices: =menu fanoutSELM How the SELM value affects which links to process and in which order is as follows: =over =item * B Links are processed in numerical order - LNK0, LNK1, etc. =item * B The sum of the values in the SELN and OFFS fields is used as the specifier of which link to process. For instance, with OFFS=0 and SELN=1, the record targeted by LNK1 will be processed. =item * B The individual bits in SELN are shifted by SHFT bits (negative means shift left) and the result used to select which links to process as follows: =over =item * If bit 0 (LSB) is set, LNK0 is processed. =item * If bit 1 is set, LNK2 is processed. =item * If bit 2 is set, LNK3 is processed, etc. =back =back SELN reads its value from SELL. SELL can be a constant, a database link, or a channel access link. If a constant, SELN is initialized with the constant value and can be changed via dbPuts. For database/channel access links, SELN is retrieved from SELL each time the record is processed and can also be changed via dbPuts. The Fanout record also has the standard scanning fields common to all records. These fields are listed in L. In addition, L explains in more detail how forward links and the scanning algorithms work. =fields SELM, SELN, SELL, OFFS, SHFT, LNK0, LNK1, LNK2, LNK3, LNK4, LNK5, LNK6, LNK7, LNK8, LNK9, LNKA, LNKB, LNKC, LNKD, LNKE, LNKF =cut field(VAL,DBF_LONG) { prompt("Used to trigger") asl(ASL0) pp(TRUE) } field(SELM,DBF_MENU) { prompt("Select Mechanism") promptgroup("30 - Action") interest(1) menu(fanoutSELM) } field(SELN,DBF_USHORT) { prompt("Link Selection") interest(1) initial("1") } field(SELL,DBF_INLINK) { prompt("Link Selection Loc") promptgroup("30 - Action") interest(1) } field(OFFS,DBF_SHORT) { prompt("Offset for Specified") promptgroup("30 - Action") interest(1) initial("0") } field(SHFT,DBF_SHORT) { prompt("Shift for Mask mode") promptgroup("30 - Action") interest(1) initial("-1") } field(LNK0,DBF_FWDLINK) { prompt("Forward Link 0") promptgroup("51 - Output 0-7") interest(1) } field(LNK1,DBF_FWDLINK) { prompt("Forward Link 1") promptgroup("51 - Output 0-7") interest(1) } field(LNK2,DBF_FWDLINK) { prompt("Forward Link 2") promptgroup("51 - Output 0-7") interest(1) } field(LNK3,DBF_FWDLINK) { prompt("Forward Link 3") promptgroup("51 - Output 0-7") interest(1) } field(LNK4,DBF_FWDLINK) { prompt("Forward Link 4") promptgroup("51 - Output 0-7") interest(1) } field(LNK5,DBF_FWDLINK) { prompt("Forward Link 5") promptgroup("51 - Output 0-7") interest(1) } field(LNK6,DBF_FWDLINK) { prompt("Forward Link 6") promptgroup("51 - Output 0-7") interest(1) } field(LNK7,DBF_FWDLINK) { prompt("Forward Link 7") promptgroup("51 - Output 0-7") interest(1) } field(LNK8,DBF_FWDLINK) { prompt("Forward Link 8") promptgroup("52 - Output 8-F") interest(1) } field(LNK9,DBF_FWDLINK) { prompt("Forward Link 9") promptgroup("52 - Output 8-F") interest(1) } field(LNKA,DBF_FWDLINK) { prompt("Forward Link 10") promptgroup("52 - Output 8-F") interest(1) } field(LNKB,DBF_FWDLINK) { prompt("Forward Link 11") promptgroup("52 - Output 8-F") interest(1) } field(LNKC,DBF_FWDLINK) { prompt("Forward Link 12") promptgroup("52 - Output 8-F") interest(1) } field(LNKD,DBF_FWDLINK) { prompt("Forward Link 13") promptgroup("52 - Output 8-F") interest(1) } field(LNKE,DBF_FWDLINK) { prompt("Forward Link 14") promptgroup("52 - Output 8-F") interest(1) } field(LNKF,DBF_FWDLINK) { prompt("Forward Link 15") promptgroup("52 - Output 8-F") interest(1) } =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. See L for more on these fields. =fields NAME, DESC =head3 Alarm Parameters The Fanout record has the alarm parameters common to all record types. L lists other fields related to a alarms that are common to all record types. =head3 Run-time Parameters The VAL field performs no specific function, but a Channel Access put to it will cause the record to process. =fields VAL =head2 Record Support =head3 Record Support Routines =head4 init_record This routine initializes SELN with the value of SELL, if SELL type is CONSTANT link, or creates a channel access link if SELL type is PV_LINK. =head4 process See next section. =head3 Record Processing Routine process implements the following algorithm: =over =item 1. PACT is set to TRUE. =item 2. The link selection SELN is fetched. =item 3. Depending on the selection mechanism, the link selection forward links are processed, and UDF is set to FALSE. =item 4. Check to see if monitors should be invoked: =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * NSEV and NSTA are reset to 0. =back =item 5. Scan forward link field FLNK if used, set PACT FALSE, and return. =back =cut } base-7.0.3.1/modules/database/src/std/rec/histogramRecord.c0000664000577000060420000003152413557101274022266 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* histogramRecord.c - Record Support Routines for Histogram records */ /* * Author: Janet Anderson * Date: 5/20/91 */ #include #include #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "epicsPrint.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "special.h" #include "recSup.h" #include "recGbl.h" #include "menuYesNo.h" #define GEN_SIZE_OFFSET #include "histogramRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" #define indexof(field) histogramRecord##field /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL static long cvt_dbaddr(DBADDR *); static long get_array_info(DBADDR *, long *, long *); #define put_array_info NULL static long get_units(DBADDR *, char *); static long get_precision(const DBADDR *paddr,long *precision); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL #define get_alarm_double NULL static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd); static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd); rset histogramRSET = { RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,histogramRSET); int histogramSDELprecision = 2; epicsExportAddress(int, histogramSDELprecision); struct histogramdset { /* histogram input dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN read_histogram;/*(0,2)=> success and add_count, don't add_count)*/ /* if add_count then sgnl added to array */ DEVSUPFUN special_linconv; }; /* control block for callback*/ typedef struct myCallback { epicsCallback callback; histogramRecord *prec; } myCallback; static long add_count(histogramRecord *); static long clear_histogram(histogramRecord *); static void monitor(histogramRecord *); static long readValue(histogramRecord *); static void wdogCallback(epicsCallback *arg) { myCallback *pcallback; histogramRecord *prec; callbackGetUser(pcallback, arg); prec = pcallback->prec; /* force post events for any count change */ if (prec->mcnt > 0){ dbScanLock((struct dbCommon *)prec); recGblGetTimeStamp(prec); db_post_events(prec, prec->bptr, DBE_VALUE | DBE_LOG); prec->mcnt = 0; dbScanUnlock((struct dbCommon *)prec); } if (prec->sdel > 0) { /* restart timer */ callbackRequestDelayed(&pcallback->callback, prec->sdel); } return; } static long wdogInit(histogramRecord *prec) { myCallback *pcallback; if (!prec->wdog && prec->sdel > 0) { /* initialize a callback object */ pcallback = calloc(1, sizeof(myCallback)); pcallback->prec = prec; if (!pcallback) return -1; callbackSetCallback(wdogCallback, &pcallback->callback); callbackSetUser(pcallback, &pcallback->callback); callbackSetPriority(priorityLow, &pcallback->callback); prec->wdog = pcallback; } if (!prec->wdog) return -1; pcallback = prec->wdog; if (!pcallback) return -1; if (prec->sdel > 0) { /* start new timer on monitor */ callbackRequestDelayed(&pcallback->callback, prec->sdel); } return 0; } static long init_record(struct dbCommon *pcommon, int pass) { struct histogramRecord *prec = (struct histogramRecord *)pcommon; struct histogramdset *pdset; if (pass == 0) { /* allocate space for histogram array */ if (!prec->bptr) { if (prec->nelm <= 0) prec->nelm = 1; prec->bptr = calloc(prec->nelm, sizeof(epicsUInt32)); } /* calulate width of array element */ prec->wdth = (prec->ulim - prec->llim) / prec->nelm; return 0; } wdogInit(prec); recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); recGblInitConstantLink(&prec->siol, DBF_DOUBLE, &prec->sval); /* must have device support defined */ pdset = (struct histogramdset *) prec->dset; if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "histogram: init_record"); return S_dev_noDSET; } /* must have read_histogram function defined */ if (pdset->number < 6 || !pdset->read_histogram) { recGblRecordError(S_dev_missingSup, prec, "histogram: init_record"); return S_dev_missingSup; } /* call device support init_record */ if (pdset->init_record) { long status = pdset->init_record(prec); if (status) return status; } return 0; } static long process(struct dbCommon *pcommon) { struct histogramRecord *prec = (struct histogramRecord *)pcommon; struct histogramdset *pdset = (struct histogramdset *) prec->dset; int pact = prec->pact; long status; if (!pdset || !pdset->read_histogram) { prec->pact = TRUE; recGblRecordError(S_dev_missingSup, prec, "read_histogram"); return S_dev_missingSup; } status = readValue(prec); /* read the new value */ /* check if device support set pact */ if (!pact && prec->pact) return 0; prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); if (status == 0) add_count(prec); else if (status == 2) status = 0; monitor(prec); recGblFwdLink(prec); prec->pact=FALSE; return status; } static long special(DBADDR *paddr, int after) { histogramRecord *prec = (histogramRecord *) paddr->precord; if (paddr->special == SPC_MOD && dbGetFieldIndex(paddr) == histogramRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return 0; } if (!after) return 0; switch (paddr->special) { case SPC_CALC: if (prec->cmd <= 1) { clear_histogram(prec); prec->cmd = 0; } else if (prec->cmd == 2) { prec->csta = TRUE; prec->cmd = 0; } else if (prec->cmd == 3) { prec->csta = FALSE; prec->cmd = 0; } return 0; case SPC_MOD: /* increment frequency in histogram array */ add_count(prec); return 0; case SPC_RESET: if (dbGetFieldIndex(paddr) == histogramRecordSDEL) { wdogInit(prec); } else { prec->wdth = (prec->ulim - prec->llim) / prec->nelm; clear_histogram(prec); } return 0; default: recGblDbaddrError(S_db_badChoice, paddr, "histogram: special"); return S_db_badChoice; } } static void monitor(histogramRecord *prec) { unsigned short monitor_mask = recGblResetAlarms(prec); /* post events for count change */ if (prec->mcnt > prec->mdel){ monitor_mask |= DBE_VALUE | DBE_LOG; /* reset counts since monitor */ prec->mcnt = 0; } /* send out monitors connected to the value field */ if (monitor_mask) db_post_events(prec, prec->bptr, monitor_mask); return; } static long cvt_dbaddr(DBADDR *paddr) { histogramRecord *prec = (histogramRecord *) paddr->precord; paddr->pfield = prec->bptr; paddr->no_elements = prec->nelm; paddr->field_type = DBF_ULONG; paddr->field_size = sizeof(epicsUInt32); paddr->dbr_field_type = DBF_ULONG; return 0; } static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) { histogramRecord *prec = (histogramRecord *) paddr->precord; *no_elements = prec->nelm; *offset = 0; return 0; } static long add_count(histogramRecord *prec) { double temp; epicsUInt32 *pdest; int i; if (prec->csta == FALSE) return 0; if (prec->llim >= prec->ulim) { if (prec->nsev < INVALID_ALARM) { prec->stat = SOFT_ALARM; prec->sevr = INVALID_ALARM; return -1; } } if (prec->sgnl < prec->llim || prec->sgnl >= prec->ulim) return 0; temp = prec->sgnl - prec->llim; for (i = 1; i <= prec->nelm; i++){ if (temp <= (double) i * prec->wdth) break; } pdest = prec->bptr + i - 1; if (*pdest == (epicsUInt32) UINT_MAX) *pdest = 0; (*pdest)++; prec->mcnt++; return 0; } static long clear_histogram(histogramRecord *prec) { int i; for (i = 0; i < prec->nelm; i++) prec->bptr[i] = 0; prec->mcnt = prec->mdel + 1; prec->udf = FALSE; return 0; } static long readValue(histogramRecord *prec) { struct histogramdset *pdset = (struct histogramdset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: status = pdset->read_histogram(prec); break; case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbGetLink(&prec->siol, DBR_DOUBLE, &prec->sval, 0, 0); if (status == 0) { prec->sgnl = prec->sval; prec->udf = FALSE; } prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } static long get_units(DBADDR *paddr, char *units) { if (dbGetFieldIndex(paddr) == indexof(SDEL)) { strcpy(units,"s"); } /* We should have EGU for other DOUBLE values or probably get it from input link SVL */ return 0; } static long get_precision(const DBADDR *paddr,long *precision) { histogramRecord *prec = (histogramRecord *) paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(ULIM): case indexof(LLIM): case indexof(SGNL): case indexof(SVAL): case indexof(WDTH): *precision = prec->prec; break; case indexof(SDEL): *precision = histogramSDELprecision; break; default: recGblGetPrec(paddr,precision); } return 0; } static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd) { histogramRecord *prec = (histogramRecord *) paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): pgd->upper_disp_limit = prec->hopr; pgd->lower_disp_limit = prec->lopr; break; case indexof(WDTH): pgd->upper_disp_limit = prec->ulim - prec->llim; pgd->lower_disp_limit = 0.0; break; default: recGblGetGraphicDouble(paddr,pgd); } return 0; } static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd) { histogramRecord *prec = (histogramRecord *) paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): pcd->upper_ctrl_limit = prec->hopr; pcd->lower_ctrl_limit = prec->lopr; break; case indexof(WDTH): pcd->upper_ctrl_limit = prec->ulim - prec->llim; pcd->lower_ctrl_limit = 0.0; break; default: recGblGetControlDouble(paddr, pcd); } return 0; } base-7.0.3.1/modules/database/src/std/rec/histogramRecord.dbd0000664000577000060420000000763213557101274022600 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* menu(histogramCMD) { choice(histogramCMD_Read,"Read") choice(histogramCMD_Clear,"Clear") choice(histogramCMD_Start,"Start") choice(histogramCMD_Stop,"Stop") } recordtype(histogram) { include "dbCommon.dbd" field(VAL,DBF_NOACCESS) { prompt("Value") asl(ASL0) special(SPC_DBADDR) extra("void * val") #=type ULONG[] #=read Yes #=write Yes } field(NELM,DBF_USHORT) { prompt("Num of Array Elements") promptgroup("30 - Action") special(SPC_NOMOD) interest(1) initial("1") } field(CSTA,DBF_SHORT) { prompt("Collection Status") special(SPC_NOMOD) interest(1) initial("1") } field(CMD,DBF_MENU) { prompt("Collection Control") asl(ASL0) special(SPC_CALC) interest(1) menu(histogramCMD) } field(ULIM,DBF_DOUBLE) { prompt("Upper Signal Limit") promptgroup("30 - Action") special(SPC_RESET) interest(1) prop(YES) } field(LLIM,DBF_DOUBLE) { prompt("Lower Signal Limit ") promptgroup("30 - Action") special(SPC_RESET) interest(1) prop(YES) } field(WDTH,DBF_DOUBLE) { prompt("Element Width") special(SPC_NOMOD) interest(3) } field(SGNL,DBF_DOUBLE) { prompt("Signal Value") special(SPC_MOD) } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) prop(YES) } field(SVL,DBF_INLINK) { prompt("Signal Value Location") promptgroup("40 - Input") interest(1) } field(BPTR,DBF_NOACCESS) { prompt("Buffer Pointer") special(SPC_NOMOD) interest(4) extra("epicsUInt32 *bptr") } field(WDOG,DBF_NOACCESS) { prompt("Watchdog callback") special(SPC_NOMOD) interest(4) extra("void * wdog") } field(MDEL,DBF_SHORT) { prompt("Monitor Count Deadband") promptgroup("80 - Display") interest(1) } field(MCNT,DBF_SHORT) { prompt("Counts Since Monitor") special(SPC_NOMOD) interest(3) } field(SDEL,DBF_DOUBLE) { prompt("Monitor Seconds Dband") promptgroup("80 - Display") special(SPC_RESET) interest(1) } field(SIOL,DBF_INLINK) { prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } field(SVAL,DBF_DOUBLE) { prompt("Simulation Value") } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } field(HOPR,DBF_ULONG) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_ULONG) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } } variable(histogramSDELprecision, int) base-7.0.3.1/modules/database/src/std/rec/image/compress-1.png0000664000577000060420000000411613557101274022544 0ustar anjaesctl‰PNG  IHDRæ=¹“W|IDATx^íœ ±45… h@€$  h@€$À|§ª«oÞ“}Ì©úR{w&ïôI:™ÙûíÛ7cªüt„?ŽðÏ~æBàÇ#üu„¿ð+Œ1× '"Ìür„þûÓs%¬†ßU“U2òÛÿŸÆ˜‹‘›ÊªIˆX˜Æ<» ¬–¬š¬žïc.„߸‡dŸ©U’{Ùµ5Æ\@>må ˆU“ÕÒ'±Æ<‰Ò’Ç#¸°Þ_ó$J«"×üìÒ˜û:D+ùhØ?R^†²qgK÷Œ¹ á÷#°Bñ©=Þ#ÁMeà°§tÀÃ}Îs{âá‹1æE°0yA,Lc^ Ó˜ÄÂ4æ±0yAv óÏ#Ï#ƒ_p7·a—0y9 ŠhåÇÎÔg™<ç¤^.³1 F]E žk¬NgÀ…•0 góËPGÿO  ‚‘z€Ñ¯öþç]‘{¬°ûØÝb'NÙ§k`ø¿.¥½Ëõaæf6…´”уÊéEmiø^Û«ðbwKd¤Íÿ§¦—æŽÐGêsÆø`‡yì±±ÒDAÜXçÒxb3Ñ^É»´È6G¾|WP=V¶§í“B©TO<4–Ìf!_òe¤.0ÒðÒ0*E\*ØcEdfõ»Â3NTgÆž¸ÔQ⬹à#yáѵʥî³RϰÅ>%žTªç'g"bnu^„Šw¤A[>“ÌjÚw@}OXY!Î2;öÔ¸¤)­òÜ™`°íV¹ºO™3¬ØÚ—4¬†^×f+Z¦{ ô&ˆÈ–†O0ê½3ê_ÉÌØG!j/™a¬GÆK«n iã [û’† µÂW\XòQ§ÈÈþt âD©á”ÉDÀÀñ™Yé, Í3÷^WAÿÅý&ýu”×Ú/FâX`W%û¯–ͱ(qeì·Ø§—#ó}Å…™Ë‡/U$BâC©áJËõÒž¹”¦íaàø¼šHfûk}ÏF€¸ÙE%m>Q±£Öþ’2Ès$Ÿ%[S^\²OU0Î «.l®Ìè¬CYÄËékäFPŽŒ²J³à—†w _Ègvrzwè?Æ‚pÅ~Sc?ÒÏÄÍõɶÃçÈ83¯<´¢i7ùœ¶O*@£+±âÂÒ±¤£`ùè=7…НEì¨Üˆx|J³ï—†7 .ù`˜Ôm5äÙ½eí ;ÐÞ@_<úi´ÞØTF“¿ll´¾Ú_ž`möId¹¸¶£©¥‘q·ˆ[‚:1¢Øˆ„PëìZšt}˽ 2öì">‚ÞØGj¢“ȨwI¼%Ù¾mö©’µÆ·h­ ä]ò©•¡ìÒ,"rg×ÎÕVûZšrgï!cöèIidìqkv¦|°“ûe\¿}A<>IG] ­´ÛìS3Ÿ³K;ƒ×fÏU¡Bܯí1è:%Rjõ&Õ%P­³zPvÎë“‘‘ׯc'½±·e›Z\FÆ›Ëø‰*Úakâ*ÙÚ’}¢þÑŠF¨XψnF uh \R#ø®|(kDÌ£ôÚø)h%iM´;i}¦7Œ÷h^Z,z‹vD„œËo¥ßfŸDš9ð¡B†æJ2¸1 ÓòO\ÊSøÎýÒõLMdÄ¥-¹ÑpF˜gÒ¾ Œ «FøHjcOgdgÔ‹xØPilEÉV"ØW.w¤½9¶@j\mŸ/ÃJ#Þ©áAœÔJõfâÓŠƒ‘0èyrœƒmí›î ýÝmúIIÉ ÿtû¬r—†Kœ|Ö@Pg äß*ãÎàv2&Dl‰ÉÑ ܡáx‰³¶G§]g =†vVÜ™«ö©†qÈ.2 ýVs«ï`ŸEîÐpê‹X¨3Â,¹™Ü;# 7­&úUÈ÷S„¹Âm…ÉŒÅ,×;j'.ñä~¼ QˆÚKf$Þ$ø^ÿÍB¾­ƒ‘;pû¼-Q˜¬@ˆ(»N«‡5Ú7í\Õ˜ ¨Omu7æíaæÌ¢Áà™U#¬˜³ V´•´N=ÙƒQ?…Ý«°1/AÉEåF/w‡Ï×·‰|øœ L ˆ:Š0‡ýüëgoó€ Àˆá¼ñq¶ï׾ƞ©>° –€À¾0â}µgçk(ñ±ËëcëÒ]2F¼F]°‹D0âDb‘*”îr¯×kÆ^à€ÀñÈ,@-æúõ>ùL‘ž(¡!ðD€CG]@ 7’»;ÇÔŒÍMƒì!FœP4R†À…@¹bÄ” „ `ÄÂâ*ÔñyW|ü·ôe­Š%xÀˆ=i c ”þ’Ž’é¾ýåcwÁjØœF¼y°}@˜K#žËŸÕ!@`sñæÀö!@`.Œx.V‡ Í `ěۇ ¹0â¹üY€6'€o^l€æÀˆçògu@ØœF¼y°}@˜K#žËŸÕ!@`sql\ÿ ÁØ•ˆ@ |ç¦@b #ŽåKt@ |#ZµñPÜ,$ €cÄCËô#þç Oþùóç•\%Tò9ò8)ñQbtòQÑìd£¨›#…ºž¯‡ŠshSV\ŒO&±ª`ľ* #®?*šaÄy4È¿µÂˆëk¹g$FŒ÷ÔÍs”ŒOáîê GõšýYö1Fln„†1Fl(Ÿ_Siêï1âºòš]C1F\W©1£0bŒØ\Y³›è}Jù`Äuå5[3Œ#®«Ô˜Q1Fl®¬ÙM#n—Íx4ÝR5¼#n¡Õ>#ƈ۫æ6ƒ¦Î£ikÍ®!#¶Ö°e>FŒ[ê‡wÄõsQúcg³Oí)FŒ›¡!F\ÑH¨ê»ÆM;bC’ø0‡cÄÖ¶ÌLj1bKýH4Qµ»«k>ª øðôgÕ`ı¹`ı¡|~M¥©sGl-¢Ù5„cÄÖ¶ÌLj1bKý`ÄõsQxÅqæ2ÛøÔžb`ı¹˜Œø,Þ£ÁÜÁ]¯µä×Û žr9_o.–»½ûš_ùÕð±>âŒÐª·©Giå‘Ï©ùñßk]÷šhn‘Zµ2ŠÖÊ’§V1F\Ó‡£Æ˜ølV×Fuši©öÌy2Ì{¯¸µB\×{úÿÖ|zú5×7M,Zµ6Ñ·ñ^Zyäã¡Õ{ëq¤V­ŒJÚ´ÖñÛù©ã}®®<.¹ñÇfoB¤¶ó÷È ÜjÄ=ÌÛ¼^ãÃˆë ¼U3o­0âz­¬ îó[µÇˆë´Âˆë8õŽÂˆ1âÿÁZ¿¶Ì¾ËjmÆñû!ˆÖ #îmÝcçaı¼1âFüöX­Fbîˆk(ýãaÄ­¸#®×ÊÈ=´ºž/þ=â¿õÈÛkºeF<ȈKw%Ñ0â¡ß×­FlÕ #®×ÊjÄ^ZaÄßšaÄí5Ý2Ãňϻsáë·•[YöÜÍ<ä'Ójmέ âýþ)ýzýé n%ѼŒØS«^Fol,us7¿–ºóÖÊˈ#´jÕ,Z+¯|Ž8=çêºþE7|çÖRêжënFü”FùõÌ©1ð¨¸oøKë•®?ûýƒN‹ô¥õJ׿Öj[_º^ÚwëüÒøÒõ›VÝJë•®—øÔœ—ZƒôÈE!«#®9UýcÌF|~}3Ž–;“–Ù²ÞyÈZsQÊÇãŽØ[«Úf=J+Å|ztûªWK-_u¨5й´œ³¨|0bŒ¸ßFí3MFl_þïµ "bí§˜*ùô4ôÝõq$'UÝTêZE3Œ#Žì¥ØqJÃRmè-w3¥bôº®¢ÙÉæÜWÏ/&OO%Èç7Œ#Ž:k5q1bŒ¸¦N>Ç(ŸÚÕPhögIcı¹`ı¡|~M¥©¿#ĈëÊkv aÄq]¥ÆŒÂˆ1bseÍn¢Ê]1âºòš]C1F\W©1£0bŒØ\Y³›(FÜ.!šñhº¥jøãK-´ÚÇbÄq{ÕÜfÐÔy4m-¢Ù5Ä1wÄÖ¶ÌLj1bKýðŽ¸¢~Î!|KùûËL>1Fln„†ÿ±!S!¬D€'±÷§^+©+¸ŒXPR‚¦Àˆ1⡈ÅÍb€@1Fœ LcS<>(µ|bé÷EWc¤–OÕØYjŒÔò‰¥Ÿ,ºRCN†Žt!@v±! @Ý0ântL„ Ø `Äv†µxGSKJczièP›zÕ’bœŒxœ$4Šq¬=VB/Šãb ×8Ö¬äL#vúŽF1޵ÇJèåAq\ ôÇÚc%ôºPĈ=Jª.…WÇIez©(Q—zÕqR…^ñ”Z¤ð¦`ï^½ºÑM™ˆ^S°w/Š^qwñX&Rxzãç¢×xæ–ÑËBoü\ôˆÇWÝ?(¼)Ø»E¯ntS&¢×ìÝ‹¢FÜ]<–‰ž…Þø¹è5ž¹eEô²Ð?½0âñUÇñæ–Eizãç¢×xæ–Ñ #¶Ôs!@ÀH#ƈ%Ät@°Àˆ1bKý0€ àG€¿ÐÃ%‘ @Í0âfdL€ øÀˆýX–"ñN¤DHë:ziéQʽJ„¸.K#' bk•Ð˃â¸è5Ž5+9Àˆ~„£QŒcí±zyP½Æ±öX ½.1b’ª‹AáÕqR…^*JÔå^uœTF¡F<¥)¼)Ø»E¯ntS&¢×ìÝ‹¢FÜ]<–‰ž…Þø¹è5ž¹eEô²Ð?½0âñUÇß5=…¹eQ……Þø¹è5ž¹eEôˆ-õÓ=—ÂëF7e"zMÁÞ½(zu£›2½0b o \‹Ò(Ð+\Ùr¾0â\K¶€#€cÄ‹•4Û ‹FŒçªX²… u ðz¬«-;ƒ 0â"‘bj<‚Ë%z}럀zƈ ¾„¤€Ç±öXÉK/¯8{Z9†g¯8«²†O€²qÔ`#æ ŒùÄîÅÙ+θJ»’µ8c)Ž[͋󸌬„É«€½âŒÛùØ•¼ø¨ÅKqÜjjœ½òGpìJ^|¼âŒÝ}Ðjq؇°^…çgÜÎÇ®äÅG-ÎXŠãVSãì•Ï8‚cWòâãgìîƒVȃÀbÄãÀÞVò:àjq¦ ^X³W>Áئ…÷âãgÏ…1bOšß±¼ Ï+θ]É‹Zœ±Ç­¦ÆÙ+ŸqÇ®äÅÇ+ÎØÝ­†åŽxX±öXØ«!«Åñ`£Îª`ÄPƒCz„à4§…W㣖Ï4a^V㣖ϪzÁù¢,F¬Væå|(à1¯ÊJ0ƒõìA1_ tLjóUí%c #N]ÀA¯VbÂ^6#Àñf‚³]@Ð"€kéA6€ °Œx3ÁÙ. hÀˆµô ›õðN?—¦èÅw0†W,F<9 ~X± ®¸§SÂ÷¶âž<õZ™Ï´æŒOCß½ðÊÁco1ºÅy˜¨–ÚÞÔø¨åƒ^žDcaÄ¢Âß5ÍæúëZ×kçÏÏŸ¿úÿ#Viì1æ:÷þû·œžòùRM­‘zäƒ^ãÎ)zñhz\µý·F<¹yAk£x2ÃV½›cÁ^׸͵ӸK5kåcèÀšzy+k4è5V¯%V+5µ%6¹Ø&<û›î÷Ø×ß]»›doœ§ywùJ5kåã].Ö|¾æ÷rF¯w•Ñ+öƒŠ÷ùZ"^©©-±ÉÅ6±[£h­Q+ïr±æ“͈Ñë÷kš{-)~pÊ®—÷y¯U„)I²èhì¹>±£z0bšù#Œ8_ax4öc×Oïi¯ï·¾ÞuÝm>=ê<×°¾?~ËõM9+æsçwe^ÞjýúB¡¥/¢—¿&ËG´ÜòpD7hmg#¿n¯ö[ÓwsýúýýÖ×õJr5ßsì9¿T³|Ô¤ÿbp½vÿÒzÍQre½æ]|ÕRS[|û)·—Áhfæ8sí”õß+›Õ Ы½j`ÖÎLzƬÃ' …äÌhf„C ×PÜæÅÐËŒP+F¬¥Ç Ù<=*]a_«î½r)‹^¹ôªÊ#®ÂÄ @€@ Œ8†+Q!pà1b®Z@¯o½àPÏqTBBàB€Æ•«Ð #^±ñpäæi¹z¡—ùÐ  žÄÀˆ ‡ä ÐØƒKlhxµzVËg¨‹Á§R댸•ØüñŒx~úe VÏjùø‘ö‰ŽDÁˆ ‡ä `ÄÁ%64¼Z=«å3TŒŠÅàS©uFÜJlþxF<¿ ý2P«gµ|üHûD‚Gîˆ8Ž ÉAÈeÄ#k#ãZÔsFÕÈÙ•wÄ®8‡ój\^q¼6í•W¯}yÅQÛ—W>^q¼8{ÅQÛ—Z>^œ—ˆƒ/!c×&Ô¦Z>]P'©ñQË'}Wh5>jùtA]uF¼ª²å}©Lµ|ÊÇŽP㣖ÏX5Ê«©ñQ˧Lp£ñFbß¶ªv0ÕòQ« 5>jù ×7ôR«K>±°8Á©©Lµ|‚ñ7‡W㣖O3Ðà j|Ôò ÆŸ+ž¬=b©ñQËǃ±g 5>jùx²N #N/a÷Jó¸~þQ'¥|º7ºÈÄôÒ½´ôÎfDƒ•°qr¥Fq ©ã…päZ^9ŒSçfŒWÎ#×òÊydœ>5c¼r¹–WÎÛÄÁˆ·‘ú¯ÖÌš1^G®å•óÈ85|jÆxåjù4 ž ÆG-Ÿ`ü¹ÂcĹôòÌVí`ªåãÉÚ#–µ|<{ÆP㣖'ëô±0âôvo@í`ªåÓ 6h¢µ|‚°w‡U㣖O7Ø'bÄ+ªZ·'µƒ©–OÅq£Ôø¨å3N‰º•Ôø¨åSGq“Qñ&B³M@Ð$€kêBV€ ° Œx¡Ù& hÀˆ5u™Õñ>éüõöÇ×kÞ;±¨µ8UÑ+ŽmTd4‹"›0.Í1¡hƒR~2ÙûÏjŒ¸Ö°mkÙeÐ+Ÿ´h–O³Œ1â¬)‚–L´¦IÔn´´f]&Ybˆ^e†#G”ôz«ùšyOû(Í+]Ɇµn0â}K¢t0Kýé‘óõqÛAöúXûëŸJĈËuˆ^eFJ#JzÕ±ç«ÉG‰ßV¹`Ä[ÉýÇfKónªçäû?x‡|îÓ?•Øòïï«ÌóÎÑ+WE”ô:øiWg¬&Ÿ\„Ê#^HÌÆ­”féŽøþ‰þëýqÍ»åR>Û[nx‰ziI^Ò«æŽØóŒÕä£Ep£l0âľmµt0{ûÓã视S{_eúïˆïç¹ôá½âª¬t¾z¸W³š|âhù“F¼o”fY>5úÒ#ìÚ´¯2cŒøþhôíq(zõUbé|Õrõ:c5ùôí”Yf±aÚ¥ƒÙkÄO5Å£i{™ —áÈ%½,FÜsÆjòɇµ.0â}Ëáë`^¿¨õöz”ÆœÐîãžæ½5¥}Õù{çè•«JÆW:?×/6ÞG_I<{‹M¿­!„f@Z¥FÑšBÍôWLï|ZóWïͽb÷Ö«öúmWùÄÜ(:F¼‘ØÁ[=?…?ýq¦à¥ ßA½: Mž‚f“ˆZ#Ž"»gÜ·ÇÎ{ÒÐß5zéktÏÍòiVÌ#."b @ ŽFÇ–È€ "Œ¸ˆˆ€ 8q[õÈjߢTËGM?5>jù ×7ôR«K>±°8Á©©Lµ|‚ñ7‡W㣖O3Ðà j|Ôò ÆŸ+½óJ\¢â–ÖÍrÝÂÇ2#ÎR!<šN¨”Ê=‡ýœÓ3·´ƒˆ˜¥53]ïáƒ^óîÑëÈ6J³Þ|æÜheîˆ7û¶ÕžƒùÕ$Žk篚¿'·ôOöí«ÌóÎÑ+WEôèU2bËëÍ'õ¤ÙbÄI…sH»õ`ÞÇ?ÍÿSšßš‚T!Zù”x_›þ â:§4¿5ŸT°’íáSbnѬ' „¨!€×PZsLëÁôhw’_ÿîšÔûw…^ýìfÌlÕ«d²O–îs®wÌ×§SO±g0aÍñ¾¥ÑÚ(¾ù•âÛãëÒz¥ëû*õkç­|ÐknÅ´êujüõaõnÆ5Žß |.VÿƒFLAÔxk*_§[šDMŒ©'€^õ¬TF¢™ŠòÀˆ'@O¸dK“x»{;ïÐx_èÏØ{4ó&š(FœH¬I©^qÞMôL©öÐ÷Ç¥ÔŸ¿¨èåÏ4:"šEO#ˆô @`mñÚú²;@'€‹ ˜^Ï·:ÓiþVpd.бÑKQ•÷œÐ+—^S³Åˆ§âŸº8b*þæÅÑ«ÙÔ è5®Å1â\zyfK£ð¤ ½â{®€^ž4…/.ðÇöh¹´G/ô²P«Ë^–›‹/'iõ†Ô¦Z>Õ  Tã£–Ï ª—Q㣖O5ÈbÄ;¨ü¼Gµƒ©–Ze¨ñQ˽¾  —Z…\òÁˆ…Å NMí`ªåŒ¿9¼µ|šOP㣖O0þ\á1â\zyf«v0ÕòñdíKZ>Œ=c¨ñQËÇ“uúXqz »7 v0Õòé4QZ>AػêñQ˧ìŠ1âUeO€ †FœF*…  `Ä+ªÊž @ Œ8T$ @+ÀˆWT•=A€@q©ÜUû¥Z>îÀÕø¨åcÄë>]Z>îÀ3Ĉ3«gË]í`ªåc£ë?[Z>þÄmÕø¨åc£»ØlŒx1A¶c=˜Çüó—GYóiØzÊ¡V>è5VvôË;õj 45’ï&àÝØ»abôªÂ$3½d¤ˆO#Žg¼ê 4Š\Ê¢zå"°Q¶ñFb;o•Æî 48zv^Î@•ÃaÄÊêhçF£ÐÖçžz¡W.e‹o$¶óViìÎ@ƒÃ¡W0`çðèå T9F¬¬Žvn4 m}¸#Î¥zåÖË”=Fl·õdŒ8—üè…^¹l”-F¼‘ØÎ[¥±; ‡^Á€Ã£—3Påp±²:Ú¹Ñ(´õáQg.}Ð+·^¦ì1b¾­'cĹäG/ôÊE`£l1âÄvÞ*Ýhp8ô 콜*‡Ãˆ•ÕÑÎF¡­:séƒ^¹õ2e›ðm=#Î%?z¡W.e‹o$¶óViìÎ@ƒÃ¡W0`çðèå T9F¬¬Žvn4 m}xÔ™KôÊ­—){ŒØ„oëÉq.ùÑ ½rØ([Œx#±·Jcw½‚;‡G/g Êá0beuôr»6‡¯ì¨+ íÐKC‡Ú,Ы–Ôbãh˜‹ :`;5Í‚º DåèU Jdz‰12 æHÚk¬E£È¥#z¡W.f‹o(ºqË4v#ÀÁÓÑk0pãrèe˜q:FœQµù95 jj¾>÷ ÐKO“¯ŒÐ+—^ælišf„[ Qä’½Ð+Ͳň7Üi»4v'ƒÂ × ÐNË —È,a0â,JiåI£ÐÒ£” z•i]G/-=³ÁˆÃ/»ÀS³ žtåF/]mž2C¯\z™²¥qšðm=™F‘K~ôB¯\6Ê#ÞHlç­Ò؇C¯`ÀÎáÑ˨r8ŒXYíÜî‚ZB/m¹²ã|åÒË”-ÍÓ„oëÉ4Š\ò£zå"°Q¶ñFb;o•Æî 48zv^Î@•ÃaÄÊêhçF£ÐÖçžz¡W.e‹o$¶óV¯:r†½ †D¯@¸j¡i jŠäɇF‘G«#SôB¯\6Ê#ÞHlç­Ò؇C¯`ÀÎáÑ˨r8ŒXYrƒ å `ÄËKÌ!@@™F¬¬¹A€Àò0âå%fƒ€  L#VV‡Ü @`yñò³A@`·o9—¾ý|\ëÃ¥¹Ç¶ZÖ­‰7KÜ `ÄÔ o¦Z2Û¯>ü5÷4âûüëœÓ|¯cJ1}h¥šF\Š€> ¼™Þ1é©×ž†iÔOf‹2F,&é@© ÜM®ÆdkƼA©1Õ–œRÃÏš¼ÃúM½Úk\aF­éõŽ»ï#VPݘFl˜lºÒ;¬ëãºÚ¦” ·9]½JÌjÌÀ #I€ëãé§GÕç6jk¾Ä¶çziNÔ뤉¯£eÍNTÞa•šâ—š*z½™Géç55¹â˜Úw¿÷½½G~ãTzbÕsEM¤÷„KË’\ÉŸî~zû×'ÿëÝði8|«óYîÙz•ô )ÜDAK¯XZžt”>”¶Ä⃓ha̦UÛØ{ÇÝS¯‰S3&‰tèZ6½ãJzaÄ}åÑóáõiNég¥ë|ÐíÓoè,Œx(n‰Åf¿Ãêià&%1[¯š»¨Ò]Û$tS—ýºS=»?ºÿüzg}ßÌ9÷éØÛØ-§BÛuqŒxOåg¾Ãzk<š~¯Å™zÕ4oŒx^éaß3gÞ7X#Þ@ä‡-Î|‡Uó•Fñ§h3õˆµ{DÏY陣M!yvqr éÏz‡u]÷úØõºÅßÂÎÒëȤ¤G麡L™Z Púv?WOÅ<™F #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #include "menuYesNo.h" #define GEN_SIZE_OFFSET #include "int64inRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Hysterisis for alarm filtering: 1-1/e */ #define THRESHOLD 0.6321 /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(dbCommon *, int); static long process(dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL static long get_units(DBADDR *, char *); #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *, struct dbr_grDouble *); static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); static long get_alarm_double(DBADDR *, struct dbr_alDouble *); rset int64inRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,int64inRSET); struct int64indset { /* int64in input dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN read_int64in; /*returns: (-1,0)=>(failure,success)*/ }; static void checkAlarms(int64inRecord *prec, epicsTimeStamp *timeLast); static void monitor(int64inRecord *prec); static long readValue(int64inRecord *prec); static long init_record(dbCommon *pcommon, int pass) { int64inRecord *prec = (int64inRecord*)pcommon; struct int64indset *pdset; long status; if (pass == 0) return 0; /* int64in.siml and .siol must be a CONSTANT or a PV_LINK or a DB_LINK */ recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); recGblInitConstantLink(&prec->siol, DBF_INT64, &prec->sval); if(!(pdset = (struct int64indset *)(prec->dset))) { recGblRecordError(S_dev_noDSET,(void *)prec,"int64in: init_record"); return(S_dev_noDSET); } /* must have read_int64in function defined */ if( (pdset->number < 5) || (pdset->read_int64in == NULL) ) { recGblRecordError(S_dev_missingSup,(void *)prec,"int64in: init_record"); return(S_dev_missingSup); } if( pdset->init_record ) { if((status=(*pdset->init_record)(prec))) return(status); } prec->mlst = prec->val; prec->alst = prec->val; prec->lalm = prec->val; return(0); } static long process(dbCommon *pcommon) { int64inRecord *prec = (int64inRecord*)pcommon; struct int64indset *pdset = (struct int64indset *)(prec->dset); long status; unsigned char pact=prec->pact; epicsTimeStamp timeLast; if( (pdset==NULL) || (pdset->read_int64in==NULL) ) { prec->pact=TRUE; recGblRecordError(S_dev_missingSup,(void *)prec,"read_int64in"); return(S_dev_missingSup); } timeLast = prec->time; status=readValue(prec); /* read the new value */ /* check if device support set pact */ if ( !pact && prec->pact ) return(0); prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); if (status==0) prec->udf = FALSE; /* check for alarms */ checkAlarms(prec, &timeLast); /* check event list */ monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact=FALSE; return(status); } static long special(DBADDR *paddr, int after) { int64inRecord *prec = (int64inRecord *)(paddr->precord); int special_type = paddr->special; switch(special_type) { case(SPC_MOD): if (dbGetFieldIndex(paddr) == int64inRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return(0); } default: recGblDbaddrError(S_db_badChoice, paddr, "int64in: special"); return(S_db_badChoice); } } #define indexof(field) int64inRecord##field static long get_units(DBADDR *paddr, char *units) { int64inRecord *prec = (int64inRecord *) paddr->precord; if (paddr->pfldDes->field_type == DBF_INT64) { strncpy(units, prec->egu, DB_UNITS_SIZE); } return 0; } static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) { int64inRecord *prec=(int64inRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): case indexof(SVAL): pgd->upper_disp_limit = prec->hopr; pgd->lower_disp_limit = prec->lopr; break; default: recGblGetGraphicDouble(paddr,pgd); } return(0); } static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) { int64inRecord *prec=(int64inRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): case indexof(SVAL): pcd->upper_ctrl_limit = prec->hopr; pcd->lower_ctrl_limit = prec->lopr; break; default: recGblGetControlDouble(paddr,pcd); } return(0); } static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) { int64inRecord *prec=(int64inRecord *)paddr->precord; if(dbGetFieldIndex(paddr) == indexof(VAL)){ pad->upper_alarm_limit = prec->hihi; pad->upper_warning_limit = prec->high; pad->lower_warning_limit = prec->low; pad->lower_alarm_limit = prec->lolo; } else recGblGetAlarmDouble(paddr,pad); return(0); } static void checkAlarms(int64inRecord *prec, epicsTimeStamp *timeLast) { enum { range_Lolo = 1, range_Low, range_Normal, range_High, range_Hihi } alarmRange; static const epicsEnum16 range_stat[] = { SOFT_ALARM, LOLO_ALARM, LOW_ALARM, NO_ALARM, HIGH_ALARM, HIHI_ALARM }; double aftc, afvl; epicsInt64 val, hyst, lalm; epicsInt64 alev; epicsEnum16 asev; if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, prec->udfs); prec->afvl = 0; return; } val = prec->val; hyst = prec->hyst; lalm = prec->lalm; /* check VAL against alarm limits */ if ((asev = prec->hhsv) && (val >= (alev = prec->hihi) || ((lalm == alev) && (val >= alev - hyst)))) alarmRange = range_Hihi; else if ((asev = prec->llsv) && (val <= (alev = prec->lolo) || ((lalm == alev) && (val <= alev + hyst)))) alarmRange = range_Lolo; else if ((asev = prec->hsv) && (val >= (alev = prec->high) || ((lalm == alev) && (val >= alev - hyst)))) alarmRange = range_High; else if ((asev = prec->lsv) && (val <= (alev = prec->low) || ((lalm == alev) && (val <= alev + hyst)))) alarmRange = range_Low; else { alev = val; asev = NO_ALARM; alarmRange = range_Normal; } aftc = prec->aftc; afvl = 0; if (aftc > 0) { /* Apply level filtering */ afvl = prec->afvl; if (afvl == 0) { afvl = (double)alarmRange; } else { double t = epicsTimeDiffInSeconds(&prec->time, timeLast); double alpha = aftc / (t + aftc); /* The sign of afvl indicates whether the result should be * rounded up or down. This gives the filter hysteresis. * If afvl > 0 the floor() function rounds to a lower alarm * level, otherwise to a higher. */ afvl = alpha * afvl + ((afvl > 0) ? (1 - alpha) : (alpha - 1)) * alarmRange; if (afvl - floor(afvl) > THRESHOLD) afvl = -afvl; /* reverse rounding */ alarmRange = abs((int)floor(afvl)); switch (alarmRange) { case range_Hihi: asev = prec->hhsv; alev = prec->hihi; break; case range_High: asev = prec->hsv; alev = prec->high; break; case range_Normal: asev = NO_ALARM; break; case range_Low: asev = prec->lsv; alev = prec->low; break; case range_Lolo: asev = prec->llsv; alev = prec->lolo; break; } } } prec->afvl = afvl; if (asev) { /* Report alarm condition, store LALM for future HYST calculations */ if (recGblSetSevr(prec, range_stat[alarmRange], asev)) prec->lalm = alev; } else { /* No alarm condition, reset LALM */ prec->lalm = val; } } /* DELTA calculates the absolute difference between its arguments * expressed as an unsigned 32-bit integer */ #define DELTA(last, val) \ ((epicsUInt32) ((last) > (val) ? (last) - (val) : (val) - (last))) static void monitor(int64inRecord *prec) { unsigned short monitor_mask = recGblResetAlarms(prec); if (prec->mdel < 0 || DELTA(prec->mlst, prec->val) > (epicsUInt32) prec->mdel) { /* post events for value change */ monitor_mask |= DBE_VALUE; /* update last value monitored */ prec->mlst = prec->val; } if (prec->adel < 0 || DELTA(prec->alst, prec->val) > (epicsUInt32) prec->adel) { /* post events for archive value change */ monitor_mask |= DBE_LOG; /* update last archive value monitored */ prec->alst = prec->val; } /* send out monitors connected to the value field */ if (monitor_mask) db_post_events(prec, &prec->val, monitor_mask); } static long readValue(int64inRecord *prec) { struct int64indset *pdset = (struct int64indset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: status = pdset->read_int64in(prec); break; case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbGetLink(&prec->siol, DBR_INT64, &prec->sval, 0, 0); if (status == 0) { prec->val = prec->sval; prec->udf = FALSE; } prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } base-7.0.3.1/modules/database/src/std/rec/int64inRecord.dbd.pod0000664000577000060420000004117213557101274022654 0ustar anjaesctl#************************************************************************* # Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title 64bit Integer Input Record (int64in) This record type is normally used to obtain an integer value of up to 64 bits from a hardware input. The record supports alarm limits, alarm filtering, graphics and control limits. =head2 Parameter Fields The record-specific fields are described below. =recordtype int64in =cut recordtype(int64in) { =head3 Input Specification These fields control where the record will read data from when it is processed: =fields DTYP, INP The DTYP field selects which device support layer should be responsible for providing input data to the record. The int64in device support layers provided by EPICS Base are documented in the 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 is loaded for the record type, which will usually be the C support that comes with Base. The INP link field contains a database or channel access link or provides hardware address information that the device support uses to determine where the input data should come from. The format for the INP field value depends on the device support layer that is selected by the DTYP field. See L
for a description of the various hardware address formats supported. =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. They do not affect the functioning of the record. =over =item * DESC is a string that is usually used to briefly describe the record. =item * EGU is a string of up to 16 characters naming the engineering units that the VAL field represents. =item * The HOPR and LOPR fields set the upper and lower display limits for the VAL, HIHI, HIGH, LOW, and LOLO fields. =back =fields DESC, EGU, HOPR, LOPR =head3 Alarm Limits The user configures limit alarms by putting numerical values into the HIHI, HIGH, LOW and LOLO fields, and by setting the associated alarm severity in the corresponding HHSV, HSV, LSV and LLSV menu fields. The HYST field controls hysteresis to prevent alarm chattering from an input signal that is close to one of the limits and suffers from significant readout noise. The AFTC field sets the time constant on a low-pass filter that delays the reporting of limit alarms until the signal has been within the alarm range for that number of seconds (the default AFTC value of zero retains the previous behavior). The LALM field is used by the record at run-time to implement the alarm limit functionality. =fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, AFTC, LALM =head3 Monitor Parameters These parameters are used to determine when to send monitors placed on the VAL field. The monitors are sent when the current value exceeds the last transmitted value by the appropriate deadband. If these fields are set to zero, a monitor will be triggered every time the value changes; if set to -1, a monitor will be sent every time the record is processed. The ADEL field sets the deadband for archive monitors (C events), while the MDEL field controls value monitors (C events). The remaining fields are used by the record at run-time to implement the record monitoring deadband functionality. =fields ADEL, MDEL, ALST, MLST =cut include "dbCommon.dbd" field(VAL,DBF_INT64) { prompt("Current value") promptgroup("40 - Input") asl(ASL0) pp(TRUE) } field(INP,DBF_INLINK) { prompt("Input Specification") promptgroup("40 - Input") interest(1) } field(EGU,DBF_STRING) { prompt("Units name") promptgroup("80 - Display") interest(1) size(16) prop(YES) } field(HOPR,DBF_INT64) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_INT64) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(HIHI,DBF_INT64) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOLO,DBF_INT64) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HIGH,DBF_INT64) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOW,DBF_INT64) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { prompt("Lolo Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(HSV,DBF_MENU) { prompt("High Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(LSV,DBF_MENU) { prompt("Low Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(HYST,DBF_INT64) { prompt("Alarm Deadband") promptgroup("70 - Alarm") interest(1) } field(AFTC, DBF_DOUBLE) { prompt("Alarm Filter Time Constant") promptgroup("70 - Alarm") interest(1) } field(AFVL, DBF_DOUBLE) { prompt("Alarm Filter Value") special(SPC_NOMOD) interest(3) } field(ADEL,DBF_INT64) { prompt("Archive Deadband") promptgroup("80 - Display") interest(1) } field(MDEL,DBF_INT64) { prompt("Monitor Deadband") promptgroup("80 - Display") interest(1) } field(LALM,DBF_INT64) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(ALST,DBF_INT64) { prompt("Last Value Archived") special(SPC_NOMOD) interest(3) } field(MLST,DBF_INT64) { prompt("Last Val Monitored") special(SPC_NOMOD) interest(3) } =head3 Simulation Mode The record provides several fields to support simulation of absent hardware. If the SIML field is set it is used to read a value into the SIMM field, which controls whether simulation is used or not: =over =item * SIMM must be zero (C) for the record to request a value from the device support. =item * If SIMM is C and the SIOL link field is set, a simulated value in engineering units is read using the link into the SVAL field, from where it will subsequently be copied into the VAL field. =back The SIMS field can be set to give the record an alarm severity while it is in simulation mode. =fields SIML, SIMM, SIOL, SVAL, SIMS =cut field(SIOL,DBF_INLINK) { prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } field(SVAL,DBF_INT64) { prompt("Simulation Value") } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } } =head2 Record Support =head3 Record Support Routines The following are the record support routines that would be of interest to an application developer. Other routines are the C, C, C and C routines, which are used to collect properties from the record for the complex DBR data structures. =head4 init_record This routine first initializes the simulation mode mechanism by setting SIMM if SIML is a constant, and setting SVAL if SIOL is a constant. It then checks if the device support and the device support's C routine are defined. If either one does not exist, an error message is issued and processing is terminated. If device support includes C, it is called. Finally, the deadband mechanisms for monitors and level alarms are initialized. =head4 process See next section. =head3 Record Processing Routine C implements the following algorithm: =over =item 1. Check to see that the appropriate device support module and its C routine are defined. If either one does not exist, an error message is issued and processing is terminated with the PACT field set to TRUE, effectively blocking the record to avoid error storms. =item 2. Determine the value: If PACT is TRUE, call the device support C routine and return. If PACT is FALSE, read the value, honoring simulation mode: =over =item * Get SIMM by reading the SIML link. =item * If SIMM is C, call the device support C routine and return. =item * If SIMM is C, read the simulated value into SVAL using the SIOL link, then copy the value into VAL and set UDF to 0 on success. =item * Raise an alarm for other values of SIMM. =item * Set the record to the severity configured in SIMS. =back =item 3. If PACT has been changed to TRUE, the device support signals asynchronous processing: its C output routine has started, but not completed reading the new value. In this case, the processing routine merely returns, leaving PACT TRUE. =item 4. Set PACT to TRUE. Get the processing time stamp. Set UDF to 0 if reading the value was successful. =item 5. Check UDF and level alarms: This routine checks to see if the record is undefined (UDF is TRUE) or if the new VAL causes the alarm status and severity to change. In the latter case, NSEV, NSTA and LALM are set. It also honors the alarm hysteresis factor (HYST): the value must change by at least HYST between level alarm status and severity changes. If AFTC is set, alarm level filtering is applied. =item 6. Check to see if monitors should be invoked: =over =item * Alarm monitors are posted if the alarm status or severity have changed. =item * Archive and value change monitors are posted if ADEL and MDEL conditions (see L) are met. =back =item 7. Scan (process) forward link if necessary, set PACT to FALSE, and return. =back =head2 Device Support =head3 Device Support Interface The record requires device support to provide an entry table (dset) which defines the following members: typedef struct { long number; long (*report)(int level); long (*init)(int after); long (*init_record)(int64inRecord *prec); long (*get_ioint_info)(int cmd, int64inRecord *prec, IOSCANPVT *piosl); long (*read_int64in)(int64inRecord *prec); } int64indset; The module must set C to at least 5, and provide a pointer to its C routine; the other function pointers may be C if their associated functionality is not required for this support layer. Most device supports also provide an C routine to configure the record instance and connect it to the hardware or driver support layer. The individual routines are described below. =head3 Device Support Routines =head4 long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. =head4 long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. =head4 long init_record(int64inRecord *prec) This optional routine is called by the record initialization code for each int64in record instance that has its DTYP field set to use this device support. It is normally used to check that the INP address is the expected type and that it points to a valid device; to allocate any record-specific buffer space and other memory; and to connect any communication channels needed for the C routine to work properly. =head4 long get_ioint_info(int cmd, int64inRecord *prec, IOSCANPVT *piosl) This optional routine is called whenever the record's SCAN field is being changed to or from the value C to find out which I/O Interrupt Scan list the record should be added to or deleted from. If this routine is not provided, it will not be possible to set the SCAN field to the value C at all. The C parameter is zero when the record is being added to the scan list, and one when it is being removed from the list. The routine must determine which interrupt source the record should be connected to, which it indicates by the scan list that it points the location at C<*piosl> to before returning. It can prevent the SCAN field from being changed at all by returning a non-zero value to its caller. In most cases the device support will create the I/O Interrupt Scan lists that it returns for itself, by calling C once for each separate interrupt source. That routine allocates memory and inializes the list, then passes back a pointer to the new list in the location at C<*piosl>. When the device support receives notification that the interrupt has occurred, it announces that to the IOC by calling C which will arrange for the appropriate records to be processed in a suitable thread. The C routine is safe to call from an interrupt service routine on embedded architectures (vxWorks and RTEMS). =head4 long read_int64in(int64inRecord *prec) This essential routine is called when the record wants a new value from the addressed device. It is responsible for performing (or at least initiating) a read operation, and (eventually) returning its value to the record. If the device may take more than a few microseconds to return the new value, this routine must never block (busy-wait), but use the asynchronous processing mechanism. In that case it signals the asynchronous operation by setting the record's PACT field to TRUE before it returns, having arranged for the record's C routine to be called later once the read operation is finished. When that happens, the C routine will be called again with PACT still set to TRUE; it should then set it to FALSE to indicate the read has completed, and return. A return value of zero indicates success, any other value indicates that an error occurred. =head3 Extended Device Support ... =cut =head2 Device Support For Soft Records Two soft device support modules, Soft Channel and Soft Callback Channel, are provided for input records not related to actual hardware devices. The INP link type must be either a CONSTANT, DB_LINK, or CA_LINK. =head3 Soft Channel This module reads the value using the record's INP link. C calls C to read the value. =head3 Soft Callback Channel This module is like the previous except that it reads the value using asynchronous processing that will not complete until an asynchronous processing of the INP target record has completed. =cut base-7.0.3.1/modules/database/src/std/rec/int64outRecord.c0000664000577000060420000002756513557101274021777 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Author: Janet Anderson * Date: 9/23/91 */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #include "menuYesNo.h" #include "menuIvoa.h" #include "menuOmsl.h" #define GEN_SIZE_OFFSET #include "int64outRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(dbCommon *, int); static long process(dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL static long get_units(DBADDR *, char *); #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *, struct dbr_grDouble *); static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); static long get_alarm_double(DBADDR *, struct dbr_alDouble *); rset int64outRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,int64outRSET); struct int64outdset { /* int64out input dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN write_int64out;/*(-1,0)=>(failure,success*/ }; static void checkAlarms(int64outRecord *prec); static void monitor(int64outRecord *prec); static long writeValue(int64outRecord *prec); static void convert(int64outRecord *prec, epicsInt64 value); static long init_record(dbCommon *pcommon, int pass) { int64outRecord *prec = (int64outRecord*)pcommon; struct int64outdset *pdset; long status=0; if (pass == 0) return 0; recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if(!(pdset = (struct int64outdset *)(prec->dset))) { recGblRecordError(S_dev_noDSET,(void *)prec,"int64out: init_record"); return(S_dev_noDSET); } /* must have write_int64out functions defined */ if( (pdset->number < 5) || (pdset->write_int64out == NULL) ) { recGblRecordError(S_dev_missingSup,(void *)prec,"int64out: init_record"); return(S_dev_missingSup); } if (prec->dol.type == CONSTANT) { if(recGblInitConstantLink(&prec->dol,DBF_INT64,&prec->val)) prec->udf=FALSE; } if( pdset->init_record ) { if((status=(*pdset->init_record)(prec))) return(status); } prec->mlst = prec->val; prec->alst = prec->val; prec->lalm = prec->val; return(0); } static long process(dbCommon *pcommon) { int64outRecord *prec = (int64outRecord*)pcommon; struct int64outdset *pdset = (struct int64outdset *)(prec->dset); long status=0; epicsInt64 value; unsigned char pact=prec->pact; if( (pdset==NULL) || (pdset->write_int64out==NULL) ) { prec->pact=TRUE; recGblRecordError(S_dev_missingSup,(void *)prec,"write_int64out"); return(S_dev_missingSup); } if (!prec->pact) { if((prec->dol.type != CONSTANT) && (prec->omsl == menuOmslclosed_loop)) { status = dbGetLink(&(prec->dol),DBR_INT64, &value,0,0); if (prec->dol.type!=CONSTANT && RTN_SUCCESS(status)) prec->udf=FALSE; } else { value = prec->val; } if (!status) convert(prec,value); } /* check for alarms */ checkAlarms(prec); if (prec->nsev < INVALID_ALARM ) status=writeValue(prec); /* write the new value */ else { switch (prec->ivoa) { case (menuIvoaContinue_normally) : status=writeValue(prec); /* write the new value */ break; case (menuIvoaDon_t_drive_outputs) : break; case (menuIvoaSet_output_to_IVOV) : if(prec->pact == FALSE){ prec->val=prec->ivov; } status=writeValue(prec); /* write the new value */ break; default : status=-1; recGblRecordError(S_db_badField,(void *)prec, "int64out:process Illegal IVOA field"); } } /* check if device support set pact */ if ( !pact && prec->pact ) return(0); prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, NULL); /* check event list */ monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact=FALSE; return(status); } static long special(DBADDR *paddr, int after) { int64outRecord *prec = (int64outRecord *)(paddr->precord); int special_type = paddr->special; switch(special_type) { case(SPC_MOD): if (dbGetFieldIndex(paddr) == int64outRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return(0); } default: recGblDbaddrError(S_db_badChoice, paddr, "int64out: special"); return(S_db_badChoice); } } #define indexof(field) int64outRecord##field static long get_units(DBADDR *paddr,char *units) { int64outRecord *prec=(int64outRecord *)paddr->precord; if(paddr->pfldDes->field_type == DBF_INT64) { strncpy(units,prec->egu,DB_UNITS_SIZE); } return(0); } static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd) { int64outRecord *prec=(int64outRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): pgd->upper_disp_limit = prec->hopr; pgd->lower_disp_limit = prec->lopr; break; default: recGblGetGraphicDouble(paddr,pgd); } return(0); } static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd) { int64outRecord *prec=(int64outRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): /* do not change pre drvh/drvl behavior */ if(prec->drvh > prec->drvl) { pcd->upper_ctrl_limit = prec->drvh; pcd->lower_ctrl_limit = prec->drvl; } else { pcd->upper_ctrl_limit = prec->hopr; pcd->lower_ctrl_limit = prec->lopr; } break; default: recGblGetControlDouble(paddr,pcd); } return(0); } static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad) { int64outRecord *prec=(int64outRecord *)paddr->precord; if(dbGetFieldIndex(paddr) == indexof(VAL)) { pad->upper_alarm_limit = prec->hihi; pad->upper_warning_limit = prec->high; pad->lower_warning_limit = prec->low; pad->lower_alarm_limit = prec->lolo; } else recGblGetAlarmDouble(paddr,pad); return(0); } static void checkAlarms(int64outRecord *prec) { epicsInt64 val, hyst, lalm; epicsInt64 alev; epicsEnum16 asev; if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, prec->udfs); return; } val = prec->val; hyst = prec->hyst; lalm = prec->lalm; /* alarm condition hihi */ asev = prec->hhsv; alev = prec->hihi; if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { if (recGblSetSevr(prec, HIHI_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition lolo */ asev = prec->llsv; alev = prec->lolo; if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { if (recGblSetSevr(prec, LOLO_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition high */ asev = prec->hsv; alev = prec->high; if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { if (recGblSetSevr(prec, HIGH_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition low */ asev = prec->lsv; alev = prec->low; if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { if (recGblSetSevr(prec, LOW_ALARM, asev)) prec->lalm = alev; return; } /* we get here only if val is out of alarm by at least hyst */ prec->lalm = val; return; } /* DELTA calculates the absolute difference between its arguments * expressed as an unsigned 64-bit integer */ #define DELTA(last, val) \ ((epicsUInt64) ((last) > (val) ? (last) - (val) : (val) - (last))) static void monitor(int64outRecord *prec) { unsigned short monitor_mask = recGblResetAlarms(prec); if (prec->mdel < 0 || DELTA(prec->mlst, prec->val) > (epicsUInt64) prec->mdel) { /* post events for value change */ monitor_mask |= DBE_VALUE; /* update last value monitored */ prec->mlst = prec->val; } if (prec->adel < 0 || DELTA(prec->alst, prec->val) > (epicsUInt64) prec->adel) { /* post events for archive value change */ monitor_mask |= DBE_LOG; /* update last archive value monitored */ prec->alst = prec->val; } /* send out monitors connected to the value field */ if (monitor_mask) db_post_events(prec, &prec->val, monitor_mask); } static long writeValue(int64outRecord *prec) { struct int64outdset *pdset = (struct int64outdset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: status = pdset->write_int64out(prec); break; case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbPutLink(&prec->siol, DBR_INT64, &prec->val, 1); prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } static void convert(int64outRecord *prec, epicsInt64 value) { /* check drive limits */ if(prec->drvh > prec->drvl) { if (value > prec->drvh) value = prec->drvh; else if (value < prec->drvl) value = prec->drvl; } prec->val = value; } base-7.0.3.1/modules/database/src/std/rec/int64outRecord.dbd.pod0000664000577000060420000004356113557101274023061 0ustar anjaesctl#************************************************************************* # Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title 64bit Integer Output Record (int64out) This record type is normally used to send an integer value of up to 64 bits to an output device. The record supports alarm, drive, graphics and control limits. =head2 Parameter Fields The record-specific fields are described below. =recordtype int64out =cut recordtype(int64out) { =head3 Output Value Determination These fields control how the record determines the value to be output when it gets processed: =fields OMSL, DOL, DRVH, DRVL, VAL The following steps are performed in order during record processing. =head4 Fetch Value The OMSL menu field is used to determine whether the DOL link field should be used during processing or not: =over =item * If OMSL is C the DOL link field is not used. The new output value is taken from the VAL field, which may have been set from elsewhere. =item * If OMSL is C the DOL link field is used to obtain a value. =back =head4 Drive Limits The output value is clipped to the range DRVL to DRVH inclusive, provided that DRVH > DRVL. The result is copied into the VAL field. =head3 Output Specification These fields control where the record will read data from when it is processed: =fields DTYP, OUT The DTYP field selects which device support layer should be responsible for writing output data. The int64out device support layers provided by EPICS Base are documented in the 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 is loaded for the record type, which will usually be the C support that comes with Base. The OUT link field contains a database or channel access link or provides hardware address information that the device support uses to determine where the output data should be sent to. The format for the OUT field value depends on the device support layer that is selected by the DTYP field. See L
for a description of the various hardware address formats supported. =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. They do not affect the functioning of the record. =over =item * DESC is a string that is usually used to briefly describe the record. =item * EGU is a string of up to 16 characters naming the engineering units that the VAL field represents. =item * The HOPR and LOPR fields set the upper and lower display limits for the VAL, HIHI, HIGH, LOW, and LOLO fields. =back =fields DESC, EGU, HOPR, LOPR =head3 Alarm Limits The user configures limit alarms by putting numerical values into the HIHI, HIGH, LOW and LOLO fields, and by setting the associated alarm severities in the corresponding HHSV, HSV, LSV and LLSV menu fields. The HYST field controls hysteresis to prevent alarm chattering from an input signal that is close to one of the limits and suffers from significant readout noise. The LALM field is used by the record at run-time to implement the alarm limit hysteresis functionality. =fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, LALM =head3 Monitor Parameters These parameters are used to determine when to send monitors placed on the VAL field. The monitors are sent when the current value exceeds the last transmitted value by the appropriate deadband. If these fields are set to zero, a monitor will be triggered every time the value changes; if set to -1, a monitor will be sent every time the record is processed. The ADEL field sets the deadband for archive monitors (C events), while the MDEL field controls value monitors (C events). The remaining fields are used by the record at run-time to implement the record monitoring deadband functionality. =fields ADEL, MDEL, ALST, MLST =cut include "dbCommon.dbd" field(VAL,DBF_INT64) { prompt("Desired Output") promptgroup("50 - Output") asl(ASL0) pp(TRUE) } field(OUT,DBF_OUTLINK) { prompt("Output Specification") promptgroup("50 - Output") interest(1) } field(DOL,DBF_INLINK) { prompt("Desired Output Loc") promptgroup("40 - Input") interest(1) } field(OMSL,DBF_MENU) { prompt("Output Mode Select") promptgroup("50 - Output") interest(1) menu(menuOmsl) } field(EGU,DBF_STRING) { prompt("Units name") promptgroup("80 - Display") interest(1) size(16) prop(YES) } field(DRVH,DBF_INT64) { prompt("Drive High Limit") promptgroup("30 - Action") pp(TRUE) interest(1) prop(YES) } field(DRVL,DBF_INT64) { prompt("Drive Low Limit") promptgroup("30 - Action") pp(TRUE) interest(1) prop(YES) } field(HOPR,DBF_INT64) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_INT64) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(HIHI,DBF_INT64) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOLO,DBF_INT64) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HIGH,DBF_INT64) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOW,DBF_INT64) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { prompt("Lolo Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(HSV,DBF_MENU) { prompt("High Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(LSV,DBF_MENU) { prompt("Low Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(HYST,DBF_INT64) { prompt("Alarm Deadband") promptgroup("70 - Alarm") interest(1) } field(ADEL,DBF_INT64) { prompt("Archive Deadband") promptgroup("80 - Display") interest(1) } field(MDEL,DBF_INT64) { prompt("Monitor Deadband") promptgroup("80 - Display") interest(1) } field(LALM,DBF_INT64) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(ALST,DBF_INT64) { prompt("Last Value Archived") special(SPC_NOMOD) interest(3) } field(MLST,DBF_INT64) { prompt("Last Val Monitored") special(SPC_NOMOD) interest(3) } =head3 Simulation Mode The record provides several fields to support simulation of absent hardware. If the SIML field is set it is used to read a value into the SIMM field, which controls whether simulation is used or not: =over =item * SIMM must be zero (C) for the record to write a value to the device support. =item * If SIMM is C and the SIOL link field is set, the value in engineering units is written using the link. =back The SIMS field can be set to give the record an alarm severity while it is in simulation mode. =fields SIML, SIMM, SIOL, SIMS =cut field(SIOL,DBF_OUTLINK) { prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } =head3 Invalid Alarm Output Action Whenever an output record is put into INVALID alarm severity, IVOA specifies the action to take. =over =item C (default) Write the value. Same as if severity is lower than INVALID. =item C Do not write value. =item C Set VAL to IVOV, then write the value. =back =fields IVOA, IVOV =cut field(IVOA,DBF_MENU) { prompt("INVALID output action") promptgroup("50 - Output") interest(2) menu(menuIvoa) } field(IVOV,DBF_INT64) { prompt("INVALID output value") promptgroup("50 - Output") interest(2) } } =head2 Record Support =head3 Record Support Routines The following are the record support routines that would be of interest to an application developer. Other routines are the C, C, C and C routines, which are used to collect properties from the record for the complex DBR data structures. =head4 init_record This routine first initializes the simulation mode mechanism by setting SIMM if SIML is a constant. It then checks if the device support and the device support's C routine are defined. If either one does not exist, an error message is issued and processing is terminated. If DOL is a constant, then VAL is initialized with its value and UDF is set to FALSE. If device support includes C, it is called. Finally, the deadband mechanisms for monitors and level alarms are initialized. =head4 process See next section. =head3 Record Processing Routine C implements the following algorithm: =over =item 1. Check to see that the appropriate device support module and its C routine are defined. If either one does not exist, an error message is issued and processing is terminated with the PACT field set to TRUE, effectively blocking the record to avoid error storms. =item 2. Check PACT. If PACT is FALSE, do the following: =over =item * Determine value, honoring closed loop mode: if DOL is not a CONSTANT and OMSL is CLOSED_LOOP then get value from DOL setting UDF to FALSE in case of success, else use the VAL field. =item * Call C: if drive limits are defined then force value to be within those limits. =back =item 3. Check UDF and level alarms: This routine checks to see if the record is undefined (UDF is TRUE) or if the new VAL causes the alarm status and severity to change. In the latter case, NSEV, NSTA and LALM are set. It also honors the alarm hysteresis factor (HYST): the value must change by at least HYST between level alarm status and severity changes. =item 4. Check severity and write the new value. See L for details on how invalid alarms affect output records. =item 5. If PACT has been changed to TRUE, the device support signals asynchronous processing: its C output routine has started, but not completed writing the new value. In this case, the processing routine merely returns, leaving PACT TRUE. =item 6. Check to see if monitors should be invoked: =over =item * Alarm monitors are posted if the alarm status or severity have changed. =item * Archive and value change monitors are posted if ADEL and MDEL conditions (see L) are met. =item * NSEV and NSTA are reset to 0. =back =item 7. Scan (process) forward link if necessary, set PACT to FALSE, and return. =back =head2 Device Support =head3 Device Support Interface The record requires device support to provide an entry table (dset) which defines the following members: typedef struct { long number; long (*report)(int level); long (*init)(int after); long (*init_record)(int64outRecord *prec); long (*get_ioint_info)(int cmd, int64outRecord *prec, IOSCANPVT *piosl); long (*write_int64out)(int64outRecord *prec); } int64outdset; The module must set C to at least 5, and provide a pointer to its C routine; the other function pointers may be C if their associated functionality is not required for this support layer. Most device supports also provide an C routine to configure the record instance and connect it to the hardware or driver support layer. The individual routines are described below. =head3 Device Support Routines =head4 long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. =head4 long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. =head4 long init_record(int64outRecord *prec) This optional routine is called by the record initialization code for each int64out record instance that has its DTYP field set to use this device support. It is normally used to check that the OUT address is the expected type and that it points to a valid device, to allocate any record-specific buffer space and other memory, and to connect any communication channels needed for the C routine to work properly. =head4 long get_ioint_info(int cmd, int64outRecord *prec, IOSCANPVT *piosl) This optional routine is called whenever the record's SCAN field is being changed to or from the value C to find out which I/O Interrupt Scan list the record should be added to or deleted from. If this routine is not provided, it will not be possible to set the SCAN field to the value C at all. The C parameter is zero when the record is being added to the scan list, and one when it is being removed from the list. The routine must determine which interrupt source the record should be connected to, which it indicates by the scan list that it points the location at C<*piosl> to before returning. It can prevent the SCAN field from being changed at all by returning a non-zero value to its caller. In most cases the device support will create the I/O Interrupt Scan lists that it returns for itself, by calling C once for each separate interrupt source. That routine allocates memory and inializes the list, then passes back a pointer to the new list in the location at C<*piosl>. When the device support receives notification that the interrupt has occurred, it announces that to the IOC by calling C which will arrange for the appropriate records to be processed in a suitable thread. The C routine is safe to call from an interrupt service routine on embedded architectures (vxWorks and RTEMS). =head4 long write_int64out(int64outRecord *prec) This essential routine is called when the record wants to write a new value to the addressed device. It is responsible for performing (or at least initiating) a write operation, using the value from the record's VAL field. If the device may take more than a few microseconds to accept the new value, this routine must never block (busy-wait), but use the asynchronous processing mechanism. In that case it signals the asynchronous operation by setting the record's PACT field to TRUE before it returns, having arranged for the record's C routine to be called later once the write operation is over. When that happens, the C routine will be called again with PACT still set to TRUE; it should then set it to FALSE to indicate the write has completed, and return. A return value of zero indicates success, any other value indicates that an error occurred. =head3 Extended Device Support ... =cut =head2 Device Support For Soft Records Two soft device support modules, Soft Channel and Soft Callback Channel, are provided for output records not related to actual hardware devices. The OUT link type must be either a CONSTANT, DB_LINK, or CA_LINK. =head3 Soft Channel This module writes the current value using the record's VAL field. C calls C to write the current value. =head3 Soft Callback Channel This module is like the previous except that it writes the current value using asynchronous processing that will not complete until an asynchronous processing of the target record has completed. =cut base-7.0.3.1/modules/database/src/std/rec/longinRecord.c0000664000577000060420000003051413557101274021555 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* recLongin.c - Record Support Routines for Longin records */ /* * Author: Janet Anderson * Date: 9/23/91 */ #include #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "epicsMath.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #include "menuYesNo.h" #define GEN_SIZE_OFFSET #include "longinRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Hysterisis for alarm filtering: 1-1/e */ #define THRESHOLD 0.6321 /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL static long get_units(DBADDR *, char *); #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *, struct dbr_grDouble *); static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); static long get_alarm_double(DBADDR *, struct dbr_alDouble *); rset longinRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,longinRSET); struct longindset { /* longin input dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN read_longin; /*returns: (-1,0)=>(failure,success)*/ }; static void checkAlarms(longinRecord *prec, epicsTimeStamp *timeLast); static void monitor(longinRecord *prec); static long readValue(longinRecord *prec); static long init_record(struct dbCommon *pcommon, int pass) { struct longinRecord *prec = (struct longinRecord *)pcommon; struct longindset *pdset = (struct longindset *) prec->dset; if (pass == 0) return 0; recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); recGblInitConstantLink(&prec->siol, DBF_LONG, &prec->sval); if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "longin: init_record"); return S_dev_noDSET; } /* must have read_longin function defined */ if ((pdset->number < 5) || (pdset->read_longin == NULL)) { recGblRecordError(S_dev_missingSup, prec, "longin: init_record"); return S_dev_missingSup; } if (pdset->init_record) { long status = pdset->init_record(prec); if (status) return status; } prec->mlst = prec->val; prec->alst = prec->val; prec->lalm = prec->val; return 0; } static long process(struct dbCommon *pcommon) { struct longinRecord *prec = (struct longinRecord *)pcommon; struct longindset *pdset = (struct longindset *)(prec->dset); long status; unsigned char pact=prec->pact; epicsTimeStamp timeLast; if( (pdset==NULL) || (pdset->read_longin==NULL) ) { prec->pact=TRUE; recGblRecordError(S_dev_missingSup,(void *)prec,"read_longin"); return(S_dev_missingSup); } timeLast = prec->time; status=readValue(prec); /* read the new value */ /* check if device support set pact */ if ( !pact && prec->pact ) return(0); prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); if (status==0) prec->udf = FALSE; /* check for alarms */ checkAlarms(prec, &timeLast); /* check event list */ monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact=FALSE; return(status); } static long special(DBADDR *paddr, int after) { longinRecord *prec = (longinRecord *)(paddr->precord); int special_type = paddr->special; switch(special_type) { case(SPC_MOD): if (dbGetFieldIndex(paddr) == longinRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return(0); } default: recGblDbaddrError(S_db_badChoice, paddr, "longin: special"); return(S_db_badChoice); } } #define indexof(field) longinRecord##field static long get_units(DBADDR *paddr,char *units) { longinRecord *prec=(longinRecord *)paddr->precord; if(paddr->pfldDes->field_type == DBF_LONG) { strncpy(units,prec->egu,DB_UNITS_SIZE); } return(0); } static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) { longinRecord *prec=(longinRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): case indexof(SVAL): pgd->upper_disp_limit = prec->hopr; pgd->lower_disp_limit = prec->lopr; break; default: recGblGetGraphicDouble(paddr,pgd); } return(0); } static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) { longinRecord *prec=(longinRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): case indexof(SVAL): pcd->upper_ctrl_limit = prec->hopr; pcd->lower_ctrl_limit = prec->lopr; break; default: recGblGetControlDouble(paddr,pcd); } return(0); } static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) { longinRecord *prec=(longinRecord *)paddr->precord; if (dbGetFieldIndex(paddr) == indexof(VAL)){ pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN; pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN; pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN; pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN; } else recGblGetAlarmDouble(paddr,pad); return 0; } static void checkAlarms(longinRecord *prec, epicsTimeStamp *timeLast) { enum { range_Lolo = 1, range_Low, range_Normal, range_High, range_Hihi } alarmRange; static const epicsEnum16 range_stat[] = { SOFT_ALARM, LOLO_ALARM, LOW_ALARM, NO_ALARM, HIGH_ALARM, HIHI_ALARM }; double aftc, afvl; epicsInt32 val, hyst, lalm; epicsInt32 alev; epicsEnum16 asev; if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, prec->udfs); prec->afvl = 0; return; } val = prec->val; hyst = prec->hyst; lalm = prec->lalm; /* check VAL against alarm limits */ if ((asev = prec->hhsv) && (val >= (alev = prec->hihi) || ((lalm == alev) && (val >= alev - hyst)))) alarmRange = range_Hihi; else if ((asev = prec->llsv) && (val <= (alev = prec->lolo) || ((lalm == alev) && (val <= alev + hyst)))) alarmRange = range_Lolo; else if ((asev = prec->hsv) && (val >= (alev = prec->high) || ((lalm == alev) && (val >= alev - hyst)))) alarmRange = range_High; else if ((asev = prec->lsv) && (val <= (alev = prec->low) || ((lalm == alev) && (val <= alev + hyst)))) alarmRange = range_Low; else { alev = val; asev = NO_ALARM; alarmRange = range_Normal; } aftc = prec->aftc; afvl = 0; if (aftc > 0) { /* Apply level filtering */ afvl = prec->afvl; if (afvl == 0) { afvl = (double)alarmRange; } else { double t = epicsTimeDiffInSeconds(&prec->time, timeLast); double alpha = aftc / (t + aftc); /* The sign of afvl indicates whether the result should be * rounded up or down. This gives the filter hysteresis. * If afvl > 0 the floor() function rounds to a lower alarm * level, otherwise to a higher. */ afvl = alpha * afvl + ((afvl > 0) ? (1 - alpha) : (alpha - 1)) * alarmRange; if (afvl - floor(afvl) > THRESHOLD) afvl = -afvl; /* reverse rounding */ alarmRange = abs((int)floor(afvl)); switch (alarmRange) { case range_Hihi: asev = prec->hhsv; alev = prec->hihi; break; case range_High: asev = prec->hsv; alev = prec->high; break; case range_Normal: asev = NO_ALARM; break; case range_Low: asev = prec->lsv; alev = prec->low; break; case range_Lolo: asev = prec->llsv; alev = prec->lolo; break; } } } prec->afvl = afvl; if (asev) { /* Report alarm condition, store LALM for future HYST calculations */ if (recGblSetSevr(prec, range_stat[alarmRange], asev)) prec->lalm = alev; } else { /* No alarm condition, reset LALM */ prec->lalm = val; } } /* DELTA calculates the absolute difference between its arguments * expressed as an unsigned 32-bit integer */ #define DELTA(last, val) \ ((epicsUInt32) ((last) > (val) ? (last) - (val) : (val) - (last))) static void monitor(longinRecord *prec) { unsigned short monitor_mask = recGblResetAlarms(prec); if (prec->mdel < 0 || DELTA(prec->mlst, prec->val) > (epicsUInt32) prec->mdel) { /* post events for value change */ monitor_mask |= DBE_VALUE; /* update last value monitored */ prec->mlst = prec->val; } if (prec->adel < 0 || DELTA(prec->alst, prec->val) > (epicsUInt32) prec->adel) { /* post events for archive value change */ monitor_mask |= DBE_LOG; /* update last archive value monitored */ prec->alst = prec->val; } /* send out monitors connected to the value field */ if (monitor_mask) db_post_events(prec, &prec->val, monitor_mask); } static long readValue(longinRecord *prec) { struct longindset *pdset = (struct longindset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: status = pdset->read_longin(prec); break; case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbGetLink(&prec->siol, DBR_LONG, &prec->sval, 0, 0); if (status == 0) { prec->val = prec->sval; prec->udf = FALSE; } prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } base-7.0.3.1/modules/database/src/std/rec/longinRecord.dbd.pod0000664000577000060420000003241313557101274022645 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Long Input Record (longin) The normal use for the long input record or "longin" record is to retrieve a long integer value of up to 32 bits. Device support routines are provided to support direct interfaces to hardware. In addition, the C<<< Soft Channel >>> device module is provided to obtain input via database or channel access links or via dbPutField or dbPutLink requests. =recordtype longin =cut recordtype(longin) { =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters The long input record has the standard fields for specifying under what circumstances the record will be processed. These fields are listed in L. In addition, L explains how these fields are used. Note that I/O event scanning is only supported for those card types that interrupt. =head3 Read Parameters The device support routines use the INP field to obtain the record's input. For records that obtain their input from devices, the INP field must contain the address of the I/O card, and the DTYP field must specify the proper device support module. Be aware that the address format differs according to the I/O bus used. For soft records, the INP can be a constant, a database link, or a channel access link. The value is read directly into VAL. The C<<< Soft Channel >>> device support module is available for longin records. See L
for information on the format of hardware addresses and a database links. =fields VAL, INP, DTYP =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. These fields are used to display the value and other parameters of the long input either textually or graphically. EGU is a string of up to 16 characters describing the units that the long input measures. It is retrieved by the C<<< get_units >>> record support routine. The HOPR and LOPR fields set the upper and lower display limits for the VAL, HIHI, HIGH, LOW, and LOLO fields. Both the C<<< get_graphic_double >>> and C<<< get_control_double >>> record support routines retrieve these fields. See L for more on the record name (NAME) and description (DESC) fields. =fields EGU, HOPR, LOPR, NAME, DESC =head3 Alarm Parameters The possible alarm conditions for long inputs are the SCAN, READ, and limit alarms. The SCAN and READ alarms are called by the record or device support routines. The limit alarms are configured by the user in the HIHI, LOLO, HIGH, and LOW fields using numerical values. For each of these fields, there is a corresponding severity field which can be either NO_ALARM, MINOR, or MAJOR. The HYST field can be used to specify a deadband around each limit. See L for a complete explanation of alarms and these fields. L lists other fields related to a alarms that are common to all record types. =fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST =head3 Monitor Parameters These parameters are used to determine when to send monitors placed on the value field. The monitors are sent when the value field exceeds the last monitored field (see the next section) by the appropriate deadband. If these fields have a value of zero, everytime the value changes, a monitor will be triggered; if they have a value of -1, everytime the record is scanned, monitors are triggered. The ADEL field is used by archive monitors and the MDEL field for all other types of monitors. See L for a complete explanation of monitors. =fields ADEL, MDEL =head3 Run-time and Simulation Mode Parameters The LALM, MLST, and ALST fields are used to implement the hysteresis factors for monitor callbacks. Only if the difference between these fields and the corresponding value field is greater than the appropriate delta (MDEL, ADEL, HYST)--only then are monitors triggered. For instance, only if the difference between VAL and MLST is greater than MDEL are the monitors triggered for VAL. =fields LALM, ALST, MLST The following fields are used to operate the long input in the simulation mode. See L for more information on these fields. =fields SIOL, SVAL, SIML, SIMM, SIMS =head2 Record Support =head3 Record Support Routines =head4 init_record This routine initializes SIMM with the value of SIML if SIML type is CONSTANT link or creates a channel access link if SIML type is PV_LINK. SVAL is likewise initialized if SIOL is CONSTANT or PV_LINK. This routine next checks to see that device support is available and a device support read routine is defined. If either does not exist, an error message is issued and processing is terminated. If device support includes C, it is called. =head4 process See next section. =head4 get_units Retrieves EGU. =head4 get_graphic_double Sets the upper display and lower display limits for a field. If the field is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. =head4 get_control_double Sets the upper control and the lower control limits for a field. If the field is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. =head4 get_alarm_double Sets the following values: upper_alarm_limit = HIHI upper_warning_limit = HIGH lower_warning_limit = LOW lower_alarm_limit = LOLO =head3 Record Processing Routine process implements the following algorithm: =over =item 1. Check to see that the appropriate device support module exists. If it doesn't, an error message is issued and processing is terminated with the PACT field still set to TRUE. This ensures that processes will no longer be called for this record. Thus error storms will not occur. =item 2. readValue is called. See L for more information. =item 3. If PACT has been changed to TRUE, the device support read routine has started but has not completed reading a new input value. In this case, the processing routine merely returns, leaving PACT TRUE. =item 4. Check alarms. This routine checks to see if the new VAL causes the alarm status and severity to change. If so, NSEV, NSTA and LALM are set. It also honors the alarm hysteresis factor (HYST). Thus the value must change by more than HYST before the alarm status and severity is lowered. =item 5. Check to see if monitors should be invoked: =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if ADEL and MDEL conditions are met. =item * NSEV and NSTA are reset to 0. =back =item 6. Scan forward link if necessary, set PACT FALSE, and return. =back =begin html


=end html =head2 Device Support =head3 Fields Of Interest To Device Support Each long input record must have an associated set of device support routines. The primary responsibility of the device support routines is to obtain a new input value whenever read_longin is called. The device support routines are primarily interested in the following fields: =fields PACT, DPVT, UDF, NSEV, NSTA, VAL, INP =head3 Device Support Routines Device support consists of the following routines: =head4 long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. =head4 long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. =head4 init_record init_record(precord) This routine is optional. If provided, it is called by the record support C routine. =head4 get_ioint_info get_ioint_info(int cmd,struct dbCommon *precord,IOSCANPVT *ppvt) This routine is called by the ioEventScan system each time the record is added or deleted from an I/O event scan list. cmd has the value (0,1) if the record is being (added to, deleted from) an I/O event list. It must be provided for any device type that can use the ioEvent scanner. =head4 read_longin read_longin(precord) This routine must provide a new input value. It returns the following values: =over =item * 0: Success. A new value is placed in VAL. =item * Other: Error. =back =head3 Device Support For Soft Records The C<<< Soft Channel >>> device support module places a value directly in VAL. If the INP link type is constant, then the constant value is stored into VAL by C, and UDF is set to FALSE. If the INP link type is PV_LINK, then dbCaAddInlink is called by C. C<<< read_longin >>> calls recGblGetLinkValue to read the current value of VAL. See L for more information If the return status of C<<< recGblGetLinkValue >>> is zero then read_longin sets UDF to FALSE. read_longin returns the status of C. =cut include "dbCommon.dbd" field(VAL,DBF_LONG) { prompt("Current value") promptgroup("40 - Input") asl(ASL0) pp(TRUE) } field(INP,DBF_INLINK) { prompt("Input Specification") promptgroup("40 - Input") interest(1) } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) prop(YES) } field(HOPR,DBF_LONG) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_LONG) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(HIHI,DBF_LONG) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOLO,DBF_LONG) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HIGH,DBF_LONG) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOW,DBF_LONG) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { prompt("Lolo Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(HSV,DBF_MENU) { prompt("High Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(LSV,DBF_MENU) { prompt("Low Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(HYST,DBF_LONG) { prompt("Alarm Deadband") promptgroup("70 - Alarm") interest(1) } field(AFTC, DBF_DOUBLE) { prompt("Alarm Filter Time Constant") promptgroup("70 - Alarm") interest(1) } field(AFVL, DBF_DOUBLE) { prompt("Alarm Filter Value") special(SPC_NOMOD) interest(3) } field(ADEL,DBF_LONG) { prompt("Archive Deadband") promptgroup("80 - Display") interest(1) } field(MDEL,DBF_LONG) { prompt("Monitor Deadband") promptgroup("80 - Display") interest(1) } field(LALM,DBF_LONG) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(ALST,DBF_LONG) { prompt("Last Value Archived") special(SPC_NOMOD) interest(3) } field(MLST,DBF_LONG) { prompt("Last Val Monitored") special(SPC_NOMOD) interest(3) } field(SIOL,DBF_INLINK) { prompt("Sim Input Specifctn") promptgroup("90 - Simulate") interest(1) } field(SVAL,DBF_LONG) { prompt("Simulation Value") } field(SIML,DBF_INLINK) { prompt("Sim Mode Location") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Sim mode Alarm Svrty") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } } base-7.0.3.1/modules/database/src/std/rec/longoutRecord.c0000664000577000060420000003000513557101274021751 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Janet Anderson * Date: 9/23/91 */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "epicsMath.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #include "menuYesNo.h" #include "menuIvoa.h" #include "menuOmsl.h" #define GEN_SIZE_OFFSET #include "longoutRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL static long get_units(DBADDR *, char *); #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *, struct dbr_grDouble *); static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); static long get_alarm_double(DBADDR *, struct dbr_alDouble *); rset longoutRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,longoutRSET); struct longoutdset { /* longout input dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN write_longout;/*(-1,0)=>(failure,success*/ }; static void checkAlarms(longoutRecord *prec); static void monitor(longoutRecord *prec); static long writeValue(longoutRecord *prec); static void convert(longoutRecord *prec, epicsInt32 value); static long init_record(struct dbCommon *pcommon, int pass) { struct longoutRecord *prec = (struct longoutRecord *)pcommon; struct longoutdset *pdset = (struct longoutdset *) prec->dset; if (pass == 0) return 0; recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "longout: init_record"); return S_dev_noDSET; } /* must have write_longout functions defined */ if ((pdset->number < 5) || (pdset->write_longout == NULL)) { recGblRecordError(S_dev_missingSup, prec, "longout: init_record"); return S_dev_missingSup; } if (recGblInitConstantLink(&prec->dol, DBF_LONG, &prec->val)) prec->udf=FALSE; if (pdset->init_record) { long status = pdset->init_record(prec); if (status) return status; } prec->mlst = prec->val; prec->alst = prec->val; prec->lalm = prec->val; return 0; } static long process(struct dbCommon *pcommon) { struct longoutRecord *prec = (struct longoutRecord *)pcommon; struct longoutdset *pdset = (struct longoutdset *)(prec->dset); long status=0; epicsInt32 value; unsigned char pact=prec->pact; if( (pdset==NULL) || (pdset->write_longout==NULL) ) { prec->pact=TRUE; recGblRecordError(S_dev_missingSup,(void *)prec,"write_longout"); return(S_dev_missingSup); } if (!prec->pact) { if (!dbLinkIsConstant(&prec->dol) && prec->omsl == menuOmslclosed_loop) { status = dbGetLink(&prec->dol, DBR_LONG, &value, 0, 0); if (!dbLinkIsConstant(&prec->dol) && !status) prec->udf=FALSE; } else { value = prec->val; } if (!status) convert(prec,value); } /* check for alarms */ checkAlarms(prec); if (prec->nsev < INVALID_ALARM ) status=writeValue(prec); /* write the new value */ else { switch (prec->ivoa) { case (menuIvoaContinue_normally) : status=writeValue(prec); /* write the new value */ break; case (menuIvoaDon_t_drive_outputs) : break; case (menuIvoaSet_output_to_IVOV) : if(prec->pact == FALSE){ prec->val=prec->ivov; } status=writeValue(prec); /* write the new value */ break; default : status=-1; recGblRecordError(S_db_badField,(void *)prec, "longout:process Illegal IVOA field"); } } /* check if device support set pact */ if ( !pact && prec->pact ) return(0); prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, NULL); /* check event list */ monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact=FALSE; return(status); } static long special(DBADDR *paddr, int after) { longoutRecord *prec = (longoutRecord *)(paddr->precord); int special_type = paddr->special; switch(special_type) { case(SPC_MOD): if (dbGetFieldIndex(paddr) == longoutRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return(0); } default: recGblDbaddrError(S_db_badChoice, paddr, "longout: special"); return(S_db_badChoice); } } #define indexof(field) longoutRecord##field static long get_units(DBADDR *paddr,char *units) { longoutRecord *prec=(longoutRecord *)paddr->precord; if(paddr->pfldDes->field_type == DBF_LONG) { strncpy(units,prec->egu,DB_UNITS_SIZE); } return(0); } static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd) { longoutRecord *prec=(longoutRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): pgd->upper_disp_limit = prec->hopr; pgd->lower_disp_limit = prec->lopr; break; default: recGblGetGraphicDouble(paddr,pgd); } return(0); } static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd) { longoutRecord *prec=(longoutRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): /* do not change pre drvh/drvl behavior */ if(prec->drvh > prec->drvl) { pcd->upper_ctrl_limit = prec->drvh; pcd->lower_ctrl_limit = prec->drvl; } else { pcd->upper_ctrl_limit = prec->hopr; pcd->lower_ctrl_limit = prec->lopr; } break; default: recGblGetControlDouble(paddr,pcd); } return(0); } static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad) { longoutRecord *prec=(longoutRecord *)paddr->precord; if (dbGetFieldIndex(paddr) == indexof(VAL)) { pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN; pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN; pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN; pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN; } else recGblGetAlarmDouble(paddr,pad); return 0; } static void checkAlarms(longoutRecord *prec) { epicsInt32 val, hyst, lalm; epicsInt32 alev; epicsEnum16 asev; if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, prec->udfs); return; } val = prec->val; hyst = prec->hyst; lalm = prec->lalm; /* alarm condition hihi */ asev = prec->hhsv; alev = prec->hihi; if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { if (recGblSetSevr(prec, HIHI_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition lolo */ asev = prec->llsv; alev = prec->lolo; if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { if (recGblSetSevr(prec, LOLO_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition high */ asev = prec->hsv; alev = prec->high; if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { if (recGblSetSevr(prec, HIGH_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition low */ asev = prec->lsv; alev = prec->low; if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { if (recGblSetSevr(prec, LOW_ALARM, asev)) prec->lalm = alev; return; } /* we get here only if val is out of alarm by at least hyst */ prec->lalm = val; return; } /* DELTA calculates the absolute difference between its arguments * expressed as an unsigned 32-bit integer */ #define DELTA(last, val) \ ((epicsUInt32) ((last) > (val) ? (last) - (val) : (val) - (last))) static void monitor(longoutRecord *prec) { unsigned short monitor_mask = recGblResetAlarms(prec); if (prec->mdel < 0 || DELTA(prec->mlst, prec->val) > (epicsUInt32) prec->mdel) { /* post events for value change */ monitor_mask |= DBE_VALUE; /* update last value monitored */ prec->mlst = prec->val; } if (prec->adel < 0 || DELTA(prec->alst, prec->val) > (epicsUInt32) prec->adel) { /* post events for archive value change */ monitor_mask |= DBE_LOG; /* update last archive value monitored */ prec->alst = prec->val; } /* send out monitors connected to the value field */ if (monitor_mask) db_post_events(prec, &prec->val, monitor_mask); } static long writeValue(longoutRecord *prec) { struct longoutdset *pdset = (struct longoutdset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: status = pdset->write_longout(prec); break; case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbPutLink(&prec->siol, DBR_LONG, &prec->val, 1); prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } static void convert(longoutRecord *prec, epicsInt32 value) { /* check drive limits */ if(prec->drvh > prec->drvl) { if (value > prec->drvh) value = prec->drvh; else if (value < prec->drvl) value = prec->drvl; } prec->val = value; } base-7.0.3.1/modules/database/src/std/rec/longoutRecord.dbd.pod0000664000577000060420000003704413557101274023053 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Long Output Record (longout) The normal use for the long output or "longout" record type is to store long integer values of up to 32 bits and write them to hardware devices. The C<<< Soft Channel >>> device support layer can also be used to write values to other records via database or channel access links. The OUT field determines how the record is used. The record supports alarm limits and graphics and control limits. =recordtype longout =cut recordtype(longout) { =head2 Parameter Fields The fields in this record fall into the following categories: =over =item * L =item * L =item * L =item * L =item * L =item * L =item * L =back =head3 Scan Parameters The longout record has the standard fields for specifying under what circumstances it will be processed. These fields are listed in L. In addition, L explains how these fields are used. Note that I/O event scanning is only supported for those card types that interrupt. =head3 Desired Output Parameters The record must specify where the desired output originates, i.e., the 32 bit integer value it is to write. The output mode select (OMSL) field determines whether the output originates from another record or from database access. When set to C<<< closed_loop >>>, the desired output is retrieved from the link specified in the desired output (DOL) field (which can specify either a database or channel access link) and placed into the VAL field. When set to C<<< supervisory >>>, the desired output can be written into the VAL field via dpPuts at run-time. A third type of value for the DOL field is a constant in which case, when the record is initialized, the VAL field will be initialized with this constant value. The VAL field's value will be clipped within limits specified in the fields DRVH and DRVL if these have been configured by the database designer: DRVL <= VAL <= DRVH Note: These limits are only enforced as long as DRVH E DRVL. If they are not set or DRVH E= DRVL they will not be used. =fields DOL, OMSL, DRVH, DRVL, VAL =head3 Write Parameters The OUT link field determines where the record is to send its output. For records that write values to hardware devices, the OUT output link field must specify the address of the I/O card, and the DTYP field must specify the name of the corresponding device support module. For soft records, the OUT output link can be a constant, a database link, or a channel access link. If the link is a constant, the result is no output. The DTYP field must then specify the C<<< Soft Channel >>> device support routine. See L
for information on the format of hardware addresses and database links. =fields OUT, DTYP =cut include "dbCommon.dbd" field(VAL,DBF_LONG) { prompt("Desired Output") promptgroup("50 - Output") asl(ASL0) pp(TRUE) } field(OUT,DBF_OUTLINK) { prompt("Output Specification") promptgroup("50 - Output") interest(1) } field(DOL,DBF_INLINK) { prompt("Desired Output Loc") promptgroup("40 - Input") interest(1) } field(OMSL,DBF_MENU) { prompt("Output Mode Select") promptgroup("50 - Output") interest(1) menu(menuOmsl) } =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. They display the value and other parameters of the long output either textually or graphically. EGU is a string of up to 16 characters describing the units that the long output measures. It is retrieved by the C<<< get_units >>> record support routine. The HOPR and LOPR fields set the upper and lower display limits for the VAL, HIHI, HIGH, LOW, and LOLO fields. Both the C<<< get_graphic_double >>> and C<<< get_control_double >>> record support routines retrieve these fields. See L for more on the record name (NAME) and description (DESC) fields. =fields EGU, HOPR, LOPR, NAME, DESC =cut field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) prop(YES) } field(DRVH,DBF_LONG) { prompt("Drive High Limit") promptgroup("30 - Action") pp(TRUE) interest(1) prop(YES) } field(DRVL,DBF_LONG) { prompt("Drive Low Limit") promptgroup("30 - Action") pp(TRUE) interest(1) prop(YES) } field(HOPR,DBF_LONG) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_LONG) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } =head3 Alarm Parameters The possible alarm conditions for long inputs are the SCAN, READ, INVALID, and limit alarms. The SCAN and READ alarms are not configurable by the user because their severity is always MAJOR. The INVALID alarm is called by the record support routine when the record or device support routines cannot write the record's output. The IVOA field specifies the action to take in this case. The limit alarms are configured by the user in the HIHI, LOLO, HIGH, and LOW fields using floating-point values. For each of these fields, there is a corresponding severity field which can be either NO_ALARM, MINOR, or MAJOR. The HYST field contains the alarm deadband around each limit alarm. See the See L for a complete explanation of alarms and these fields. For an explanation of the IVOA and IVOV fields, see L. L lists other fields related to a alarms that are common to all record types. =fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, IVOA, IVOV =cut field(HIHI,DBF_LONG) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOLO,DBF_LONG) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HIGH,DBF_LONG) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOW,DBF_LONG) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { prompt("Lolo Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(HSV,DBF_MENU) { prompt("High Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(LSV,DBF_MENU) { prompt("Low Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(HYST,DBF_LONG) { prompt("Alarm Deadband") promptgroup("70 - Alarm") interest(1) } =head3 Monitor Parameters These parameters are used to determine when to send monitors placed on the value field. The monitors are sent when the value field exceeds the last monitored field by the appropriate delta. If these fields have a value of zero, everytime the value changes, a monitor will be triggered; if they have a value of -1, everytime the record is scanned, monitors are triggered. The ADEL field is the delta for archive monitors, and the MDEL field is the delta for all other types of monitors. See L for a complete explanation of monitors. =fields ADEL, MDEL =cut field(ADEL,DBF_LONG) { prompt("Archive Deadband") promptgroup("80 - Display") interest(1) } field(MDEL,DBF_LONG) { prompt("Monitor Deadband") promptgroup("80 - Display") interest(1) } field(LALM,DBF_LONG) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(ALST,DBF_LONG) { prompt("Last Value Archived") special(SPC_NOMOD) interest(3) } field(MLST,DBF_LONG) { prompt("Last Val Monitored") special(SPC_NOMOD) interest(3) } =head3 Run-time and Simulation Mode Parameters The LALM, MLST, and ALST fields are used to implement the hysteresis factors for monitor callbacks. Only if the difference between these fields and the corresponding value field is greater than the appropriate delta (MDEL, ADEL, HYST)--only then are monitors triggered. For instance, only if the difference between VAL and MLST is greater than MDEL are the monitors triggered for VAL. =fields LALM, ALST, MLST The following fields are used to operate the long output in the simulation mode. See L for more information on the simulation mode fields =fields SIOL, SIML, SIMM, SIMS =cut field(SIOL,DBF_OUTLINK) { prompt("Sim Output Specifctn") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { prompt("Sim Mode Location") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Sim mode Alarm Svrty") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } field(IVOA,DBF_MENU) { prompt("INVALID output action") promptgroup("50 - Output") interest(2) menu(menuIvoa) } field(IVOV,DBF_LONG) { prompt("INVALID output value") promptgroup("50 - Output") interest(2) } =begin html


=end html =head2 Record Support =head3 Record Support Routines =head4 init_record This routine initializes SIMM if SIML is a constant or creates a channel access link if SIML is PV_LINK. If SIOL is PV_LINK a channel access link is created. This routine next checks to see that device support is available. The routine next checks to see if the device support write routine is defined. If either device support or the device support write routine does not exist, an error message is issued and processing is terminated. If DOL is a constant, then VAL is initialized to its value and UDF is set to FALSE. If DOL type is a PV_LINK then dbCaAddInlink is called to create a channel access link. If device support includes C, it is called. =head4 process See next section. =head4 get_units Retrieves EGU. =head4 get_graphic_double Sets the upper display and lower display limits for a field. If the field is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. =head4 get_control_double Sets the upper control and the lower control limits for a field. If the field is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. =head4 get_alarm_double Sets the following values: upper_alarm_limit = HIHI upper_warning_limit = HIGH lower_warning_limit = LOW lower_alarm_limit = LOLO =head3 Record Processing Routine process implements the following algorithm: =over =item 1. Check to see that the appropriate device support module exists. If it doesn't, an error message is issued and processing is terminated with the PACT field still set to TRUE. This ensures that processes will no longer be called for this record. Thus error storms will not occur. =item 2. If PACT is FALSE and OMSL is CLOSED_LOOP recGblGetLinkValue is called to read the current value of VAL. See L for more information. If the return status of recGblGetLinkValue is zero then UDF is set to FALSE. =item 3. Check alarms. This routine checks to see if the new VAL causes the alarm status and severity to change. If so, NSEV, NSTA and LALM are set. It also honors the alarm hysteresis factor (HYST). Thus the value must change by more than HYST before the alarm status and severity is lowered. =item 4. Check severity and write the new value. See L for information on how INVALID alarms affect output records. =item 5. If PACT has been changed to TRUE, the device support write output routine has started but has not completed writing the new value. In this case, the processing routine merely returns, leaving PACT TRUE. =item 6. Check to see if monitors should be invoked: =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if ADEL and MDEL conditions are met. =item * NSEV and NSTA are reset to 0. =back =item 7. Scan forward link if necessary, set PACT FALSE, and return. =back =begin html


=end html =head2 Device Support =head3 Fields Of Interest To Device Support Each long output record must have an associated set of device support routines. The primary responsibility of the device support routines is to output a new value whenever write_longout is called. The device support routines are primarily interested in the following fields: =fields PACT, DPVT, NSEV, NSTA, OUT =head3 Device Support Routines Device support consists of the following routines: =head4 long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. =head4 long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. =head4 init_record init_record(precord) This routine is optional. If provided, it is called by the record support C routine. =head4 get_ioint_info get_ioint_info(int cmd,struct dbCommon *precord,IOSCANPVT *ppvt) This routine is called by the ioEventScan system each time the record is added or deleted from an I/O event scan list. cmd has the value (0,1) if the record is being (added to, deleted from) an I/O event list. It must be provided for any device type that can use the ioEvent scanner. =head4 write_longout write_longout(precord) This routine must output a new value. It returns the following values: =over =item * 0: Success. =item * Other: Error. =back =head3 Device Support For Soft Records The C<<< Soft Channel >>> module writes the current value of VAL. If the OUT link type is PV_LINK, then dbCaAddInlink is called by C. write_longout calls recGblPutLinkValue to write the current value of VAL. See L for a further explanation. =cut } #end of the DBD file base-7.0.3.1/modules/database/src/std/rec/lsiRecord.c0000664000577000060420000001705513557101274021063 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Long String Input record type */ /* * Author: Andrew Johnson * Date: 2012-11-27 */ #include #include #include #include "dbDefs.h" #include "errlog.h" #include "alarm.h" #include "callback.h" #include "cantProceed.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "errMdef.h" #include "menuPost.h" #include "menuYesNo.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #define GEN_SIZE_OFFSET #include "lsiRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" static void monitor(lsiRecord *); static long readValue(lsiRecord *); static long init_record(struct dbCommon *pcommon, int pass) { struct lsiRecord *prec = (struct lsiRecord *)pcommon; lsidset *pdset; if (pass == 0) { size_t sizv = prec->sizv; if (sizv < 16) { sizv = 16; /* Enforce a minimum size for the VAL field */ prec->sizv = sizv; } prec->val = callocMustSucceed(1, sizv, "lsi::init_record"); prec->len = 0; prec->oval = callocMustSucceed(1, sizv, "lsi::init_record"); prec->olen = 0; return 0; } recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); pdset = (lsidset *) prec->dset; if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "lsi: init_record"); return S_dev_noDSET; } /* must have a read_string function */ if (pdset->number < 5 || !pdset->read_string) { recGblRecordError(S_dev_missingSup, prec, "lsi: init_record"); return S_dev_missingSup; } if (pdset->init_record) { long status = pdset->init_record(prec); if (status) return status; } if (prec->len) { strcpy(prec->oval, prec->val); prec->olen = prec->len; prec->udf = FALSE; } return 0; } static long process(struct dbCommon *pcommon) { struct lsiRecord *prec = (struct lsiRecord *)pcommon; int pact = prec->pact; lsidset *pdset = (lsidset *) prec->dset; long status = 0; if (!pdset || !pdset->read_string) { prec->pact = TRUE; recGblRecordError(S_dev_missingSup, prec, "lsi: read_string"); return S_dev_missingSup; } status = readValue(prec); /* read the new value */ if (!pact && prec->pact) return 0; prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); monitor(prec); /* Wrap up */ recGblFwdLink(prec); prec->pact = FALSE; return status; } static long cvt_dbaddr(DBADDR *paddr) { lsiRecord *prec = (lsiRecord *) paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); if (fieldIndex == lsiRecordVAL) { paddr->pfield = prec->val; paddr->special = SPC_MOD; } else if (fieldIndex == lsiRecordOVAL) { paddr->pfield = prec->oval; paddr->special = SPC_NOMOD; } else { errlogPrintf("lsiRecord::cvt_dbaddr called for %s.%s\n", prec->name, paddr->pfldDes->name); return -1; } paddr->no_elements = 1; paddr->field_type = DBF_STRING; paddr->dbr_field_type = DBF_STRING; paddr->field_size = prec->sizv; return 0; } static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) { lsiRecord *prec = (lsiRecord *) paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); if (fieldIndex == lsiRecordVAL) *no_elements = prec->len; else if (fieldIndex == lsiRecordOVAL) *no_elements = prec->olen; else return -1; *offset = 0; return 0; } static long put_array_info(DBADDR *paddr, long nNew) { lsiRecord *prec = (lsiRecord *) paddr->precord; if (nNew >= prec->sizv) nNew = prec->sizv - 1; /* truncated string */ if (paddr->field_type == DBF_CHAR) prec->val[nNew] = 0; /* ensure data is terminated */ return 0; } static long special(DBADDR *paddr, int after) { lsiRecord *prec = (lsiRecord *) paddr->precord; int special_type = paddr->special; if (special_type == SPC_MOD && dbGetFieldIndex(paddr) == lsiRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return 0; } if (!after) return 0; /* We set prec->len here and not in put_array_info() * because that does not get called if the put was * done using a DBR_STRING type. */ prec->len = strlen(prec->val) + 1; db_post_events(prec, &prec->len, DBE_VALUE | DBE_LOG); return 0; } static void monitor(lsiRecord *prec) { epicsUInt16 events = recGblResetAlarms(prec); if (prec->len != prec->olen || memcmp(prec->oval, prec->val, prec->len)) { events |= DBE_VALUE | DBE_LOG; memcpy(prec->oval, prec->val, prec->len); } if (prec->len != prec->olen) { prec->olen = prec->len; db_post_events(prec, &prec->len, DBE_VALUE | DBE_LOG); } if (prec->mpst == menuPost_Always) events |= DBE_VALUE; if (prec->apst == menuPost_Always) events |= DBE_LOG; if (events) db_post_events(prec, prec->val, events); } static long readValue(lsiRecord *prec) { struct lsidset *pdset = (struct lsidset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: status = pdset->read_string(prec); break; case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbGetLinkLS(&prec->siol, prec->val, prec->sizv, &prec->len); if (status == 0) prec->udf = FALSE; prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } /* Create Record Support Entry Table */ #define report NULL #define initialize NULL /* init_record */ /* process */ /* special */ #define get_value NULL /* cvt_dbaddr */ /* get_array_info */ /* put_array_info */ #define get_units NULL #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset lsiRSET = { RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset, lsiRSET); base-7.0.3.1/modules/database/src/std/rec/lsiRecord.dbd0000664000577000060420000000574313557101274021373 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* recordtype(lsi) { include "dbCommon.dbd" %#include "devSup.h" % %/* Declare Device Support Entry Table */ %typedef struct lsidset { % long number; % DEVSUPFUN report; % DEVSUPFUN init; % DEVSUPFUN init_record; % DEVSUPFUN get_ioint_info; % DEVSUPFUN read_string; %} lsidset; % field(VAL,DBF_NOACCESS) { prompt("Current Value") asl(ASL0) pp(TRUE) special(SPC_DBADDR) extra("char *val") } field(OVAL,DBF_NOACCESS) { prompt("Old Value") special(SPC_DBADDR) interest(3) extra("char *oval") } field(SIZV,DBF_USHORT) { prompt("Size of buffers") promptgroup("40 - Input") special(SPC_NOMOD) interest(1) initial("41") } field(LEN,DBF_ULONG) { prompt("Length of VAL") special(SPC_NOMOD) } field(OLEN,DBF_ULONG) { prompt("Length of OVAL") special(SPC_NOMOD) } field(INP,DBF_INLINK) { prompt("Input Specification") promptgroup("40 - Input") interest(1) } field(MPST,DBF_MENU) { prompt("Post Value Monitors") promptgroup("80 - Display") interest(1) menu(menuPost) } field(APST,DBF_MENU) { prompt("Post Archive Monitors") promptgroup("80 - Display") interest(1) menu(menuPost) } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(SIOL,DBF_INLINK) { prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } } base-7.0.3.1/modules/database/src/std/rec/lsoRecord.c0000664000577000060420000002125113557101274021062 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Long String Output record type */ /* * Author: Andrew Johnson * Date: 2012-11-28 */ #include #include #include #include "dbDefs.h" #include "errlog.h" #include "alarm.h" #include "callback.h" #include "cantProceed.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "menuIvoa.h" #include "menuOmsl.h" #include "menuPost.h" #include "menuYesNo.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #define GEN_SIZE_OFFSET #include "lsoRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" static void monitor(lsoRecord *); static long writeValue(lsoRecord *); static long init_record(struct dbCommon *pcommon, int pass) { struct lsoRecord *prec = (struct lsoRecord *)pcommon; lsodset *pdset; if (pass == 0) { size_t sizv = prec->sizv; if (sizv < 16) { sizv = 16; /* Enforce a minimum size for the VAL field */ prec->sizv = sizv; } prec->val = callocMustSucceed(1, sizv, "lso::init_record"); prec->len = 0; prec->oval = callocMustSucceed(1, sizv, "lso::init_record"); prec->olen = 0; return 0; } recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); pdset = (lsodset *) prec->dset; if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "lso: init_record"); return S_dev_noDSET; } /* must have a write_string function defined */ if (pdset->number < 5 || !pdset->write_string) { recGblRecordError(S_dev_missingSup, prec, "lso: init_record"); return S_dev_missingSup; } dbLoadLinkLS(&prec->dol, prec->val, prec->sizv, &prec->len); if (pdset->init_record) { long status = pdset->init_record(prec); if (status) return status; } if (prec->len) { strcpy(prec->oval, prec->val); prec->olen = prec->len; prec->udf = FALSE; } return 0; } static long process(struct dbCommon *pcommon) { struct lsoRecord *prec = (struct lsoRecord *)pcommon; int pact = prec->pact; lsodset *pdset = (lsodset *) prec->dset; long status = 0; if (!pdset || !pdset->write_string) { prec->pact = TRUE; recGblRecordError(S_dev_missingSup, prec, "lso: write_string"); return S_dev_missingSup; } if (!pact && prec->omsl == menuOmslclosed_loop) if (!dbGetLinkLS(&prec->dol, prec->val, prec->sizv, &prec->len)) prec->udf = FALSE; if (prec->udf) recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); if (prec->nsev < INVALID_ALARM ) status = writeValue(prec); /* write the new value */ else { switch (prec->ivoa) { case menuIvoaContinue_normally: status = writeValue(prec); /* write the new value */ break; case menuIvoaDon_t_drive_outputs: break; case menuIvoaSet_output_to_IVOV: if (!prec->pact) { size_t size = prec->sizv - 1; strncpy(prec->val, prec->ivov, size); prec->val[size] = 0; prec->len = strlen(prec->val) + 1; } status = writeValue(prec); /* write the new value */ break; default: status = -1; recGblRecordError(S_db_badField, prec, "lso:process Bad IVOA choice"); } } /* Asynchronous if device support set pact */ if (!pact && prec->pact) return status; prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, NULL); monitor(prec); /* Wrap up */ recGblFwdLink(prec); prec->pact = FALSE; return status; } static long cvt_dbaddr(DBADDR *paddr) { lsoRecord *prec = (lsoRecord *) paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); if (fieldIndex == lsoRecordVAL) { paddr->pfield = prec->val; paddr->special = SPC_MOD; } else if (fieldIndex == lsoRecordOVAL) { paddr->pfield = prec->oval; paddr->special = SPC_NOMOD; } else { errlogPrintf("lsoRecord::cvt_dbaddr called for %s.%s\n", prec->name, paddr->pfldDes->name); return -1; } paddr->no_elements = 1; paddr->field_type = DBF_STRING; paddr->dbr_field_type = DBF_STRING; paddr->field_size = prec->sizv; return 0; } static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) { lsoRecord *prec = (lsoRecord *) paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); if (fieldIndex == lsoRecordVAL) *no_elements = prec->len; else if (fieldIndex == lsoRecordOVAL) *no_elements = prec->olen; else return -1; *offset = 0; return 0; } static long put_array_info(DBADDR *paddr, long nNew) { lsoRecord *prec = (lsoRecord *) paddr->precord; if (nNew >= prec->sizv) nNew = prec->sizv - 1; /* truncated string */ if (paddr->field_type == DBF_CHAR) prec->val[nNew] = 0; /* ensure data is terminated */ return 0; } static long special(DBADDR *paddr, int after) { lsoRecord *prec = (lsoRecord *) paddr->precord; int special_type = paddr->special; if (special_type == SPC_MOD && dbGetFieldIndex(paddr) == lsoRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return 0; } if (!after) return 0; /* We set prec->len here and not in put_array_info() * because that does not get called if the put was * done using a DBR_STRING type. */ prec->len = strlen(prec->val) + 1; db_post_events(prec, &prec->len, DBE_VALUE | DBE_LOG); return 0; } static void monitor(lsoRecord *prec) { epicsUInt16 events = recGblResetAlarms(prec); if (prec->len != prec->olen || memcmp(prec->oval, prec->val, prec->len)) { events |= DBE_VALUE | DBE_LOG; memcpy(prec->oval, prec->val, prec->len); } if (prec->len != prec->olen) { prec->olen = prec->len; db_post_events(prec, &prec->len, DBE_VALUE | DBE_LOG); } if (prec->mpst == menuPost_Always) events |= DBE_VALUE; if (prec->apst == menuPost_Always) events |= DBE_LOG; if (events) db_post_events(prec, prec->val, events); } static long writeValue(lsoRecord *prec) { lsodset *pdset = (lsodset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: status = pdset->write_string(prec); break; case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbPutLinkLS(&prec->siol, prec->val, prec->len); prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } /* Create Record Support Entry Table*/ #define report NULL #define initialize NULL /* init_record */ /* process */ /* special */ #define get_value NULL /* cvt_dbaddr */ /* get_array_info */ /* put_array_info */ #define get_units NULL #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset lsoRSET = { RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset, lsoRSET); base-7.0.3.1/modules/database/src/std/rec/lsoRecord.dbd0000664000577000060420000000707713557101274021403 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* recordtype(lso) { include "dbCommon.dbd" %#include "devSup.h" % %/* Declare Device Support Entry Table */ %typedef struct lsodset { % long number; % DEVSUPFUN report; % DEVSUPFUN init; % DEVSUPFUN init_record; % DEVSUPFUN get_ioint_info; % DEVSUPFUN write_string; %} lsodset; % field(VAL,DBF_NOACCESS) { prompt("Current Value") asl(ASL0) pp(TRUE) special(SPC_DBADDR) extra("char *val") } field(OVAL,DBF_NOACCESS) { prompt("Previous Value") special(SPC_DBADDR) interest(3) extra("char *oval") } field(SIZV,DBF_USHORT) { prompt("Size of buffers") promptgroup("50 - Output") special(SPC_NOMOD) interest(1) initial("41") } field(LEN,DBF_ULONG) { prompt("Length of VAL") special(SPC_NOMOD) } field(OLEN,DBF_ULONG) { prompt("Length of OVAL") special(SPC_NOMOD) interest(3) } field(DOL,DBF_INLINK) { prompt("Desired Output Link") promptgroup("40 - Input") interest(1) } field(IVOA,DBF_MENU) { prompt("INVALID Output Action") promptgroup("50 - Output") interest(2) menu(menuIvoa) } field(IVOV,DBF_STRING) { prompt("INVALID Output Value") promptgroup("50 - Output") interest(2) size(40) } field(OMSL,DBF_MENU) { prompt("Output Mode Select") promptgroup("50 - Output") interest(1) menu(menuOmsl) } field(OUT,DBF_OUTLINK) { prompt("Output Specification") promptgroup("50 - Output") interest(1) } field(MPST,DBF_MENU) { prompt("Post Value Monitors") promptgroup("80 - Display") interest(1) menu(menuPost) } field(APST,DBF_MENU) { prompt("Post Archive Monitors") promptgroup("80 - Display") interest(1) menu(menuPost) } field(SIML,DBF_INLINK) { prompt("Simulation Mode link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(SIOL,DBF_OUTLINK) { prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } } base-7.0.3.1/modules/database/src/std/rec/mbbiDirectRecord.c0000664000577000060420000002014313557101274022330 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * Copyright (c) 2002 Southeastern Universities Research Association, as * Operator of Thomas Jefferson National Accelerator Facility. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* mbbiDirectRecord.c - Record Support routines for mbbiDirect records */ /* * Original Authors: Bob Dalesio and Matthew Needes * Date: 10-07-93 */ #include #include #include #include #include #include "dbDefs.h" #include "errlog.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "menuSimm.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #define GEN_SIZE_OFFSET #include "mbbiDirectRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL #define get_units NULL static long get_precision(const DBADDR *, long *); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset mbbiDirectRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,mbbiDirectRSET); struct mbbidset { /* multi bit binary input dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (-1,0)=>(failure, success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN read_mbbi; /*returns: (0,2)=>(success, success no convert)*/ }; static void monitor(mbbiDirectRecord *); static long readValue(mbbiDirectRecord *); #define NUM_BITS 32 static long init_record(struct dbCommon *pcommon, int pass) { struct mbbiDirectRecord *prec = (struct mbbiDirectRecord *)pcommon; struct mbbidset *pdset = (struct mbbidset *) prec->dset; long status = 0; if (pass == 0) return 0; if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "mbbiDirect: init_record"); return S_dev_noDSET; } if ((pdset->number < 5) || (pdset->read_mbbi == NULL)) { recGblRecordError(S_dev_missingSup, prec, "mbbiDirect: init_record"); return S_dev_missingSup; } recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); recGblInitConstantLink(&prec->siol, DBF_ULONG, &prec->sval); /* Initialize MASK if the user set NOBT instead */ if (prec->mask == 0 && prec->nobt <= 32) prec->mask = ((epicsUInt64) 1u << prec->nobt) - 1; if (pdset->init_record) { status = pdset->init_record(prec); if (status == 0) { epicsUInt32 val = prec->val; epicsUInt8 *pBn = &prec->b0; int i; /* Initialize B0 - B1F from VAL */ for (i = 0; i < NUM_BITS; i++, pBn++, val >>= 1) *pBn = !! (val & 1); } } prec->mlst = prec->val; prec->oraw = prec->rval; return status; } static long process(struct dbCommon *pcommon) { struct mbbiDirectRecord *prec = (struct mbbiDirectRecord *)pcommon; struct mbbidset *pdset = (struct mbbidset *) prec->dset; long status; int pact = prec->pact; if ((pdset == NULL) || (pdset->read_mbbi == NULL)) { prec->pact = TRUE; recGblRecordError(S_dev_missingSup, prec, "read_mbbi"); return S_dev_missingSup; } status = readValue(prec); /* Done if device support set PACT */ if (!pact && prec->pact) return 0; prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); if (status == 0) { /* Convert RVAL to VAL */ epicsUInt32 rval = prec->rval; if (prec->shft > 0) rval >>= prec->shft; prec->val = rval; prec->udf = FALSE; } else if (status == 2) status = 0; if (prec->udf) recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); monitor(prec); /* Wrap up */ recGblFwdLink(prec); prec->pact = FALSE; return status; } static long special(DBADDR *paddr, int after) { mbbiDirectRecord *prec = (mbbiDirectRecord *)(paddr->precord); int special_type = paddr->special; switch(special_type) { case(SPC_MOD): if (dbGetFieldIndex(paddr) == mbbiDirectRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return(0); } default: recGblDbaddrError(S_db_badChoice, paddr, "mbbiDirect: special"); return(S_db_badChoice); } } static long get_precision(const DBADDR *paddr,long *precision) { mbbiDirectRecord *prec=(mbbiDirectRecord *)paddr->precord; if(dbGetFieldIndex(paddr)==mbbiDirectRecordVAL) *precision = prec->nobt; else recGblGetPrec(paddr,precision); return 0; } static void monitor(mbbiDirectRecord *prec) { epicsUInt16 events = recGblResetAlarms(prec); epicsUInt16 vl_events = events | DBE_VALUE | DBE_LOG; epicsUInt32 val = prec->val; epicsUInt8 *pBn = &prec->b0; int i; /* Update B0 - BF from VAL and post monitors */ for (i = 0; i < NUM_BITS; i++, pBn++, val >>= 1) { epicsUInt8 oBn = *pBn; *pBn = !! (val & 1); if (oBn != *pBn) db_post_events(prec, pBn, vl_events); else if (events) db_post_events(prec, pBn, events); } if (prec->mlst != prec->val) { events = vl_events; prec->mlst = prec->val; } if (events) db_post_events(prec, &prec->val, events); if (prec->oraw != prec->rval) { db_post_events(prec, &prec->rval, vl_events); prec->oraw = prec->rval; } } static long readValue(mbbiDirectRecord *prec) { struct mbbidset *pdset = (struct mbbidset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuSimmNO: status = pdset->read_mbbi(prec); break; case menuSimmYES: case menuSimmRAW: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbGetLink(&prec->siol, DBR_ULONG, &prec->sval, 0, 0); if (status == 0) { if (prec->simm == menuSimmYES) { prec->val = prec->sval; status = 2; /* Don't convert */ } else { prec->rval = prec->sval; } prec->udf = FALSE; } prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } base-7.0.3.1/modules/database/src/std/rec/mbbiDirectRecord.dbd.pod0000664000577000060420000003574613557101274023437 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Multi-Bit Binary Input Direct Record (mbbiDirect) The mbbiDirect record retrieves a 32-bit hardware value and converts it to an array of 32 unsigned characters, each representing a bit of the word. These fields (B0-B9, BA-BF, B10-B19, B1A-B1F) are set to 1 if the corresponding bit is set, and 0 if not. This record's operation is similar to that of the multi-bit binary input record, and it has many fields in common with it. This record also has two available soft device support modules: C and C. =recordtype mbbiDirect =cut recordtype(mbbiDirect) { =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters The mbbiDirect record has the standard fields for specifying under what circumstances the record will be processed. These fields are listed in L. In addition, L explains how these fields are used. Note that I/O event scanning is only supported for those card types that interrupt. =head3 Read and Convert Parameters The device support routines obtain the record's input from the device or link specified in the INP field. For records that obtain their input from devices, the INP field must contain the address of the I/O card, and the DTYP field must specify the proper device support module. Be aware that the address format differs according to the I/O bus used. See L
for information on the format of hardware addresses. Two soft device support modules can be specified in DTYP C and C<<< Raw Soft Channel >>>. C<<< Raw Soft Channel >>> reads the value into RVAL, upon which the normal conversion process is undergone. C<<< Soft Channel >>> reads any unsigned integer directly into VAL. For a soft mbbiDirect record, the INP field can be a constant, a database, or a channel access link. If INP is a constant, then the VAL is initialized to the INP value but can be changed at run-time via dbPutField or dbPutLink. See L
for information on how to database links. For records that don't use C<<< Soft Channel >>> device support, RVAL is used to determine VAL as follows: =over =item 1. RVAL is assigned to a temporary variable I = RVAL =item 2. I is shifted right SHFT number of bits. =item 3. VAL is set equal to I. =back Each of the fields, B0-BF and B10-B1F, represents one bit of the word. =fields VAL, INP, RVAL, SHFT, B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, BA, BB, BC, BD, BE, BF, B10, B11, B12, B13, B14, B15, B16, B17, B18, B19, B1A, B1B, B1C, B1D, B1E, B1F =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. See L for more on the record name (NAME) and description (DESC) fields. =fields NAME, DESC =cut include "dbCommon.dbd" field(VAL,DBF_LONG) { prompt("Current Value") promptgroup("40 - Input") asl(ASL0) pp(TRUE) } field(NOBT,DBF_SHORT) { prompt("Number of Bits") promptgroup("40 - Input") special(SPC_NOMOD) interest(1) } field(INP,DBF_INLINK) { prompt("Input Specification") promptgroup("40 - Input") interest(1) } field(RVAL,DBF_ULONG) { prompt("Raw Value") pp(TRUE) } field(ORAW,DBF_ULONG) { prompt("Prev Raw Value") special(SPC_NOMOD) interest(3) } field(MASK,DBF_ULONG) { prompt("Hardware Mask") special(SPC_NOMOD) interest(1) } field(MLST,DBF_LONG) { prompt("Last Value Monitored") special(SPC_NOMOD) interest(3) } field(SHFT,DBF_USHORT) { prompt("Shift") promptgroup("40 - Input") interest(1) } =head3 Run-time and Simulation Mode Parameters These parameters are used by the run-time code for processing the mbbi direct record. They are not configurable prior to run-time. MASK is used by device support routine to read hardware register. Record support sets low order NOBT bits in MASK. Device support can shift this value. MLST holds the value when the last monitor for value change was triggered. =fields NOBT, ORAW, MASK, MLST The following fields are used to operate the mbbiDirect record in the simulation mode. See L for more information on these fields. =fields SIOL, SVAL, SIML, SIMM, SIMS, SSCN, SDLY =cut field(SIOL,DBF_INLINK) { prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } field(SVAL,DBF_LONG) { prompt("Simulation Value") } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuSimm) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } =head3 Alarm Parameters The possible alarm conditions for multi-bit binary input direct records are the SCAN and READ alarms. These alarms are not configurable by the user since they are always of MAJOR severity. See L for a complete explanation of Scan and Read alarms. No fields exist for the mbbi direct record to have state alarms. L lists other fields related to a alarms that are common to all record types. =cut field(B0,DBF_UCHAR) { prompt("Bit 0") pp(TRUE) interest(1) } field(B1,DBF_UCHAR) { prompt("Bit 1") pp(TRUE) interest(1) } field(B2,DBF_UCHAR) { prompt("Bit 2") pp(TRUE) interest(1) } field(B3,DBF_UCHAR) { prompt("Bit 3") pp(TRUE) interest(1) } field(B4,DBF_UCHAR) { prompt("Bit 4") pp(TRUE) interest(1) } field(B5,DBF_UCHAR) { prompt("Bit 5") pp(TRUE) interest(1) } field(B6,DBF_UCHAR) { prompt("Bit 6") pp(TRUE) interest(1) } field(B7,DBF_UCHAR) { prompt("Bit 7") pp(TRUE) interest(1) } field(B8,DBF_UCHAR) { prompt("Bit 8") pp(TRUE) interest(1) } field(B9,DBF_UCHAR) { prompt("Bit 9") pp(TRUE) interest(1) } field(BA,DBF_UCHAR) { prompt("Bit 10") pp(TRUE) interest(1) } field(BB,DBF_UCHAR) { prompt("Bit 11") pp(TRUE) interest(1) } field(BC,DBF_UCHAR) { prompt("Bit 12") pp(TRUE) interest(1) } field(BD,DBF_UCHAR) { prompt("Bit 13") pp(TRUE) interest(1) } field(BE,DBF_UCHAR) { prompt("Bit 14") pp(TRUE) interest(1) } field(BF,DBF_UCHAR) { prompt("Bit 15") pp(TRUE) interest(1) } field(B10,DBF_UCHAR) { prompt("Bit 16") pp(TRUE) interest(1) } field(B11,DBF_UCHAR) { prompt("Bit 17") pp(TRUE) interest(1) } field(B12,DBF_UCHAR) { prompt("Bit 18") pp(TRUE) interest(1) } field(B13,DBF_UCHAR) { prompt("Bit 19") pp(TRUE) interest(1) } field(B14,DBF_UCHAR) { prompt("Bit 20") pp(TRUE) interest(1) } field(B15,DBF_UCHAR) { prompt("Bit 21") pp(TRUE) interest(1) } field(B16,DBF_UCHAR) { prompt("Bit 22") pp(TRUE) interest(1) } field(B17,DBF_UCHAR) { prompt("Bit 23") pp(TRUE) interest(1) } field(B18,DBF_UCHAR) { prompt("Bit 24") pp(TRUE) interest(1) } field(B19,DBF_UCHAR) { prompt("Bit 25") pp(TRUE) interest(1) } field(B1A,DBF_UCHAR) { prompt("Bit 26") pp(TRUE) interest(1) } field(B1B,DBF_UCHAR) { prompt("Bit 27") pp(TRUE) interest(1) } field(B1C,DBF_UCHAR) { prompt("Bit 28") pp(TRUE) interest(1) } field(B1D,DBF_UCHAR) { prompt("Bit 29") pp(TRUE) interest(1) } field(B1E,DBF_UCHAR) { prompt("Bit 30") pp(TRUE) interest(1) } field(B1F,DBF_UCHAR) { prompt("Bit 31") pp(TRUE) interest(1) } =head2 Record Support =head3 Record Support Routines =head4 init_record This routine initializes SIMM with the value of SIML if SIML type is CONSTANT link or creates a channel access link if SIML type is PV_LINK. SVAL is likewise initialized if SIOL is CONSTANT or PV_LINK. This routine next checks to see that device support is available and a device support read routine is defined. If either does not exist, an error message is issued and processing is terminated. Clears MASK and then sets the NOBT low order bits. If device support includes C, it is called. refresh_bits is then called to refresh all the bit fields based on a hardware value. =head4 process See next section. =head3 Record Processing Routine process implements the following algorithm: =over =item 1. Check to see that the appropriate device support module exists. If it doesn't, an error message is issued and processing is terminated with the PACT field still set to TRUE. This ensures that processes will no longer be called for this record. Thus error storms will not occur. =item 2. readValue is called. See L for information. =item 3. If PACT has been changed to TRUE, the device support read routine has started but has not completed reading a new input value. In this case, the processing routine merely returns, leaving PACT TRUE. =item 4. Convert. =over =item * status = read_mbbiDirect =item * PACT = TRUE =item * C is called. =item * If status is 0, then determine VAL =over =item * Set rval = RVAL =item * Shift rval right SHFT bits =item * Set VAL = RVAL =back =item * If status is 1, return 0 =item * If status is 2, set status = 0 =back =item 5. Check to see if monitors should be invoked. =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if MLST is not equal to VAL. =item * Monitors for RVAL are checked whenever other monitors are invoked. =item * NSEV and NSTA are reset to 0. =back =item 6. Scan forward link if necessary, set PACT FALSE, and return. =back =begin html


=end html =head2 Device Support =head3 Fields Of Interest To Device Support Each input record must have an associated set of device support routines. The primary responsibility of the device support routines is to obtain a new raw input value whenever read_mbbiDirect is called. The device support routines are primarily interested in the following fields: =fields PACT, DPVT, UDF, NSEV, NSTA, NOBT, VAL, INP, RVAL, MASK, SHFT =head3 Device Support Routines Device support consists of the following routines: =head4 long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. =head4 long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. =head4 init_record init_record(precord) This routine is optional. If provided, it is called by the record support C routine. If it uses MASK, it should shift it as necessary and also give SHFT a value. =head4 get_ioint_info get_ioint_info(int cmd,struct dbCommon *precord,IOSCANPVT *ppvt) This routine is called by the ioEventScan system each time the record is added or deleted from an IEO event scan list. cmd has the value (0,1) if the record is being (added to, deleted from) an IEO event list. It must be provided for any device type that can use the ioEvent scanner. =head4 read_mbbiDirect read_mbbiDirect(precord) This routine must provide a new input value. It returns the following values: =over =item * 0: Success. A new raw value is placed in RVAL. The record support module determines VAL from RVAL and SHFT. =item * 2: Success, but don't modify VAL. =item * Other: Error. =back =head3 Device Support For Soft Records Two soft device support modules, C<<< Soft Channel >>> and C<<< Raw Soft Channel >>>, are provided for multi-bit binary input direct records not related to actual hardware devices. The INP link type must be either CONSTANT, DB_LINK, or CA_LINK. =head4 Soft Channel For this module, read_mbbiDirect always returns a value of 2, which means that no conversion is performed. If the INP link type is constant, then the constant value is stored into VAL by C, and UDF is set to FALSE. VAL can be changed via dbPut requests. If the INP link type is PV_LINK, then dbCaAddInlink is called by C. read_mbbiDirect calls recGblGetLinkValue to read the current value of VAL. See L for a further explanation. If the return status of recGblGetLinkValue is zero, then read_mbbi sets UDF to FALSE. The status of recGblGetLinkValue is returned. =head4 Raw Soft Channel This module is like the previous except that values are read into RVAL, VAL is computed from RVAL, and read_mbbiDirect returns a value of 0. Thus the record processing routine will determine VAL in the normal way. =cut } base-7.0.3.1/modules/database/src/std/rec/mbbiRecord.c0000664000577000060420000002633313557101274021204 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Author: Bob Dalesio * Date: 5-9-88 */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "epicsMath.h" #include "errMdef.h" #include "menuSimm.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #define GEN_SIZE_OFFSET #include "mbbiRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Hysterisis for alarm filtering: 1-1/e */ #define THRESHOLD 0.6321 /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL #define get_units NULL #define get_precision NULL static long get_enum_str(const DBADDR *, char *); static long get_enum_strs(const DBADDR *, struct dbr_enumStrs *); static long put_enum_str(const DBADDR *, const char *); #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset mbbiRSET = { RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,mbbiRSET); struct mbbidset { /* multi bit binary input dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /* returns: (-1,0) => (failure, success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN read_mbbi;/* (0, 2) => (success, success no convert)*/ }; static void checkAlarms(mbbiRecord *, epicsTimeStamp *); static void monitor(mbbiRecord *); static long readValue(mbbiRecord *); static void init_common(mbbiRecord *prec) { epicsUInt32 *pstate_values = &prec->zrvl; char *pstate_string = prec->zrst; int i; /* Check if any states are defined */ for (i = 0; i < 16; i++, pstate_string += sizeof(prec->zrst)) { if ((pstate_values[i] != 0) || (*pstate_string != '\0')) { prec->sdef = TRUE; return; } } prec->sdef = FALSE; } static long init_record(struct dbCommon *pcommon, int pass) { struct mbbiRecord *prec = (struct mbbiRecord *)pcommon; struct mbbidset *pdset = (struct mbbidset *) prec->dset; long status = 0; if (pass == 0) return 0; pdset = (struct mbbidset *) prec->dset; if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "mbbi: init_record"); return S_dev_noDSET; } if ((pdset->number < 5) || (pdset->read_mbbi == NULL)) { recGblRecordError(S_dev_missingSup, prec, "mbbi: init_record"); return S_dev_missingSup; } recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); recGblInitConstantLink(&prec->siol, DBF_USHORT, &prec->sval); /* Initialize MASK if the user set NOBT instead */ if (prec->mask == 0 && prec->nobt <= 32) prec->mask = ((epicsUInt64) 1u << prec->nobt) - 1; if (pdset->init_record) status = pdset->init_record(prec); init_common(prec); prec->mlst = prec->val; prec->lalm = prec->val; prec->oraw = prec->rval; return status; } static long process(struct dbCommon *pcommon) { struct mbbiRecord *prec = (struct mbbiRecord *)pcommon; struct mbbidset *pdset = (struct mbbidset *) prec->dset; long status; int pact = prec->pact; epicsTimeStamp timeLast; if ((pdset == NULL) || (pdset->read_mbbi == NULL)) { prec->pact = TRUE; recGblRecordError(S_dev_missingSup, prec, "read_mbbi"); return S_dev_missingSup; } timeLast = prec->time; status = readValue(prec); /* Done if device support set PACT */ if (!pact && prec->pact) return 0; prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); if (status == 0) { /* Convert RVAL to VAL */ epicsUInt32 *pstate_values; short i; epicsUInt32 rval = prec->rval; prec->udf = FALSE; if (prec->shft > 0) rval >>= prec->shft; if (prec->sdef) { pstate_values = &(prec->zrvl); prec->val = 65535; /* Initalize to unknown state*/ for (i = 0; i < 16; i++) { if (*pstate_values == rval) { prec->val = i; break; } pstate_values++; } } else /* No states defined, set VAL = RVAL */ prec->val = rval; } else if (status == 2) status = 0; checkAlarms(prec, &timeLast); monitor(prec); /* Wrap up */ recGblFwdLink(prec); prec->pact=FALSE; return status; } static long special(DBADDR *paddr, int after) { mbbiRecord *prec = (mbbiRecord *) paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); switch (paddr->special) { case SPC_MOD: if (fieldIndex == mbbiRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return 0; } if (!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); } return 0; default: recGblDbaddrError(S_db_badChoice, paddr, "mbbi: special"); return S_db_badChoice; } } static long get_enum_str(const DBADDR *paddr, char *pstring) { mbbiRecord *prec = (mbbiRecord *) paddr->precord; int index; unsigned short *pfield = paddr->pfield; epicsEnum16 val = *pfield; index = dbGetFieldIndex(paddr); if (index != mbbiRecordVAL) { strcpy(pstring, "Illegal_Value"); } else if (val <= 15) { char *pstate = prec->zrst + val * sizeof(prec->zrst); strncpy(pstring, pstate, sizeof(prec->zrst)); } else { strcpy(pstring, "Illegal Value"); } return 0; } static long get_enum_strs(const DBADDR *paddr, struct dbr_enumStrs *pes) { mbbiRecord *prec = (mbbiRecord *) paddr->precord; char *pstate = prec->zrst; int i; short states = 0; memset(pes->strs, '\0', sizeof(pes->strs)); for (i = 0; i < 16; i++, pstate += sizeof(prec->zrst) ) { strncpy(pes->strs[i], pstate, sizeof(prec->zrst)); if (*pstate!=0) states = i+1; } pes->no_str = states; return 0; } static long put_enum_str(const DBADDR *paddr, const char *pstring) { mbbiRecord *prec = (mbbiRecord *) paddr->precord; char *pstate; short i; if (prec->sdef) { pstate = prec->zrst; for (i = 0; i < 16; i++) { if (strncmp(pstate, pstring, sizeof(prec->zrst)) == 0) { prec->val = i; prec->udf = FALSE; return 0; } pstate += sizeof(prec->zrst); } } return S_db_badChoice; } static void checkAlarms(mbbiRecord *prec, epicsTimeStamp *timeLast) { double aftc, afvl; unsigned short alarm; epicsEnum16 asev; epicsEnum16 val = prec->val; /* Check for UDF alarm */ if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, prec->udfs); prec->afvl = 0; return; } /* Check for STATE alarm */ if (val > 15) { /* Unknown state */ alarm = prec->unsv; } else { /* State has a severity field */ epicsEnum16 *severities = &prec->zrsv; alarm = severities[prec->val]; } aftc = prec->aftc; afvl = 0; if (aftc > 0) { afvl = prec->afvl; if (afvl == 0) { afvl = (double) alarm; } else { double t = epicsTimeDiffInSeconds(&prec->time, timeLast); double alpha = aftc / (t + aftc); afvl = alpha * afvl + ((afvl > 0) ? (1.0 - alpha) : (alpha - 1.0)) * alarm; if (afvl - floor(afvl) > THRESHOLD) afvl = -afvl; alarm = abs((int)floor(afvl)); } } asev = alarm; recGblSetSevr(prec, STATE_ALARM, asev); /* Check for COS alarm */ if (val == prec->lalm || recGblSetSevr(prec, COS_ALARM, prec->cosv)) return; prec->lalm = val; } static void monitor(mbbiRecord *prec) { epicsUInt16 events = recGblResetAlarms(prec); if (prec->mlst != prec->val) { events |= DBE_VALUE | DBE_LOG; prec->mlst = prec->val; } if (events) db_post_events(prec, &prec->val, events); if (prec->oraw != prec->rval) { db_post_events(prec, &prec->rval, events | DBE_VALUE | DBE_LOG); prec->oraw = prec->rval; } } static long readValue(mbbiRecord *prec) { struct mbbidset *pdset = (struct mbbidset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuSimmNO: status = pdset->read_mbbi(prec); break; case menuSimmYES: case menuSimmRAW: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbGetLink(&prec->siol, DBR_ULONG, &prec->sval, 0, 0); if (status == 0) { if (prec->simm == menuSimmYES) { prec->val = prec->sval; status = 2; /* Don't convert */ } else { prec->rval = prec->sval; } prec->udf = FALSE; } prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } base-7.0.3.1/modules/database/src/std/rec/mbbiRecord.dbd.pod0000664000577000060420000005406413557101274022276 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Multi-Bit Binary Input Record (mbbi) The normal use for the multi-bit binary input record is to read contiguous, multiple bit inputs from hardware. The binary value represents a state from a range of up to 16 states. The multi-bit input record interfaces with devices that use more than one bit. Most device support modules obtain values from hardware and place the value in RVAL. For these device support modules record processing uses RVAL to determine the current state (VAL is given a value between 0 and 15). Device support modules may optionally read a value directly into VAL. Soft device modules are provided to obtain input via database or channel access links or via dbPutField or dbPutLink requests. Two soft device support modules are provided: C<<< Soft Channel >>> allows VAL to be an arbitrary unsigned short integer. C<<< Raw Soft Channel >>> reads the value into RVAL just like normal device support modules. =recordtype mbbi =cut recordtype(mbbi) { =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters The multi-bit binary input record has the standard fields for specifying under what circumstances it will be processed. These fields are listed in L. In addition, L explains how these fields are used. Note that I/O event scanning is only supported for those card types that interrupt. =head3 Read and Convert Parameters The device support routines obtain the record's input from the device or link specified in the INP field. For records that obtain their input from devices, the INP field must contain the address of the I/O card, and the DTYP field must specify the proper device support module. Be aware that the address format differs according to the I/O bus used. See L
for information on the format of hardware addresses. Two soft device support modules can be specified in DTYP C and C<<< Raw Soft Channel >>>. C<<< Raw Soft Channel >>> reads the value into RVAL, upon which the normal conversion process is undergone. C<<< Soft Channel >>> reads any unsigned integer directly into VAL. For a soft mbbi record, the INP field can be a constant, a database, or a channel access link. If INP is a constant, then the VAL is initialized to the constant value but can be changed at run-time via dbPutField or dbPutLink. See L
for information on the format of database addresses. MASK is used by the raw soft channel read routine, and by typical device support read routines, to select only the desired bits when reading the hardware register. It is initialized to ((1 EE NOBT) - 1) by record initialization. The user can configure the NOBT field, but the device support routines may set it, in which case the value given to it by the user is simply overridden. The device support routines may also override MASK or shift it left by SHFT bits. If MASK is non-zero, only the bits specified by MASK will appear in RVAL. Unless the device support routine specifies no conversion, RVAL is used to determine VAL as follows: =over =item 1. RVAL is assigned to a temporary variable -- rval = RVAL =item 2. rval is shifted right SHFT number of bits. =item 3. A match is sought between rval and one of the state value fields, ZRVL-FFVL. =back Each of the fields, ZRVL-FFVL, represents one of the possible sixteen states (not all sixteen have to be used). Alternatively, the input value can be read as a string, in which case, a match is sought with one of the strings specified in the ZRST-FFST fields. Then RVAL is set equal to the corresponding value for that string, and the conversion process occurs. =fields VAL, INP, MASK, NOBT, RVAL, SHFT, ZRVL, ONVL, TWVL, THVL, FRVL, FVVL, SXVL, SVVL, EIVL, NIVL, TEVL, ELVL, TVVL, TTVL, FTVL, FFVL =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. They display the value and other parameters of the mbbi record either textually or graphically. The ZRST-FFST fields contain strings describing one of the possible states of the record. The C<<< get_enum_str >>> and C<<< get_enum_strs >>> record routines retrieve these strings for the operator. C<<< Get_enum_str >>> gets the string corresponding to the value set in VAL, and C<<< get_enum_strs >>> retrieves all the strings. See L for more on the record name (NAME) and description (DESC) fields. =fields NAME, DESC, ZRST, ONST, TWST, THST, FRST, FVST, SXST, SVST, EIST, NIST, TEST, ELST, TVST, TTST, FTST, FFST =cut include "dbCommon.dbd" field(VAL,DBF_ENUM) { prompt("Current Value") promptgroup("40 - Input") asl(ASL0) pp(TRUE) } field(NOBT,DBF_USHORT) { prompt("Number of Bits") promptgroup("40 - Input") special(SPC_NOMOD) interest(1) } field(INP,DBF_INLINK) { prompt("Input Specification") promptgroup("40 - Input") interest(1) } field(ZRVL,DBF_ULONG) { prompt("Zero Value") promptgroup("41 - Input 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(ONVL,DBF_ULONG) { prompt("One Value") promptgroup("41 - Input 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(TWVL,DBF_ULONG) { prompt("Two Value") promptgroup("41 - Input 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(THVL,DBF_ULONG) { prompt("Three Value") promptgroup("41 - Input 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(FRVL,DBF_ULONG) { prompt("Four Value") promptgroup("41 - Input 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(FVVL,DBF_ULONG) { prompt("Five Value") promptgroup("41 - Input 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(SXVL,DBF_ULONG) { prompt("Six Value") promptgroup("41 - Input 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(SVVL,DBF_ULONG) { prompt("Seven Value") promptgroup("41 - Input 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(EIVL,DBF_ULONG) { prompt("Eight Value") promptgroup("42 - Input 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(NIVL,DBF_ULONG) { prompt("Nine Value") promptgroup("42 - Input 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(TEVL,DBF_ULONG) { prompt("Ten Value") promptgroup("42 - Input 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(ELVL,DBF_ULONG) { prompt("Eleven Value") promptgroup("42 - Input 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(TVVL,DBF_ULONG) { prompt("Twelve Value") promptgroup("42 - Input 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(TTVL,DBF_ULONG) { prompt("Thirteen Value") promptgroup("42 - Input 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(FTVL,DBF_ULONG) { prompt("Fourteen Value") promptgroup("42 - Input 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(FFVL,DBF_ULONG) { prompt("Fifteen Value") promptgroup("42 - Input 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(ZRST,DBF_STRING) { prompt("Zero String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(ONST,DBF_STRING) { prompt("One String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(TWST,DBF_STRING) { prompt("Two String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(THST,DBF_STRING) { prompt("Three String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(FRST,DBF_STRING) { prompt("Four String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(FVST,DBF_STRING) { prompt("Five String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(SXST,DBF_STRING) { prompt("Six String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(SVST,DBF_STRING) { prompt("Seven String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(EIST,DBF_STRING) { prompt("Eight String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(NIST,DBF_STRING) { prompt("Nine String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(TEST,DBF_STRING) { prompt("Ten String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(ELST,DBF_STRING) { prompt("Eleven String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(TVST,DBF_STRING) { prompt("Twelve String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(TTST,DBF_STRING) { prompt("Thirteen String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(FTST,DBF_STRING) { prompt("Fourteen String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(FFST,DBF_STRING) { prompt("Fifteen String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } =head3 Alarm Parameters The possible alarm conditions for multi-bit binary inputs are the SCAN, READ, and state alarms. The state alarms are configured in the below severity fields. These fields have the usual possible values for severity fields: NO_ALARM, MINOR, and MAJOR. The unknown state severity (UNSV) field, if set to MINOR or MAJOR, triggers an alarm when the record support routine cannot find a matching value in the state value fields for C<<< rval >>>. The change of state severity (COSV) field triggers an alarm when any change of state occurs, if set to MAJOR or MINOR. The other fields, when set to MAJOR or MINOR, trigger an alarm when VAL equals the corresponding state. See the See L for a complete explanation of discrete alarms and these fields. L lists other fields related to a alarms that are common to all record types. =fields UNSV, COSV, ZRSV, ONSV, TWSV, THSV, FRSV, FVSV, SXSV, SVSV, EISV, NISV, TESV, ELSV, TVSV, TTSV, FTSV, FFSV =cut field(ZRSV,DBF_MENU) { prompt("State Zero Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(ONSV,DBF_MENU) { prompt("State One Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(TWSV,DBF_MENU) { prompt("State Two Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(THSV,DBF_MENU) { prompt("State Three Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(FRSV,DBF_MENU) { prompt("State Four Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(FVSV,DBF_MENU) { prompt("State Five Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(SXSV,DBF_MENU) { prompt("State Six Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(SVSV,DBF_MENU) { prompt("State Seven Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(EISV,DBF_MENU) { prompt("State Eight Severity") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(NISV,DBF_MENU) { prompt("State Nine Severity") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(TESV,DBF_MENU) { prompt("State Ten Severity") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(ELSV,DBF_MENU) { prompt("State Eleven Severity") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(TVSV,DBF_MENU) { prompt("State Twelve Severity") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(TTSV,DBF_MENU) { prompt("State Thirteen Sevr") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(FTSV,DBF_MENU) { prompt("State Fourteen Sevr") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(FFSV,DBF_MENU) { prompt("State Fifteen Severity") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(AFTC, DBF_DOUBLE) { prompt("Alarm Filter Time Constant") promptgroup("70 - Alarm") interest(1) } field(AFVL, DBF_DOUBLE) { prompt("Alarm Filter Value") special(SPC_NOMOD) interest(3) } field(UNSV,DBF_MENU) { prompt("Unknown State Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(COSV,DBF_MENU) { prompt("Change of State Svr") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } =head3 Run-time Parameters These parameters are used by the run-time code for processing the multi-bit binary input. ORAW is used by record processing to hold the prior RVAL for use in determining when to post a monitor event for the RVAL field. The LALM field implements the change of state alarm severity by holding the value of VAL when the previous change of state alarm was issued. MLST holds the value when the last monitor for value change was triggered. SDEF is used by record support to save time if no states are defined. =fields ORAW, LALM, MLST, SDEF =cut field(RVAL,DBF_ULONG) { prompt("Raw Value") pp(TRUE) } field(ORAW,DBF_ULONG) { prompt("Prev Raw Value") special(SPC_NOMOD) interest(3) } field(MASK,DBF_ULONG) { prompt("Hardware Mask") special(SPC_NOMOD) interest(1) } field(MLST,DBF_USHORT) { prompt("Last Value Monitored") special(SPC_NOMOD) interest(3) } field(LALM,DBF_USHORT) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(SDEF,DBF_SHORT) { prompt("States Defined") special(SPC_NOMOD) interest(3) } field(SHFT,DBF_USHORT) { prompt("Shift") promptgroup("40 - Input") interest(1) } =head3 Simulation Mode Parameters The following fields are used to operate the mbbi record in the simulation mode. See L for more information on these fields. =fields SIOL, SVAL, SIML, SIMM, SIMS, SSCN, SDLY =cut field(SIOL,DBF_INLINK) { prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } field(SVAL,DBF_ULONG) { prompt("Simulation Value") } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuSimm) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } =head2 Record Support =head3 Record Support Routines =head4 init_record This routine initializes SIMM with the value of SIML if SIML type is CONSTANT link or creates a channel access link if SIML type is PV_LINK. SVAL is likewise initialized if SIOL is CONSTANT or PV_LINK. This routine next checks to see that device support is available and a device support read routine is defined. If either does not exist, an error message is issued and processing is terminated. Clears MASK and then sets the NOBT low order bits. If device support includes C, it is called. init_common is then called to determine if any states are defined. If states are defined, SDEF is set to TRUE. =head4 process See next section. =head4 special Calls init_common to compute SDEF when any of the fields ZRVL, ... FFVL change value. =head4 get_enum_str Retrieves ASCII string corresponding to VAL. =head4 get_enum_strs Retrieves ASCII strings for ZRST,...FFST. =head4 put_enum_str Checks if string matches ZRST,...FFST and if it does, sets VAL. =head3 Record Processing Routine process implements the following algorithm: =over =item 1. Check to see that the appropriate device support module exists. If it doesn't, an error message is issued and processing is terminated with the PACT field still set to TRUE. This ensures that processes will no longer be called for this record. Thus error storms will not occur. =item 2. readValue is called. See L for more information. =item 3. If PACT has been changed to TRUE, the device support read routine has started but has not completed reading a new input value. In this case, the processing routine merely returns, leaving PACT TRUE. =item 4. Convert: =over =item * status=read_mbbi =item * PACT = TRUE =item * C is called. =item * If status is 0, then determine VAL =over =item * Set rval = RVAL =item * Shift rval right SHFT bits =back =item * If at least one state value is defined =over =item * Set UDF to TRUE =back =item * If RVAL is ZRVL,...,FFVL then set =over =item * VAL equals index of state =item * UDF set to FALSE =back =item * Else set VAL = undefined =over =item * Else set VAL = RVAL =back =item * Set UDF to FALSE =over =item * If status is 1, return 0 =item * If status is 2, set status = 0 =back =back =item 5. Check alarms. This routine checks to see if the new VAL causes the alarm status and severity to change. If so, NSEV, NSTA and LALM are set. =item 6. Check to see if monitors should be invoked. =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if MLST is not equal to VAL. =item * Monitors for RVAL are checked whenever other monitors are invoked. =item * NSEV and NSTA are reset to 0. =back =item 7. Scan forward link if necessary, set PACT FALSE, and return. =back =head2 Device Support =head3 Fields Of Interest To Device Support Each input record must have an associated set of device support routines. The primary responsibility of the device support routines is to obtain a new raw input value whenever read_mbbi is called. The device support routines are primarily interested in the following fields: =fields PACT, DPVT, UDF, NSEV, NSTA, NOBT, VAL, INP, RVAL, MASK, SHFT =head3 Device Support Routines Device support consists of the following routines: =head4 long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. =head4 long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. =head4 init_record init_record(precord) This routine is optional. If provided, it is called by the record support C routine. If it uses MASK, it should shift it as necessary and also give SHFT a value. =head4 get_ioint_info get_ioint_info(int cmd,struct dbCommon *precord,IOSCANPVT *ppvt) This routine is called by the ioEventScan system each time the record is added or deleted from an I/O event scan list. cmd has the value (0,1) if the record is being (added to, deleted from) an I/O event list. It must be provided for any device type that can use the I/O Event scanner. =head4 read_mbbi read_mbbi(precord) This routine must provide a new input value. It returns the following values: =over =item * 0: Success. A new raw value is placed in RVAL. The record support module determines VAL from RVAL, SHFT, and ZEVL ... FFVL. =item * 2: Success, but don't modify VAL. =item * Other: Error. =back =head3 Device Support For Soft Records Two soft device support modules C<<< Soft Channel >>> and C<<< Raw Soft Channel >>> are provided for multi-bit binary input records not related to actual hardware devices. The INP link type must be either CONSTANT, DB_LINK, or CA_LINK. =head4 Soft Channel read_mbbi always returns a value of 2, which means that no conversion is performed. If the INP link type is constant, then the constant value is stored into VAL by C, and UDF is set to FALSE. VAL can be changed via dbPut requests. If the INP link type is PV_LINK, then dbCaAddInlink is called by C. read_mbbi calls recGblGetLinkValue to read the current value of VAL. See L. If the return status of recGblGetLinkValue is zero, then read_mbbi sets UDF to FALSE. The status of recGblGetLinkValue is returned. =head4 Raw Soft Channel This module is like the previous except that values are read into RVAL, VAL is computed from RVAL, and read_mbbi returns a value of 0. Thus the record processing routine will determine VAL in the normal way. =cut } base-7.0.3.1/modules/database/src/std/rec/mbboDirectRecord.c0000664000577000060420000002506613557101274022347 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* mbboDirectRecord.c - Record Support for mbboDirect records */ /* * Original Author: Bob Dalesio * Date: 10-06-93 */ #include #include #include #include #include #include "dbDefs.h" #include "errlog.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #include "menuOmsl.h" #include "menuIvoa.h" #include "menuYesNo.h" #define GEN_SIZE_OFFSET #include "mbboDirectRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL #define get_units NULL static long get_precision(const DBADDR *, long *); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset mbboDirectRSET = { RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset, mbboDirectRSET); struct mbbodset { /* multi bit binary output dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (0, 2)=>(success, success no convert)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN write_mbbo; /*returns: (0, 2)=>(success, success no convert)*/ }; static void convert(mbboDirectRecord *); static void monitor(mbboDirectRecord *); static long writeValue(mbboDirectRecord *); #define NUM_BITS 32 static long init_record(struct dbCommon *pcommon, int pass) { struct mbboDirectRecord *prec = (struct mbboDirectRecord *)pcommon; struct mbbodset *pdset = (struct mbbodset *) prec->dset; long status = 0; if (pass == 0) return 0; if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "mbboDirect: init_record"); return S_dev_noDSET; } if ((pdset->number < 5) || (pdset->write_mbbo == NULL)) { recGblRecordError(S_dev_missingSup, prec, "mbboDirect: init_record"); return S_dev_missingSup; } recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (recGblInitConstantLink(&prec->dol, DBF_ULONG, &prec->val)) prec->udf = FALSE; /* Initialize MASK if the user set NOBT instead */ if (prec->mask == 0 && prec->nobt <= 32) prec->mask = ((epicsUInt64) 1u << prec->nobt) - 1; if (pdset->init_record) { status = pdset->init_record(prec); if (status == 0) { /* Convert initial read-back */ epicsUInt32 rval = prec->rval; if (prec->shft > 0) rval >>= prec->shft; prec->val = rval; prec->udf = FALSE; } else if (status == 2) status = 0; } if (!prec->udf && prec->omsl == menuOmslsupervisory) { /* Set initial B0 - B1F from VAL */ epicsUInt32 val = prec->val; epicsUInt8 *pBn = &prec->b0; int i; for (i = 0; i < NUM_BITS; i++) { *pBn++ = !! (val & 1); val >>= 1; } } prec->mlst = prec->val; prec->oraw = prec->rval; prec->orbv = prec->rbv; return status; } static long process(struct dbCommon *pcommon) { struct mbboDirectRecord *prec = (struct mbboDirectRecord *)pcommon; struct mbbodset *pdset = (struct mbbodset *)(prec->dset); long status = 0; int pact = prec->pact; if ((pdset == NULL) || (pdset->write_mbbo == NULL)) { prec->pact = TRUE; recGblRecordError(S_dev_missingSup, prec, "write_mbbo"); return S_dev_missingSup; } if (!pact) { if (!dbLinkIsConstant(&prec->dol) && prec->omsl == menuOmslclosed_loop) { epicsUInt32 val; if (dbGetLink(&prec->dol, DBR_ULONG, &val, 0, 0)) { recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM); goto CONTINUE; } prec->val = val; } else if (prec->omsl == menuOmslsupervisory) { epicsUInt8 *pBn = &prec->b0; epicsUInt32 val = 0; epicsUInt32 bit = 1; int i; /* Construct VAL from B0 - B1F */ for (i = 0; i < NUM_BITS; i++, bit <<= 1) if (*pBn++) val |= bit; prec->val = val; } else if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, prec->udfs); goto CONTINUE; } prec->udf = FALSE; /* Convert VAL to RVAL */ convert(prec); } CONTINUE: if (prec->nsev < INVALID_ALARM) status = writeValue(prec); else { switch (prec->ivoa) { case menuIvoaSet_output_to_IVOV: if (!prec->pact) { prec->val = prec->ivov; convert(prec); } /* No break, fall through... */ case menuIvoaContinue_normally: status = writeValue(prec); break; case menuIvoaDon_t_drive_outputs: break; default: status = -1; recGblRecordError(S_db_badField, prec, "mbboDirect: process Illegal IVOA field"); } } /* Done if device support set PACT */ if (!pact && prec->pact) return 0; prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, NULL); monitor(prec); /* Wrap up */ recGblFwdLink(prec); prec->pact = FALSE; return status; } static long special(DBADDR *paddr, int after) { mbboDirectRecord *prec = (mbboDirectRecord *) paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); if (paddr->special == SPC_MOD && fieldIndex == mbboDirectRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return 0; } if (!after) return 0; switch (paddr->special) { case SPC_MOD: /* Bn field modified */ if (prec->omsl == menuOmslsupervisory) { /* Adjust VAL corresponding to the bit changed */ epicsUInt8 *pBn = (epicsUInt8 *) paddr->pfield; epicsUInt32 bit = 1 << (pBn - &prec->b0); if (*pBn) prec->val |= bit; else prec->val &= ~bit; prec->udf = FALSE; convert(prec); } break; case SPC_RESET: /* OMSL field modified */ if (prec->omsl == menuOmslclosed_loop) { /* Construct VAL from B0 - B1F */ epicsUInt8 *pBn = &prec->b0; epicsUInt32 val = 0, bit = 1; int i; for (i = 0; i < NUM_BITS; i++, bit <<= 1) if (*pBn++) val |= bit; prec->val = val; } else if (prec->omsl == menuOmslsupervisory) { /* Set B0 - B1F from VAL and post monitors */ epicsUInt32 val = prec->val; epicsUInt8 *pBn = &prec->b0; int i; for (i = 0; i < NUM_BITS; i++, pBn++, val >>= 1) { epicsUInt8 oBn = *pBn; *pBn = !! (val & 1); if (oBn != *pBn) db_post_events(prec, pBn, DBE_VALUE | DBE_LOG); } } break; default: recGblDbaddrError(S_db_badChoice, paddr, "mbboDirect: special"); return S_db_badChoice; } prec->udf = FALSE; return 0; } static long get_precision(const DBADDR *paddr,long *precision) { mbboDirectRecord *prec=(mbboDirectRecord *)paddr->precord; if(dbGetFieldIndex(paddr)==mbboDirectRecordVAL) *precision = prec->nobt; else recGblGetPrec(paddr,precision); return 0; } static void monitor(mbboDirectRecord *prec) { epicsUInt16 events = recGblResetAlarms(prec); if (prec->mlst != prec->val) { events |= DBE_VALUE | DBE_LOG; prec->mlst = prec->val; } if (events) db_post_events(prec, &prec->val, events); events |= DBE_VALUE | DBE_LOG; if (prec->oraw != prec->rval) { db_post_events(prec, &prec->rval, events); prec->oraw = prec->rval; } if (prec->orbv != prec->rbv) { db_post_events(prec, &prec->rbv, events); prec->orbv = prec->rbv; } } static void convert(mbboDirectRecord *prec) { /* Convert VAL to RVAL */ prec->rval = prec->val; if (prec->shft > 0) prec->rval <<= prec->shft; } static long writeValue(mbboDirectRecord *prec) { struct mbbodset *pdset = (struct mbbodset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: status = pdset->write_mbbo(prec); break; case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbPutLink(&prec->siol, DBR_ULONG, &prec->val, 1); prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } base-7.0.3.1/modules/database/src/std/rec/mbboDirectRecord.dbd.pod0000664000577000060420000004363413557101274023440 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Multi-Bit Binary Output Direct Record (mbboDirect) The mbboDirect record performs the opposite function to that of the mbbiDirect record. It accumulates bits (in the fields B0 - BF) as unsigned characters, and converts them to a word which is then written out to hardware. If a bit field is non-zero, it is interpreted as a binary 1. On the other hand, if it is zero, it is interpreted as a binary 0. =recordtype mbboDirect =cut recordtype(mbboDirect) { =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters The mbboDirect record has the standard fields for specifying under what circumstances it will be processed. These fields are listed in L. In addition, L explains how these fields are used. Note that I/O event scanning is only supported for those card types that interrupt. =head3 Desired Output Parameters The mbboDirect record, like all output records, must specify where its output originates. The output mode select field (OMSL) determines whether the output originates from another record or from database access. When set to C<<< closed_loop >>>, the desired output is retrieved from the link specified in the desired output (DOL) field--which can specify either a database or channel access link--and placed into the VAL field. When set to C<<< supervisory >>>, the DOL field is ignored and the current value of VAL is used. The desired output can be written into the VAL field via dpPuts at run-time when the record is in C<<< supervisory >>> mode. DOL can also be a constant, in which case VAL is initialized to the constant value. Note that OMSL cannot be C<<< closed_loop >>> when DOL is a constant. See L
for information on how to specify database links. VAL is then converted to RVAL in the routine described in the next section. However, the C<<< Soft Channel >>> device support module for the mbboDirect record writes the VAL field's value without any conversion. =fields OMSL, DOL, VAL =head3 Convert and Write Parameters For records that are to write values to hardware devices, the OUT output link must contain the address of the I/O card, and the DTYP field must specify the proper device support module. Be aware that the address format differs according to the I/O bus used. See L
for information on the format of hardware addresses. If the mbboDirect record does not use the C<<< Soft Channel >>> device support module, then VAL is converted to RVAL, and RVAL is the actual 16-bit word sent out. RVAL is set equal to VAL and then shifted left by the number of bits specified in the SHFT field (the SHFT value is set by device support and is not configurable by the user). RVAL is then sent out to the location specified in the OUT field. For mbboDirect records that specify a database link, a channel access link, or a constant, the DTYP field must specify either one of two soft device support routines--{Soft Channel} or C<<< Raw Soft Channel >>>. The difference between the two is that C<<< Soft Channel >>> writes the desired output value from VAL directly to the output link while C<<< Raw Soft Channel >>> writes the value from RVAL to the output link after it has undergone the conversion described above. See L
for information on how to specify database links. =fields OUT, RVAL, SHFT, B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, BA, BB, BC, BD, BE, BF =head3 Operator Display Parameters See L for more on the record name (NAME) and description (DESC) fields. =fields NAME, DESC =cut include "dbCommon.dbd" field(VAL,DBF_LONG) { prompt("Word") promptgroup("50 - Output") asl(ASL0) pp(TRUE) } field(OMSL,DBF_MENU) { prompt("Output Mode Select") promptgroup("50 - Output") special(SPC_RESET) pp(TRUE) interest(1) menu(menuOmsl) } field(NOBT,DBF_SHORT) { prompt("Number of Bits") promptgroup("50 - Output") special(SPC_NOMOD) interest(1) } field(DOL,DBF_INLINK) { prompt("Desired Output Loc") promptgroup("40 - Input") interest(1) } field(OUT,DBF_OUTLINK) { prompt("Output Specification") promptgroup("50 - Output") interest(1) } field(RVAL,DBF_ULONG) { prompt("Raw Value") special(SPC_NOMOD) pp(TRUE) } field(ORAW,DBF_ULONG) { prompt("Prev Raw Value") special(SPC_NOMOD) interest(3) } field(RBV,DBF_ULONG) { prompt("Readback Value") special(SPC_NOMOD) } field(ORBV,DBF_ULONG) { prompt("Prev Readback Value") special(SPC_NOMOD) interest(3) } field(MASK,DBF_ULONG) { prompt("Hardware Mask") special(SPC_NOMOD) interest(1) } field(MLST,DBF_LONG) { prompt("Last Value Monitored") special(SPC_NOMOD) interest(3) } field(SHFT,DBF_USHORT) { prompt("Shift") promptgroup("50 - Output") interest(1) } =head3 Run-time and Simulation Mode Parameters These parameters are used by the run-time code for processing the mbbo Direct record. MASK is used by device support routine to read the hardware register. Record support sets low order NOBT bits. Device support can shift this value. MLST holds the value when the last monitor for value change was triggered. =fields NOBT, ORAW, MASK, MLST The following fields are used to operate the mbboDirect record in the simulation mode. See L for more information on the simulation mode fields. =fields SIOL, SIML, SIMM, SIMS, SSCN, SDLY =cut field(SIOL,DBF_OUTLINK) { prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } =head3 Alarm Parameters The possible alarm conditions for mbboDirect records are the SCAN, READ, and INVALID alarms. The SCAN and READ alarms are not configurable by the user since they are always of MAJOR severity. See L for a complete explanation of Scan and Read alarms. The IVOA field specifies an action to take when the INVALID alarm is triggered. There are three possible actions: C<<< Continue normally >>>, C<<< Don't drive outputs >>>, or C<<< Set output to IVOV >>>. When C<<< Set output to IVOV >>> is specified and a INVALID alarm is triggered, the record will write the value in the IVOV field to output. See L for more information. L lists other fields related to a alarms that are common to all record types. =fields IVOA, IVOV =cut field(IVOA,DBF_MENU) { prompt("INVALID outpt action") promptgroup("50 - Output") interest(2) menu(menuIvoa) } field(IVOV,DBF_LONG) { prompt("INVALID output value") promptgroup("50 - Output") interest(2) } field(B0,DBF_UCHAR) { prompt("Bit 0") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) interest(1) } field(B1,DBF_UCHAR) { prompt("Bit 1") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) interest(1) } field(B2,DBF_UCHAR) { prompt("Bit 2") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) interest(1) } field(B3,DBF_UCHAR) { prompt("Bit 3") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) interest(1) } field(B4,DBF_UCHAR) { prompt("Bit 4") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) interest(1) } field(B5,DBF_UCHAR) { prompt("Bit 5") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) interest(1) } field(B6,DBF_UCHAR) { prompt("Bit 6") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) interest(1) } field(B7,DBF_UCHAR) { prompt("Bit 7") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) interest(1) } field(B8,DBF_UCHAR) { prompt("Bit 8") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) interest(1) } field(B9,DBF_UCHAR) { prompt("Bit 9") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) interest(1) } field(BA,DBF_UCHAR) { prompt("Bit 10") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) interest(1) } field(BB,DBF_UCHAR) { prompt("Bit 11") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) interest(1) } field(BC,DBF_UCHAR) { prompt("Bit 12") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) interest(1) } field(BD,DBF_UCHAR) { prompt("Bit 13") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) interest(1) } field(BE,DBF_UCHAR) { prompt("Bit 14") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) interest(1) } field(BF,DBF_UCHAR) { prompt("Bit 15") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) interest(1) } field(B10,DBF_UCHAR) { prompt("Bit 16") promptgroup("53 - Output 16-23") special(SPC_MOD) pp(TRUE) interest(1) } field(B11,DBF_UCHAR) { prompt("Bit 17") promptgroup("53 - Output 16-23") special(SPC_MOD) pp(TRUE) interest(1) } field(B12,DBF_UCHAR) { prompt("Bit 18") promptgroup("53 - Output 16-23") special(SPC_MOD) pp(TRUE) interest(1) } field(B13,DBF_UCHAR) { prompt("Bit 19") promptgroup("53 - Output 16-23") special(SPC_MOD) pp(TRUE) interest(1) } field(B14,DBF_UCHAR) { prompt("Bit 20") promptgroup("53 - Output 16-23") special(SPC_MOD) pp(TRUE) interest(1) } field(B15,DBF_UCHAR) { prompt("Bit 21") promptgroup("53 - Output 16-23") special(SPC_MOD) pp(TRUE) interest(1) } field(B16,DBF_UCHAR) { prompt("Bit 22") promptgroup("53 - Output 16-23") special(SPC_MOD) pp(TRUE) interest(1) } field(B17,DBF_UCHAR) { prompt("Bit 23") promptgroup("53 - Output 16-23") special(SPC_MOD) pp(TRUE) interest(1) } field(B18,DBF_UCHAR) { prompt("Bit 24") promptgroup("54 - Output 24-31") special(SPC_MOD) pp(TRUE) interest(1) } field(B19,DBF_UCHAR) { prompt("Bit 25") promptgroup("54 - Output 24-31") special(SPC_MOD) pp(TRUE) interest(1) } field(B1A,DBF_UCHAR) { prompt("Bit 26") promptgroup("54 - Output 24-31") special(SPC_MOD) pp(TRUE) interest(1) } field(B1B,DBF_UCHAR) { prompt("Bit 27") promptgroup("54 - Output 24-31") special(SPC_MOD) pp(TRUE) interest(1) } field(B1C,DBF_UCHAR) { prompt("Bit 28") promptgroup("54 - Output 24-31") special(SPC_MOD) pp(TRUE) interest(1) } field(B1D,DBF_UCHAR) { prompt("Bit 29") promptgroup("54 - Output 24-31") special(SPC_MOD) pp(TRUE) interest(1) } field(B1E,DBF_UCHAR) { prompt("Bit 30") promptgroup("54 - Output 24-31") special(SPC_MOD) pp(TRUE) interest(1) } field(B1F,DBF_UCHAR) { prompt("Bit 31") promptgroup("54 - Output 24-31") special(SPC_MOD) pp(TRUE) interest(1) } =head2 Record Support =head3 Record Support Routines =head4 init_record This routine initializes SIMM if SIML is a constant or creates a channel access link if SIML is PV_LINK. If SIOL is PV_LINK a channel access link is created. This routine next checks to see that device support is available.The routine next checks to see if the device support write routine is defined. If either device support or the device support write routine does not exist, an error message is issued and processing is terminated. If DOL is a constant, then VAL is initialized to its value and UDF is set to FALSE. MASK is cleared and then the NOBT low order bits are set. If device support includes C, it is called. If device support returns success, VAL is then set from RVAL and UDF is set to FALSE. =head4 Process See next section. =head3 Record Processing Routine process implements the following algorithm: =over =item 1. Check to see that the appropriate device support module exists. If it doesn't, an error message is issued and processing is terminated with the PACT field still set to TRUE. This ensures that processes will no longer be called for this record. Thus error storms will not occur. =item 2. If PACT is FALSE =over =item * If DOL is DB_LINK and OMSL is CLOSED_LOOP =over =item * Get value from DOL =item * Set PACT to FALSE =back =back =item 3. Convert =over =item * If PACT is FALSE, compute RVAL =over =item * Set RVAL = VAL =item * Shift RVAL left SHFT bits =back =item * Status=write_mbboDirect =back =item 4. If PACT has been changed to TRUE, the device support write output routine has started but has not completed writing the new value. In this case, the processing routine merely returns, leaving PACT TRUE. =item 5. Check to see if monitors should be invoked. =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if MLST is not equal to VAL. =item * Monitors for RVAL and RBV are checked whenever other monitors are invoked. =item * NSEV and NSTA are reset to 0. =back =item 6. Scan forward link if necessary, set PACT FALSE, and return. =back =begin html


=end html =head2 Device Support =head3 Fields Of Interest To Device Support Each mbboDirect record must have an associated set of device support routines. The primary responsibility of the device support routines is to obtain a new raw mbbo value whenever write_mbboDirect is called. The device support routines are primarily interested in the following fields: =fields PACT, DPVT, UDF, NSEV, NSTA, NOBT, OUT, RVAL, RBV, MASK, SHFT =head3 Device Support Routines Device support consists of the following routines: =head4 long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. =head4 long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. =head4 init_record init_record(precord) This routine is optional. If provided, it is called by the record support C routine. If MASK is used, it should be shifted if necessary and SHFT given a value. =head4 get_ioint_info get_ioint_info(int cmd,struct dbCommon *precord,IOSCANPVT *ppvt) This routine is called by the ioEventScan system each time the record is added or deleted from an I/O event scan list. cmd has the value (0,1) if the record is being (added to, deleted from) an I/O event list. It must be provided for any device type that can use the ioEvent scanner. =head4 write_mbboDirect write_mbboDirect(precord) This routine must output a new value. It returns the following values: =over =item * 0: Success. =item * Other: Error. =back =head3 Device Support For Soft Records This C<<< SOft Channel >>> module writes the current value of VAL. If the OUT link type is PV_LINK, then dbCaAddInlink is called by C. write_mbboDirect calls recGblPutLinkValue to write the current value of VAL. See L. =cut } base-7.0.3.1/modules/database/src/std/rec/mbboRecord.c0000664000577000060420000003071613557101274021212 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* mbboRecord.c - Record Support Routines for multi bit binary Output records */ /* * Original Author: Bob Dalesio * Date: 7-17-87 */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #include "menuOmsl.h" #include "menuIvoa.h" #include "menuYesNo.h" #define GEN_SIZE_OFFSET #include "mbboRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL static long cvt_dbaddr(DBADDR *); #define get_array_info NULL #define put_array_info NULL #define get_units NULL #define get_precision NULL static long get_enum_str(const DBADDR *, char *); static long get_enum_strs(const DBADDR *, struct dbr_enumStrs *); static long put_enum_str(const DBADDR *, const char *); #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset mbboRSET = { RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,mbboRSET); struct mbbodset { /* multi bit binary output dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (0, 2) => (success, success no convert)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN write_mbbo; /*returns: (0, 2) => (success, success no convert)*/ }; static void checkAlarms(mbboRecord *); static void convert(mbboRecord *); static void monitor(mbboRecord *); static long writeValue(mbboRecord *); static void init_common(mbboRecord *prec) { epicsUInt32 *pstate_values = &prec->zrvl; char *pstate_string = prec->zrst; int i; /* Check if any states are defined */ for (i = 0; i < 16; i++, pstate_string += sizeof(prec->zrst)) { if ((pstate_values[i] != 0) || (*pstate_string != '\0')) { prec->sdef = TRUE; return; } } prec->sdef = FALSE; } static long init_record(struct dbCommon *pcommon, int pass) { struct mbboRecord *prec = (struct mbboRecord *)pcommon; struct mbbodset *pdset; long status; if (pass == 0) { init_common(prec); return 0; } pdset = (struct mbbodset *) prec->dset; if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "mbbo: init_record"); return S_dev_noDSET; } if ((pdset->number < 5) || (pdset->write_mbbo == NULL)) { recGblRecordError(S_dev_missingSup, prec, "mbbo: init_record"); return S_dev_missingSup; } recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (recGblInitConstantLink(&prec->dol, DBF_USHORT, &prec->val)) prec->udf = FALSE; /* Initialize MASK if the user set NOBT instead */ if (prec->mask == 0 && prec->nobt <= 32) prec->mask = ((epicsUInt64) 1u << prec->nobt) - 1; if (pdset->init_record) { status = pdset->init_record(prec); init_common(prec); if (status == 0) { /* Convert initial read-back */ epicsUInt32 rval = prec->rval; if (prec->shft > 0) rval >>= prec->shft; if (prec->sdef) { epicsUInt32 *pstate_values = &prec->zrvl; int i; prec->val = 65535; /* initalize to unknown state */ for (i = 0; i < 16; i++) { if (*pstate_values == rval) { prec->val = i; break; } pstate_values++; } } else { /* No defined states, punt */ prec->val = rval; } prec->udf = FALSE; } else if (status == 2) status = 0; } else { init_common(prec); status = 0; } /* Convert VAL to RVAL */ convert(prec); prec->mlst = prec->val; prec->lalm = prec->val; prec->oraw = prec->rval; prec->orbv = prec->rbv; return status; } static long process(struct dbCommon *pcommon) { struct mbboRecord *prec = (struct mbboRecord *)pcommon; struct mbbodset *pdset = (struct mbbodset *) prec->dset; long status = 0; int pact = prec->pact; if ((pdset == NULL) || (pdset->write_mbbo == NULL)) { prec->pact = TRUE; recGblRecordError(S_dev_missingSup, prec, "write_mbbo"); return S_dev_missingSup; } if (!pact) { if (!dbLinkIsConstant(&prec->dol) && prec->omsl == menuOmslclosed_loop) { epicsUInt16 val; if (dbGetLink(&prec->dol, DBR_USHORT, &val, 0, 0)) { recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM); goto CONTINUE; } prec->val = val; } else if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, prec->udfs); goto CONTINUE; } prec->udf = FALSE; /* Convert VAL to RVAL */ convert(prec); } CONTINUE: /* Check for alarms */ checkAlarms(prec); if (prec->nsev < INVALID_ALARM) status = writeValue(prec); else { switch (prec->ivoa) { case menuIvoaSet_output_to_IVOV: if (!prec->pact) { prec->val = prec->ivov; convert(prec); } /* No break, fall through... */ case menuIvoaContinue_normally: status = writeValue(prec); break; case menuIvoaDon_t_drive_outputs: break; default : status = -1; recGblRecordError(S_db_badField, prec, "mbbo::process Illegal IVOA field"); } } /* Done if device support set pact */ if (!pact && prec->pact) return 0; prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, NULL); monitor(prec); /* Wrap up */ recGblFwdLink(prec); prec->pact = FALSE; return status; } static long special(DBADDR *paddr, int after) { mbboRecord *prec = (mbboRecord *) paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); switch (paddr->special) { case SPC_MOD: if (fieldIndex == mbboRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return 0; } if (!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); } return 0; default: recGblDbaddrError(S_db_badChoice, paddr, "mbbo: special"); return S_db_badChoice; } } static long cvt_dbaddr(DBADDR *paddr) { mbboRecord *prec = (mbboRecord *) paddr->precord; if (dbGetFieldIndex(paddr) != mbboRecordVAL) { recGblDbaddrError(S_db_badField, paddr, "mbbo: cvt_dbaddr"); return 0; } if (!prec->sdef) { paddr->field_type = DBF_USHORT; paddr->dbr_field_type = DBF_USHORT; } return 0; } static long get_enum_str(const DBADDR *paddr, char *pstring) { mbboRecord *prec = (mbboRecord *) paddr->precord; epicsEnum16 *pfield = paddr->pfield; epicsEnum16 val = *pfield; if (dbGetFieldIndex(paddr) != mbboRecordVAL) { strcpy(pstring, "Bad Field"); } else if (val <= 15) { const char *pstate = prec->zrst + val * sizeof(prec->zrst); strncpy(pstring, pstate, sizeof(prec->zrst)); } else { strcpy(pstring, "Illegal Value"); } return 0; } static long get_enum_strs(const DBADDR *paddr, struct dbr_enumStrs *pes) { mbboRecord *prec = (mbboRecord *) paddr->precord; const char *pstate; int i, states = 0; memset(pes->strs, '\0', sizeof(pes->strs)); pstate = prec->zrst; for (i = 0; i < 16; i++) { strncpy(pes->strs[i], pstate, sizeof(prec->zrst)); if (*pstate) states = i + 1; pstate += sizeof(prec->zrst); } pes->no_str = states; return 0; } static long put_enum_str(const DBADDR *paddr, const char *pstring) { mbboRecord *prec = (mbboRecord *) paddr->precord; const char *pstate; int i; if (prec->sdef) { pstate = prec->zrst; for (i = 0; i < 16; i++) { if (strncmp(pstate, pstring, sizeof(prec->zrst)) == 0) { prec->val = i; return 0; } pstate += sizeof(prec->zrst); } } return S_db_badChoice; } static void checkAlarms(mbboRecord *prec) { epicsEnum16 val = prec->val; /* Check for STATE alarm */ if (val > 15) { /* Unknown state */ recGblSetSevr(prec, STATE_ALARM, prec->unsv); } else { /* State has a severity field */ epicsEnum16 *severities = &prec->zrsv; recGblSetSevr(prec, STATE_ALARM, severities[prec->val]); } /* Check for COS alarm */ if (val == prec->lalm || recGblSetSevr(prec, COS_ALARM, prec->cosv)) return; prec->lalm = val; } static void monitor(mbboRecord *prec) { epicsUInt16 events = recGblResetAlarms(prec); if (prec->mlst != prec->val) { events |= DBE_VALUE | DBE_LOG; prec->mlst = prec->val; } if (events) db_post_events(prec, &prec->val, events); events |= DBE_VALUE | DBE_LOG; if (prec->oraw != prec->rval) { db_post_events(prec, &prec->rval, events); prec->oraw = prec->rval; } if (prec->orbv != prec->rbv) { db_post_events(prec, &prec->rbv, events); prec->orbv = prec->rbv; } } static void convert(mbboRecord *prec) { /* Convert VAL to RVAL */ if (prec->sdef) { epicsUInt32 *pvalues = &prec->zrvl; if (prec->val > 15) { recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); return; } prec->rval = pvalues[prec->val]; } else prec->rval = prec->val; if (prec->shft > 0) prec->rval <<= prec->shft; } static long writeValue(mbboRecord *prec) { struct mbbodset *pdset = (struct mbbodset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: status = pdset->write_mbbo(prec); break; case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbPutLink(&prec->siol, DBR_USHORT, &prec->val, 1); prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } base-7.0.3.1/modules/database/src/std/rec/mbboRecord.dbd.pod0000664000577000060420000005665113557101274022310 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Multi-Bit Binary Output Record (mbbo) The normal use for the mbbo record type is to send a binary value (representing one of up to 16 states) to a Digital Output module. It is used for any device that uses more than one contiguous bit to control it. The mbbo record can also be used to write discrete values to other records via database or channel access links. =recordtype mbbo =cut recordtype(mbbo) { =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters The mbbo record has the standard fields for specifying under what circumstances it will be processed. These fields are listed in L. In addition, L explains how these fields are used. Note that I/O event scanning is only supported for those card types that interrupt. =head3 Desired Output Parameters The multi-bit binary output record, like all output records, must specify where its output originates. The output mode select (OMSL) field determines whether the output originates from another record or from database access (i.e., the operator). When set to C<<< closed_loop >>>, the desired output is retrieved from the link specified in the desired output (DOL) field--which can specify either a database or channel access link--and placed into the VAL field. When set to C<<< supervisory >>>, the DOL field is ignored and the current value of VAL is simply written. VAL can be changed via dpPuts at run-time when OMSL is C<<< supervisory >>>. The DOL field can also be a constant, in which case the VAL field is initialized to the constant value. If DOL is a constant, OMSL cannot be set to C<<< closed_loop >>>. The VAL field itself usually consists of an index that specifies one of the states. The actual output written is the value of RVAL, which is converted from VAL following the routine explained in the next section. However, records that use the C<<< Soft Channel >>> device support module write the VAL field's value without any conversion. =fields OMSL, DOL, VAL =head3 Convert and Write Parameters The device support routines write the desired output to the location specified in the OUT field. If the record uses soft device support, OUT can contain a constant, a database link, or a channel access link; however, if OUT is a constant, no value will be written. For records that write their values to hardware devices, the OUT output link must specify the address of the I/O card, and the DTYP field must specify the corresponding device support module. Be aware that the address format differs according to the I/O bus used. See L
for information on the format of hardware addresses. For mbbo records that write to hardware, the value written to the output location is the value contained in RVAL, which is converted from VAL, VAL containing an index of one of the 16 states (0-15). RVAL is then set to the corresponding state value, the value in one of the fields ZRVL through FFVL. Then this value is shifted left according to the number in the SHFT field so that the value is in the correct position for the bits being used (the SHFT value is set by device support and is not configurable by the user). The state value fields ZRVL through FFVL must be configured by the user before run-time. When the state values are not defined, the states defined (SDEF) field is set to FALSE at initialization time by the record routines. When SDEF is FALSE, then the record processing routine does not try to find a match, RVAL is set equal to VAL, the bits are shifted using the number in SHFT, and the value is written thus. If the OUT output link specifies a database link, channel access link, or constant, then the DTYP field must specify either one of the two soft device support modules-- C<<< Soft Channel >>> or C<<< Raw Soft Channel >>>. C<<< Soft >>> C<<< Channel >>> writes the value of VAL to the output link, without any conversion, while C<<< Raw Soft Channel >>> writes the value from RVAL after it has undergone the above conversion. See L
for information on specifying links. Note also that when a string is retrieved as the desired output, a record support routine is provided (C<<< put_enum_str() >>>) that will check to see if the string matches one of the strings in the ZRST through FFST fields. If a match is found, RVAL is set equal to the corresponding state value of that string. =fields OUT, DTYP, RVAL, SHFT, SDEF, ZRVL, ONVL, TWVL, THVL, FRVL, FVVL, SXVL, SVVL, EIVL, NIVL, TEVL, ELVL, TVVL, TTVL, FTVL, FFVL =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. These fields are used to display the value and other parameters of the mbbo record either textually or graphically. The ZRST-FFST fields contain strings describing each of the corresponding states. The C<<< get_enum_str() >>> and C<<< get_enum_strs() >>> record routines retrieve these strings for the operator. C<<< get_enum_str() >>> gets the string corresponding to the value in VAL, and C<<< get_enum_strs() >>> retrieves all the strings. See L for more on the record name (NAME) and description (DESC) fields. =fields NAME, DESC, ZRST, ONST, TWST, THST, FRST, FVST, SXST, SVST, EIST, NIST, TEST, ELST, TVST, TTST, FTST, FFST =head3 Alarm Parameters The possible alarm conditions for multi-bit binary outputs are the SCAN, READ, INVALID, and state alarms. The SCAN and READ alarms are called by the support modules and are not configurable by the user, as their severity is always MAJOR. The IVOA field specifies an action to take from a number of possible choices when the INVALID alarm is triggered. The IVOV field contains a value to be written once the INVALID alarm has been triggered if C<<< Set output to IVOV >>> has been chosen in the IVOA field. The severity of the INVALID alarm is not configurable by the user. The state alarms are configured in the below severity fields. These fields have the usual possible values for severity fields: NO_ALARM, MINOR, and MAJOR. The unknown state severity field (UNSV), if set to MINOR or MAJOR, triggers an alarm when the record support routine cannot find a matching value in the state value fields for VAL or when VAL is out of range. The change of state severity field (COSV) triggers an alarm when the record's state changes, if set to MAJOR or MINOR. The state severity (ZRSV-FFSV) fields, when set to MAJOR or MINOR, trigger an alarm when VAL equals the corresponding field. See L for a complete explanation of discrete alarms and these fields. See L for an explanation of the IVOA and IVOV fields. L lists other fields related to a alarms that are common to all record types. =fields UNSV, COSV, IVOA, IVOV, ZRSV, ONSV, TWSV, THSV, FRSV, FVSV, SXSV, SVSV, EISV, NISV, TESV, ELSV, TVSV, TTSV, FTSV, FFSV =head3 Run-Time and Simulation Mode Parameters These parameters are used by the run-time code for processing the multi-bit binary output. MASK is used by device support routine to read the hardware register. Record support sets low order of MASK the number of bits specified in NOBT. Device support can shift this value. The LALM field implements the change of state alarm severity by holding the value of VAL when the previous change of state alarm was issued. MLST holds the value when the last monitor for value change was triggered. SDEF is used by record support to save time if no states are defined; it is used for converting VAL to RVAL. =fields NOBT, ORAW, MASK, LALM, MLST, SDEF The following fields are used to operate the mbbo record in the simulation mode. See L for more information on the simulation mode fields. =fields SIOL, SIML, SIMM, SIMS, SSCN, SDLY =cut include "dbCommon.dbd" field(VAL,DBF_ENUM) { prompt("Desired Value") promptgroup("50 - Output") special(SPC_DBADDR) asl(ASL0) pp(TRUE) #=read Yes #=write Yes } field(DOL,DBF_INLINK) { prompt("Desired Output Loc") promptgroup("40 - Input") interest(1) } field(OMSL,DBF_MENU) { prompt("Output Mode Select") promptgroup("50 - Output") interest(1) menu(menuOmsl) } field(NOBT,DBF_USHORT) { prompt("Number of Bits") promptgroup("50 - Output") special(SPC_NOMOD) interest(1) } field(OUT,DBF_OUTLINK) { prompt("Output Specification") promptgroup("50 - Output") interest(1) } field(ZRVL,DBF_ULONG) { prompt("Zero Value") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(ONVL,DBF_ULONG) { prompt("One Value") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(TWVL,DBF_ULONG) { prompt("Two Value") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(THVL,DBF_ULONG) { prompt("Three Value") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(FRVL,DBF_ULONG) { prompt("Four Value") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(FVVL,DBF_ULONG) { prompt("Five Value") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(SXVL,DBF_ULONG) { prompt("Six Value") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(SVVL,DBF_ULONG) { prompt("Seven Value") promptgroup("51 - Output 0-7") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(EIVL,DBF_ULONG) { prompt("Eight Value") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(NIVL,DBF_ULONG) { prompt("Nine Value") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(TEVL,DBF_ULONG) { prompt("Ten Value") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(ELVL,DBF_ULONG) { prompt("Eleven Value") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(TVVL,DBF_ULONG) { prompt("Twelve Value") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(TTVL,DBF_ULONG) { prompt("Thirteen Value") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(FTVL,DBF_ULONG) { prompt("Fourteen Value") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(FFVL,DBF_ULONG) { prompt("Fifteen Value") promptgroup("52 - Output 8-15") special(SPC_MOD) pp(TRUE) base(HEX) interest(1) } field(ZRST,DBF_STRING) { prompt("Zero String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(ONST,DBF_STRING) { prompt("One String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(TWST,DBF_STRING) { prompt("Two String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(THST,DBF_STRING) { prompt("Three String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(FRST,DBF_STRING) { prompt("Four String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(FVST,DBF_STRING) { prompt("Five String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(SXST,DBF_STRING) { prompt("Six String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(SVST,DBF_STRING) { prompt("Seven String") promptgroup("81 - Display 0-7") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(EIST,DBF_STRING) { prompt("Eight String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(NIST,DBF_STRING) { prompt("Nine String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(TEST,DBF_STRING) { prompt("Ten String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(ELST,DBF_STRING) { prompt("Eleven String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(TVST,DBF_STRING) { prompt("Twelve String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(TTST,DBF_STRING) { prompt("Thirteen String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(FTST,DBF_STRING) { prompt("Fourteen String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(FFST,DBF_STRING) { prompt("Fifteen String") promptgroup("82 - Display 8-15") special(SPC_MOD) pp(TRUE) interest(1) size(26) } field(ZRSV,DBF_MENU) { prompt("State Zero Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(ONSV,DBF_MENU) { prompt("State One Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(TWSV,DBF_MENU) { prompt("State Two Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(THSV,DBF_MENU) { prompt("State Three Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(FRSV,DBF_MENU) { prompt("State Four Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(FVSV,DBF_MENU) { prompt("State Five Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(SXSV,DBF_MENU) { prompt("State Six Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(SVSV,DBF_MENU) { prompt("State Seven Severity") promptgroup("71 - Alarm 0-7") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(EISV,DBF_MENU) { prompt("State Eight Severity") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(NISV,DBF_MENU) { prompt("State Nine Severity") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(TESV,DBF_MENU) { prompt("State Ten Severity") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(ELSV,DBF_MENU) { prompt("State Eleven Severity") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(TVSV,DBF_MENU) { prompt("State Twelve Severity") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(TTSV,DBF_MENU) { prompt("State Thirteen Sevr") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(FTSV,DBF_MENU) { prompt("State Fourteen Sevr") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(FFSV,DBF_MENU) { prompt("State Fifteen Sevr") promptgroup("72 - Alarm 8-15") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(UNSV,DBF_MENU) { prompt("Unknown State Sevr") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(COSV,DBF_MENU) { prompt("Change of State Sevr") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(RVAL,DBF_ULONG) { prompt("Raw Value") pp(TRUE) } field(ORAW,DBF_ULONG) { prompt("Prev Raw Value") special(SPC_NOMOD) interest(3) } field(RBV,DBF_ULONG) { prompt("Readback Value") special(SPC_NOMOD) } field(ORBV,DBF_ULONG) { prompt("Prev Readback Value") special(SPC_NOMOD) interest(3) } field(MASK,DBF_ULONG) { prompt("Hardware Mask") special(SPC_NOMOD) interest(1) } field(MLST,DBF_USHORT) { prompt("Last Value Monitored") special(SPC_NOMOD) interest(3) } field(LALM,DBF_USHORT) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(SDEF,DBF_SHORT) { prompt("States Defined") special(SPC_NOMOD) interest(3) } field(SHFT,DBF_USHORT) { prompt("Shift") promptgroup("50 - Output") interest(1) } field(SIOL,DBF_OUTLINK) { prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } field(IVOA,DBF_MENU) { prompt("INVALID outpt action") promptgroup("50 - Output") interest(2) menu(menuIvoa) } field(IVOV,DBF_USHORT) { prompt("INVALID output value") promptgroup("50 - Output") interest(2) } } =head2 Record Support =head3 Record Support Routines =head4 init_record This routine initializes SIMM if SIML is a constant or creates a channel access link if SIML is PV_LINK. If SIOL is PV_LINK a channel access link is created. This routine next checks to see that device support is available. The routine next checks to see if the device support write routine is defined. If either device support or the device support write routine does not exist, an error message is issued and processing is terminated. If DOL is a constant, then VAL is initialized to its value and UDF is set to FALSE. MASK is cleared and then the NOBT low order bits are set. If device support includes C, it is called. init_common is then called to determine if any states are defined. If states are defined, SDEF is set to TRUE. If device support returns success, VAL is then set from RVAL and UDF is set to FALSE. =head4 process See next section. =head4 special Computes SDEF when any of the fields ZRVL,...FFVL change value. =head4 get_value Fills in the values of struct valueDes so that they refer to VAL. =head4 get_enum_str Retrieves ASCII string corresponding to VAL. =head4 get_enum_strs Retrieves ASCII strings for ZRST,...FFST. =head4 put_enum_str Checks if string matches ZRST,...FFST and if it does, sets VAL. =head3 Record Processing Routine process implements the following algorithm: =over =item 1. Check to see that the appropriate device support module exists. If it doesn't, an error message is issued and processing is terminated with the PACT field still set to TRUE. This ensures that processes will not longer be called for this record. Thus error storms will not occur. =item 2. If PACT is FALSE =over =item * If DOL is DB_LINK and OMSL is CLOSED_LOOP =over =item * Get value from DOL =item * Set UDF to FALSE =item * Check for link alarm =back =item * If any state values are defined =over =item * If VAL E 15, then raise alarm and go to 4 =item * Else using VAL as index set RVAL = one of ZRVL,...FFVL =back =item * Else set RVAL = VAL =item * Shift RVAL left SHFT bits =back =item 3. Convert =over =item * If PACT is FALSE, compute RVAL =over =item * If VAL is 0,...,15, set RVAL from ZRVL,...,FFVL =item * If VAL out of range, set RVAL = undefined =back =item * Status = write_mbbo =back =item 4. Check alarms. This routine checks to see if the new VAL causes the alarm status and severity to change. If so, NSEV, NSTA and LALM are set. =item 5. Check severity and write the new value. See L and L for more information. =item 6. If PACT has been changed to TRUE, the device support write output routine has started but has not completed writing the new value. In this case, the processing routine merely returns, leaving PACT TRUE. =item 7. Check to see if monitors should be invoked. =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if MLST is not equal to VAL. =item * Monitors for RVAL and RBV are checked whenever other monitors are invoked. =item * NSEV and NSTA are reset to 0. =back =item 8. Scan forward link if necessary, set PACT FALSE, and return. =back =head2 Device Support =head3 Fields Of Interest To Device Support Each mbbo record must have an associated set of device support routines. The primary responsibility of the device support routines is to obtain a new raw mbbo value whenever write_mbbo is called. The device support routines are primarily interested in the following fields: =fields PACT, DPVT, NSEV, NSTA, NOBT, OUT, RVAL, RBV, MASK, SHFT =head3 Device Support Routines Device support consists of the following routines: =head4 long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. =head4 long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. =head4 init_record init_record(precord) This routine is optional. If provided, it is called by the record support's C routine. If MASK is used, it should be shifted if necessary and SHFT given a value. =head4 get_ioint_info get_ioint_info(int cmd,struct dbCommon *precord,IOSCANPVT *ppvt) This routine is called by the ioEventScan system each time the record is added or deleted from an I/O event scan list. cmd has the value (0,1) if the record is being (added to, deleted from) an I/O event list. It must be provided for any device type that can use the ioEvent scanner. =head4 write_mbbo write_mbbo(precord) This routine must output a new value. It returns the following values: =over =item * 0: Success. =item * Other: Error. =back =head3 Device Support For Soft Records =head4 Soft Channel The C<<< Soft Channel >>> module writes the current value of VAL. If the OUT link type is PV_LINK, then dbCaAddInlink is called by C. write_mbbo calls recGblPutLinkValue to write the current value of VAL. See L for more information. =head4 Raw Soft Channel This module writes RVAL to the location specified in the output link. It returns a 0. =cut base-7.0.3.1/modules/database/src/std/rec/permissiveRecord.c0000664000577000060420000000530013557101274022450 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* recPermissive.c - Record Support Routines for Permissive records */ /* * Original Author: Bob Dalesio * Date: 10-10-90 */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #define GEN_SIZE_OFFSET #include "permissiveRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL #define init_record NULL static long process(struct dbCommon *); #define special NULL #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL #define get_units NULL #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset permissiveRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,permissiveRSET); static void monitor(permissiveRecord *); static long process(struct dbCommon *pcommon) { struct permissiveRecord *prec = (struct permissiveRecord *)pcommon; prec->pact=TRUE; prec->udf=FALSE; recGblGetTimeStamp(prec); monitor(prec); recGblFwdLink(prec); prec->pact=FALSE; return(0); } static void monitor(permissiveRecord *prec) { unsigned short monitor_mask; unsigned short val,oval,wflg,oflg; monitor_mask = recGblResetAlarms(prec); /* get val,oval,wflg,oflg*/ val=prec->val; oval=prec->oval; wflg=prec->wflg; oflg=prec->oflg; /*set oval and oflg*/ prec->oval = val; prec->oflg = wflg; if(oval != val) { db_post_events(prec,&prec->val, monitor_mask|DBE_VALUE|DBE_LOG); } if(oflg != wflg) { db_post_events(prec,&prec->wflg, monitor_mask|DBE_VALUE|DBE_LOG); } return; } base-7.0.3.1/modules/database/src/std/rec/permissiveRecord.dbd.pod0000664000577000060420000000713213557101274023545 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Permissive Record (permissive) The permissive record is for communication between a server and a client. An example would be a sequence program server and an operator interface client. By using multiple permissive records a sequence program can communicate its current state to the client. B =recordtype permissive =cut recordtype(permissive) { include "dbCommon.dbd" =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters The permissive record has the standard fields for specifying under what circumstances the record will be processed. These fields are listed in L. In addition, L explains how these fields are used. Since the permissive record supports no direct interfaces to hardware, its SCAN field cannot be C<<< I/O Intr >>>. =head3 Client-server Parameters The client and server communicate through the VAL and watchdog flag (WFLG) fields. At initialization, both fields are set equal to 0, which means OFF. The server sets WFLG equal to ON when it is ready to accept a request. The client monitors WFLG and when WFLG equals 1, the client-server action is performed (a private matter between server and client). When WFLG is off--when the server is busy--the client program may turn the VAL field from OFF to ON. After the server finishes its task, it will notice that VAL is ON and will turn both WFLG and VAL OFF and performs the requested service. Note that when WFLG is ON, the client program ''must not'' turn VAL to on. =fields VAL, WFLG =cut field(VAL,DBF_USHORT) { prompt("Status") promptgroup("40 - Input") asl(ASL0) pp(TRUE) } field(WFLG,DBF_USHORT) { prompt("Wait Flag") pp(TRUE) } =head3 Operator Display Parameters The label field (LABL) contains a string given to it that should describe the record in further detail. In addition to the DESC field. See L for more on the record name (NAME) and description (DESC) fields. =fields LABL, NAME, DESC =cut field(LABL,DBF_STRING) { prompt("Button Label") promptgroup("80 - Display") pp(TRUE) interest(1) size(20) } =head3 Alarm Parameters The Permissive record has the alarm parameters common to all record types. L lists other fields related to a alarms that are common to all record types. =head3 Run-time Parameters These fields are used to trigger monitors for each field. Monitors for the VAL field are triggered when OVAL, the old value field, does not equal VAL. Likewise, OFLG causes monitors to be invoked for WFLG when WFLG does not equal OLFG. =fields OVAL, OFLG =cut field(OVAL,DBF_USHORT) { prompt("Old Status") special(SPC_NOMOD) interest(3) } field(OFLG,DBF_USHORT) { prompt("Old Flag") special(SPC_NOMOD) interest(3) } =head2 Record Support =head3 Record Support Routines =head4 process long (*process)(struct dbCommon *precord) C<<< process() >>> sets UDF to FALSE, triggers monitors on VAL and WFLG when they change, and scans the forward link if necessary. =cut } base-7.0.3.1/modules/database/src/std/rec/printfRecord.c0000664000577000060420000003413713557101274021576 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Printf record type */ /* * Author: Andrew Johnson * Date: 2012-09-18 */ #include #include #include "dbDefs.h" #include "errlog.h" #include "alarm.h" #include "cantProceed.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "epicsMath.h" #include "epicsStdio.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #define GEN_SIZE_OFFSET #include "printfRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Flag bits */ #define F_CHAR 1 #define F_SHORT 2 #define F_LONG 4 #define F_LONGLONG 8 #define F_LEFT 0x10 #define F_BADFMT 0x40 #define F_BADLNK 0x80 #define F_BAD (F_BADFMT | F_BADLNK) #define GET_PRINT(VALTYPE, DBRTYPE) \ VALTYPE val; \ int ok; \ \ if (dbLinkIsConstant(plink)) \ ok = recGblInitConstantLink(plink++, DBRTYPE, &val); \ else \ ok = ! dbGetLink(plink++, DBRTYPE, &val, 0, 0); \ if (ok) \ added = epicsSnprintf(pval, vspace + 1, format, val); \ else \ flags |= F_BADLNK static void doPrintf(printfRecord *prec) { const char *pfmt = prec->fmt; DBLINK *plink = &prec->inp0; int linkn = 0; char *pval = prec->val; int vspace = prec->sizv - 1; int ch; while (vspace > 0 && (ch = *pfmt++)) { if (ch != '%') { /* Copy literal strings directly into prec->val */ *pval++ = ch; --vspace; } else { char format[20]; char *pformat = format; int width = 0; int precision = 0; int *pnum = &width; int flags = 0; int added = 0; int cont = 1; /* The format directive parsing here is not comprehensive, * in most cases we just copy each directive into format[] * and get epicsSnprintf() do all the work. We do replace * all variable-length field width or precision '*' chars * with an integer read from the next input link, and we * also convert %ls (long string) directives ourself, so * we need to know the width, precision and justification. */ *pformat++ = ch; /* '%' */ while (cont && (ch = *pfmt++)) { *pformat++ = ch; switch (ch) { case '+': case ' ': case '#': break; case '-': flags |= F_LEFT; break; case '.': pnum = &precision; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': *pnum = *pnum * 10 + ch - '0'; break; case '*': if (*pnum) { flags |= F_BADFMT; } else if (linkn++ < PRINTF_NLINKS) { epicsInt16 i; int ok; if (dbLinkIsConstant(plink)) ok = recGblInitConstantLink(plink++, DBR_SHORT, &i); else ok = ! dbGetLink(plink++, DBR_SHORT, &i, 0, 0); if (ok) { *pnum = i; added = epicsSnprintf(--pformat, 6, "%d", i); pformat += added; } else /* No more LNKn fields */ flags |= F_BADLNK; } else flags |= F_BADLNK; break; case 'h': if (flags & (F_LONGLONG | F_LONG | F_CHAR)) flags |= F_BADFMT; else if (flags & F_SHORT) flags = (flags & ~F_SHORT) | F_CHAR; else flags |= F_SHORT; break; case 'l': if (flags & (F_LONGLONG | F_SHORT | F_CHAR)) flags |= F_BADFMT; else if (flags & F_LONG) flags = (flags & ~F_LONG) | F_LONGLONG; else flags |= F_LONG; break; default: if (strchr("diouxXeEfFgGcs%", ch) == NULL) flags |= F_BADFMT; cont = 0; break; } } if (!ch) /* End of format string */ break; if (flags & F_BAD) goto bad_format; *pformat = 0; /* Terminate our format string */ if (width < 0) { width = -width; flags |= F_LEFT; } if (precision < 0) precision = 0; if (ch == '%') { added = epicsSnprintf(pval, vspace + 1, "%s", format); } else if (linkn++ >= PRINTF_NLINKS) { /* No more LNKn fields */ flags |= F_BADLNK; } else switch (ch) { /* Conversion character */ case 'c': case 'd': case 'i': if (ch == 'c' || flags & F_CHAR) { GET_PRINT(epicsInt8, DBR_CHAR); } else if (flags & F_SHORT) { GET_PRINT(epicsInt16, DBR_SHORT); } else if (flags & F_LONGLONG) { GET_PRINT(epicsInt64, DBR_INT64); } else { /* F_LONG has no real effect */ GET_PRINT(epicsInt32, DBR_LONG); } break; case 'o': case 'x': case 'X': case 'u': if (flags & F_CHAR) { GET_PRINT(epicsUInt8, DBR_UCHAR); } else if (flags & F_SHORT) { GET_PRINT(epicsUInt16, DBR_USHORT); } else if (flags & F_LONGLONG) { GET_PRINT(epicsUInt64, DBR_UINT64); } else { /* F_LONG has no real effect */ GET_PRINT(epicsUInt32, DBR_ULONG); } break; case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': if (flags & F_SHORT) { GET_PRINT(epicsFloat32, DBR_FLOAT); } else { GET_PRINT(epicsFloat64, DBR_DOUBLE); } break; case 's': if (flags & F_LONG) { long n = vspace + 1; long status; if (precision && n > precision) n = precision + 1; /* If set, precision is the maximum number of * characters to be printed from the string. * It does not limit the field width however. */ if (dbLinkIsConstant(plink)) { epicsUInt32 len = n; status = dbLoadLinkLS(plink++, pval, n, &len); n = len; } else status = dbGetLink(plink++, DBR_CHAR, pval, 0, &n); if (status) flags |= F_BADLNK; else { int padding; /* Terminate string and measure its length */ pval[n] = 0; added = strlen(pval); padding = width - added; if (padding > 0) { if (flags & F_LEFT) { /* add spaces on RHS */ if (width > vspace) padding = vspace - added; memset(pval + added, ' ', padding); } else { /* insert spaces on LHS */ int trunc = width - vspace; if (trunc < added) { added -= trunc; memmove(pval + padding, pval, added); } else { padding = vspace; added = 0; } memset(pval, ' ', padding); } added += padding; } } } else { char val[MAX_STRING_SIZE]; int ok; if (dbLinkIsConstant(plink)) ok = recGblInitConstantLink(plink++, DBR_STRING, val); else ok = ! dbGetLink(plink++, DBR_STRING, val, 0, 0); if (ok) added = epicsSnprintf(pval, vspace + 1, format, val); else flags |= F_BADLNK; } break; default: errlogPrintf("printfRecord: Unexpected conversion '%s'\n", format); flags |= F_BADFMT; break; } if (flags & F_BAD) { bad_format: added = epicsSnprintf(pval, vspace + 1, "%s", flags & F_BADLNK ? prec->ivls : format); } if (added <= vspace) { pval += added; vspace -= added; } else { /* Output was truncated */ pval += vspace; vspace = 0; } } } *pval++ = 0; /* Terminate the VAL string */ prec->len = pval - prec->val; } static long init_record(struct dbCommon *pcommon, int pass) { struct printfRecord *prec = (struct printfRecord *)pcommon; printfdset *pdset; if (pass == 0) { size_t sizv = prec->sizv; if (sizv < 16) { sizv = 16; /* Enforce a minimum size for the VAL field */ prec->sizv = sizv; } prec->val = callocMustSucceed(1, sizv, "printf::init_record"); prec->len = 0; return 0; } pdset = (printfdset *) prec->dset; if (!pdset) return 0; /* Device support is optional */ if (pdset->number < 5) { recGblRecordError(S_dev_missingSup, prec, "printf::init_record"); return S_dev_missingSup; } if (pdset->init_record) { long status = pdset->init_record(prec); if (status) return status; } return 0; } static long process(struct dbCommon *pcommon) { struct printfRecord *prec = (struct printfRecord *)pcommon; int pact = prec->pact; printfdset *pdset; long status = 0; epicsUInt16 events; if (!pact) { doPrintf(prec); prec->udf = FALSE; recGblGetTimeStamp(prec); } /* Call device support */ pdset = (printfdset *) prec->dset; if (pdset && pdset->number >= 5 && pdset->write_string) { status = pdset->write_string(prec); /* Asynchronous if device support set pact */ if (!pact && prec->pact) return status; } prec->pact = TRUE; /* Post monitor */ events = recGblResetAlarms(prec); db_post_events(prec, prec->val, events | DBE_VALUE | DBE_LOG); db_post_events(prec, &prec->len, events | DBE_VALUE | DBE_LOG); /* Wrap up */ recGblFwdLink(prec); prec->pact = FALSE; return status; } static long cvt_dbaddr(DBADDR *paddr) { printfRecord *prec = (printfRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); if (fieldIndex == printfRecordVAL) { paddr->pfield = prec->val; paddr->no_elements = 1; paddr->field_type = DBF_STRING; paddr->dbr_field_type = DBF_STRING; paddr->field_size = prec->sizv; } else errlogPrintf("printfRecord::cvt_dbaddr called for %s.%s\n", prec->name, paddr->pfldDes->name); return 0; } static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) { printfRecord *prec = (printfRecord *) paddr->precord; *no_elements = prec->len; *offset = 0; return 0; } /* Create Record Support Entry Table */ #define report NULL #define initialize NULL /* init_record */ /* process */ #define special NULL #define get_value NULL /* cvt_dbaddr */ /* get_array_info */ #define put_array_info NULL #define get_units NULL #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset printfRSET = { RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset, printfRSET); base-7.0.3.1/modules/database/src/std/rec/printfRecord.dbd0000664000577000060420000000534513557101274022104 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* recordtype(printf) { include "dbCommon.dbd" %#include "devSup.h" % %/* Declare Device Support Entry Table */ %typedef struct printfdset { % long number; % DEVSUPFUN report; % DEVSUPFUN init; % DEVSUPFUN init_record; % DEVSUPFUN get_ioint_info; % DEVSUPFUN write_string; %} printfdset; % field(VAL,DBF_NOACCESS) { prompt("Result") asl(ASL0) pp(TRUE) special(SPC_DBADDR) extra("char *val") } field(SIZV,DBF_USHORT) { prompt("Size of VAL buffer") promptgroup("30 - Action") special(SPC_NOMOD) interest(1) initial("41") } field(LEN,DBF_ULONG) { prompt("Length of VAL") special(SPC_NOMOD) } field(OUT,DBF_OUTLINK) { prompt("Output Specification") promptgroup("50 - Output") interest(1) } field(FMT,DBF_STRING) { prompt("Format String") promptgroup("30 - Action") pp(TRUE) size(81) } field(IVLS,DBF_STRING) { prompt("Invalid Link String") promptgroup("30 - Action") size(16) initial("LNK") } field(INP0,DBF_INLINK) { prompt("Input 0") promptgroup("40 - Input") interest(1) } field(INP1,DBF_INLINK) { prompt("Input 1") promptgroup("40 - Input") interest(1) } field(INP2,DBF_INLINK) { prompt("Input 2") promptgroup("40 - Input") interest(1) } field(INP3,DBF_INLINK) { prompt("Input 3") promptgroup("40 - Input") interest(1) } field(INP4,DBF_INLINK) { prompt("Input 4") promptgroup("40 - Input") interest(1) } field(INP5,DBF_INLINK) { prompt("Input 5") promptgroup("40 - Input") interest(1) } field(INP6,DBF_INLINK) { prompt("Input 6") promptgroup("40 - Input") interest(1) } field(INP7,DBF_INLINK) { prompt("Input 7") promptgroup("40 - Input") interest(1) } field(INP8,DBF_INLINK) { prompt("Input 8") promptgroup("40 - Input") interest(1) } field(INP9,DBF_INLINK) { prompt("Input 9") promptgroup("40 - Input") interest(1) } %/* Number of INPx fields defined */ %#define PRINTF_NLINKS 10 } base-7.0.3.1/modules/database/src/std/rec/selRecord.c0000664000577000060420000002566413557101274021064 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* selRecord.c - Record Support Routines for Select records */ /* * Original Author: Bob Dalesio * Date: 6-2-89 */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "epicsMath.h" #include "alarm.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #define GEN_SIZE_OFFSET #include "selRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); #define special NULL #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL static long get_units(DBADDR *, char *); static long get_precision(const DBADDR *, long *); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *, struct dbr_grDouble *); static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); static long get_alarm_double(DBADDR *, struct dbr_alDouble *); rset selRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,selRSET); #define SEL_MAX 12 static void checkAlarms(selRecord *); static void do_sel(selRecord *); static int fetch_values(selRecord *); static void monitor(selRecord *); static long init_record(struct dbCommon *pcommon, int pass) { struct selRecord *prec = (struct selRecord *)pcommon; struct link *plink; int i; double *pvalue; if (pass==0) return 0; /* get seln initial value if nvl is a constant*/ recGblInitConstantLink(&prec->nvl, DBF_USHORT, &prec->seln); plink = &prec->inpa; pvalue = &prec->a; for (i=0; ipact = TRUE; if ( RTN_SUCCESS(fetch_values(prec)) ) { do_sel(prec); } recGblGetTimeStamp(prec); /* check for alarms */ checkAlarms(prec); /* check event list */ monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact=FALSE; return(0); } #define indexof(field) selRecord##field static long get_units(DBADDR *paddr, char *units) { selRecord *prec=(selRecord *)paddr->precord; if(paddr->pfldDes->field_type == DBF_DOUBLE) { strncpy(units,prec->egu,DB_UNITS_SIZE); } return(0); } static long get_precision(const DBADDR *paddr, long *precision) { selRecord *prec=(selRecord *)paddr->precord; double *pvalue,*plvalue; int i; *precision = prec->prec; if(paddr->pfield==(void *)&prec->val){ return(0); } pvalue = &prec->a; plvalue = &prec->la; for(i=0; ipfield==(void *)&pvalue || paddr->pfield==(void *)&plvalue){ return(0); } } recGblGetPrec(paddr,precision); return(0); } static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) { selRecord *prec=(selRecord *)paddr->precord; int index = dbGetFieldIndex(paddr); switch (index) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): #ifdef __GNUC__ case indexof(A) ... indexof(L): case indexof(LA) ... indexof(LL): break; default: #else break; default: if((index >= indexof(A) && index <= indexof(L)) || (index >= indexof(LA) && index <= indexof(LL))) break; #endif recGblGetGraphicDouble(paddr,pgd); return(0); } pgd->upper_disp_limit = prec->hopr; pgd->lower_disp_limit = prec->lopr; return(0); } static long get_control_double(struct dbAddr *paddr, struct dbr_ctrlDouble *pcd) { selRecord *prec=(selRecord *)paddr->precord; int index = dbGetFieldIndex(paddr); switch (index) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): #ifdef __GNUC__ case indexof(A) ... indexof(L): case indexof(LA) ... indexof(LL): break; default: #else break; default: if((index >= indexof(A) && index <= indexof(L)) || (index >= indexof(LA) && index <= indexof(LL))) break; #endif recGblGetControlDouble(paddr,pcd); return(0); } pcd->upper_ctrl_limit = prec->hopr; pcd->lower_ctrl_limit = prec->lopr; return(0); } static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) { selRecord *prec=(selRecord *)paddr->precord; if(dbGetFieldIndex(paddr) == indexof(VAL)) { pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN; pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN; pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN; pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN; } else recGblGetAlarmDouble(paddr,pad); return(0); } static void checkAlarms(selRecord *prec) { double val, hyst, lalm; double alev; epicsEnum16 asev; if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, prec->udfs); return; } val = prec->val; hyst = prec->hyst; lalm = prec->lalm; /* alarm condition hihi */ asev = prec->hhsv; alev = prec->hihi; if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { if (recGblSetSevr(prec, HIHI_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition lolo */ asev = prec->llsv; alev = prec->lolo; if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { if (recGblSetSevr(prec, LOLO_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition high */ asev = prec->hsv; alev = prec->high; if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { if (recGblSetSevr(prec, HIGH_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition low */ asev = prec->lsv; alev = prec->low; if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { if (recGblSetSevr(prec, LOW_ALARM, asev)) prec->lalm = alev; return; } /* we get here only if val is out of alarm by at least hyst */ prec->lalm = val; return; } static void monitor(selRecord *prec) { unsigned monitor_mask; double *pnew; double *pprev; int i; monitor_mask = recGblResetAlarms(prec); /* check for value change */ recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE); /* check for archive change */ recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE); /* send out monitors connected to the value field */ if (monitor_mask) db_post_events(prec, &prec->val, monitor_mask); monitor_mask |= DBE_VALUE|DBE_LOG; /* trigger monitors of the SELN field */ if (prec->nlst != prec->seln) { prec->nlst = prec->seln; db_post_events(prec, &prec->seln, monitor_mask); } /* check all input fields for changes, even if VAL hasn't changed */ for(i=0, pnew=&prec->a, pprev=&prec->la; ia; switch (prec->selm){ case (selSELM_Specified): if (prec->seln >= SEL_MAX) { recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); return; } val = *(pvalue+prec->seln); break; case (selSELM_High_Signal): val = -epicsINF; for (i = 0; i < SEL_MAX; i++,pvalue++){ if (!isnan(*pvalue) && val < *pvalue) { val = *pvalue; prec->seln = i; } } break; case (selSELM_Low_Signal): val = epicsINF; for (i = 0; i < SEL_MAX; i++,pvalue++){ if (!isnan(*pvalue) && val > *pvalue) { val = *pvalue; prec->seln = i; } } break; case (selSELM_Median_Signal): count = 0; order[0] = epicsNAN; for (i = 0; i < SEL_MAX; i++,pvalue++){ if (!isnan(*pvalue)){ /* Insertion sort */ j = count; while ((j > 0) && (order[j-1] > *pvalue)){ order[j] = order[j-1]; j--; } order[j] = *pvalue; count++; } } prec->seln = count; val = order[count / 2]; break; default: recGblSetSevr(prec,CALC_ALARM,INVALID_ALARM); return; } prec->val = val; prec->udf = isnan(prec->val); return; } /* * FETCH_VALUES * * fetch the values for the variables from which to select */ static int fetch_values(selRecord *prec) { struct link *plink; double *pvalue; int i; long status; plink = &prec->inpa; pvalue = &prec->a; /* If mechanism is selSELM_Specified, only get the selected input*/ if(prec->selm == selSELM_Specified) { /* fetch the select index */ status=dbGetLink(&(prec->nvl),DBR_USHORT,&(prec->seln),0,0); if (!RTN_SUCCESS(status) || (prec->seln >= SEL_MAX)) return(status); plink += prec->seln; pvalue += prec->seln; status=dbGetLink(plink,DBR_DOUBLE, pvalue,0,0); return(status); } /* fetch all inputs*/ for(i=0; i>>, C<<< High Signal >>>, C<<< Low Signal >>>, C<<< Median Signal >>>. Each input can be a constant, a database link, or a channel access link. =recordtype sel =cut menu(selSELM) { choice(selSELM_Specified,"Specified") choice(selSELM_High_Signal,"High Signal") choice(selSELM_Low_Signal,"Low Signal") choice(selSELM_Median_Signal,"Median Signal") } recordtype(sel) { =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters The select record has the standard fields for specifying under what circumstances the record will be processed. These fields are listed in L. In addition, L explains how these fields work. =head3 Read Parameters The INPA-L links determine where the selection record retrieves the values from which it is to select or compute its final value. The INPA-L links are input links configured by the user to be either constants, channel access links, or database links. If channel access or database links, a value is retrieved for each link and placed in the corresponding value field, A-L. If any input link is a constant, the value field for that link will be initialized with the constant value given to it and can be modified via dbPuts. See L
for information on how to specify database links. Any links not defined are ignored by the selection record and its algorithm. An undefined link is any constant link whose value is 0. At initialization time, the corresponding value links for such fields are set to NaN, which means MISSING. The value field of an undefined link can be changed at run-time from NaN to another value in order to define the link and its field. Note that all undefined links must be recognized as such if the selection algorithm is to work as expected. =fields INPA, INPB, INPC, INPD, INPE, INPF, INPG, INPH, INPI, INPJ, INPK, INPL, A, B, C, D, E, F, G, H, I, J, K, L =head3 Select Parameters The selection algorithm is determined by three fields configurable by the user: the select mechanism (SELM) field, the select number (SELN) field, and the index value location (NVL) field. The SELM field has four choices, i.e., four algorithms as follows: =head4 Menu selSELM =menu selSELM The selection record's VAL field is determined differently for each algorithm. For C<<< Specified >>>, the VAL field is set equal to the value field (A, B, C, D, E, F, G, H, I, J, K, or L) specified by the SELN field. The SELN field contains a number from 0-11 which corresponds to the value field to be used (0 means use A; 1 means use B, etc.). How the NVL field is configured determines, in turn, SELN's value. NVL is an input link from which a value for SELN can be retrieved, Like most other input links NVL can be a constant, or a channel access or database link. If NVL is a link, SELN is retrieved from the location in NVL. If a constant, SELN is initialized to the value given to the constant and can be changed via dbPuts. See L
for information on how to specify database links. The C<<< High Signal >>>, C<<< Low Signal >>>, and C<<< Median Signal >>> algorithms do not use SELN or NVL. If C<<< High Signal >>> is chosen, VAL is set equal to the highest value out of all the defined value fields (A-L). If C<<< Low Signal >>> is chosen, VAL is set equal to lowest value of all the defined fields (A-L). And if C<<< Median Signal >>> is chosen, VAL is set equal to the median value of the defined value fields (A-L). (Note that these algorithms select from the value fields; they do not select from the value field index. For instance, C<<< Low Signal >>> will not select the A field's value unless the value itself is the lowest of all the defined values.) =fields SELM, SELN, NVL =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. They display the value and other parameters of the select record either textually or graphically. EGU is a string of up to 16 characters describing the units that the selection record manipulates. It is retrieved by the C<<< get_units >>> record support routine. The HOPR and LOPR fields set the upper and lower display limits for the VAL, HIHI, HIGH, LOW, and LOLO fields. Both the C<<< get_graphic_double >>> and C<<< get_control_double >>> record support routines retrieve these fields. The PREC field determines the floating point precision with which to display VAL. It is used whenever the C<<< get_precision >>> record support routine is called. See L for more on the record name (NAME) and description (DESC) fields. =fields EGU, HOPR, LOPR, PREC, NAME, DESC =head3 Alarm Parameters The possible alarm conditions for select records are the SCAN, READ, and limit alarms. The SCAN and READ alarms are called by the record or device support routines. The limit alarms are configured by the user in the HIHI, LOLO, HIGH, and LOW fields using numerical values. They specify conditions for the VAL field. For each of these fields, there is a corresponding severity field which can be either NO_ALARM, MINOR, or MAJOR. See L for a complete explanation of alarms and these fields. L lists other fields related to a alarms that are common to all record types. =fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST =head3 Monitor Parameters These fields are configurable by the user. They are used as deadbands for the archiver and monitor calls for the VAL field. Unless, VAL changes by more than the value specified by each, then the respective monitors will not be called. If these fields have a value of zero, everytime the VAL changes, monitors are triggered; if they have a value of -1, everytime the record is processed, monitors are triggered. L gives a complete explanation of alarms and deadbands. =fields ADEL, MDEL =head3 Run-time Parameters These parameters are used by the run-time code for processing the selection record. They are not configurable prior to run-time, nor are they modifiable at run-time. They represent the current state of the record. The record support routines use some of them for more efficient processing. The VAL field is the result of the selection record's processing. It can be accessed in the normal way by another record or through database access, but is not modifiable except by the record itself. The LALM, ALST, and the MLST are used to implement the HYST, ADEL, and MDEL hysteresis factors for the alarms, archiver, and monitors, respectively. The LA-LL fields are used to implement the monitors for each of the value fields, A-L. They represent previous input values. For example, unless LA is not equal to A, no monitor is invoked for A. =fields VAL, LALM, ALST, MLST, LA, LB, LC, LD, LE, LF, LG, LH, LI, LJ, LK, LL =head2 Record Support =head3 Record Support Routines =head4 init_record long (*init_record)(struct dbCommon *precord, int pass) IF NVL is a constant, SELN is set to its value. If NVL is a PV_LINK a channel access link is created. For each constant input link, the corresponding value field is initialized with the constant value (or NaN if the constant has the value 0). For each input link that is of type PV_LINK, a database or channel access link is created. =head4 process long (*process)(struct dbCommon *precord) See L. =head4 get_units long (*get_units)(struct dbAddr *paddr, char *units) Retrieves EGU. =head4 get_precision long (*get_precision)(const struct dbAddr *paddr, long *precision) Retrieves PREC. =head4 get_graphic_double long (*get_graphic_double)(struct dbAddr *paddr, struct dbr_grDouble *p) Sets the upper display and lower display limits for a field. If the field is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. =head4 get_control_double long (*get_control_double)(struct dbAddr *paddr, struct dbr_ctrlDouble *p) Sets the upper control and the lower control limits for a field. If the field is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. =head4 get_alarm_double long (*get_alarm_double)(struct dbAddr *paddr, struct dbr_alDouble *p) Sets the following values: upper_alarm_limit = HIHI upper_warning_limit = HIGH lower_warning_limit = LOW lower_alarm_limit = LOLO =head3 Record Processing Routine process implements the following algorithm: =over =item 1. If NVL is a database or channel access link, SELN is obtained from NVL. Fetch all values if database or channel access links. If SELM is SELECTED, then only the selected link is fetched. =item 2. Implement the appropriate selection algorithm. For SELECT_HIGH, SELECT_LOW, and SELECT_MEDIAN, input fields are ignored if they are undefined. If success, UDF is set to FALSE. =item 3. Check alarms. This routine checks to see if the new VAL causes the alarm status and severity to change. If so, NSEV, NSTA, and LALM are set. It also honors the alarm hysteresis factor (HYST). Thus the value must change by more than HYST before the alarm status and severity is lowered. =item 4. Check to see if monitors should be invoked. =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if ADEL and MDEL conditions are met =item * Monitors for A-L are checked whenever other monitors are invoked =item * NSEV and NSTA are reset to 0. =back =item 5. Scan forward link if necessary, set PACT FALSE, and return. =back =cut include "dbCommon.dbd" field(VAL,DBF_DOUBLE) { prompt("Result") promptgroup("40 - Input") asl(ASL0) special(SPC_NOMOD) } field(SELM,DBF_MENU) { prompt("Select Mechanism") promptgroup("30 - Action") menu(selSELM) } field(SELN,DBF_USHORT) { prompt("Index value") } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) } field(NVL,DBF_INLINK) { prompt("Index Value Location") promptgroup("30 - Action") interest(1) } field(INPA,DBF_INLINK) { prompt("Input A") promptgroup("41 - Input A-F") interest(1) } field(INPB,DBF_INLINK) { prompt("Input B") promptgroup("41 - Input A-F") interest(1) } field(INPC,DBF_INLINK) { prompt("Input C") promptgroup("41 - Input A-F") interest(1) } field(INPD,DBF_INLINK) { prompt("Input D") promptgroup("41 - Input A-F") interest(1) } field(INPE,DBF_INLINK) { prompt("Input E") promptgroup("41 - Input A-F") interest(1) } field(INPF,DBF_INLINK) { prompt("Input F") promptgroup("41 - Input A-F") interest(1) } field(INPG,DBF_INLINK) { prompt("Input G") promptgroup("42 - Input G-L") interest(1) } field(INPH,DBF_INLINK) { prompt("Input H") promptgroup("42 - Input G-L") interest(1) } field(INPI,DBF_INLINK) { prompt("Input I") promptgroup("42 - Input G-L") interest(1) } field(INPJ,DBF_INLINK) { prompt("Input J") promptgroup("42 - Input G-L") interest(1) } field(INPK,DBF_INLINK) { prompt("Input K") promptgroup("42 - Input G-L") interest(1) } field(INPL,DBF_INLINK) { prompt("Input L") promptgroup("42 - Input G-L") interest(1) } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) prop(YES) } field(HOPR,DBF_DOUBLE) { prompt("High Operating Rng") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { prompt("Lolo Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(HSV,DBF_MENU) { prompt("High Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(LSV,DBF_MENU) { prompt("Low Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { prompt("Alarm Deadband") promptgroup("70 - Alarm") interest(1) } field(ADEL,DBF_DOUBLE) { prompt("Archive Deadband") promptgroup("80 - Display") interest(1) } field(MDEL,DBF_DOUBLE) { prompt("Monitor Deadband") promptgroup("80 - Display") interest(1) } field(A,DBF_DOUBLE) { prompt("Value of Input A") pp(TRUE) } field(B,DBF_DOUBLE) { prompt("Value of Input B") pp(TRUE) } field(C,DBF_DOUBLE) { prompt("Value of Input C") pp(TRUE) } field(D,DBF_DOUBLE) { prompt("Value of Input D") pp(TRUE) } field(E,DBF_DOUBLE) { prompt("Value of Input E") pp(TRUE) } field(F,DBF_DOUBLE) { prompt("Value of Input F") pp(TRUE) } field(G,DBF_DOUBLE) { prompt("Value of Input G") pp(TRUE) } field(H,DBF_DOUBLE) { prompt("Value of Input H") pp(TRUE) } field(I,DBF_DOUBLE) { prompt("Value of Input I") pp(TRUE) } field(J,DBF_DOUBLE) { prompt("Value of Input J") pp(TRUE) } field(K,DBF_DOUBLE) { prompt("Value of Input K") pp(TRUE) } field(L,DBF_DOUBLE) { prompt("Value of Input L") pp(TRUE) } field(LA,DBF_DOUBLE) { prompt("Prev Value of A") special(SPC_NOMOD) interest(3) } field(LB,DBF_DOUBLE) { prompt("Prev Value of B") special(SPC_NOMOD) interest(3) } field(LC,DBF_DOUBLE) { prompt("Prev Value of C") special(SPC_NOMOD) interest(3) } field(LD,DBF_DOUBLE) { prompt("Prev Value of D") special(SPC_NOMOD) interest(3) } field(LE,DBF_DOUBLE) { prompt("Prev Value of E") special(SPC_NOMOD) interest(3) } field(LF,DBF_DOUBLE) { prompt("Prev Value of F") special(SPC_NOMOD) interest(3) } field(LG,DBF_DOUBLE) { prompt("Prev Value of G") special(SPC_NOMOD) interest(3) } field(LH,DBF_DOUBLE) { prompt("Prev Value of H") special(SPC_NOMOD) interest(3) } field(LI,DBF_DOUBLE) { prompt("Prev Value of I") special(SPC_NOMOD) interest(3) } field(LJ,DBF_DOUBLE) { prompt("Prev Value of J") special(SPC_NOMOD) interest(3) } field(LK,DBF_DOUBLE) { prompt("Prev Value of K") special(SPC_NOMOD) interest(3) } field(LL,DBF_DOUBLE) { prompt("Prev Value of L") special(SPC_NOMOD) interest(3) } field(LALM,DBF_DOUBLE) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(ALST,DBF_DOUBLE) { prompt("Last Value Archived") special(SPC_NOMOD) interest(3) } field(MLST,DBF_DOUBLE) { prompt("Last Val Monitored") special(SPC_NOMOD) interest(3) } field(NLST,DBF_USHORT) { prompt("Last Index Monitored") special(SPC_NOMOD) interest(3) } } base-7.0.3.1/modules/database/src/std/rec/seqRecord.c0000664000577000060420000002337013557101274021061 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: John Winans * Date: 09-21-92 */ #include #include #include #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "epicsTypes.h" #include "link.h" #include "recSup.h" #include "recGbl.h" #define GEN_SIZE_OFFSET #include "seqRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" static void processNextLink(seqRecord *prec); static long asyncFinish(seqRecord *prec); static void processCallback(epicsCallback *arg); /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *prec, int pass); static long process(struct dbCommon *prec); #define special NULL #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL static long get_units(DBADDR *, char *); static long get_precision(const DBADDR *paddr, long *); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *, struct dbr_grDouble *); static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); static long get_alarm_double(DBADDR *, struct dbr_alDouble *); rset seqRSET = { RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset, seqRSET); int seqDLYprecision = 2; epicsExportAddress(int, seqDLYprecision); double seqDLYlimit = 100000; epicsExportAddress(double, seqDLYlimit); /* Total number of link-groups */ #define NUM_LINKS 16 /* Each link-group looks like this */ typedef struct linkGrp { double dly; /* Delay in seconds */ DBLINK dol; /* Input link */ double dov; /* Value storage */ DBLINK lnk; /* Output link */ } linkGrp; /* The list of link-groups for processing */ typedef struct seqRecPvt { epicsCallback callback; seqRecord *prec; linkGrp *grps[NUM_LINKS + 1]; /* List of link-groups */ int index; /* Where we are now */ } seqRecPvt; static long init_record(struct dbCommon *pcommon, int pass) { struct seqRecord *prec = (struct seqRecord *)pcommon; int index; linkGrp *grp; seqRecPvt *pseqRecPvt; if (pass == 0) return 0; pseqRecPvt = (seqRecPvt *)calloc(1, sizeof(seqRecPvt)); pseqRecPvt->prec = prec; callbackSetCallback(processCallback, &pseqRecPvt->callback); callbackSetUser(pseqRecPvt, &pseqRecPvt->callback); prec->dpvt = pseqRecPvt; recGblInitConstantLink(&prec->sell, DBF_USHORT, &prec->seln); grp = (linkGrp *) &prec->dly0; for (index = 0; index < NUM_LINKS; index++, grp++) { recGblInitConstantLink(&grp->dol, DBF_DOUBLE, &grp->dov); } prec->oldn = prec->seln; return 0; } static long process(struct dbCommon *pcommon) { struct seqRecord *prec = (struct seqRecord *)pcommon; seqRecPvt *pcb = (seqRecPvt *) prec->dpvt; linkGrp *pgrp; epicsUInt16 lmask; int i; if (prec->pact) return asyncFinish(prec); prec->pact = TRUE; /* Set callback from PRIO */ callbackSetPriority(prec->prio, &pcb->callback); if (prec->selm == seqSELM_All) lmask = (1 << NUM_LINKS) - 1; else { /* Get SELN value */ dbGetLink(&prec->sell, DBR_USHORT, &prec->seln, 0, 0); if (prec->selm == seqSELM_Specified) { int grpn = prec->seln + prec->offs; if (grpn < 0 || grpn >= NUM_LINKS) { recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); return asyncFinish(prec); } if (grpn == 0) return asyncFinish(prec); lmask = 1 << grpn; } else if (prec->selm == seqSELM_Mask) { int shft = prec->shft; if (shft < -15 || shft > 15) { /* Shifting by more than the number of bits in the * value produces undefined behavior in C */ recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); return asyncFinish(prec); } lmask = (shft >= 0) ? prec->seln >> shft : prec->seln << -shft; } else { recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); return asyncFinish(prec); } } /* Figure out which groups are to be processed */ pcb->index = 0; pgrp = (linkGrp *) &prec->dly0; for (i = 0; lmask; lmask >>= 1) { if ((lmask & 1) && (!dbLinkIsConstant(&pgrp->lnk) || !dbLinkIsConstant(&pgrp->dol))) { pcb->grps[i++] = pgrp; } pgrp++; } pcb->grps[i] = NULL; /* mark the end of the list */ if (!i) return asyncFinish(prec); /* Start processing link groups (we have at least one) */ processNextLink(prec); return 0; } static void processNextLink(seqRecord *prec) { seqRecPvt *pcb = (seqRecPvt *) prec->dpvt; linkGrp *pgrp = pcb->grps[pcb->index]; if (pgrp == NULL) { /* None left, finish up. */ prec->rset->process((dbCommon *)prec); return; } /* Always use the callback task to avoid recursion */ if (pgrp->dly > 0.0) callbackRequestDelayed(&pcb->callback, pgrp->dly); else callbackRequest(&pcb->callback); } static long asyncFinish(seqRecord *prec) { epicsUInt16 events; prec->udf = FALSE; recGblGetTimeStamp(prec); /* post monitors */ events = recGblResetAlarms(prec); if (events) db_post_events(prec, &prec->val, events); if (prec->seln != prec->oldn) { db_post_events(prec, &prec->seln, events | DBE_VALUE | DBE_LOG); prec->oldn = prec->seln; } /* process the forward scan link record */ recGblFwdLink(prec); prec->pact = FALSE; return 0; } static void processCallback(epicsCallback *arg) { seqRecPvt *pcb; seqRecord *prec; linkGrp *pgrp; double odov; callbackGetUser(pcb, arg); prec = pcb->prec; dbScanLock((struct dbCommon *)prec); pgrp = pcb->grps[pcb->index]; /* Save the old value */ odov = pgrp->dov; dbGetLink(&pgrp->dol, DBR_DOUBLE, &pgrp->dov, 0, 0); recGblGetTimeStamp(prec); /* Dump the value to the destination field */ dbPutLink(&pgrp->lnk, DBR_DOUBLE, &pgrp->dov, 1); if (odov != pgrp->dov) { db_post_events(prec, &pgrp->dov, DBE_VALUE | DBE_LOG); } /* Start the next link-group */ pcb->index++; processNextLink(prec); dbScanUnlock((struct dbCommon *)prec); } #define indexof(field) seqRecord##field #define get_dol(prec, fieldOffset) \ &((linkGrp *) &prec->dly0)[fieldOffset >> 2].dol static long get_units(DBADDR *paddr, char *units) { seqRecord *prec = (seqRecord *) paddr->precord; int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1); if (fieldOffset >= 0) switch (fieldOffset & 2) { case 0: /* DLYn */ strcpy(units, "s"); break; case 2: /* DOn */ dbGetUnits(get_dol(prec, fieldOffset), units, DB_UNITS_SIZE); } return 0; } static long get_precision(const DBADDR *paddr, long *pprecision) { seqRecord *prec = (seqRecord *) paddr->precord; int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1); short precision; if (fieldOffset >= 0) switch (fieldOffset & 2) { case 0: /* DLYn */ *pprecision = seqDLYprecision; return 0; case 2: /* DOn */ if (dbGetPrecision(get_dol(prec, fieldOffset), &precision) == 0) { *pprecision = precision; return 0; } } *pprecision = prec->prec; recGblGetPrec(paddr, pprecision); return 0; } static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) { seqRecord *prec = (seqRecord *) paddr->precord; int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1); if (fieldOffset >= 0) switch (fieldOffset & 2) { case 0: /* DLYn */ pgd->lower_disp_limit = 0.0; pgd->lower_disp_limit = 10.0; return 0; case 2: /* DOn */ dbGetGraphicLimits(get_dol(prec, fieldOffset), &pgd->lower_disp_limit, &pgd->upper_disp_limit); return 0; } recGblGetGraphicDouble(paddr, pgd); return 0; } static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) { int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1); if (fieldOffset >= 0 && (fieldOffset & 2) == 0) { /* DLYn */ pcd->lower_ctrl_limit = 0.0; pcd->upper_ctrl_limit = seqDLYlimit; } else recGblGetControlDouble(paddr, pcd); return 0; } static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) { seqRecord *prec = (seqRecord *) paddr->precord; int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1); if (fieldOffset >= 0 && (fieldOffset & 2) == 2) /* DOn */ dbGetAlarmLimits(get_dol(prec, fieldOffset), &pad->lower_alarm_limit, &pad->lower_warning_limit, &pad->upper_warning_limit, &pad->upper_alarm_limit); else recGblGetAlarmDouble(paddr, pad); return 0; } base-7.0.3.1/modules/database/src/std/rec/seqRecord.dbd.pod0000664000577000060420000004226613557101274022156 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Sequence Record (seq) The Sequence record is used to trigger the processing of up to ten other records and send values to those records. It is similar to the fanout record, except that it will fetch an input value and write an output value instead of simply processing a collection of forward links. It can also specify one of several selection algorithms that determine which values to write. It has no associated device support. =recordtype seq =cut menu(seqSELM) { choice(seqSELM_All,"All") choice(seqSELM_Specified,"Specified") choice(seqSELM_Mask,"Mask") } recordtype(seq) { =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters The sequence record has the standard fields for specifying under what circumstances it will be processed. These fields are listed in L. In addition, L explains how these fields are used. =head3 Desired Output Parameters These fields determine where the record retrieves the values it is to write to other records. All of these values are not necessarily used, depending on the selection algorithm. The sequence record can retrieve up to 16 values from 16 locations. The user specifies the locations in the Desired Output Link fields (DOL0-DOLF), which can be either constants, database links, or channel access links. If a Desired Output Link is a constant, the corresponding value field for that link is initialized to the constant value and ''cannot'' be changed via dbputs. Otherwise, if the Desired Output Link is a database or channel access link, a value is fetched from the link each time the record is processed (provided that the output link is part of the record's selection algorithm). See L
for information on how to specify database links. The value fetched from the Desired Output Links are stored in the corresponding Desired Output Value fields (DO0-DOF). These fields can be initialized to a constant value, but they cannot be changed via dbPuts. =head4 Desired Output Link Fields =fields DOL0, DOL1, DOL2, DOL3, DOL4, DOL5, DOL6, DOL7, DOL8, DOL9, DOLA, DOLB, DOLC, DOLD, DOLE, DOLF =head4 Desired Output Value Fields =fields DO0, DO1, DO2, DO3, DO4, DO5, DO6, DO7, DO8, DO9, DOA, DOB, DOC, DOD, DOE, DOF =head3 Output Parameters When the record is processed, the desired output values are retrieved for the links in the record's selection algorithm and are written to the corresponding output link (LNK0-LNKF). These output links can be database links or channel access links; they cannot be device addresses. There are sixteen output links, one for each desired output link. Only those that are defined are used. =fields LNK0, LNK1, LNK2, LNK3, LNK4, LNK5, LNK6, LNK7, LNK8, LNK9, LNKA, LNKB, LNKC, LNKD, LNKE, LNKF =head3 Selection Algorithm Parameters When the sequence record is processed, it uses a selection algorithm similar to that of the selection record to decide which links to process.The select mechanism field (SELM) has three algorithms to choose from: C<<< All >>>, C<<>> or C<<< Mask >>>. =head4 Record fields related to the Selection Algorithm =fields SELM, SELN, SELL, SHFT, OFFS =head4 Fields Description B =menu seqSELM See L below; B This field can be initialized as a CONSTANT or as a LINK to any other record. SELN will fetch its value from this field when the seq record is processed. Thus, when using I or I modes, the links that seq will process can be dinamically changed by the record pointed by SELL. B When B> this is the index number of the link that will be processed, used in combination with the C field: SELN = SELN + OFFS I<(By default, the OFFS is initalized to ZERO)> When B> this field is the bitmask that will be used to determine which links will be processed by the seq record, in combination with the C field: if (SHFT >= 0) SELN = SELN << -SHFT else SELN = SELN >> SHFT I<(By default, the SHFT is initalized to -1)> =head4 B The first versions of seq record had DO, DOL, LNK and DLY fields starting with index ONE (DO1, DOL1, LNK1 and DLY1). New version of the seq record now supports 16 links, starting by index ZERO (DO0, DOL0, LNK0 and DLY0). The SHFT and OFFS fields were introduced to keep compatibility of old databases that used seq record with its links indexed from one onwards. B =head4 Selection Algorithms Description B The C<<< All >>> algorithm causes the record to process each input and output link each time the record is processed, in order from 0 to 15. So when SELM is C<<< All >>>, the desired output value from DOL0 will fetched and sent to LNK0, then the desired output value from DOL1 will be fetched and sent to the location in LNK1, and so on until the last input and output link DOF and LNKF. (Note that undefined links are not used.) If DOLI is a constant, the current value field is simply used and the desired output link is ignored. The SELN field is not used when C<<< All >>> is the algorithm. B When the C<<< Specified >>> algorithm is chosen, each time the record is processed it gets the integer value in the Link Selection (SELN) field and uses that as the index of the link to process. For instance, if SELN is 4, the desired output value from DO4 will be retrieved and sent to LNK4. If DOLI is a constant, DOI is simply used without the value being fetched from the input link. B When C<<< Mask >>> is chosen, the record uses the individual bits of the SELN field to determine the links to process. When bit 0 of SELN is set, the value from DO0 will be written to the location in LNK0; when bit 1 is set, the valud from DO1 will be written to the location in LNK1 etc. Thus for example if SELN is 3, the record will retrieve the values from DO0 and DO1 and write them to the locations in LNK0 and LNK1, respectively. If SELN is 63, DO0...DO5 will be written to LNK0...LNK5. =head3 Delay Parameters The delay parameters consist of 16 fields, one for each I/O link discussed above. These fields can be configured to cause the record to delay processing the link. For instance, if the user gives the DLY1 field a value of 3.0, each time the record is processed at run-time, the record will delay processing the DOL1, DOV1, and LNK1 fields for three seconds. That is, the desired output value will not be fetched and written to the output link until three seconds have lapsed. =fields DLY0, DLY1, DLY2, DLY3, DLY4, DLY5, DLY6, DLY7, DLY8, DLY9, DLYA, DLYB, DLYC, DLYD, DLYE, DLYF =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. The Precision field (PREC) determines the decimal precision for the VAL field when it is displayed. It is used when the C<<< get_precision >>> record routine is called. See L for more on the record name (NAME) and description (DESC) fields. =fields PREC, NAME, DESC =head3 Alarm Parameters The sequence record has the alarm parameters common to all record types. L lists other fields related to a alarms that are common to all record types. =head2 Record Support =head3 Record Processing Routine process implements the following algorithm: =over =item 1. First, PACT is set to TRUE, and the link selection is fetched. Depending on the selection mechanism, the link selection output links are processed in order from LNK0 to LNKF. When LNKI is processed, the corresponding DLYI value is used to generate a delay via watchdog timer. =item 2. After DLYI seconds have expired, the input value is fetched from DOI (if DOLI is constant) or DOLI (if DOLI is a database link or channel access link) and written to LNKI. =item 3. When all links are completed, an asynchronous completion call back to dbProcess is made (see the Application Developer's Guide for more information on asynchronous processing.) =item 4. Then UDF is set to FALSE. =item 5. Monitors are checked. =item 6. The forward link is scanned, PACT is set FALSE, and the process routine returns. =back For the delay mechanism to operate properly, the record is processed asynchronously. The only time the record will not be processed asynchronously is when there are no non-NULL output links selected (i.e. when it has nothing to do.) The processing of the links is done via callback tasks at the priority set in the PRIO field in dbCommon (see the Application Developer's Guide for more information on call =cut include "dbCommon.dbd" field(VAL,DBF_LONG) { prompt("Used to trigger") asl(ASL0) pp(TRUE) } field(SELM,DBF_MENU) { prompt("Select Mechanism") promptgroup("30 - Action") interest(1) menu(seqSELM) } field(SELN,DBF_USHORT) { prompt("Link Selection") interest(1) initial("1") } field(SELL,DBF_INLINK) { prompt("Link Selection Loc") promptgroup("30 - Action") interest(1) } field(OFFS,DBF_SHORT) { prompt("Offset for Specified") promptgroup("30 - Action") interest(1) initial("0") } field(SHFT,DBF_SHORT) { prompt("Shift for Mask mode") promptgroup("30 - Action") interest(1) initial("-1") } field(OLDN,DBF_USHORT) { prompt("Old Selection") interest(4) } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) } field(DLY0,DBF_DOUBLE) { prompt("Delay 0") promptgroup("41 - Link 0-7") interest(1) } field(DOL0,DBF_INLINK) { prompt("Input link 0") promptgroup("41 - Link 0-7") interest(1) } field(DO0,DBF_DOUBLE) { prompt("Value 0") interest(1) } field(LNK0,DBF_OUTLINK) { prompt("Output Link 0") promptgroup("41 - Link 0-7") interest(1) } field(DLY1,DBF_DOUBLE) { prompt("Delay 1") promptgroup("41 - Link 0-7") interest(1) } field(DOL1,DBF_INLINK) { prompt("Input link1") promptgroup("41 - Link 0-7") interest(1) } field(DO1,DBF_DOUBLE) { prompt("Value 1") interest(1) } field(LNK1,DBF_OUTLINK) { prompt("Output Link 1") promptgroup("41 - Link 0-7") interest(1) } field(DLY2,DBF_DOUBLE) { prompt("Delay 2") promptgroup("41 - Link 0-7") interest(1) } field(DOL2,DBF_INLINK) { prompt("Input link 2") promptgroup("41 - Link 0-7") interest(1) } field(DO2,DBF_DOUBLE) { prompt("Value 2") interest(1) } field(LNK2,DBF_OUTLINK) { prompt("Output Link 2") promptgroup("41 - Link 0-7") interest(1) } field(DLY3,DBF_DOUBLE) { prompt("Delay 3") promptgroup("41 - Link 0-7") interest(1) } field(DOL3,DBF_INLINK) { prompt("Input link 3") promptgroup("41 - Link 0-7") interest(1) } field(DO3,DBF_DOUBLE) { prompt("Value 3") interest(1) } field(LNK3,DBF_OUTLINK) { prompt("Output Link 3") promptgroup("41 - Link 0-7") interest(1) } field(DLY4,DBF_DOUBLE) { prompt("Delay 4") promptgroup("41 - Link 0-7") interest(1) } field(DOL4,DBF_INLINK) { prompt("Input link 4") promptgroup("41 - Link 0-7") interest(1) } field(DO4,DBF_DOUBLE) { prompt("Value 4") interest(1) } field(LNK4,DBF_OUTLINK) { prompt("Output Link 4") promptgroup("41 - Link 0-7") interest(1) } field(DLY5,DBF_DOUBLE) { prompt("Delay 5") promptgroup("41 - Link 0-7") interest(1) } field(DOL5,DBF_INLINK) { prompt("Input link 5") promptgroup("41 - Link 0-7") interest(1) } field(DO5,DBF_DOUBLE) { prompt("Value 5") interest(1) } field(LNK5,DBF_OUTLINK) { prompt("Output Link 5") promptgroup("41 - Link 0-7") interest(1) } field(DLY6,DBF_DOUBLE) { prompt("Delay 6") promptgroup("41 - Link 0-7") interest(1) } field(DOL6,DBF_INLINK) { prompt("Input link 6") promptgroup("41 - Link 0-7") interest(1) } field(DO6,DBF_DOUBLE) { prompt("Value 6") interest(1) } field(LNK6,DBF_OUTLINK) { prompt("Output Link 6") promptgroup("41 - Link 0-7") interest(1) } field(DLY7,DBF_DOUBLE) { prompt("Delay 7") promptgroup("41 - Link 0-7") interest(1) } field(DOL7,DBF_INLINK) { prompt("Input link 7") promptgroup("41 - Link 0-7") interest(1) } field(DO7,DBF_DOUBLE) { prompt("Value 7") interest(1) } field(LNK7,DBF_OUTLINK) { prompt("Output Link 7") promptgroup("41 - Link 0-7") interest(1) } field(DLY8,DBF_DOUBLE) { prompt("Delay 8") promptgroup("42 - Link 8-F") interest(1) } field(DOL8,DBF_INLINK) { prompt("Input link 8") promptgroup("42 - Link 8-F") interest(1) } field(DO8,DBF_DOUBLE) { prompt("Value 8") interest(1) } field(LNK8,DBF_OUTLINK) { prompt("Output Link 8") promptgroup("42 - Link 8-F") interest(1) } field(DLY9,DBF_DOUBLE) { prompt("Delay 9") promptgroup("42 - Link 8-F") interest(1) } field(DOL9,DBF_INLINK) { prompt("Input link 9") promptgroup("42 - Link 8-F") interest(1) } field(DO9,DBF_DOUBLE) { prompt("Value 9") interest(1) } field(LNK9,DBF_OUTLINK) { prompt("Output Link 9") promptgroup("42 - Link 8-F") interest(1) } field(DLYA,DBF_DOUBLE) { prompt("Delay 10") promptgroup("42 - Link 8-F") interest(1) } field(DOLA,DBF_INLINK) { prompt("Input link 10") promptgroup("42 - Link 8-F") interest(1) } field(DOA,DBF_DOUBLE) { prompt("Value 10") interest(1) } field(LNKA,DBF_OUTLINK) { prompt("Output Link 10") promptgroup("42 - Link 8-F") interest(1) } field(DLYB,DBF_DOUBLE) { prompt("Delay 11") promptgroup("42 - Link 8-F") interest(1) } field(DOLB,DBF_INLINK) { prompt("Input link 11") promptgroup("42 - Link 8-F") interest(1) } field(DOB,DBF_DOUBLE) { prompt("Value 11") interest(1) } field(LNKB,DBF_OUTLINK) { prompt("Output Link 11") promptgroup("42 - Link 8-F") interest(1) } field(DLYC,DBF_DOUBLE) { prompt("Delay 12") promptgroup("42 - Link 8-F") interest(1) } field(DOLC,DBF_INLINK) { prompt("Input link 12") promptgroup("42 - Link 8-F") interest(1) } field(DOC,DBF_DOUBLE) { prompt("Value 12") interest(1) } field(LNKC,DBF_OUTLINK) { prompt("Output Link 12") promptgroup("42 - Link 8-F") interest(1) } field(DLYD,DBF_DOUBLE) { prompt("Delay 13") promptgroup("42 - Link 8-F") interest(1) } field(DOLD,DBF_INLINK) { prompt("Input link 13") promptgroup("42 - Link 8-F") interest(1) } field(DOD,DBF_DOUBLE) { prompt("Value 13") interest(1) } field(LNKD,DBF_OUTLINK) { prompt("Output Link 13") promptgroup("42 - Link 8-F") interest(1) } field(DLYE,DBF_DOUBLE) { prompt("Delay 14") promptgroup("42 - Link 8-F") interest(1) } field(DOLE,DBF_INLINK) { prompt("Input link 14") promptgroup("42 - Link 8-F") interest(1) } field(DOE,DBF_DOUBLE) { prompt("Value 14") interest(1) } field(LNKE,DBF_OUTLINK) { prompt("Output Link 14") promptgroup("42 - Link 8-F") interest(1) } field(DLYF,DBF_DOUBLE) { prompt("Delay 15") promptgroup("42 - Link 8-F") interest(1) } field(DOLF,DBF_INLINK) { prompt("Input link 15") promptgroup("42 - Link 8-F") interest(1) } field(DOF,DBF_DOUBLE) { prompt("Value 15") interest(1) } field(LNKF,DBF_OUTLINK) { prompt("Output Link 15") promptgroup("42 - Link 8-F") interest(1) } } variable(seqDLYprecision, int) variable(seqDLYlimit, double) base-7.0.3.1/modules/database/src/std/rec/stateRecord.c0000664000577000060420000000504713557101274021412 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* recState.c - Record Support Routines for State records */ /* * Original Author: Bob Dalesio * Date: 10-10-90 */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #define GEN_SIZE_OFFSET #include "stateRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL #define init_record NULL static long process(struct dbCommon *); #define special NULL #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL #define get_units NULL #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset stateRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,stateRSET); static void monitor(stateRecord *); static long process(struct dbCommon *pcommon) { struct stateRecord *prec = (struct stateRecord *)pcommon; prec->udf = FALSE; prec->pact=TRUE; recGblGetTimeStamp(prec); monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact=FALSE; return(0); } static void monitor(stateRecord *prec) { unsigned short monitor_mask; /* get previous stat and sevr and new stat and sevr*/ monitor_mask = recGblResetAlarms(prec); if(strncmp(prec->oval,prec->val,sizeof(prec->val))) { db_post_events(prec,&(prec->val[0]),monitor_mask|DBE_VALUE|DBE_LOG); strncpy(prec->oval,prec->val,sizeof(prec->oval)); } return; } base-7.0.3.1/modules/database/src/std/rec/stateRecord.dbd.pod0000664000577000060420000000472113557101274022500 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title State Record (state) The state record is a means for a state program to communicate with the operator interface. Its only function is to provide a place in the database through which the state program can inform the operator interface of its state by storing an arbitrary ASCII string in its VAL field. B =recordtype state =cut recordtype(state) { include "dbCommon.dbd" =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters The state record has the standard fields for specifying under what circumstances it will be processed. These fields are listed in L. In addition, L explains how these fields are used. =head3 Operator Display Parameters See L for more on the record name (NAME) and description (DESC) fields. =fields NAME, DESC =head3 Alarm Parameters The state record has the alarm parameters common to all record types. L lists other fields related to a alarms that are common to all record types. =head3 Run-time Parameters These parameters are used by the application code to convey the state of the program to the operator interface. The VAL field holds the string retrieved from the state program. The OVAL is used to implement monitors for the VAL field. When the string in OVAL differs from the one in VAL, monitors are triggered. They represent the current state of the sequence program. =fields VAL, OVAL =cut field(VAL,DBF_STRING) { prompt("Value") promptgroup("40 - Input") asl(ASL0) pp(TRUE) size(20) } field(OVAL,DBF_STRING) { prompt("Prev Value") special(SPC_NOMOD) interest(3) size(20) } =head2 Record Support =head3 Record Support Routines =head4 process long (*process)(struct dbCommon *precord) C triggers monitors on VAL when it changes and scans the forward link if necessary. =cut } base-7.0.3.1/modules/database/src/std/rec/stringinRecord.c0000664000577000060420000001475313557101274022133 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* recStringin.c - Record Support Routines for Stringin records */ /* * Author: Janet Anderson * Date: 4/23/91 */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #include "menuYesNo.h" #define GEN_SIZE_OFFSET #include "stringinRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL #define get_units NULL #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset stringinRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,stringinRSET); struct stringindset { /* stringin input dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN read_stringin; /*returns: (-1,0)=>(failure,success)*/ }; static void monitor(stringinRecord *); static long readValue(stringinRecord *); static long init_record(struct dbCommon *pcommon, int pass) { struct stringinRecord *prec = (struct stringinRecord *)pcommon; STATIC_ASSERT(sizeof(prec->oval)==sizeof(prec->val)); STATIC_ASSERT(sizeof(prec->sval)==sizeof(prec->val)); struct stringindset *pdset = (struct stringindset *) prec->dset; if (pass == 0) return 0; recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); recGblInitConstantLink(&prec->siol, DBF_STRING, prec->sval); if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "stringin: init_record"); return S_dev_noDSET; } /* must have read_stringin function defined */ if ((pdset->number < 5) || (pdset->read_stringin == NULL)) { recGblRecordError(S_dev_missingSup, prec, "stringin: init_record"); return S_dev_missingSup; } if (pdset->init_record) { long status = pdset->init_record(prec); if (status) return status; } strncpy(prec->oval, prec->val, sizeof(prec->oval)); return 0; } /* */ static long process(struct dbCommon *pcommon) { struct stringinRecord *prec = (struct stringinRecord *)pcommon; struct stringindset *pdset = (struct stringindset *)(prec->dset); long status; unsigned char pact=prec->pact; if( (pdset==NULL) || (pdset->read_stringin==NULL) ) { prec->pact=TRUE; recGblRecordError(S_dev_missingSup,(void *)prec,"read_stringin"); return(S_dev_missingSup); } status=readValue(prec); /* read the new value */ /* check if device support set pact */ if ( !pact && prec->pact ) return(0); prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); /* check event list */ monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact=FALSE; return(status); } static long special(DBADDR *paddr, int after) { stringinRecord *prec = (stringinRecord *)(paddr->precord); int special_type = paddr->special; switch(special_type) { case(SPC_MOD): if (dbGetFieldIndex(paddr) == stringinRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return(0); } default: recGblDbaddrError(S_db_badChoice, paddr, "stringin: special"); return(S_db_badChoice); } } static void monitor(stringinRecord *prec) { int monitor_mask = recGblResetAlarms(prec); if (strncmp(prec->oval, prec->val, sizeof(prec->val))) { monitor_mask |= DBE_VALUE | DBE_LOG; strncpy(prec->oval, prec->val, sizeof(prec->oval)); } if (prec->mpst == stringinPOST_Always) monitor_mask |= DBE_VALUE; if (prec->apst == stringinPOST_Always) monitor_mask |= DBE_LOG; if (monitor_mask) db_post_events(prec, prec->val, monitor_mask); } static long readValue(stringinRecord *prec) { struct stringindset *pdset = (struct stringindset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: status = pdset->read_stringin(prec); break; case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbGetLink(&prec->siol, DBR_STRING, prec->sval, 0, 0); if (status == 0) { strncpy(prec->val, prec->sval, sizeof(prec->val)); prec->udf = FALSE; } prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } base-7.0.3.1/modules/database/src/std/rec/stringinRecord.dbd.pod0000664000577000060420000001763413557101274023224 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title String Input Record (stringin) The string input record retrieves an arbitrary ASCII string of up to 40 characters. Several device support routines are available, all of which are soft device support for retrieving values from other records or other software components. =recordtype stringin =cut menu(stringinPOST) { choice(stringinPOST_OnChange,"On Change") choice(stringinPOST_Always,"Always") } recordtype(stringin) { include "dbCommon.dbd" field(VAL,DBF_STRING) { prompt("Current Value") promptgroup("40 - Input") asl(ASL0) pp(TRUE) size(40) } field(OVAL,DBF_STRING) { prompt("Previous Value") special(SPC_NOMOD) interest(3) size(40) } =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters The string input record has the standard fields for specifying under what circumstances it will be processed. These fields are listed in L. In addition, L explains how these fields are used. =head3 Read Parameters The INP field determines where the string input record gets its string. It can be a database or channel access link, or a constant. If constant, the VAL field is initialized with the constant and can be changed via dbPuts. Otherwise, the string is read from the specified location each time the record is processed and placed in the VAL field. The maximum number of characters that the string in VAL can be is 40. In addition, the appropriate device support module must be entered into the DTYP field. See L
for information on specifying links. =fields VAL, INP, DTYP =cut field(INP,DBF_INLINK) { prompt("Input Specification") promptgroup("40 - Input") interest(1) } =head3 Monitor Parameters These parameters are used to specify when the monitor post should be sent by C routine. There are two possible choices: =head4 Menu stringinPOST =menu stringinPOST APST is used for archiver monitors and MPST is for all other type of monitors. =fields MPST, APST =cut field(MPST,DBF_MENU) { prompt("Post Value Monitors") promptgroup("80 - Display") interest(1) menu(stringinPOST) } field(APST,DBF_MENU) { prompt("Post Archive Monitors") promptgroup("80 - Display") interest(1) menu(stringinPOST) } =head3 Operator Display Parameters See L for more on the record name (NAME) and description (DESC) fields. =fields NAME, DESC =head3 Alarm Parameters The string input record has the alarm parameters common to all record types. L lists other fields related to a alarms that are common to all record types. =head3 Run-time and Simulation Mode Parameters The old value field (OVAL) of the string input is used to implement value change monitors for VAL. If VAL is not equal to OVAL, then monitors are triggered. =fields OVAL The following fields are used to operate the string input in the simulation mode. See L for more information on simulation mode fields. =fields SIOL, SVAL, SIML, SIMM, SIMS, SSCN, SDLY =cut field(SIOL,DBF_INLINK) { prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } field(SVAL,DBF_STRING) { prompt("Simulation Value") pp(TRUE) size(40) } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } =head2 Record Support =head3 Record Support Routines =head4 init_record long (*init_record)(struct dbCommon *precord, int pass) This routine initializes SIMM with the value of SIML if SIML type is CONSTANT link or creates a channel access link if SIML type is PV_LINK. SVAL is likewise initialized if SIOL is CONSTANT or PV_LINK. This routine next checks to see that device support is available and a record support read routine is defined. If either does not exist, an error message is issued and processing is terminated. If device support includes an C routine it is called. =head4 process long (*process)(struct dbCommon *precord) See L. =head3 Record Processing Routine process implements the following algorithm: =over =item 1. Check to see that the appropriate device support module exists. If it doesn't, an error message is issued and processing is terminated with the PACT field still set to TRUE. This ensures that processes will no longer be called for this record. Thus error storms will not occur. =item 2. readValue is called. See L for more information on simulation mode fields and how they affect input. =item 3. If PACT has been changed to TRUE, the device support read routine has started but has not completed reading a new input value. In this case, the processing routine merely returns, leaving PACT TRUE. =item 4. C is called. =item 5. Check to see if monitors should be invoked. =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if OVAL is not equal to VAL. =item * NSEV and NSTA are reset to 0. =back =item 6. Scan forward link if necessary, set PACT FALSE, and return. =back =begin html


=end html =head2 Device Support =head3 Fields Of Interest To Device Support Each stringin input record must have an associated set of device support routines. The primary responsibility of the device support routines is to obtain a new ASCII string value whenever read_stringin is called. The device support routines are primarily interested in the following fields: =fields PACT, DPVT, UDF, VAL, INP =head3 Device Support Routines (devSiSoft.c) =head4 init_record long init_record(stringinRecord *prec) This routine is optional. If provided, it is called by the record support C routine. =head4 read_stringin long read_stringin(stringinRecord *prec) This routine must provide a new input value. It returns the following values: =over =item * 0: Success. A new ASCII string is stored into VAL. =item * Other: Error. =back =head3 Device Support for Soft Records The C<<< Soft Channel >>> module places a value directly in VAL. If the INP link type is constant, the double constant, if non-zero, is converted to a string and stored into VAL by C, and UDF is set to FALSE. If the INP link type is PV_LINK, then dbCaAddInlink is called by C. read_stringin calls recGblGetLinkValue to read the current value of VAL. See L. If the return status of recGblGetLinkValue is zero, then read_stringin sets UDF to FALSE. The status of recGblGetLinkValue is returned. =cut } base-7.0.3.1/modules/database/src/std/rec/stringoutRecord.c0000664000577000060420000001727213557101274022333 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* recStringout.c - Record Support Routines for Stringout records */ /* * Author: Janet Anderson * Date: 4/23/91 */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #include "menuOmsl.h" #include "menuIvoa.h" #include "menuYesNo.h" #define GEN_SIZE_OFFSET #include "stringoutRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL #define get_units NULL #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset stringoutRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,stringoutRSET); struct stringoutdset { /* stringout input dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN write_stringout;/*(-1,0)=>(failure,success)*/ }; static void monitor(stringoutRecord *); static long writeValue(stringoutRecord *); static long init_record(struct dbCommon *pcommon, int pass) { struct stringoutRecord *prec = (struct stringoutRecord *)pcommon; STATIC_ASSERT(sizeof(prec->oval)==sizeof(prec->val)); STATIC_ASSERT(sizeof(prec->ivov)==sizeof(prec->val)); struct stringoutdset *pdset = (struct stringoutdset *) prec->dset; if (pass == 0) return 0; recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "stringout: init_record"); return S_dev_noDSET; } /* must have write_stringout functions defined */ if ((pdset->number < 5) || (pdset->write_stringout == NULL)) { recGblRecordError(S_dev_missingSup, prec, "stringout: init_record"); return S_dev_missingSup; } /* get the initial value dol is a constant*/ if (recGblInitConstantLink(&prec->dol, DBF_STRING, prec->val)) prec->udf = FALSE; if (pdset->init_record) { long status = pdset->init_record(prec); if(status) return status; } strncpy(prec->oval, prec->val, sizeof(prec->oval)); return 0; } static long process(struct dbCommon *pcommon) { struct stringoutRecord *prec = (struct stringoutRecord *)pcommon; struct stringoutdset *pdset = (struct stringoutdset *)(prec->dset); long status=0; unsigned char pact=prec->pact; if( (pdset==NULL) || (pdset->write_stringout==NULL) ) { prec->pact=TRUE; recGblRecordError(S_dev_missingSup,(void *)prec,"write_stringout"); return(S_dev_missingSup); } if (!prec->pact && !dbLinkIsConstant(&prec->dol) && prec->omsl == menuOmslclosed_loop) { status = dbGetLink(&prec->dol, DBR_STRING, prec->val, 0, 0); if (!dbLinkIsConstant(&prec->dol) && !status) prec->udf=FALSE; } if(prec->udf == TRUE ){ recGblSetSevr(prec,UDF_ALARM,prec->udfs); } if (prec->nsev < INVALID_ALARM ) status=writeValue(prec); /* write the new value */ else { switch (prec->ivoa) { case (menuIvoaContinue_normally) : status=writeValue(prec); /* write the new value */ break; case (menuIvoaDon_t_drive_outputs) : break; case (menuIvoaSet_output_to_IVOV) : if(prec->pact == FALSE){ strncpy(prec->val, prec->ivov, sizeof(prec->val)); } status=writeValue(prec); /* write the new value */ break; default : status=-1; recGblRecordError(S_db_badField,(void *)prec, "stringout:process Illegal IVOA field"); } } /* check if device support set pact */ if ( !pact && prec->pact ) return(0); prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, NULL); monitor(prec); recGblFwdLink(prec); prec->pact=FALSE; return(status); } static long special(DBADDR *paddr, int after) { stringoutRecord *prec = (stringoutRecord *)(paddr->precord); int special_type = paddr->special; switch(special_type) { case(SPC_MOD): if (dbGetFieldIndex(paddr) == stringoutRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return 0; } default: recGblDbaddrError(S_db_badChoice, paddr, "stringout: special"); return(S_db_badChoice); } } static void monitor(stringoutRecord *prec) { int monitor_mask = recGblResetAlarms(prec); if (strncmp(prec->oval, prec->val, sizeof(prec->val))) { monitor_mask |= DBE_VALUE | DBE_LOG; strncpy(prec->oval, prec->val, sizeof(prec->oval)); } if (prec->mpst == stringoutPOST_Always) monitor_mask |= DBE_VALUE; if (prec->apst == stringoutPOST_Always) monitor_mask |= DBE_LOG; if (monitor_mask) db_post_events(prec, prec->val, monitor_mask); } static long writeValue(stringoutRecord *prec) { struct stringoutdset *pdset = (struct stringoutdset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: status = pdset->write_stringout(prec); break; case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0.)) { status = dbPutLink(&prec->siol, DBR_STRING, &prec->val, 1); prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ epicsCallback *pvt = prec->simpvt; if (!pvt) { pvt = calloc(1, sizeof(epicsCallback)); /* very lazy allocation of callback structure */ prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } base-7.0.3.1/modules/database/src/std/rec/stringoutRecord.dbd.pod0000664000577000060420000002334213557101274023416 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title String Output Record (stringout) The stringout record is used to write an arbitrary ASCII string of up to 40 characters to other records or software variables. =recordtype stringout =cut include "menuIvoa.dbd" menu(stringoutPOST) { choice(stringoutPOST_OnChange,"On Change") choice(stringoutPOST_Always,"Always") } recordtype(stringout) { include "dbCommon.dbd" field(VAL,DBF_STRING) { prompt("Current Value") promptgroup("50 - Output") asl(ASL0) pp(TRUE) size(40) } field(OVAL,DBF_STRING) { prompt("Previous Value") special(SPC_NOMOD) interest(3) size(40) } =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters The string output record has the standard fields for specifying under what circumstances it will be processed. These fields are listed in L. In addition, L explains how these fields are used. =head3 Desired Output Parameters The string output record must specify from where it gets its desired output string. The first field that determines where the desired output originates is the output mode select (OSML) field, which can have two possible value: C<<< closed_loop >>> or C<<< supervisory >>>. If C<<< supervisory >>> is specified, DOL is ignored, the current value of VAL is written, and the VAL can be changed externally via dbPuts at run-time. If C<<< closed_loop >>> is specified, the VAL field's value is obtained from the address specified in the desired output location field (DOL) which can be either a database link or a channel access link. DOL can also be a constant in addition to a link, in which case VAL is initialized to the constant value. However, your string constant may be interpreted as a CA link name, so if you want to initialize your string output record, it's best to use the VAL field. Note that if DOL is a constant, OMSL cannot be C<<< closed_loop. >>> See L
for information on specifying links. =fields VAL, DOL, OMSL =cut field(DOL,DBF_INLINK) { prompt("Desired Output Loc") promptgroup("40 - Input") interest(1) } field(OMSL,DBF_MENU) { prompt("Output Mode Select") promptgroup("50 - Output") interest(1) menu(menuOmsl) } =head3 Write Parameters The output link specified in the OUT field specifies where the string output record is to write its string. The link can be a database or channel access link. If the OUT field is a constant, no output will be written. See L
for information on specifying links. In addition, the appropriate device support module must be entered into the DTYP field. =fields OUT, DTYP =cut field(OUT,DBF_OUTLINK) { prompt("Output Specification") promptgroup("50 - Output") interest(1) } =head3 Monitor Parameters These parameters are used to specify when the monitor post should be sent by C routine. There are two possible choices: =head4 Menu stringoutPOST =menu stringoutPOST APST is used for archiver monitors and MPST is for all other type of monitors. =fields MPST, APST =cut field(MPST,DBF_MENU) { prompt("Post Value Monitors") promptgroup("80 - Display") interest(1) menu(stringoutPOST) } field(APST,DBF_MENU) { prompt("Post Archive Monitors") promptgroup("80 - Display") interest(1) menu(stringoutPOST) } =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. These fields are used to display the value and other parameters of the string output either textually or graphically. See L for more on the record name (NAME) and description (DESC) fields. =fields NAME, DESC =head3 Run-time and Simulation Mode Parameters The old value field (OVAL) of the string input is used to implement value change monitors for VAL. If VAL is not equal to OVAL, then monitors are triggered. =fields OVAL The following fields are used to operate the string output in the simulation mode. See L for more information on these fields. =fields SIOL, SIML, SIMM, SIMS, SSCN, SDLY =cut field(SIOL,DBF_OUTLINK) { prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } =head3 Alarm Parameters The possible alarm conditions for the string output record are the SCAN, READ, and INVALID alarms. The severity of the first two is always MAJOR and not configurable. The IVOA field specifies an action to take when the INVALID alarm is triggered. There are three possible actions: =head4 Menu menuIvoa =menu menuIvoa When C<<< Set output to IVOV >>>, the value contained in the IVOV field is written to the output link during an alarm condition. See L for more information on the IVOA and IVOV fields. L lists other fields related to a alarms that are common to all record types. =fields IVOA, IVOV =cut field(IVOA,DBF_MENU) { prompt("INVALID output action") promptgroup("50 - Output") interest(2) menu(menuIvoa) } field(IVOV,DBF_STRING) { prompt("INVALID output value") promptgroup("50 - Output") interest(2) size(40) } =head2 Record Support =head3 Record Support Routines =head4 init_record long (*init_record)(struct dbCommon *precord, int pass) This routine initializes SIMM if SIML is a constant or creates a channel access link if SIML is PV_LINK. If SIOL is PV_LINK a channel access link is created. This routine next checks to see that device support is available. The routine next checks to see if the device support write routine is defined. If either device support or the device support write routine does not exist, an error message is issued and processing is terminated. If DOL is a constant, then the type double constant, if non-zero, is converted to a string and stored into VAL and UDF is set to FALSE. If DOL type is a PV_LINK then dbCaAddInlink is called to create a channel access link. If device support includes C, it is called. =head4 process long (*process)(struct dbCommon *precord) See L. =head3 Record Processing Routine process implements the following algorithm: =over =item 1. Check to see that the appropriate device support module exists. If it doesn't, an error message is issued and processing is terminated with the PACT field still set to TRUE. This ensures that processes will no longer be called for this record. Thus error storms will not occur. =item 2. If PACT is FALSE and OMSL is CLOSED_LOOP, recGblGetLinkValue is called to read the current value of VAL. See L. If the return status of recGblGetLinkValue is zero then UDF is set to FALSE. =item 3. Check severity and write the new value. See L and L for details on how the simulation mode and the INVALID alarm conditions affect output. =item 4. If PACT has been changed to TRUE, the device support write output routine has started but has not completed writing the new value. In this case, the processing routine merely returns, leaving PACT TRUE. =item 5. Check to see if monitors should be invoked. =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if OVAL is not equal to VAL. =item * NSEV and NSTA are reset to 0. =back =item 6. Scan forward link if necessary, set PACT FALSE, and return. =back =begin html


=end html =head2 Device Support =head3 Fields Of Interest To Device Support Each stringout output record must have an associated set of device support routines. The primary responsibility of the device support routines is to write a new value whenever write_stringout is called. The device support routines are primarily interested in the following fields: =fields PACT, DPVT, NSEV, NSTA, VAL, OUT =head3 Device Support Routines (devSoSoft.c) =head4 write_stringout long write_stringout(stringoutRecord *prec) This routine must output a new value. It returns the following values: =over =item * 0: Success. =item * Other: Error. =back =head3 Device Support for Soft Records The C<<< Soft Channel >>> device support module writes the current value of VAL. If the OUT link type is PV_LINK, then dbCaAddInlink is called by C. write_so calls recGblPutLinkValue to write the current value of VAL. See L. =cut } base-7.0.3.1/modules/database/src/std/rec/subArrayRecord.c0000664000577000060420000002111113557101274022050 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 Lawrence Berkeley Laboratory,The Control Systems * Group, Systems Engineering Department * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* recSubArray.c - Record Support Routines for SubArray records * * * Author: Carl Lionberger * Date: 090293 * * NOTES: * Derived from waveform record. * Modification Log: * ----------------- */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "dbScan.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "cantProceed.h" #define GEN_SIZE_OFFSET #include "subArrayRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *prec, int pass); static long process(struct dbCommon *prec); #define special NULL #define get_value NULL static long cvt_dbaddr(DBADDR *paddr); static long get_array_info(DBADDR *paddr, long *no_elements, long *offset); static long put_array_info(DBADDR *paddr, long nNew); static long get_units(DBADDR *paddr, char *units); static long get_precision(const DBADDR *paddr, long *precision); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd); static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd); #define get_alarm_double NULL rset subArrayRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,subArrayRSET); struct sadset { /* subArray dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN read_sa; /*returns: (-1,0)=>(failure,success)*/ }; static void monitor(subArrayRecord *prec); static long readValue(subArrayRecord *prec); static long init_record(struct dbCommon *pcommon, int pass) { struct subArrayRecord *prec = (struct subArrayRecord *)pcommon; struct sadset *pdset; if (pass==0){ if (prec->malm <= 0) prec->malm = 1; if (prec->ftvl > DBF_ENUM) prec->ftvl = DBF_UCHAR; prec->bptr = callocMustSucceed(prec->malm, dbValueSize(prec->ftvl), "subArrayRecord calloc failed"); prec->nord = 0; if (prec->nelm > prec->malm) prec->nelm = prec->malm; return 0; } /* must have dset defined */ if (!(pdset = (struct sadset *)(prec->dset))) { recGblRecordError(S_dev_noDSET,(void *)prec,"sa: init_record"); return S_dev_noDSET; } /* must have read_sa function defined */ if ( (pdset->number < 5) || (pdset->read_sa == NULL) ) { recGblRecordError(S_dev_missingSup,(void *)prec,"sa: init_record"); return S_dev_missingSup; } if (pdset->init_record) return pdset->init_record(prec); return 0; } static long process(struct dbCommon *pcommon) { struct subArrayRecord *prec = (struct subArrayRecord *)pcommon; struct sadset *pdset = (struct sadset *)(prec->dset); long status; unsigned char pact=prec->pact; if ((pdset==NULL) || (pdset->read_sa==NULL)) { prec->pact=TRUE; recGblRecordError(S_dev_missingSup, (void *)prec, "read_sa"); return S_dev_missingSup; } if (pact && prec->busy) return 0; status=readValue(prec); /* read the new value */ if (!pact && prec->pact) return 0; prec->pact = TRUE; recGblGetTimeStamp(prec); prec->udf = !!status; /* 0 or 1 */ if (status) recGblSetSevr(prec, UDF_ALARM, prec->udfs); monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact=FALSE; return 0; } static long cvt_dbaddr(DBADDR *paddr) { subArrayRecord *prec = (subArrayRecord *) paddr->precord; paddr->pfield = prec->bptr; paddr->no_elements = prec->malm; paddr->field_type = prec->ftvl; paddr->field_size = dbValueSize(prec->ftvl); paddr->dbr_field_type = prec->ftvl; return 0; } static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) { subArrayRecord *prec = (subArrayRecord *) paddr->precord; if (prec->udf) *no_elements = 0; else *no_elements = prec->nord; *offset = 0; return 0; } static long put_array_info(DBADDR *paddr, long nNew) { subArrayRecord *prec = (subArrayRecord *) paddr->precord; epicsUInt32 nord = prec->nord; prec->nord = nNew; if (prec->nord > prec->malm) prec->nord = prec->malm; if (nord != prec->nord) db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); return 0; } #define indexof(field) subArrayRecord##field static long get_units(DBADDR *paddr, char *units) { subArrayRecord *prec = (subArrayRecord *) paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM) break; case indexof(HOPR): case indexof(LOPR): strncpy(units,prec->egu,DB_UNITS_SIZE); } return 0; } static long get_precision(const DBADDR *paddr, long *precision) { subArrayRecord *prec = (subArrayRecord *) paddr->precord; *precision = prec->prec; if (dbGetFieldIndex(paddr) != indexof(VAL)) recGblGetPrec(paddr, precision); return 0; } static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) { subArrayRecord *prec = (subArrayRecord *) paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): pgd->upper_disp_limit = prec->hopr; pgd->lower_disp_limit = prec->lopr; break; case indexof(INDX): pgd->upper_disp_limit = prec->malm - 1; pgd->lower_disp_limit = 0; break; case indexof(NELM): pgd->upper_disp_limit = prec->malm; pgd->lower_disp_limit = 0; break; case indexof(NORD): pgd->upper_disp_limit = prec->malm; pgd->lower_disp_limit = 0; break; case indexof(BUSY): pgd->upper_disp_limit = 1; pgd->lower_disp_limit = 0; break; default: recGblGetGraphicDouble(paddr, pgd); } return 0; } static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) { subArrayRecord *prec = (subArrayRecord *) paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): pcd->upper_ctrl_limit = prec->hopr; pcd->lower_ctrl_limit = prec->lopr; break; case indexof(INDX): pcd->upper_ctrl_limit = prec->malm - 1; pcd->lower_ctrl_limit = 0; break; case indexof(NELM): pcd->upper_ctrl_limit = prec->malm; pcd->lower_ctrl_limit = 1; break; case indexof(NORD): pcd->upper_ctrl_limit = prec->malm; pcd->lower_ctrl_limit = 0; break; case indexof(BUSY): pcd->upper_ctrl_limit = 1; pcd->lower_ctrl_limit = 0; break; default: recGblGetControlDouble(paddr, pcd); } return 0; } static void monitor(subArrayRecord *prec) { unsigned short monitor_mask; monitor_mask = recGblResetAlarms(prec); monitor_mask |= (DBE_LOG|DBE_VALUE); db_post_events(prec, prec->bptr, monitor_mask); return; } static long readValue(subArrayRecord *prec) { long status; struct sadset *pdset = (struct sadset *) (prec->dset); if (prec->nelm > prec->malm) prec->nelm = prec->malm; if (prec->indx >= prec->malm) prec->indx = prec->malm - 1; status = (*pdset->read_sa)(prec); if (prec->nord <= 0) status = -1; return status; } base-7.0.3.1/modules/database/src/std/rec/subArrayRecord.dbd.pod0000664000577000060420000002627313557101274023156 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Sub-Array Record (subArray) The normal use for the subArray record type is to obtain sub-arrays from waveform records. Setting either the number of elements (NELM) or index (INDX) fields causes the record to be processed anew so that applications in which the length and position of a sub-array in a waveform record vary dynamically can be implemented using standard EPICS operator interface tools. The first element of the sub-array, that at location INDX in the referenced waveform record, can be displayed as a scalar, or the entire subarray (of length NELM) can be displayed in the same way as a waveform record. If there are fewer than NELM elements in the referenced waveform after the INDX, only the number of elements actually available are returned, and the number of elements read field (NORD) is set to reflect this. This record type does not support writing new values into waveform records. =recordtype subArray =cut recordtype(subArray) { =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters The subArray record has the standard fields for specifying under what circumstances the record will be processed. These fields are listed in L. In addition, L explains how these fields are used. =head3 Read Parameters The subArray's input link (INP) should be configured to reference the Waveform record. It should specify the VAL field of a Waveform record. The INP field can be a channel access link, in addition to a database link. See L
for information on specifying links. In addition, the DTYP field must specify a device support module. Currently, the only device support module is C<<< Soft Channel >>>. =fields INP, DTYP =head3 Array Parameters These parameters determine the number of array elements (the array length) and the data type of those elements. The Field Type of Value (FTVL) field determines the data type of the array. The user specifies the maximum number of elements allowed in the subarray in the MALM field. Generally, the number should be equal to the number of elements of the Waveform array (found in the Waveform's NELM field). The MALM field is used to allocate memory. The subArray's Number of Elements (NELM) field is where the user specifies the actual number of elements that the subArray will contain. It should of course be no greater than MALM; if it is, the record processing routine sets it equal to MALM. The INDX field determines the offset of the subArray record's array in relation to the Waveform's. For instance, if INDX is 2, then the subArray will read NELM elements starting with the third element of the Waveform's array. Thus, it equals the index number of the Waveform's array. The actual sub-array is referenced by the VAL field. =fields FTVL, VAL, MALM, NELM, INDX =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. They display the value and other parameters of the subarray record either textually or graphically. EGU is a string of up to 16 characters describing the engineering units (if any) of the values which the subArray holds. It is retrieved by the C<<< get_units >>> record support routine. The HOPR and LOPR fields set the upper and lower display limits for the sub-array elements. Both the C<<< get_graphic_double >>> and C<<< get_control_double >>> record support routines retrieve these fields. The PREC field determines the floating point precision with which to display VAL. It is used whenever the C<<< get_precision >>> record support routine is called. See L for more on the record name (NAME) and description (DESC) fields. =fields EGU, HOPR, LOPR, PREC, NAME, DESC =head3 Alarm Parameters The subarray record has the alarm parameters common to all record types. L lists other fields related to a alarms that are common to all record types. =head3 Run-time Parameters These fields are not configurable by the user. They are used for the record's internal processing or to represent the current state of the record. The NORD field holds a counter of the number of elements read into the array. It can be less than NELM even after the array is full if NELM exceeds the number of existing elements in the referenced array, i.e., the Waveform's array. BPTR contains a pointer to the record's array. =fields NORD, BPTR =begin html


=end html =head2 Record Support =head3 Record Support Routines =head4 init_record long (*init_record)(struct dbCommon *precord, int pass) Using MALM and FTVL, space for the array is allocated. The array address is stored in BPTR. This routine checks to see that device support is available and a device support read routine is defined. If either does not exist, an error message is issued and processing is terminated. If device support includes C, it is called. =head4 process long (*process)(struct dbCommon *precord) See L. =head4 cvt_dbaddr long (*cvt_dbaddr)(struct dbAddr *paddr) This is called by dbNameToAddr. It makes the dbAddr structure refer to the actual buffer holding the result. =head4 get_array_info long (*get_array_info)(struct dbAddr *paddr, long *no_elements, long *offset) Retrieves NELM. =head4 put_array_info long (*put_array_info)(struct dbAddr *paddr, long nNew) Sets NORD. =head4 get_graphic_double long (*get_graphic_double)(struct dbAddr *paddr, struct dbr_grDouble *p) For the elements in the array, this routine routines HOPR and LOPR. For the INDX field, this routine returns MALM - 1 and 0. For NELM, it returns MALM and 1. For other fields, it calls C<<< recGblGetGraphicDouble() >>>. =head4 get_control_double long (*get_control_double)(struct dbAddr *paddr, struct dbr_ctrlDouble *p) For array elements, this routine retrieves HOPR and LOPR. Otherwise, C<<< recGblGetControlDouble() >>> is called. =head4 get_units long (*get_units)(struct dbAddr *paddr, char *units) Retrieves EGU. =head4 get_precision long (*get_precision)(const struct dbAddr *paddr, long *precision) Retrieves PREC. =head3 Record Processing Routine process implements the following algorithm: =over =item 1. Check to see that the appropriate device support module exists. If it doesn't, an error message is issued and processing is terminated with the PACT field still set to TRUE. This ensures that processes will no longer be called for this record. Thus error storms will not occur. =item 2. Sanity check NELM and INDX. If NELM is greater than MALM it is set to MALM. If INDX is greater than or equal to MALM it is set to MALM-1. =item 3. Call device support read routine. This routine is expected to place the desired sub-array at the beginning of the buffer and set NORD to the number of elements of the sub-array that were read. =item 4. If PACT has been changed to TRUE, the device support read routine has started but has not completed writing the new value. In this case, the processing routine merely returns, leaving PACT TRUE. Otherwise, process sets PACT TRUE at this time. This asynchronous processing logic is not currently used but has been left in place. =item 5. Check to see if monitors should be invoked. =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are always invoked. =item * NSEV and NSTA are reset to 0. =back =item 6. Scan forward link if necessary, set PACT FALSE, and return. =back =begin html


=end html =head2 Device Support =head3 Fields Of Interest To Device Support The device support routines are primarily interested in the following fields: =fields PACT, DPVT, UDF, NSEV, NSTA, INP, FTVL, MALM, NELM, INDX, BPTR, NORD =head3 Device Support Routines (devSASoft.c) Device support consists of the following routines: =head4 long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. =head4 long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. =head4 init_record long init_record(subArrayRecord *prec) This routine is called by the record support C routine. =head4 read_sa long read_sa(subArrayRecord *prec) Enough of the source waveform is read into BPTR, from the beginning of the source, to include the requested sub-array. The sub-array is then copied to the beginning of the buffer. NORD is set to indicate how many elements of the sub-array were acquired. =head3 Device Support For Soft Records Only the device support module C<<< Soft Channel >>> is currently provided. The INP link type must be either DB_LINK or CA_LINK. =head4 Soft Channel INP is expected to point to a waveform record. =cut include "dbCommon.dbd" field(VAL,DBF_NOACCESS) { prompt("Value") asl(ASL0) special(SPC_DBADDR) pp(TRUE) extra("void * val") #=type Set by FTVL #=read Yes #=write Yes } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) prop(YES) } field(FTVL,DBF_MENU) { prompt("Field Type of Value") promptgroup("30 - Action") special(SPC_NOMOD) interest(1) menu(menuFtype) } field(INP,DBF_INLINK) { prompt("Input Specification") promptgroup("40 - Input") interest(1) } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) prop(YES) } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(MALM,DBF_ULONG) { prompt("Maximum Elements") promptgroup("30 - Action") special(SPC_NOMOD) interest(1) initial("1") } field(NELM,DBF_ULONG) { prompt("Number of Elements") promptgroup("30 - Action") pp(TRUE) initial("1") } field(INDX,DBF_ULONG) { prompt("Substring Index") promptgroup("30 - Action") pp(TRUE) } field(BUSY,DBF_SHORT) { prompt("Busy Indicator") special(SPC_NOMOD) } field(NORD,DBF_LONG) { prompt("Number elements read") special(SPC_NOMOD) } field(BPTR,DBF_NOACCESS) { prompt("Buffer Pointer") special(SPC_NOMOD) interest(4) extra("void * bptr") } } base-7.0.3.1/modules/database/src/std/rec/subRecord.c0000664000577000060420000002652313557101274021065 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Record Support Routines for Subroutine records */ /* * Original Author: Bob Dalesio * Date: 01-25-90 */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "epicsMath.h" #include "registryFunction.h" #include "alarm.h" #include "cantProceed.h" #include "dbAccess.h" #include "epicsPrint.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #define GEN_SIZE_OFFSET #include "subRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL static long get_units(DBADDR *, char *); static long get_precision(const DBADDR *, long *); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *, struct dbr_grDouble *); static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); static long get_alarm_double(DBADDR *, struct dbr_alDouble *); rset subRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset, subRSET); static void checkAlarms(subRecord *); static long do_sub(subRecord *); static long fetch_values(subRecord *); static void monitor(subRecord *); #define INP_ARG_MAX 12 static long init_record(struct dbCommon *pcommon, int pass) { struct subRecord *prec = (struct subRecord *)pcommon; SUBFUNCPTR psubroutine; struct link *plink; int i; double *pvalue; if (pass==0) return(0); plink = &prec->inpa; pvalue = &prec->a; for (i = 0; i < INP_ARG_MAX; i++, plink++, pvalue++) { recGblInitConstantLink(plink, DBF_DOUBLE, pvalue); } if (prec->inam[0]) { /* convert the initialization subroutine name */ psubroutine = (SUBFUNCPTR)registryFunctionFind(prec->inam); if (psubroutine == 0) { recGblRecordError(S_db_BadSub, (void *)prec, "Init subroutine (INAM)"); return S_db_BadSub; } /* invoke the initialization subroutine */ (*psubroutine)(prec); } if (prec->snam[0] == 0) { epicsPrintf("%s.SNAM is empty\n", prec->name); prec->pact = TRUE; return 0; } prec->sadr = (SUBFUNCPTR)registryFunctionFind(prec->snam); if (prec->sadr == NULL) { recGblRecordError(S_db_BadSub, (void *)prec, "Proc subroutine (SNAM)"); return S_db_BadSub; } prec->mlst = prec->val; prec->alst = prec->val; prec->lalm = prec->val; return 0; } static long process(struct dbCommon *pcommon) { struct subRecord *prec = (struct subRecord *)pcommon; long status = 0; int pact = prec->pact; if (!pact) { prec->pact = TRUE; status = fetch_values(prec); prec->pact = FALSE; } if (status == 0) status = do_sub(prec); /* Is subroutine asynchronous? */ if (!pact && prec->pact) return 0; prec->pact = TRUE; /* Asynchronous function (documented API!) */ if (status == 1) return 0; recGblGetTimeStamp(prec); /* check for alarms */ checkAlarms(prec); /* publish changes */ monitor(prec); recGblFwdLink(prec); prec->pact = FALSE; return 0; } static long special(DBADDR *paddr, int after) { subRecord *prec = (subRecord *)paddr->precord; if (!after) { if (prec->snam[0] == 0 && prec->pact) { prec->pact = FALSE; prec->rpro = FALSE; } return 0; } if (prec->snam[0] == 0) { epicsPrintf("%s.SNAM is empty\n", prec->name); prec->pact = TRUE; return 0; } prec->sadr = (SUBFUNCPTR)registryFunctionFind(prec->snam); if (prec->sadr) return 0; recGblRecordError(S_db_BadSub, (void *)prec, "subRecord(special) registryFunctionFind failed"); return S_db_BadSub; } #define indexof(field) subRecord##field static long get_linkNumber(int fieldIndex) { if (fieldIndex >= indexof(A) && fieldIndex <= indexof(L)) return fieldIndex - indexof(A); if (fieldIndex >= indexof(LA) && fieldIndex <= indexof(LL)) return fieldIndex - indexof(LA); return -1; } static long get_units(DBADDR *paddr, char *units) { subRecord *prec = (subRecord *)paddr->precord; int linkNumber; if(paddr->pfldDes->field_type == DBF_DOUBLE) { linkNumber = get_linkNumber(dbGetFieldIndex(paddr)); if (linkNumber >= 0) dbGetUnits(&prec->inpa + linkNumber, units, DB_UNITS_SIZE); else strncpy(units,prec->egu,DB_UNITS_SIZE); } return 0; } static long get_precision(const DBADDR *paddr, long *pprecision) { subRecord *prec = (subRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); int linkNumber; *pprecision = prec->prec; if (fieldIndex == indexof(VAL)) return 0; linkNumber = get_linkNumber(fieldIndex); if (linkNumber >= 0) { short precision; if (dbGetPrecision(&prec->inpa + linkNumber, &precision) == 0) *pprecision = precision; } else recGblGetPrec(paddr, pprecision); return 0; } static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) { subRecord *prec = (subRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); int linkNumber; switch (fieldIndex) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): pgd->lower_disp_limit = prec->lopr; pgd->upper_disp_limit = prec->hopr; break; default: linkNumber = get_linkNumber(fieldIndex); if (linkNumber >= 0) { dbGetGraphicLimits(&prec->inpa + linkNumber, &pgd->lower_disp_limit, &pgd->upper_disp_limit); } else recGblGetGraphicDouble(paddr,pgd); } return 0; } static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) { subRecord *prec = (subRecord *)paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): case indexof(HIHI): case indexof(HIGH): case indexof(LOW): case indexof(LOLO): case indexof(LALM): case indexof(ALST): case indexof(MLST): pcd->lower_ctrl_limit = prec->lopr; pcd->upper_ctrl_limit = prec->hopr; break; default: recGblGetControlDouble(paddr,pcd); } return 0; } static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) { subRecord *prec = (subRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); int linkNumber; if (fieldIndex == subRecordVAL) { pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN; pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN; pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN; pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN; } else { linkNumber = get_linkNumber(fieldIndex); if (linkNumber >= 0) { dbGetAlarmLimits(&prec->inpa + linkNumber, &pad->lower_alarm_limit, &pad->lower_warning_limit, &pad->upper_warning_limit, &pad->upper_alarm_limit); } else recGblGetAlarmDouble(paddr, pad); } return 0; } static void checkAlarms(subRecord *prec) { double val, hyst, lalm; double alev; epicsEnum16 asev; if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, prec->udfs); return; } val = prec->val; hyst = prec->hyst; lalm = prec->lalm; /* alarm condition hihi */ asev = prec->hhsv; alev = prec->hihi; if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { if (recGblSetSevr(prec, HIHI_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition lolo */ asev = prec->llsv; alev = prec->lolo; if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { if (recGblSetSevr(prec, LOLO_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition high */ asev = prec->hsv; alev = prec->high; if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { if (recGblSetSevr(prec, HIGH_ALARM, asev)) prec->lalm = alev; return; } /* alarm condition low */ asev = prec->lsv; alev = prec->low; if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { if (recGblSetSevr(prec, LOW_ALARM, asev)) prec->lalm = alev; return; } /* we get here only if val is out of alarm by at least hyst */ prec->lalm = val; return; } static void monitor(subRecord *prec) { unsigned monitor_mask; double *pnew; double *pold; int i; /* get alarm mask */ monitor_mask = recGblResetAlarms(prec); /* check for value change */ recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE); /* check for archive change */ recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE); /* send out monitors connected to the value field */ if (monitor_mask) { db_post_events(prec, &prec->val, monitor_mask); } /* check all input fields for changes */ for (i = 0, pnew = &prec->a, pold = &prec->la; i < INP_ARG_MAX; i++, pnew++, pold++) { if (*pnew != *pold) { db_post_events(prec, pnew, monitor_mask | DBE_VALUE | DBE_LOG); *pold = *pnew; } } return; } static long fetch_values(subRecord *prec) { struct link *plink = &prec->inpa; double *pvalue = &prec->a; int i; for (i = 0; i < INP_ARG_MAX; i++, plink++, pvalue++) { if (dbGetLink(plink, DBR_DOUBLE, pvalue, 0, 0)) return -1; } return 0; } static long do_sub(subRecord *prec) { SUBFUNCPTR psubroutine = prec->sadr; long status; if (psubroutine == NULL) { recGblSetSevr(prec, BAD_SUB_ALARM, INVALID_ALARM); return 0; } status = (*psubroutine)(prec); if (status < 0) { recGblSetSevr(prec, SOFT_ALARM, prec->brsv); } else { prec->udf = isnan(prec->val); } return status; } base-7.0.3.1/modules/database/src/std/rec/subRecord.dbd.pod0000664000577000060420000004233313557101274022152 0ustar anjaesctl#************************************************************************* # Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Subroutine Record (sub) The subroutine record is used to call a C initialization routine and a recurring scan routine. There is no device support for this record. =recordtype sub =cut recordtype(sub) { =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters The subroutine record has the standard fields for specifying under what circumstances it will be processed. These fields are listed in L. In addition, L explains how these fields are used. =head3 Read Parameters The subroutine record has twelve input links (INPA-INPL), each of which has a corresponding value field (A-L). These fields are used to retrieve and store values that can be passed to the subroutine that the record calls. The input links can be either channel access or database links, or constants. When constants, the corresponding value field for the link is initialized with the constant value and the field's value can be changed at run-time via dbPuts. Otherwise, the values for (A-F) are fetched from the input links when the record is processed. See L
for information on specifying links. =fields INPA, INPB, INPC, INPD, INPE, INPF, INPG, INPH, INPI, INPJ, INPK, INPL, A, B, C, D, E, F, G, H, I, J, K, L =head3 Subroutine Connection These fields are used to connect to the C subroutine. The name of the subroutine should be entered in the SNAM field. =fields INAM, SNAM =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. They display the value and other parameters of the subroutine either textually or graphically. EGU is a string of up to 16 characters that could describe any units used by the subroutine record. It is retrieved by the C<<< get_units >>> record support routine. The HOPR and LOPR fields set the upper and lower display limits for the VAL, A-L, LA-LL, HIHI, LOLO, LOW, and HIGH fields. Both the C<<< get_graphic_double >>> and C<<< get_control_double >>> record support routines retrieve these fields. The PREC field determines the floating point precision with which to display VAL. It is used whenever the C<<< get_precision >>> record support routine is called. See L for more on the record name (NAME) and description (DESC) fields. =fields EGU, HOPR, LOPR, PREC, NAME, DESC =head3 Alarm Parameters The possible alarm conditions for subroutine records are the SCAN, READ, limit alarms, and an alarm that can be triggered if the subroutine returns a negative value. The SCAN and READ alarms are called by the record or device support routines. The limit alarms are configured by the user in the HIHI, LOLO, HIGH, and LOW fields using numerical values. They apply to the VAL field. For each of these fields, there is a corresponding severity field which can be either NO_ALARM, MINOR, or MAJOR. The BRSV field is where the user can set the alarm severity in case the subroutine returns a negative value. See L for a complete explanation of alarms and these fields. L lists other fields related to a alarms that are common to all record types. =fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, BRSV, HYST =head3 Monitor Parameters These parameters are used to determine when to send monitors placed on the VAL field. The appropriate monitors are invoked when VAL differs from the values in the ALST and MLST run-time fields, i.e., when the value of VAL changes by more than the deadband specified in these fields. The ADEL and MDEL fields specify a minimum delta which the change must surpass before the value-change monitors are invoked. If these fields have a value of zero, everytime the value changes, a monitor will be triggered; if they have a value of -1, everytime the record is processed, monitors are triggered. The ADEL field is used by archive monitors and the MDEL field for all other types of monitors. See L for a complete explanation of monitors and deadbands. =fields ADEL, MDEL =head3 Run-time Parameters These parameters are used by the run-time code for processing the subroutine record. They are not configured using a database configuration tool. They represent the current state of the record. Many of them are used by the record processing routines or the monitors. VAL should be set by the subroutine. SADR holds the subroutine address and is set by the record processing routine. The rest of these fields--LALM, ALST, MLST, and the LA-LL fields--are used to implement the monitors. For example, when LA is not equal to A, the value-change monitors are called for that field. =fields VAL, SADR, LALM, ALST, MLST, LA, LB, LC, LD, LE, LF, LG, LH, LI, LJ, LK, LL =head2 Record Support =head3 Record Support Routines =head4 init_record long (*init_record)(struct dbCommon *precord, int pass) For each constant input link, the corresponding value field is initialized with the constant value. For each input link that is of type PV_LINK, a channel access link is created. If an initialization subroutine is defined, it is located and called. The processing subroutine is located and its address stored in SADR. =head4 process long (*process)(struct dbCommon *precord) See L. =head4 get_units long (*get_units)(struct dbAddr *paddr, char *units) Retrieves EGU. =head4 get_precision long (*get_precision)(const struct dbAddr *paddr, long *precision) Retrieves PREC when VAL is the field being referenced. Otherwise, calls C<<< recGblGetPrec() >>>. =head4 get_graphic_double long (*get_graphic_double)(struct dbAddr *paddr, struct dbr_grDouble *p) Sets the upper display and lower display limits for a field. If the field is VAL, A-L, LA-LL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. =head4 get_control_double long (*get_control_double)(struct dbAddr *paddr, struct dbr_ctrlDouble *p) Sets the upper control and the lower control limits for a field. If the field is VAL, A-L, LA-LL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. =head4 get_alarm_double long (*get_alarm_double)(struct dbAddr *paddr, struct dbr_alDouble *p) Sets the following values: upper_alarm_limit = HIHI upper_warning_limit = HIGH lower_warning_limit = LOW lower_alarm_limit = LOLO =head3 Record Processing Routine process implements the following algorithm: =over =item 1. If PACT is FALSE then fetch all arguments. =item 2. Call the subroutine and check return value. =over =item * Call subroutine =item * Set PACT TRUE =item * If return value is 1, return =back =item 3. Check alarms. This routine checks to see if the new VAL causes the alarm status and severity to change. If so, NSEV, NSTA and LALM are set. It also honors the alarm hysteresis factor (HYST). Thus the value must change by more than HYST before the alarm status and severity is lowered. =item 4. Check to see if monitors should be invoked. =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if ADEL and MDEL conditions are met. =item * Monitors for A-L are invoked if value has changed. =item * NSEV and NSTA are reset to 0. =back =item 5. Scan forward link if necessary, set PACT FALSE, and return. =back =head3 Example Synchronous Subroutine This is an example subroutine that merely increments VAL each time process is called. #include #include #include #include #include static long subInit(struct subRecord *psub) { printf("subInit was called\n"); return 0; } static long subProcess(struct subRecord *psub) { psub->val++; return 0; } epicsRegisterFunction(subInit); epicsRegisterFunction(subProcess); =head3 Example Asynchronous Subroutine This example for a VxWorks IOC shows an asynchronous subroutine. It uses (actually misuses) fields A and B. Field A is taken as the number of seconds until asynchronous completion. Field B is a flag to decide if messages should be printed. Lets assume A E 0 and B = 1. The following sequence of actions will occcur: =over =item 1. subProcess is called with pact FALSE. It performs the following steps. =over =item * Computes, from A, the number of ticks until asynchronous completion should occur. =item * Prints a message stating that it is requesting an asynchronous callback. =item * Calls the vxWorks watchdog start routine. =item * Sets pact TRUE and returns a value of 0. This tells record support to complete without checking alarms, monitors, or the forward link. =back =item 2. When the time expires, the system wide callback task calls myCallback. myCallback locks the record, calls process, and unlocks the record. =item 3. Process again calls subProcess, but now pact is TRUE. Thus the following is done: =over =item * VAL is incremented. =item * A completion message is printed. =item * subProcess returns 0. The record processing routine will complete record processing. =back =back #include #include #include #include #include #include #include /* control block for callback*/ struct callback { epicsCallback callback; struct dbCommon *precord; WDOG_ID wd_id; }; void myCallback(struct callback *pcallback) { struct dbCommon *precord=pcallback->precord; struct rset *prset=(struct rset *)(precord->rset); dbScanLock(precord); (*prset->process)(precord); dbScanUnlock(precord); } long subInit(struct subRecord *psub) { struct callback *pcallback; pcallback = (struct callback *)(calloc(1,sizeof(struct callback))); psub->dpvt = (void *)pcallback; callbackSetCallback(myCallback,pcallback); pcallback->precord = (struct dbCommon *)psub; pcallback->wd_id = wdCreate(); printf("subInit was called\n"); return 0; } long subProcess(struct subRecord *psub) { struct callback *pcallback=(struct callback *)(psub->dpvt); /* sub.inp must be a CONSTANT*/ if (psub->pact) { psub->val++; if (psub->b) printf("%s subProcess Completed\n", psub->name); return 0; } else { int wait_time = (long)(psub->a * vxTicksPerSecond); if (wait_time <= 0){ if (psub->b) printf("%s subProcess sync processing\n", psub->name); psub->pact = TRUE; return 0; } if (psub->b){ callbackSetPriority(psub->prio, pcallback); printf("%s Starting async processing\n", psub->name); wdStart(pcallback->wd_id, wait_time, callbackRequest, (int)pcallback); return 1; } } return 0; } =cut include "dbCommon.dbd" field(VAL,DBF_DOUBLE) { prompt("Result") asl(ASL0) pp(TRUE) } field(INAM,DBF_STRING) { prompt("Init Routine Name") promptgroup("30 - Action") special(SPC_NOMOD) interest(1) size(40) } field(SNAM,DBF_STRING) { prompt("Subroutine Name") promptgroup("30 - Action") special(SPC_MOD) interest(1) size(40) } %struct subRecord; %typedef long (*SUBFUNCPTR)(struct subRecord *); field(SADR,DBF_NOACCESS) { prompt("Subroutine Address") special(SPC_NOMOD) interest(4) extra("SUBFUNCPTR sadr") } field(INPA,DBF_INLINK) { prompt("Input A") promptgroup("41 - Input A-F") interest(1) } field(INPB,DBF_INLINK) { prompt("Input B") promptgroup("41 - Input A-F") interest(1) } field(INPC,DBF_INLINK) { prompt("Input C") promptgroup("41 - Input A-F") interest(1) } field(INPD,DBF_INLINK) { prompt("Input D") promptgroup("41 - Input A-F") interest(1) } field(INPE,DBF_INLINK) { prompt("Input E") promptgroup("41 - Input A-F") interest(1) } field(INPF,DBF_INLINK) { prompt("Input F") promptgroup("41 - Input A-F") interest(1) } field(INPG,DBF_INLINK) { prompt("Input G") promptgroup("42 - Input G-L") interest(1) } field(INPH,DBF_INLINK) { prompt("Input H") promptgroup("42 - Input G-L") interest(1) } field(INPI,DBF_INLINK) { prompt("Input I") promptgroup("42 - Input G-L") interest(1) } field(INPJ,DBF_INLINK) { prompt("Input J") promptgroup("42 - Input G-L") interest(1) } field(INPK,DBF_INLINK) { prompt("Input K") promptgroup("42 - Input G-L") interest(1) } field(INPL,DBF_INLINK) { prompt("Input L") promptgroup("42 - Input G-L") interest(1) } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) prop(YES) } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) prop(YES) } field(BRSV,DBF_MENU) { prompt("Bad Return Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { prompt("Lolo Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(HSV,DBF_MENU) { prompt("High Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(LSV,DBF_MENU) { prompt("Low Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) prop(YES) menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { prompt("Alarm Deadband") promptgroup("70 - Alarm") interest(1) } field(ADEL,DBF_DOUBLE) { prompt("Archive Deadband") promptgroup("80 - Display") interest(1) } field(MDEL,DBF_DOUBLE) { prompt("Monitor Deadband") promptgroup("80 - Display") interest(1) } field(A,DBF_DOUBLE) { prompt("Value of Input A") pp(TRUE) } field(B,DBF_DOUBLE) { prompt("Value of Input B") pp(TRUE) } field(C,DBF_DOUBLE) { prompt("Value of Input C") pp(TRUE) } field(D,DBF_DOUBLE) { prompt("Value of Input D") pp(TRUE) } field(E,DBF_DOUBLE) { prompt("Value of Input E") pp(TRUE) } field(F,DBF_DOUBLE) { prompt("Value of Input F") pp(TRUE) } field(G,DBF_DOUBLE) { prompt("Value of Input G") pp(TRUE) } field(H,DBF_DOUBLE) { prompt("Value of Input H") pp(TRUE) } field(I,DBF_DOUBLE) { prompt("Value of Input I") pp(TRUE) } field(J,DBF_DOUBLE) { prompt("Value of Input J") pp(TRUE) } field(K,DBF_DOUBLE) { prompt("Value of Input K") pp(TRUE) } field(L,DBF_DOUBLE) { prompt("Value of Input L") pp(TRUE) } field(LA,DBF_DOUBLE) { prompt("Prev Value of A") special(SPC_NOMOD) interest(3) } field(LB,DBF_DOUBLE) { prompt("Prev Value of B") special(SPC_NOMOD) interest(3) } field(LC,DBF_DOUBLE) { prompt("Prev Value of C") special(SPC_NOMOD) interest(3) } field(LD,DBF_DOUBLE) { prompt("Prev Value of D") special(SPC_NOMOD) interest(3) } field(LE,DBF_DOUBLE) { prompt("Prev Value of E") special(SPC_NOMOD) interest(3) } field(LF,DBF_DOUBLE) { prompt("Prev Value of F") special(SPC_NOMOD) interest(3) } field(LG,DBF_DOUBLE) { prompt("Prev Value of G") special(SPC_NOMOD) interest(3) } field(LH,DBF_DOUBLE) { prompt("Prev Value of H") special(SPC_NOMOD) interest(3) } field(LI,DBF_DOUBLE) { prompt("Prev Value of I") special(SPC_NOMOD) interest(3) } field(LJ,DBF_DOUBLE) { prompt("Prev Value of J") special(SPC_NOMOD) interest(3) } field(LK,DBF_DOUBLE) { prompt("Prev Value of K") special(SPC_NOMOD) interest(3) } field(LL,DBF_DOUBLE) { prompt("Prev Value of L") special(SPC_NOMOD) interest(3) } field(LALM,DBF_DOUBLE) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(ALST,DBF_DOUBLE) { prompt("Last Value Archived") special(SPC_NOMOD) interest(3) } field(MLST,DBF_DOUBLE) { prompt("Last Value Monitored") special(SPC_NOMOD) interest(3) } } base-7.0.3.1/modules/database/src/std/rec/waveformRecord.c0000664000577000060420000002533513557101274022122 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* recWaveform.c - Record Support Routines for Waveform records */ /* * Original Author: Bob Dalesio * Date: 7-14-89 */ #include #include #include #include #include #include "dbDefs.h" #include "epicsPrint.h" #include "epicsString.h" #include "alarm.h" #include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "dbScan.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "recGbl.h" #include "special.h" #include "cantProceed.h" #include "menuYesNo.h" #define GEN_SIZE_OFFSET #include "waveformRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); static long special(DBADDR *, int); #define get_value NULL static long cvt_dbaddr(DBADDR *); static long get_array_info(DBADDR *, long *, long *); static long put_array_info(DBADDR *, long); static long get_units(DBADDR *, char *); static long get_precision(const DBADDR *, long *); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *, struct dbr_grDouble *); static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); #define get_alarm_double NULL rset waveformRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,waveformRSET); struct wfdset { /* waveform dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN read_wf; /*returns: (-1,0)=>(failure,success)*/ }; static void monitor(waveformRecord *); static long readValue(waveformRecord *); static long init_record(struct dbCommon *pcommon, int pass) { struct waveformRecord *prec = (struct waveformRecord *)pcommon; struct wfdset *pdset; if (pass == 0) { if (prec->nelm <= 0) prec->nelm = 1; if (prec->ftvl > DBF_ENUM) prec->ftvl = DBF_UCHAR; prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl), "waveform calloc failed"); prec->nord = (prec->nelm == 1); return 0; } recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); /* must have dset defined */ if (!(pdset = (struct wfdset *)(prec->dset))) { recGblRecordError(S_dev_noDSET,(void *)prec,"wf: init_record"); return S_dev_noDSET; } /* must have read_wf function defined */ if ((pdset->number < 5) || (pdset->read_wf == NULL)) { recGblRecordError(S_dev_missingSup,(void *)prec,"wf: init_record"); return S_dev_missingSup; } if (!pdset->init_record) return 0; return pdset->init_record(prec); } static long process(struct dbCommon *pcommon) { struct waveformRecord *prec = (struct waveformRecord *)pcommon; struct wfdset *pdset = (struct wfdset *)(prec->dset); unsigned char pact=prec->pact; long status; if ((pdset==NULL) || (pdset->read_wf==NULL)) { prec->pact=TRUE; recGblRecordError(S_dev_missingSup, (void *)prec, "read_wf"); return S_dev_missingSup; } if (pact && prec->busy) return 0; status = readValue(prec); /* read the new value */ if (!pact && prec->pact) return 0; prec->udf = FALSE; recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact=FALSE; return status; } static long special(DBADDR *paddr, int after) { waveformRecord *prec = (waveformRecord *)(paddr->precord); int special_type = paddr->special; switch(special_type) { case SPC_MOD: if (dbGetFieldIndex(paddr) == waveformRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); return 0; } default: recGblDbaddrError(S_db_badChoice, paddr, "waveform: special"); return S_db_badChoice; } } static long cvt_dbaddr(DBADDR *paddr) { waveformRecord *prec = (waveformRecord *) paddr->precord; paddr->no_elements = prec->nelm; paddr->field_type = prec->ftvl; paddr->field_size = dbValueSize(prec->ftvl); paddr->dbr_field_type = prec->ftvl; return 0; } static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) { waveformRecord *prec = (waveformRecord *) paddr->precord; paddr->pfield = prec->bptr; *no_elements = prec->nord; *offset = 0; return 0; } static long put_array_info(DBADDR *paddr, long nNew) { waveformRecord *prec = (waveformRecord *) paddr->precord; epicsUInt32 nord = prec->nord; prec->nord = nNew; if (prec->nord > prec->nelm) prec->nord = prec->nelm; if (nord != prec->nord) db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); return 0; } #define indexof(field) waveformRecord##field static long get_units(DBADDR *paddr, char *units) { waveformRecord *prec = (waveformRecord *) paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM) break; case indexof(HOPR): case indexof(LOPR): strncpy(units,prec->egu,DB_UNITS_SIZE); } return 0; } static long get_precision(const DBADDR *paddr, long *precision) { waveformRecord *prec = (waveformRecord *) paddr->precord; *precision = prec->prec; if (dbGetFieldIndex(paddr) != indexof(VAL)) recGblGetPrec(paddr, precision); return 0; } static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) { waveformRecord *prec = (waveformRecord *) paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): pgd->upper_disp_limit = prec->hopr; pgd->lower_disp_limit = prec->lopr; break; case indexof(BUSY): pgd->upper_disp_limit = 1; pgd->lower_disp_limit = 0; break; case indexof(NORD): pgd->upper_disp_limit = prec->nelm; pgd->lower_disp_limit = 0; break; default: recGblGetGraphicDouble(paddr, pgd); } return 0; } static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) { waveformRecord *prec = (waveformRecord *) paddr->precord; switch (dbGetFieldIndex(paddr)) { case indexof(VAL): pcd->upper_ctrl_limit = prec->hopr; pcd->lower_ctrl_limit = prec->lopr; break; case indexof(BUSY): pcd->upper_ctrl_limit = 1; pcd->lower_ctrl_limit = 0; break; case indexof(NORD): pcd->upper_ctrl_limit = prec->nelm; pcd->lower_ctrl_limit = 0; break; default: recGblGetControlDouble(paddr, pcd); } return 0; } static void monitor(waveformRecord *prec) { unsigned short monitor_mask = 0; unsigned int hash = 0; monitor_mask = recGblResetAlarms(prec); if (prec->mpst == waveformPOST_Always) monitor_mask |= DBE_VALUE; if (prec->apst == waveformPOST_Always) monitor_mask |= DBE_LOG; /* Calculate hash if we are interested in OnChange events. */ if ((prec->mpst == waveformPOST_OnChange) || (prec->apst == waveformPOST_OnChange)) { hash = epicsMemHash((char *)prec->bptr, prec->nord * dbValueSize(prec->ftvl), 0); /* Only post OnChange values if the hash is different. */ if (hash != prec->hash) { if (prec->mpst == waveformPOST_OnChange) monitor_mask |= DBE_VALUE; if (prec->apst == waveformPOST_OnChange) monitor_mask |= DBE_LOG; /* Store hash for next process. */ prec->hash = hash; /* Post HASH. */ db_post_events(prec, &prec->hash, DBE_VALUE); } } if (monitor_mask) { db_post_events(prec, &prec->val, monitor_mask); } } static long readValue(waveformRecord *prec) { struct wfdset *pdset = (struct wfdset *) prec->dset; long status = 0; if (!prec->pact) { status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (status) return status; } switch (prec->simm) { case menuYesNoNO: { epicsUInt32 nord = prec->nord; status = pdset->read_wf(prec); if (nord != prec->nord) db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); break; } case menuYesNoYES: { long nRequest = prec->nelm; recGblSetSevr(prec, SIMM_ALARM, prec->sims); if (prec->pact || (prec->sdly < 0)) { status = dbGetLink(&prec->siol, prec->ftvl, prec->bptr, 0, &nRequest); if (status == 0) prec->udf = FALSE; if (nRequest != prec->nord) { prec->nord = nRequest; db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); } prec->pact = FALSE; } else { /* !prec->pact && delay >= 0 */ epicsCallback *pvt = prec->simpvt; if (!pvt) { /* very lazy allocation of callback structure */ pvt = calloc(1, sizeof(epicsCallback)); prec->simpvt = pvt; } if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); prec->pact = TRUE; } break; } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } return status; } base-7.0.3.1/modules/database/src/std/rec/waveformRecord.dbd.pod0000664000577000060420000003476613557101274023222 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* =title Waveform Record (waveform) The waveform record type is used to interface waveform digitizers. The record stores its data in arrays. The array can contain any of the supported data types. =recordtype waveform =cut include "menuFtype.dbd" menu(waveformPOST) { choice(waveformPOST_Always,"Always") choice(waveformPOST_OnChange,"On Change") } recordtype(waveform) { =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. =head3 Scan Parameters The waveform record has the standard fields for specifying under what circumstances the record will be processed. These fields are listed in L. In addition, L explains how these fields are used. Note that I/O event scanning is only supported for those card types that interrupt. =head3 Read Parameters These fields are configurable by the user to specify how and from where the record reads its data. How the INP field is configured determines where the waveform gets its input. It can be a hardware address, a channel access or database link, or a constant. Only in records that use soft device support can the INP field be a channel access link, a database link, or a constant. Otherwise, the INP field must be a hardware address. See L
for information on the format of hardware addresses and database links. =head4 Fields related to waveform reading =fields DTYP, INP, NELM, FTVL, RARM The DTYP field must contain the name of the appropriate device support module. The values retrieved from the input link are placed in an array referenced by VAL. (If the INP link is a constant, elements can be placed in the array via dbPuts.) NELM specifies the number of elements that the array will hold, while FTVL specifies the data type of the elements. The RARM field causes the device to re-arm when this field is set to 1. =head4 Possible data types for FTVL =menu menuFtype =head3 Operator Display Parameters These parameters are used to present meaningful data to the operator. They display the value and other parameters of the waveform either textually or graphically. =head4 Fields related to I =fields EGU, HOPR, LOPR, PREC, NAME, DESC EGU is a string of up to 16 characters describing the units that the waveform measures. It is retrieved by the C<<< get_units >>> record support routine. The HOPR and LOPR fields set the upper and lower display limits for array elements referenced by the VAL field. Both the C<<< get_graphic_double >>> and C<<< get_control_double >>> record support routines retrieve these fields. The PREC field determines the floating point precision with which to display the array values. It is used whenever the C<<< get_precision >>> record support routine is called. See L for more on the record name (NAME) and description (DESC) fields. =head3 Alarm Parameters The waveform record has the alarm parameters common to all record types. L lists other fields related to a alarms that are common to all record types. =head3 Monitor Parameters These parameters are used to determine when to send monitors placed on the VAL field. The APST and MPST fields are a menu with choices "Always" and "On Change". The default is "Always", thus monitors will normally be sent every time the record processes. Selecting "On Change" causes a 32-bit hash of the VAL field buffer to be calculated and compared with the previous hash value every time the record processes; the monitor will only be sent if the hash is different, indicating that the buffer has changed. Note that there is a small chance that two different value buffers might result in the same hash value, so for critical systems "Always" may be a better choice, even though it re-sends duplicate data. =head4 Record fields related to I =fields APST, MPST, HASH =head4 Menu choices for C and C fields =menu waveformPOST =head3 Run-time Parameters These parameters are used by the run-time code for processing the waveform. They are not configured using a configuration tool. Only the VAL field is modifiable at run-time. VAL references the array where the waveform stores its data. The BPTR field holds the address of the array. The NORD field holds a counter of the number of elements that have been read into the array. It is reset to 0 when the device is rearmed. The BUSY field indicates if the device is armed but has not yet been digitized. =fields VAL, BPTR, NORD, BUSY The following fields are used to operate the waveform in the simulation mode. See L for more information on the simulation mode fields. =fields SIOL, SIML, SIMM, SIMS =begin html


=end html =head2 Record Support =head3 Record Support Routines =head4 init_record static long init_record(waveformRecord *prec, int pass) Using NELM and FTVL space for the array is allocated. The array address is stored in the record. This routine initializes SIMM with the value of SIML if SIML type is CONSTANT link or creates a channel access link if SIML type is PV_LINK. VAL is likewise initialized if SIOL is CONSTANT or PV_LINK. This routine next checks to see that device support is available and a device support read routine is defined. If either does not exist, an error message is issued and processing is terminated If device support includes C, it is called. =head4 process static long process(waveformRecord *prec) See L section below. =head4 cvt_dbaddr static long cvt_dbaddr(DBADDR *paddr) This is called by dbNameToAddr. It makes the dbAddr structure refer to the actual buffer holding the result. =head4 get_array_info static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) Obtains values from the array referenced by VAL. =head4 put_array_info static long put_array_info(DBADDR *paddr, long nNew) Writes values into the array referenced by VAL. =head4 get_units static long get_units(DBADDR *paddr, char *units) Retrieves EGU. =head4 get_prec static long get_precision(DBADDR *paddr, long *precision) Retrieves PREC if field is VAL field. Otherwise, calls C<<< recGblGetPrec() >>>. =head4 get_graphic_double static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) Sets the upper display and lower display limits for a field. If the field is VAL the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. Sets the following values: upper_disp_limit = HOPR lower_disp_limit = LOPR =head4 get_control_double static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) Sets the upper control and the lower control limits for a field. If the field is VAL the limits are set to HOPR and LOPR, else if the field has upper and lower limits defined they will be used, else the upper and lower maximum values for the field type will be used. Sets the following values upper_ctrl_limit = HOPR lower_ctrl_limit = LOPR =head3 Record Processing Routine process implements the following algorithm: =over =item 1. Check to see that the appropriate device support module exists. If it doesn't, an error message is issued and processing is terminated with the PACT field still set to TRUE. This ensures that processes will no longer be called for this record. Thus error storms will not occur. =item 2. Call device support read routine. =item 3. If PACT has been changed to TRUE, the device support read routine has started but has not completed writing the new value. In this case, the processing routine merely returns, leaving PACT TRUE. =item 4. Check to see if monitors should be invoked. =over =item * Alarm monitors are invoked if the alarm status or severity has changed. =item * Archive and value change monitors are invoked if APST or MPST are Always or if the result of the hash calculation is different. =item * NSEV and NSTA are reset to 0. =back =item 5. Scan forward link if necessary, set PACT FALSE, and return. =back =begin html


=end html =head2 Device Support =head3 Fields Of Interest To Device Support Each waveform record must have an associated set of device support routines. The primary responsibility of the device support routines is to obtain a new array value whenever read_wf is called. The device support routines are primarily interested in the following fields: =fields PACT, DPVT, NSEV, NSTA, INP, NELM, FTVL, RARM, BPTR, NORD, BUSY =head3 Device Support Routines Device support consists of the following routines: =head4 long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. =head4 long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with the integer parameter C set to 0. The second call happens after all of the C calls have been made, with C set to 1. =head4 init_record init_record(precord) This routine is optional. If provided, it is called by the record support C routine. =head4 get_ioint_info get_ioint_info(int cmd,struct dbCommon *precord,IOSCANPVT *ppvt) This routine is called by the ioEventScan system each time the record is added or deleted from an I/O event scan list. cmd has the value (0,1) if the record is being (added to, deleted from) an I/O event list. It must be provided for any device type that can use the ioEvent scanner. =head4 read_wf read_wf(precord) This routine must provide a new input value. It returns the following values: =over =item * 0: Success. =item * Other: Error. =back =head3 Device Support For Soft Records The C<<< Soft Channel >>> device support module is provided to read values from other records and store them in arrays. If INP is a constant link, then read_wf does nothing. In this case, the record can be used to hold arrays written via dbPuts. If INP is a database or channel access link, the new array value is read from the link. NORD is set. This module places a value directly in VAL. If the INP link type is constant, then NORD is set to zero. If the INP link type is PV_LINK, then dbCaAddInlink is called by C. read_wf calls recGblGetLinkValue which performs the following steps: =over =item * If the INP link type is CONSTANT recGblGetLinkValue does nothing. =item * If the INP link type is DB_LINK, then dbGetLink is called to obtain a new input value. If dbGetLink returns an error, a LINK_ALARM with a severity of INVALID_ALARM is raised. =item * If the INP link type is CA_LINK, then dbCaGetLink is called to obtain a new input value. If dbCaGetLink returns an error, a LINK_ALARM with a severity of INVALID_ALARM is raised. =item * NORD is set to the number of values returned and read_wf returns. =back =cut include "dbCommon.dbd" field(VAL,DBF_NOACCESS) { prompt("Value") asl(ASL0) special(SPC_DBADDR) pp(TRUE) extra("void * val") #=type Set by FTVL #=read Yes #=write Yes } field(RARM,DBF_SHORT) { prompt("Rearm the waveform") promptgroup("30 - Action") pp(TRUE) interest(1) } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) prop(YES) } field(INP,DBF_INLINK) { prompt("Input Specification") promptgroup("40 - Input") interest(1) } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) prop(YES) } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) prop(YES) } field(NELM,DBF_ULONG) { prompt("Number of Elements") promptgroup("30 - Action") special(SPC_NOMOD) interest(1) initial("1") } field(FTVL,DBF_MENU) { prompt("Field Type of Value") promptgroup("30 - Action") special(SPC_NOMOD) interest(1) menu(menuFtype) } field(BUSY,DBF_SHORT) { prompt("Busy Indicator") special(SPC_NOMOD) } field(NORD,DBF_ULONG) { prompt("Number elements read") special(SPC_NOMOD) } field(BPTR,DBF_NOACCESS) { prompt("Buffer Pointer") special(SPC_NOMOD) interest(4) extra("void * bptr") } field(SIOL,DBF_INLINK) { prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(OLDSIMM,DBF_MENU) { prompt("Prev. Simulation Mode") special(SPC_NOMOD) interest(4) menu(menuSimm) } field(SSCN,DBF_MENU) { prompt("Sim. Mode Scan") promptgroup("90 - Simulate") interest(1) menu(menuScan) initial("65535") } field(SDLY,DBF_DOUBLE) { prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) initial("-1.0") } %#include "callback.h" field(SIMPVT,DBF_NOACCESS) { prompt("Sim. Mode Private") special(SPC_NOMOD) interest(4) extra("epicsCallback *simpvt") } field(MPST,DBF_MENU) { prompt("Post Value Monitors") promptgroup("80 - Display") interest(1) menu(waveformPOST) } field(APST,DBF_MENU) { prompt("Post Archive Monitors") promptgroup("80 - Display") interest(1) menu(waveformPOST) } field(HASH,DBF_ULONG) { prompt("Hash of OnChange data.") interest(3) } } base-7.0.3.1/modules/database/src/std/softIoc/Makefile0000664000577000060420000000165213557101274021262 0ustar anjaesctl########################################################################## # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. ########################################################################## # This is a Makefile fragment, see src/ioc/Makefile. SRC_DIRS += $(STDDIR)/softIoc PROD_IOC_DEFAULT = softIoc PROD_IOC_iOS = -nil- DBD += base.dbd DBD += asSub.dbd DBD += softIoc.dbd softIoc_DBD += base.dbd softIoc_DBD += dlload.dbd softIoc_DBD += system.dbd softIoc_SRCS += softIoc_registerRecordDeviceDriver.cpp softIoc_SRCS_DEFAULT += softMain.cpp softIoc_SRCS_vxWorks = -nil- softIoc_LIBS = $(EPICS_BASE_IOC_LIBS) DB += softIocExit.db FINAL_LOCATION ?= $(shell $(PERL) $(TOOLS)/fullPathName.pl $(INSTALL_LOCATION)) CLEANS += epicsInstallDir.h base-7.0.3.1/modules/database/src/std/softIoc/RULES0000664000577000060420000000173113557101274020435 0ustar anjaesctl########################################################################## # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. ########################################################################## # This is a Makefile fragment, see src/ioc/Makefile. softIoc.dbd$(DEP): $(COMMON_DIR)/stdRecords.dbd softIoc.dbd$(DEP): $(COMMON_DIR)/filters.dbd softIoc.dbd$(DEP): $(COMMON_DIR)/links.dbd $(COMMON_DIR)/softIoc.dbd: $(COMMON_DIR)/stdRecords.dbd $(COMMON_DIR)/softIoc.dbd: $(COMMON_DIR)/filters.dbd $(COMMON_DIR)/softIoc.dbd: $(COMMON_DIR)/links.dbd $(COMMON_DIR)/softIoc.dbd: $(STDDIR)/softIoc/Makefile softMain$(DEP): epicsInstallDir.h epicsInstallDir.h: $(TOP)/configure/CONFIG_SITE* $(ECHO) "FINAL_LOCATION=$(FINAL_LOCATION)" $(PERL) $(STDDIR)/softIoc/makeInstallDir.pl "$(FINAL_LOCATION)" > $@ base-7.0.3.1/modules/database/src/std/softIoc/asSub.dbd0000664000577000060420000000007113557101274021344 0ustar anjaesctl# Register access security subroutines registrar(asSub) base-7.0.3.1/modules/database/src/std/softIoc/base.dbd0000664000577000060420000000102513557101274021201 0ustar anjaesctl# This file includes the standard record types and device support # provided by Base and (usually) loaded into all IOCs. # Fixed menus include "menuGlobal.dbd" # Modifyable menus include "menuConvert.dbd" include "menuScan.dbd" # Record types include "stdRecords.dbd" # Channel filters & plugins include "filters.dbd" # Link types include "links.dbd" # Standard device support include "devSoft.dbd" # Access security subroutines include "asSub.dbd" # IOC Core variables include "dbCore.dbd" # RSRV server include "rsrv.dbd" base-7.0.3.1/modules/database/src/std/softIoc/makeInstallDir.pl0000664000577000060420000000155413557101274023063 0ustar anjaesctl#!/usr/bin/env perl #************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* use strict; die "$0: Argument missing, INSTALL_LOCATION\n" if @ARGV == 0; die "$0: Too many arguments, expecting one\n" unless @ARGV == 1; my $path = shift; $path =~ s/\\/\\\\/gx; $path =~ s/^'//; $path =~ s/'$//; print "/* THIS IS A GENERATED FILE. DO NOT EDIT! */\n", "\n", "#ifndef INC_epicsInstallDir_H\n", "#define INC_epicsInstallDir_H\n", "\n", "#define EPICS_BASE \"$path\"\n", "\n", "#endif /* INC_epicsInstallDir_H */\n"; base-7.0.3.1/modules/database/src/std/softIoc/softIocExit.db0000664000577000060420000000047113557101274022367 0ustar anjaesctl# softIocExit.db record(sub,"$(IOC):exit") { field(DESC,"Exit subroutine") field(SCAN,"Passive") field(SNAM,"exit") } record(stringin,"$(IOC):BaseVersion") { field(DESC,"EPICS Base Version") field(DTYP,"getenv") field(INP,"@EPICS_VERSION_FULL") field(PINI,"YES") field(DISP,1) } base-7.0.3.1/modules/database/src/std/softIoc/softMain.cpp0000664000577000060420000002000213557101274022074 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2003 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to the Software License Agreement * found in the file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Andrew Johnson Date: 2003-04-08 */ #include #include #include #include #include #include "registryFunction.h" #include "epicsThread.h" #include "epicsExit.h" #include "epicsStdio.h" #include "epicsString.h" #include "dbStaticLib.h" #include "subRecord.h" #include "dbAccess.h" #include "asDbLib.h" #include "iocInit.h" #include "iocsh.h" #include "osiFileName.h" #include "epicsInstallDir.h" extern "C" int softIoc_registerRecordDeviceDriver(struct dbBase *pdbbase); #ifndef EPICS_BASE // so IDEs knows EPICS_BASE is a string constant # define EPICS_BASE "/" # error -DEPICS_BASE required #endif #define DBD_BASE "dbd/softIoc.dbd" #define EXIT_BASE "db/softIocExit.db" #define DBD_FILE_REL "../../" DBD_BASE #define EXIT_FILE_REL "../../" EXIT_BASE #define DBD_FILE EPICS_BASE "/" DBD_BASE #define EXIT_FILE EPICS_BASE "/" EXIT_BASE namespace { static void exitSubroutine(subRecord *precord) { epicsExitLater((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE); } void usage(const char *arg0, const std::string& base_dbd) { std::cout<<"Usage: "< If used, must come first. Specify the path to the softIoc.dbdfile." " The compile-time install location is saved in the binary as a default.\n" "\n" " -h Print this mesage and exit.\n" "\n" " -S Prevents an interactive shell being started.\n" "\n" " -s Previously caused a shell to be started. Now accepted and ignored.\n" "\n" " -a Access Security configuration file. Macro substitution is\n" " performed.\n" "\n" " -m =,... Set/replace macro definitions used by subsequent -d and\n" " -a.\n" "\n" " -d Load records from file (dbLoadRecords). Macro substitution is\n" " performed.\n" "\n" " -x Load softIocExit.db. Provides a record \":exit\".\n" " Put 0 to exit with success, or non-zero to exit with an error.\n" "\n" "Any number of -m and -d arguments can be interspersed; the macros are applied\n" "to the following .db files. Each later -m option causes earlier macros to be\n" "discarded.\n" "\n" "A st.cmd file is optional. If any databases were loaded the st.cmd file will\n" "be run *after* iocInit. To perform iocsh commands before iocInit, all database\n" "loading must be performed by the script itself, or by the user from the\n" "interactive IOC shell.\n" "\n" "Compiled-in path to softIoc.dbd is:\n" "\t"<.db template is not named *.template add # _TEMPLATE = include $(TOP)/configure/RULES #---------------------------------------- # ADD EXTRA GNUMAKE RULES BELOW HERE base-7.0.3.1/modules/database/src/template/top/exampleApp/Db/_APPNAME_Version.db0000664000577000060420000000025213557101274025707 0ustar anjaesctlrecord(lsi, "$(user):_APPNAME_:version") { field(DTYP, "_APPNAME_ version") field(DESC, "Version string") field(SIZV, "$(SIZV=200)") field(PINI, "YES") } base-7.0.3.1/modules/database/src/template/top/exampleApp/Db/circle.db0000664000577000060420000000334113557101274024225 0ustar anjaesctlrecord(ao, "$(user):circle:step") { field(VAL , "1.0") field(DRVL, "0.0") field(DRVH, "359") field(PINI, "YES") } record(ao, "$(user):circle:period") { field(VAL , "1.0") field(PINI, "YES") field(OUT , "$(user):circle:tick.ODLY NPP") } record(calc, "$(user):circle:angle") { field(PINI, "RUNNING") # bootstrap field(INPA, "$(user):circle:angle NPP") field(INPB, "$(user):circle:step NPP") field(INPD, "360") field(DESC, "Angle") field(EGU , "deg") field(LOLO, "45") field(LOW , "135") field(HIGH, "225") field(HIHI, "315") field(LLSV, "MAJOR") field(LSV , "MINOR") field(HSV , "MINOR") field(HHSV, "MAJOR") field(CALC, "C:=A+B;(C>=D)?C-D:C") field(FLNK, "$(user):circle:x") field(PREC, "3") info(Q:group, { "$(user):circle":{"angle": {+channel:"VAL"}}, "$(user):line":{"a": {+channel:"VAL"}} }) alias("$(user):line:a") } record(calc, "$(user):circle:x") { field(INPA, "$(user):circle:angle NPP") field(CALC, "cos(A*PI/180)") field(TSEL, "$(user):circle:angle.TIME") field(FLNK, "$(user):circle:y") field(PREC, "3") info(Q:group, { "$(user):circle":{"x": {+channel:"VAL"}} }) } record(calc, "$(user):circle:y") { field(INPA, "$(user):circle:angle NPP") field(CALC, "sin(A*PI/180)") field(TSEL, "$(user):circle:angle.TIME") field(PREC, "3") field(FLNK, "$(user):line:b") info(Q:group, { "$(user):circle":{"y": {+channel:"VAL", +trigger:"*"}} }) } record(ai, "$(user):line:b") { field(INP, "$(user):line:a NPP") field(FLNK, "$(user):circle:tick") info(Q:group, { "$(user):line":{"b": {+channel:"VAL", +trigger:"*"}} }) } record(calcout, "$(user):circle:tick") { field(ODLY, "1.0") field(OUT , "$(user):circle:angle.PROC CA") # loop } base-7.0.3.1/modules/database/src/template/top/exampleApp/Db/dbExample1.db0000664000577000060420000000237213557101274024751 0ustar anjaesctlrecord(ai, "$(user):aiExample") { field(DESC, "Analog input") field(INP, "$(user):calcExample.VAL NPP NMS") field(EGUF, "10") field(EGU, "Counts") field(HOPR, "10") field(LOPR, "0") field(HIHI, "8") field(HIGH, "6") field(LOW, "4") field(LOLO, "2") field(HHSV, "MAJOR") field(HSV, "MINOR") field(LSV, "MINOR") field(LLSV, "MAJOR") } record(calc, "$(user):calcExample") { field(DESC, "Counter") field(SCAN,"1 second") field(FLNK, "$(user):aiExample") field(CALC, "(A/dbd DBD += xxxSupport.dbd # Build an IOC support library LIBRARY_IOC += _APPNAME_Support # Compile and add code to the support library _APPNAME_Support_SRCS += xxxRecord.c _APPNAME_Support_SRCS += devXxxSoft.c # Link locally-provided code into the support library, # rather than directly into the IOC application, that # causes problems on Windows DLL builds _APPNAME_Support_SRCS += dbSubExample.c _APPNAME_Support_SRCS += dev_APPNAME_Version.c _APPNAME_Support_SRCS += _APPNAME_Hello.c _APPNAME_Support_SRCS += initTrace.c _APPNAME_Support_LIBS += $(EPICS_BASE_IOC_LIBS) # Auto-generate a header file containing a version string. # Version comes from the VCS if available, else date+time. GENVERSION = _APPNAME_Version.h # Macro name GENVERSIONMACRO = _APPNAME_VERSION # Build the IOC application PROD_IOC = _APPNAME_ # _APPNAME_.dbd will be created and installed DBD += _APPNAME_.dbd # _APPNAME_.dbd will include these files: _APPNAME__DBD += base.dbd _APPNAME__DBD += xxxSupport.dbd _APPNAME__DBD += dbSubExample.dbd _APPNAME__DBD += dev_APPNAME_Version.dbd _APPNAME__DBD += _APPNAME_Hello.dbd _APPNAME__DBD += initTrace.dbd # _APPNAME__registerRecordDeviceDriver.cpp derives from _APPNAME_.dbd _APPNAME__SRCS += _APPNAME__registerRecordDeviceDriver.cpp # Build the main IOC entry point where needed _APPNAME__SRCS_DEFAULT += _APPNAME_Main.cpp _APPNAME__SRCS_vxWorks += -nil- # Link in the code from our support library _APPNAME__LIBS += _APPNAME_Support # To build SNL programs, SNCSEQ must be defined # in the /configure/RELEASE file ifneq ($(SNCSEQ),) # Build sncExample into _APPNAME_Support sncExample_SNCFLAGS += +r _APPNAME__DBD += sncExample.dbd # A .stt sequence program is *not* pre-processed: _APPNAME_Support_SRCS += sncExample.stt _APPNAME_Support_LIBS += seq pv _APPNAME__LIBS += seq pv # Build sncProgram as a standalone program PROD_HOST += sncProgram sncProgram_SNCFLAGS += +m # A .st sequence program *is* pre-processed: sncProgram_SRCS += sncProgram.st sncProgram_LIBS += seq pv sncProgram_LIBS += $(EPICS_BASE_HOST_LIBS) endif # Link QSRV (pvAccess Server) if available ifdef EPICS_QSRV_MAJOR_VERSION _APPNAME__LIBS += qsrv _APPNAME__LIBS += $(EPICS_BASE_PVA_CORE_LIBS) _APPNAME__DBD += PVAServerRegister.dbd _APPNAME__DBD += qsrv.dbd endif # Finally link IOC to the EPICS Base libraries _APPNAME__LIBS += $(EPICS_BASE_IOC_LIBS) include $(TOP)/configure/RULES #---------------------------------------- # ADD EXTRA GNUMAKE RULES BELOW HERE # Explicit dependency needed for generated header file dev_APPNAME_Version$(DEP): $(COMMON_DIR)/$(GENVERSION) base-7.0.3.1/modules/database/src/template/top/exampleApp/src/_APPNAME_Hello.c0000664000577000060420000000156513557101274025434 0ustar anjaesctl/* Example showing how to register a new command with iocsh */ #include #include #include /* This is the command, which the vxWorks shell will call directly */ void hello(const char *name) { if (name) { printf("Hello %s, from _APPNAME_\n", name); } else { puts("Hello from _APPNAME_"); } } /* Information needed by iocsh */ static const iocshArg helloArg0 = {"name", iocshArgString}; static const iocshArg *helloArgs[] = {&helloArg0}; static const iocshFuncDef helloFuncDef = {"hello", 1, helloArgs}; /* Wrapper called by iocsh, selects the argument types that hello needs */ static void helloCallFunc(const iocshArgBuf *args) { hello(args[0].sval); } /* Registration routine, runs at startup */ static void helloRegister(void) { iocshRegister(&helloFuncDef, helloCallFunc); } epicsExportRegistrar(helloRegister); base-7.0.3.1/modules/database/src/template/top/exampleApp/src/_APPNAME_Hello.dbd0000664000577000060420000000003113557101274025726 0ustar anjaesctlregistrar(helloRegister) base-7.0.3.1/modules/database/src/template/top/exampleApp/src/_APPNAME_Main.cpp0000664000577000060420000000063113557101274025606 0ustar anjaesctl/* _APPNAME_Main.cpp */ /* Author: Marty Kraimer Date: 17MAR2000 */ #include #include #include #include #include #include "epicsExit.h" #include "epicsThread.h" #include "iocsh.h" int main(int argc,char *argv[]) { if(argc>=2) { iocsh(argv[1]); epicsThreadSleep(.2); } iocsh(NULL); epicsExit(0); return(0); } base-7.0.3.1/modules/database/src/template/top/exampleApp/src/dbSubExample.c0000664000577000060420000000215213557101274025435 0ustar anjaesctl#include #include #include #include #include #include int mySubDebug; static long mySubInit(subRecord *precord) { if (mySubDebug) printf("Record %s called mySubInit(%p)\n", precord->name, (void*) precord); return 0; } static long mySubProcess(subRecord *precord) { if (mySubDebug) printf("Record %s called mySubProcess(%p)\n", precord->name, (void*) precord); return 0; } static long myAsubInit(aSubRecord *precord) { if (mySubDebug) printf("Record %s called myAsubInit(%p)\n", precord->name, (void*) precord); return 0; } static long myAsubProcess(aSubRecord *precord) { if (mySubDebug) printf("Record %s called myAsubProcess(%p)\n", precord->name, (void*) precord); return 0; } /* Register these symbols for use by IOC code: */ epicsExportAddress(int, mySubDebug); epicsRegisterFunction(mySubInit); epicsRegisterFunction(mySubProcess); epicsRegisterFunction(myAsubInit); epicsRegisterFunction(myAsubProcess); base-7.0.3.1/modules/database/src/template/top/exampleApp/src/dbSubExample.dbd0000664000577000060420000000015513557101274025745 0ustar anjaesctlvariable(mySubDebug) function(mySubInit) function(mySubProcess) function(myAsubInit) function(myAsubProcess) base-7.0.3.1/modules/database/src/template/top/exampleApp/src/devXxxSoft.c0000664000577000060420000000210713557101274025204 0ustar anjaesctl/* devXxxSoft.c */ /* Example device support module */ #include #include #include #include #include "alarm.h" #include "cvtTable.h" #include "dbDefs.h" #include "dbAccess.h" #include "recGbl.h" #include "recSup.h" #include "devSup.h" #include "link.h" #include "xxxRecord.h" #include "epicsExport.h" /*Create the dset for devXxxSoft */ static long init_record(); static long read_xxx(); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_xxx; }devXxxSoft={ 5, NULL, NULL, init_record, NULL, read_xxx, }; epicsExportAddress(dset,devXxxSoft); static long init_record(pxxx) struct xxxRecord *pxxx; { if(recGblInitConstantLink(&pxxx->inp,DBF_DOUBLE,&pxxx->val)) pxxx->udf = FALSE; return(0); } static long read_xxx(pxxx) struct xxxRecord *pxxx; { long status; status = dbGetLink(&(pxxx->inp),DBF_DOUBLE, &(pxxx->val),0,0); /*If return was succesful then set undefined false*/ if(!status) pxxx->udf = FALSE; return(0); } base-7.0.3.1/modules/database/src/template/top/exampleApp/src/dev_APPNAME_Version.c0000664000577000060420000000137113557101274026510 0ustar anjaesctl/* dev_APPNAME_Version.c */ /* Example device support for the lsi (long string input) record * providing the module version string as the value */ #include #include #include #include "devSup.h" #include "lsiRecord.h" #include "_APPNAME_Version.h" /* must be last include */ #include "epicsExport.h" const char version[] = _APPNAME_VERSION; static long read_string(lsiRecord *prec) { size_t N = sizeof version; char *buf = prec->val; if (N > prec->sizv) N = prec->sizv; prec->len = N; memcpy(buf, version, N); buf[N - 1] = '\0'; return 0; } static lsidset dev_CSAFEAPPNAME_Version = { 5, NULL, NULL, NULL, NULL, read_string }; epicsExportAddress(dset,dev_CSAFEAPPNAME_Version); base-7.0.3.1/modules/database/src/template/top/exampleApp/src/dev_APPNAME_Version.dbd0000664000577000060420000000010113557101274027005 0ustar anjaesctldevice(lsi,INST_IO,dev_CSAFEAPPNAME_Version,"_APPNAME_ version") base-7.0.3.1/modules/database/src/template/top/exampleApp/src/initTrace.c0000664000577000060420000000142713557101274025010 0ustar anjaesctl/* initTrace.c */ /* * An initHook routine to trace the iocInit() process. * Prints out the name of each state as it is reached. */ #include #include "initHooks.h" #include "epicsExport.h" #include "iocsh.h" static void trace(initHookState state) { printf("iocInit: Reached %s\n", initHookName(state)); } int traceIocInit(void) { static int done = 0; if (done) return -1; done = 1; initHookRegister(trace); puts("iocInit will be traced"); return 0; } static const iocshFuncDef traceInitFuncDef = {"traceIocInit", 0, NULL}; static void traceInitFunc(const iocshArgBuf *args) { traceIocInit(); } static void initTraceRegister(void) { iocshRegister(&traceInitFuncDef, traceInitFunc); } epicsExportRegistrar(initTraceRegister); base-7.0.3.1/modules/database/src/template/top/exampleApp/src/initTrace.dbd0000664000577000060420000000003513557101274025311 0ustar anjaesctlregistrar(initTraceRegister) base-7.0.3.1/modules/database/src/template/top/exampleApp/src/sncExample.dbd0000664000577000060420000000003713557101274025470 0ustar anjaesctlregistrar(sncExampleRegistrar) base-7.0.3.1/modules/database/src/template/top/exampleApp/src/sncExample.stt0000664000577000060420000000060713557101274025554 0ustar anjaesctlprogram sncExample double v; assign v to "{user}:aiExample"; monitor v; ss ss1 { state init { when (delay(10)) { printf("sncExample: Startup delay over\n"); } state low } state low { when (v > 5.0) { printf("sncExample: Changing to high\n"); } state high } state high { when (v <= 5.0) { printf("sncExample: Changing to low\n"); } state low } } base-7.0.3.1/modules/database/src/template/top/exampleApp/src/sncProgram.st0000664000577000060420000000003513557101274025377 0ustar anjaesctl#include "../sncExample.stt" base-7.0.3.1/modules/database/src/template/top/exampleApp/src/xxxRecord.c0000664000577000060420000001611313557101274025052 0ustar anjaesctl/* xxxRecord.c */ /* Example record support module */ #include #include #include #include #include "epicsMath.h" #include "alarm.h" #include "dbAccess.h" #include "recGbl.h" #include "dbEvent.h" #include "dbDefs.h" #include "dbAccess.h" #include "devSup.h" #include "errMdef.h" #include "recSup.h" #include "special.h" #define GEN_SIZE_OFFSET #include "xxxRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table */ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); #define special NULL #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL static long get_units(DBADDR *, char *); static long get_precision(const DBADDR *, long *); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL static long get_graphic_double(DBADDR *, struct dbr_grDouble *); static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); static long get_alarm_double(DBADDR *, struct dbr_alDouble *); rset xxxRSET={ RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset,xxxRSET); typedef struct xxxset { /* xxx input dset */ long number; DEVSUPFUN dev_report; DEVSUPFUN init; DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ DEVSUPFUN get_ioint_info; DEVSUPFUN read_xxx; }xxxdset; static void checkAlarms(xxxRecord *prec); static void monitor(xxxRecord *prec); static long init_record(struct dbCommon *pcommon, int pass) { xxxRecord *prec = (xxxRecord *)pcommon; xxxdset *pdset; long status; if (pass==0) return(0); if(!(pdset = (xxxdset *)(prec->dset))) { recGblRecordError(S_dev_noDSET,(void *)prec,"xxx: init_record"); return(S_dev_noDSET); } /* must have read_xxx function defined */ if( (pdset->number < 5) || (pdset->read_xxx == NULL) ) { recGblRecordError(S_dev_missingSup,(void *)prec,"xxx: init_record"); return(S_dev_missingSup); } if( pdset->init_record ) { if((status=(*pdset->init_record)(prec))) return(status); } return(0); } static long process(struct dbCommon *pcommon) { xxxRecord *prec = (xxxRecord *)pcommon; xxxdset *pdset = (xxxdset *)(prec->dset); long status; unsigned char pact=prec->pact; if( (pdset==NULL) || (pdset->read_xxx==NULL) ) { prec->pact=TRUE; recGblRecordError(S_dev_missingSup,(void *)prec,"read_xxx"); return(S_dev_missingSup); } /* pact must not be set until after calling device support */ status=(*pdset->read_xxx)(prec); /* check if device support set pact */ if ( !pact && prec->pact ) return(0); prec->pact = TRUE; recGblGetTimeStamp(prec); /* check for alarms */ checkAlarms(prec); /* check event list */ monitor(prec); /* process the forward scan link record */ recGblFwdLink(prec); prec->pact=FALSE; return(status); } static long get_units(DBADDR *paddr, char *units) { xxxRecord *prec=(xxxRecord *)paddr->precord; strncpy(units,prec->egu,DB_UNITS_SIZE); return(0); } static long get_precision(const DBADDR *paddr, long *precision) { xxxRecord *prec=(xxxRecord *)paddr->precord; *precision = prec->prec; if(paddr->pfield == (void *)&prec->val) return(0); recGblGetPrec(paddr,precision); return(0); } static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd) { xxxRecord *prec=(xxxRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); if(fieldIndex == xxxRecordVAL || fieldIndex == xxxRecordHIHI || fieldIndex == xxxRecordHIGH || fieldIndex == xxxRecordLOW || fieldIndex == xxxRecordLOLO || fieldIndex == xxxRecordHOPR || fieldIndex == xxxRecordLOPR) { pgd->upper_disp_limit = prec->hopr; pgd->lower_disp_limit = prec->lopr; } else recGblGetGraphicDouble(paddr,pgd); return(0); } static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd) { xxxRecord *prec=(xxxRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); if(fieldIndex == xxxRecordVAL || fieldIndex == xxxRecordHIHI || fieldIndex == xxxRecordHIGH || fieldIndex == xxxRecordLOW || fieldIndex == xxxRecordLOLO) { pcd->upper_ctrl_limit = prec->hopr; pcd->lower_ctrl_limit = prec->lopr; } else recGblGetControlDouble(paddr,pcd); return(0); } static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad) { xxxRecord *prec=(xxxRecord *)paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); if(fieldIndex == xxxRecordVAL) { pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN; pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN; pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN; pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN; } else recGblGetAlarmDouble(paddr,pad); return(0); } static void checkAlarms(xxxRecord *prec) { double val, hyst, lalm; float hihi, high, low, lolo; unsigned short hhsv, llsv, hsv, lsv; if(prec->udf == TRUE ){ recGblSetSevr(prec,UDF_ALARM,prec->udfs); return; } hihi = prec->hihi; lolo = prec->lolo; high = prec->high; low = prec->low; hhsv = prec->hhsv; llsv = prec->llsv; hsv = prec->hsv; lsv = prec->lsv; val = prec->val; hyst = prec->hyst; lalm = prec->lalm; /* alarm condition hihi */ if (hhsv && (val >= hihi || ((lalm==hihi) && (val >= hihi-hyst)))){ if (recGblSetSevr(prec,HIHI_ALARM,prec->hhsv)) prec->lalm = hihi; return; } /* alarm condition lolo */ if (llsv && (val <= lolo || ((lalm==lolo) && (val <= lolo+hyst)))){ if (recGblSetSevr(prec,LOLO_ALARM,prec->llsv)) prec->lalm = lolo; return; } /* alarm condition high */ if (hsv && (val >= high || ((lalm==high) && (val >= high-hyst)))){ if (recGblSetSevr(prec,HIGH_ALARM,prec->hsv)) prec->lalm = high; return; } /* alarm condition low */ if (lsv && (val <= low || ((lalm==low) && (val <= low+hyst)))){ if (recGblSetSevr(prec,LOW_ALARM,prec->lsv)) prec->lalm = low; return; } /* we get here only if val is out of alarm by at least hyst */ prec->lalm = val; return; } static void monitor(xxxRecord *prec) { unsigned short monitor_mask; double delta; monitor_mask = recGblResetAlarms(prec); /* check for value change */ delta = prec->mlst - prec->val; if(delta<0.0) delta = -delta; if (delta > prec->mdel) { /* post events for value change */ monitor_mask |= DBE_VALUE; /* update last value monitored */ prec->mlst = prec->val; } /* check for archive change */ delta = prec->alst - prec->val; if(delta<0.0) delta = -delta; if (delta > prec->adel) { /* post events on value field for archive change */ monitor_mask |= DBE_LOG; /* update last archive value monitored */ prec->alst = prec->val; } /* send out monitors connected to the value field */ if (monitor_mask){ db_post_events(prec,&prec->val,monitor_mask); } return; } base-7.0.3.1/modules/database/src/template/top/exampleApp/src/xxxRecord.dbd0000664000577000060420000000467013557101274025366 0ustar anjaesctlrecordtype(xxx) { include "dbCommon.dbd" field(VAL,DBF_DOUBLE) { prompt("Current EGU Value") promptgroup("40 - Input") asl(ASL0) pp(TRUE) } field(INP,DBF_INLINK) { prompt("Input Specification") promptgroup("40 - Input") special(SPC_NOMOD) interest(1) } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) } field(HOPR,DBF_FLOAT) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) } field(LOPR,DBF_FLOAT) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) } field(HIHI,DBF_FLOAT) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) } field(LOLO,DBF_FLOAT) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) } field(HIGH,DBF_FLOAT) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) } field(LOW,DBF_FLOAT) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { prompt("Lolo Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(HSV,DBF_MENU) { prompt("High Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(LSV,DBF_MENU) { prompt("Low Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { prompt("Alarm Deadband") promptgroup("70 - Alarm") interest(1) } field(ADEL,DBF_DOUBLE) { prompt("Archive Deadband") promptgroup("80 - Display") interest(1) } field(MDEL,DBF_DOUBLE) { prompt("Monitor Deadband") promptgroup("80 - Display") interest(1) } field(LALM,DBF_DOUBLE) { prompt("Last Value Alarmed") special(SPC_NOMOD) interest(3) } field(ALST,DBF_DOUBLE) { prompt("Last Value Archived") special(SPC_NOMOD) interest(3) } field(MLST,DBF_DOUBLE) { prompt("Last Val Monitored") special(SPC_NOMOD) interest(3) } } base-7.0.3.1/modules/database/src/template/top/exampleApp/src/xxxSupport.dbd0000664000577000060420000000010613557101274025612 0ustar anjaesctlinclude "xxxRecord.dbd" device(xxx,CONSTANT,devXxxSoft,"SoftChannel") base-7.0.3.1/modules/database/src/template/top/exampleBoot/Makefile0000664000577000060420000000017113557101274023751 0ustar anjaesctlTOP = .. include $(TOP)/configure/CONFIG DIRS += $(wildcard *ioc*) DIRS += $(wildcard as*) include $(CONFIG)/RULES_DIRS base-7.0.3.1/modules/database/src/template/top/exampleBoot/ioc/Makefile@Common0000664000577000060420000000017413557101274025777 0ustar anjaesctlTOP = ../.. include $(TOP)/configure/CONFIG ARCH = $(EPICS_HOST_ARCH) TARGETS = envPaths include $(TOP)/configure/RULES.ioc base-7.0.3.1/modules/database/src/template/top/exampleBoot/ioc/Makefile@cygwin0000664000577000060420000000017413557101274026047 0ustar anjaesctlTOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ TARGETS = envPaths relPaths.sh include $(TOP)/configure/RULES.ioc base-7.0.3.1/modules/database/src/template/top/exampleBoot/ioc/Makefile@vxWorks0000664000577000060420000000016213557101274026227 0ustar anjaesctlTOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ TARGETS = cdCommands include $(TOP)/configure/RULES.ioc base-7.0.3.1/modules/database/src/template/top/exampleBoot/ioc/Makefile@win320000664000577000060420000000017413557101274025511 0ustar anjaesctlTOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ TARGETS = envPaths dllPath.bat include $(TOP)/configure/RULES.ioc base-7.0.3.1/modules/database/src/template/top/exampleBoot/ioc/Makefile@windows0000664000577000060420000000017413557101274026241 0ustar anjaesctlTOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ TARGETS = envPaths dllPath.bat include $(TOP)/configure/RULES.ioc base-7.0.3.1/modules/database/src/template/top/exampleBoot/ioc/README@Common0000664000577000060420000000055613557101274025223 0ustar anjaesctlTo start the ioc from this directory execute the command ../../bin/_ARCH_/ st.cmd Alternatively make the st.cmd file directly executable with chmod +x st.cmd 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. base-7.0.3.1/modules/database/src/template/top/exampleBoot/ioc/README@RTEMS0000664000577000060420000000033513557101274024660 0ustar anjaesctlCopy the startup script (st.cmd) and top level db and dbd directories and contents to <>/epics/<>/ Then load the executable into the IOC (floppy disk, network boot, debugger, etc.) and start it. base-7.0.3.1/modules/database/src/template/top/exampleBoot/ioc/README@vxWorks0000664000577000060420000000000013557101274025436 0ustar anjaesctlbase-7.0.3.1/modules/database/src/template/top/exampleBoot/ioc/st.cmd@Common0000664000577000060420000000121413557101274025446 0ustar anjaesctl#!../../bin/_ARCH_/_APPNAME_ #- You may have to change _APPNAME_ to something else #- everywhere it appears in this file < envPaths cd "${TOP}" ## Register all support components dbLoadDatabase "dbd/_APPNAME_.dbd" _CSAFEAPPNAME__registerRecordDeviceDriver pdbbase ## Load record instances dbLoadTemplate "db/user.substitutions" dbLoadRecords "db/_APPNAME_Version.db", "user=_USER_" dbLoadRecords "db/dbSubExample.db", "user=_USER_" #- Set this to see messages from mySub #var mySubDebug 1 #- Run this to trace the stages of iocInit #traceIocInit cd "${TOP}/iocBoot/${IOC}" iocInit ## Start any sequence programs #seq sncExample, "user=_USER_" base-7.0.3.1/modules/database/src/template/top/exampleBoot/ioc/st.cmd@RTEMS0000664000577000060420000000115613557101274025115 0ustar anjaesctl#- Example RTEMS startup script #- You may have to change _APPNAME_ to something else #- everywhere it appears in this file #< envPaths ## Register all support components dbLoadDatabase("dbd/_APPNAME_.dbd") _CSAFEAPPNAME__registerRecordDeviceDriver(pdbbase) ## Load record instances dbLoadTemplate("db/user.substitutions") dbLoadRecords("db/_APPNAME_Version.db", "user=_USER_") dbLoadRecords("db/dbSubExample.db", "user=_USER_") #- Set this to see messages from mySub #var mySubDebug 1 #- Run this to trace the stages of iocInit #traceIocInit iocInit ## Start any sequence programs #seq(sncExample, "user=_USER_") base-7.0.3.1/modules/database/src/template/top/exampleBoot/ioc/st.cmd@vxWorks0000664000577000060420000000153513557101274025707 0ustar anjaesctl#- Example vxWorks startup file #- The following is needed if your board support package doesn't at boot time #- automatically cd to the directory containing its startup script #cd "_TOP_/iocBoot/_IOC_" < cdCommands #< ../nfsCommands cd topbin #- You may have to change _APPNAME_ to something else #- everywhere it appears in this file ld 0,0, "_APPNAME_.munch" ## Register all support components cd top dbLoadDatabase "dbd/_APPNAME_.dbd" _CSAFEAPPNAME__registerRecordDeviceDriver pdbbase ## Load record instances dbLoadTemplate "db/user.substitutions" dbLoadRecords "db/_APPNAME_Version.db", "user=_USER_" dbLoadRecords "db/dbSubExample.db", "user=_USER_" #- Set this to see messages from mySub #mySubDebug = 1 #- Run this to trace the stages of iocInit #traceIocInit cd startup iocInit ## Start any sequence programs #seq &sncExample, "user=_USER_" base-7.0.3.1/modules/database/src/template/top/exampleBoot/nfsCommands@RTEMS0000664000577000060420000000143213557101274025420 0ustar anjaesctl#- Instructions for creating and using a real nfsCommands file #- #- in order to use nfs do the following: #- 1) Create hostAdd and nfsMount commands for each nfs server #- 2) In each st.cmd file add the following two commands BEFORE any load commands #- ../nfs.cmd #- cd " #- #- The hostAdd command has the form: #- hostAdd("","xxx.xxx.xxx.xxx") #- #- You can also mount subdirectories as follows: #- nfsMount("", "/xxx/xxx/xxx", "/xxx") #- #- For example assume #- #- host is mercury with inet address 155.77.2.56 #- You want to mount the directory (which is a file system of mercury) #- /home/mercury5/iocinfo #- as #- /iocinfo #- #- The commands would be #- #- hostAdd("mercury","155.77.2.56") #- nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo") base-7.0.3.1/modules/database/src/template/top/exampleBoot/nfsCommands@vxWorks0000664000577000060420000000153413557101274026214 0ustar anjaesctl#- Instructions for creating and using a real nfsCommands file #- #- in order to use nfs do the following: #- 1) Create hostAdd and nfsMount commands for each nfs server #- 2) In each st.cmd file add the following two commands BEFORE any load commands #- ../nfs.cmd #- cd " #- #- The hostAdd command has the form: #- hostAdd("","xxx.xxx.xxx.xxx") #- #- The nfsMount command has the form: #- nfsMount("", "/xxx/xxx/xxx", "/xxx") #- #- You can also mount subdirectories as follows: #- nfsMountAll("") #- #- For example assume #- #- host is mercury with inet address 155.77.2.56 #- You want to mount the directory (which is a file system of mercury) #- /home/mercury5/iocinfo #- as #- /iocinfo #- #- The commands would be #- #- hostAdd("mercury","155.77.2.56") #- nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo") base-7.0.3.1/modules/database/src/template/top/iocApp/Db/Makefile0000664000577000060420000000105413557101274023233 0ustar anjaesctlTOP=../.. include $(TOP)/configure/CONFIG #---------------------------------------- # ADD MACRO DEFINITIONS AFTER THIS LINE #---------------------------------------------------- # Create and install (or just install) into /db # databases, templates, substitutions like this #DB += xxx.db #---------------------------------------------------- # If .db template is not named *.template add # _template = include $(TOP)/configure/RULES #---------------------------------------- # ADD RULES AFTER THIS LINE base-7.0.3.1/modules/database/src/template/top/iocApp/Makefile0000664000577000060420000000046013557101274022706 0ustar anjaesctlTOP = .. include $(TOP)/configure/CONFIG DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*)) DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*)) DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*)) DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*)) include $(TOP)/configure/RULES_DIRS base-7.0.3.1/modules/database/src/template/top/iocApp/src/Makefile0000664000577000060420000000214013557101274023472 0ustar anjaesctlTOP=../.. include $(TOP)/configure/CONFIG #---------------------------------------- # ADD MACRO DEFINITIONS AFTER THIS LINE #============================= #============================= # Build the IOC application PROD_IOC = _APPNAME_ # _APPNAME_.dbd will be created and installed DBD += _APPNAME_.dbd # _APPNAME_.dbd will be made up from these files: _APPNAME__DBD += base.dbd # Include dbd files from all support applications: #_APPNAME__DBD += xxx.dbd # Add all the support libraries needed by this IOC #_APPNAME__LIBS += xxx # _APPNAME__registerRecordDeviceDriver.cpp derives from _APPNAME_.dbd _APPNAME__SRCS += _APPNAME__registerRecordDeviceDriver.cpp # Build the main IOC entry point on workstation OSs. _APPNAME__SRCS_DEFAULT += _APPNAME_Main.cpp _APPNAME__SRCS_vxWorks += -nil- # Add support from base/src/vxWorks if needed #_APPNAME__OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary # Finally link to the EPICS Base libraries _APPNAME__LIBS += $(EPICS_BASE_IOC_LIBS) #=========================== include $(TOP)/configure/RULES #---------------------------------------- # ADD RULES AFTER THIS LINE base-7.0.3.1/modules/database/src/template/top/iocApp/src/_APPNAME_Main.cpp0000664000577000060420000000063113557101274024725 0ustar anjaesctl/* _APPNAME_Main.cpp */ /* Author: Marty Kraimer Date: 17MAR2000 */ #include #include #include #include #include #include "epicsExit.h" #include "epicsThread.h" #include "iocsh.h" int main(int argc,char *argv[]) { if(argc>=2) { iocsh(argv[1]); epicsThreadSleep(.2); } iocsh(NULL); epicsExit(0); return(0); } base-7.0.3.1/modules/database/src/template/top/iocBoot/Makefile0000664000577000060420000000017113557101274023070 0ustar anjaesctlTOP = .. include $(TOP)/configure/CONFIG DIRS += $(wildcard *ioc*) DIRS += $(wildcard as*) include $(CONFIG)/RULES_DIRS base-7.0.3.1/modules/database/src/template/top/iocBoot/ioc/Makefile@Common0000664000577000060420000000017413557101274025116 0ustar anjaesctlTOP = ../.. include $(TOP)/configure/CONFIG ARCH = $(EPICS_HOST_ARCH) TARGETS = envPaths include $(TOP)/configure/RULES.ioc base-7.0.3.1/modules/database/src/template/top/iocBoot/ioc/Makefile@cygwin0000664000577000060420000000017413557101274025166 0ustar anjaesctlTOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ TARGETS = envPaths relPaths.sh include $(TOP)/configure/RULES.ioc base-7.0.3.1/modules/database/src/template/top/iocBoot/ioc/Makefile@vxWorks0000664000577000060420000000016213557101274025346 0ustar anjaesctlTOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ TARGETS = cdCommands include $(TOP)/configure/RULES.ioc base-7.0.3.1/modules/database/src/template/top/iocBoot/ioc/Makefile@win320000664000577000060420000000017413557101274024630 0ustar anjaesctlTOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ TARGETS = envPaths dllPath.bat include $(TOP)/configure/RULES.ioc base-7.0.3.1/modules/database/src/template/top/iocBoot/ioc/Makefile@windows0000664000577000060420000000017413557101274025360 0ustar anjaesctlTOP = ../.. include $(TOP)/configure/CONFIG ARCH = _ARCH_ TARGETS = envPaths dllPath.bat include $(TOP)/configure/RULES.ioc base-7.0.3.1/modules/database/src/template/top/iocBoot/ioc/st.cmd@Common0000664000577000060420000000065613557101274024576 0ustar anjaesctl#!../../bin/_ARCH_/_APPNAME_ #- You may have to change _APPNAME_ to something else #- everywhere it appears in this file < envPaths cd "${TOP}" ## Register all support components dbLoadDatabase "dbd/_APPNAME_.dbd" _CSAFEAPPNAME__registerRecordDeviceDriver pdbbase ## Load record instances #dbLoadRecords("db/xxx.db","user=_USER_") cd "${TOP}/iocBoot/${IOC}" iocInit ## Start any sequence programs #seq sncxxx,"user=_USER_" base-7.0.3.1/modules/database/src/template/top/iocBoot/ioc/st.cmd@Cross0000664000577000060420000000064713557101274024437 0ustar anjaesctl#!../../bin/_ARCH_/_APPNAME_ #- You may have to change _APPNAME_ to something else #- everywhere it appears in this file #< envPaths ## Register all support components dbLoadDatabase("../../dbd/_APPNAME_.dbd",0,0) _CSAFEAPPNAME__registerRecordDeviceDriver(pdbbase) ## Load record instances dbLoadRecords("../../db/_APPNAME_.db","user=_USER_") iocInit() ## Start any sequence programs #seq snc_APPNAME_,"user=_USER_" base-7.0.3.1/modules/database/src/template/top/iocBoot/ioc/st.cmd@RTEMS0000664000577000060420000000070313557101274024231 0ustar anjaesctl#- Example RTEMS startup script #- You may have to change _APPNAME_ to something else #- everywhere it appears in this file #< envPaths ## Register all support components dbLoadDatabase("dbd/_APPNAME_.dbd") _CSAFEAPPNAME__registerRecordDeviceDriver(pdbbase) ## Load record instances #dbLoadTemplate("db/_APPNAME_.substitutions") #dbLoadRecords("db/_APPNAME_.db", "user=_USER_") iocInit ## Start any sequence programs #seq(sncxxx, "user=_USER_") base-7.0.3.1/modules/database/src/template/top/iocBoot/ioc/st.cmd@vxWorks0000664000577000060420000000126513557101274025026 0ustar anjaesctl#- Example vxWorks startup file #- The following is needed if your board support package doesn't at boot time #- automatically cd to the directory containing its startup script #cd "_TOP_/iocBoot/_IOC_" < cdCommands #< ../nfsCommands cd topbin ## You may have to change _APPNAME_ to something else ## everywhere it appears in this file ld 0,0, "_APPNAME_.munch" ## Register all support components cd top dbLoadDatabase "dbd/_APPNAME_.dbd" _CSAFEAPPNAME__registerRecordDeviceDriver pdbbase ## Load record instances #dbLoadTemplate "db/_APPNAME_.substitutions" #dbLoadRecords "db/_APPNAME_.db", "user=_USER_" cd startup iocInit ## Start any sequence programs #seq &sncxxx, "user=_USER_" base-7.0.3.1/modules/database/src/template/top/iocBoot/nfsCommands@RTEMS0000664000577000060420000000154113557101274024540 0ustar anjaesctl#- Instructions for creating and using a real nfsCommands file #- #- in order to use nfs do the following: #- 1) Create hostAdd and nfsMount commands for each nfs server #- 2) In each st.cmd file add the following two commands BEFORE any load commands #- ../nfs.cmd #- cd " #- #- The hostAdd command has the form: #- hostAdd("","xxx.xxx.xxx.xxx") #- #- The vxWorks nfsMount command has the form: #- nfsMount("") #- #- You can also mount subdirectories as follows: #- nfsMount("", "/xxx/xxx/xxx", "/xxx") #- #- For example assume #- #- host is mercury with inet address 155.77.2.56 #- You want to mount the directory (which is a file system of mercury) #- /home/mercury5/iocinfo #- as #- /iocinfo #- #- The commands would be #- #- hostAdd("mercury","155.77.2.56") #- nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo") base-7.0.3.1/modules/database/src/template/top/iocBoot/nfsCommands@vxWorks0000664000577000060420000000154113557101274025331 0ustar anjaesctl#- Instructions for creating and using a real nfsCommands file #- #- in order to use nfs do the following: #- 1) Create hostAdd and nfsMount commands for each nfs server #- 2) In each st.cmd file add the following two commands BEFORE any load commands #- ../nfs.cmd #- cd " #- #- The hostAdd command has the form: #- hostAdd("","xxx.xxx.xxx.xxx") #- #- The vxWorks nfsMount command has the form: #- nfsMount("") #- #- You can also mount subdirectories as follows: #- nfsMount("", "/xxx/xxx/xxx", "/xxx") #- #- For example assume #- #- host is mercury with inet address 155.77.2.56 #- You want to mount the directory (which is a file system of mercury) #- /home/mercury5/iocinfo #- as #- /iocinfo #- #- The commands would be #- #- hostAdd("mercury","155.77.2.56") #- nfsMount("mercury","/home/mercury5/iocinfo","/iocinfo") base-7.0.3.1/modules/database/src/template/top/supportApp/Db/Makefile0000664000577000060420000000105413557101274024175 0ustar anjaesctlTOP=../.. include $(TOP)/configure/CONFIG #---------------------------------------- # ADD MACRO DEFINITIONS AFTER THIS LINE #---------------------------------------------------- # Create and install (or just install) into /db # databases, templates, substitutions like this #DB += xxx.db #---------------------------------------------------- # If .db template is not named *.template add # _template = include $(TOP)/configure/RULES #---------------------------------------- # ADD RULES AFTER THIS LINE base-7.0.3.1/modules/database/src/template/top/supportApp/Makefile0000664000577000060420000000045713557101274023656 0ustar anjaesctlTOP = .. include $(TOP)/configure/CONFIG DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*)) DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*)) DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*)) DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*)) include $(TOP)/configure/RULES_DIRS base-7.0.3.1/modules/database/src/template/top/supportApp/src/Makefile0000664000577000060420000000122413557101274024436 0ustar anjaesctlTOP=../.. include $(TOP)/configure/CONFIG #---------------------------------------- # ADD MACRO DEFINITIONS AFTER THIS LINE #============================= #================================================== # build a support library LIBRARY_IOC += _APPNAME_ # xxxRecord.h will be created from xxxRecord.dbd #DBDINC += xxxRecord # install _APPNAME_.dbd into /dbd DBD += _APPNAME_.dbd # specify all source files to be compiled and added to the library #_APPNAME__SRCS += xxx _APPNAME__LIBS += $(EPICS_BASE_IOC_LIBS) #=========================== include $(TOP)/configure/RULES #---------------------------------------- # ADD RULES AFTER THIS LINE base-7.0.3.1/modules/database/src/template/top/supportApp/src/_APPNAME_.dbd0000664000577000060420000000024613557101274025033 0ustar anjaesctl# provide definitions such as #include "xxxRecord.dbd" #device(xxx,CONSTANT,devXxxSoft,"SoftChannel") #driver(myDriver) #registrar(myRegistrar) #variable(myVariable) base-7.0.3.1/modules/database/src/tools/DBD.pm0000664000577000060420000000451213557101274017507 0ustar anjaesctlpackage DBD; use strict; use warnings; use DBD::Base; use DBD::Breaktable; use DBD::Driver; use DBD::Link; use DBD::Menu; use DBD::Recordtype; use DBD::Recfield; use DBD::Record; use DBD::Registrar; use DBD::Function; use DBD::Variable; use Carp; sub new { my ($class) = @_; my $this = { 'DBD::Breaktable' => {}, 'DBD::Driver' => {}, 'DBD::Link' => {}, 'DBD::Function' => {}, 'DBD::Menu' => {}, 'DBD::Recordtype' => {}, 'DBD::Record' => {}, 'DBD::Registrar' => {}, 'DBD::Variable' => {}, 'COMMENTS' => [], 'POD' => [] }; bless $this, $class; return $this; } sub add { my ($this, $obj, $obj_name) = @_; my $obj_class = ref $obj; confess "DBD::add: Unknown DBD object type '$obj_class'" unless $obj_class =~ m/^DBD::/ and exists $this->{$obj_class}; $obj_name = $obj->name unless defined $obj_name; if (exists $this->{$obj_class}->{$obj_name}) { return if $obj->equals($this->{$obj_class}->{$obj_name}); dieContext("A different $obj->{WHAT} named '$obj_name' already exists"); } else { $this->{$obj_class}->{$obj_name} = $obj; } } sub add_comment { my $this = shift; push @{$this->{COMMENTS}}, @_; } sub comments { return @{shift->{COMMENTS}}; } sub add_pod { my $this = shift; push @{$this->{POD}}, @_; } sub pod { return @{shift->{POD}}; } sub breaktables { return shift->{'DBD::Breaktable'}; } sub breaktable { my ($this, $name) = @_; return $this->{'DBD::Breaktable'}->{$name}; } sub drivers { return shift->{'DBD::Driver'}; } sub links { return shift->{'DBD::Link'}; } sub functions { return shift->{'DBD::Function'}; } sub menus { return shift->{'DBD::Menu'}; } sub menu { my ($this, $menu_name) = @_; return $this->{'DBD::Menu'}->{$menu_name}; } sub recordtypes { return shift->{'DBD::Recordtype'}; } sub recordtype { my ($this, $rtyp_name) = @_; return $this->{'DBD::Recordtype'}->{$rtyp_name}; } sub records { return shift->{'DBD::Record'}; } sub record { my ($this, $record_name) = @_; return $this->{'DBD::Record'}->{$record_name}; } sub registrars { return shift->{'DBD::Registrar'}; } sub variables { return shift->{'DBD::Variable'}; } 1; base-7.0.3.1/modules/database/src/tools/DBD/Base.pm0000664000577000060420000000724713557101274020371 0ustar anjaesctl# Common utility functions used by the DBD components package DBD::Base; use strict; use warnings; use Carp; require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(&pushContext &popContext &dieContext &warnContext &is_reserved &escapeCcomment &escapeCstring $RXident $RXname $RXuint $RXint $RXhex $RXoct $RXuintx $RXintx $RXnum $RXdqs $RXstr); our $RXident = qr/ [a-zA-Z] [a-zA-Z0-9_]* /x; our $RXnchr = qr/ [a-zA-Z0-9_\-:.\[\]<>;] /x; our $RXname = qr/ $RXnchr+ (?: [{}] $RXnchr+ )* /x; our $RXhex = qr/ (?: 0 [xX] [0-9A-Fa-f]+ ) /x; our $RXoct = qr/ 0 [0-7]* /x; our $RXuint = qr/ [0-9]+ /x; our $RXint = qr/ -? $RXuint /x; our $RXuintx = qr/ ( $RXhex | $RXoct | $RXuint ) /x; our $RXintx = qr/ ( $RXhex | $RXoct | $RXint ) /x; our $RXnum = qr/ -? (?: [0-9]+ | [0-9]* \. [0-9]+ ) (?: [eE] [-+]? [0-9]+ )? /x; our $RXdqs = qr/ " (?> \\. | [^"\\] )* " /x; our $RXstr = qr/ ( $RXname | $RXnum | $RXdqs ) /x; our @context; sub pushContext { my ($ctxt) = @_; unshift @context, $ctxt; } sub popContext { my ($ctxt) = @_; my $pop = shift @context; ($ctxt ne $pop) and dieContext("Leaving context \"$ctxt\", found \"$pop\" instead.", "\tBraces must be closed in the same file they open in."); } sub dieContext { my $msg = join "\n\t", @_; die "$msg\nContext: ", join(' in ', @context), "\n"; } sub warnContext { my $msg = join "\n\t", @_; print STDERR "$msg\nContext: ", join(' in ', @context), "\n"; } # Reserved words from C++ and the DB/DBD file parser my %reserved = map { $_ => undef } qw(and and_eq asm auto bitand bitor bool break case catch char class compl const const_cast continue default delete do double dynamic_cast else enum explicit export extern false float for friend goto if inline int long mutable namespace new not not_eq operator or or_eq private protected public register reinterpret_cast return short signed sizeof static static_cast struct switch template this throw true try typedef typeid typename union unsigned using virtual void volatile wchar_t while xor xor_eq addpath alias breaktable choice device driver field function grecord include info menu path record recordtype registrar variable); sub is_reserved { my $id = shift; return exists $reserved{$id}; } sub identifier { my ($this, $id, $what) = @_; confess "DBD::Base::identifier: $what undefined!" unless defined $id; $id =~ m/^$RXident$/ or dieContext("Illegal $what '$id'", "Identifiers are used in C code so must start with a letter, followed", "by letters, digits and/or underscore characters only."); dieContext("Illegal $what '$id'", "Identifier is a C++ reserved word.") if is_reserved($id); return $id; } # Output filtering sub escapeCcomment { ($_) = @_; s/\*\//**/g; return $_; } sub escapeCstring { ($_) = @_; # FIXME: How to do this? return $_; } # Base methods for the DBD component objects sub new { my $class = shift; my $this = {}; bless $this, $class; return $this->init(@_); } sub init { my ($this, $name, $what) = @_; $this->{NAME} = $this->identifier($name, "$what name"); $this->{WHAT} = $what; return $this; } sub name { return shift->{NAME}; } sub what { return shift->{WHAT}; } sub add_comment { my $this = shift; confess "add_comment() not supported by $this->{WHAT} ($this)\n", "Context: ", join(' in ', @context), "\n"; } sub add_pod { my $this = shift; warnContext "Warning: Pod text inside $this->{WHAT} will be ignored"; } sub equals { my ($a, $b) = @_; return $a->{NAME} eq $b->{NAME} && $a->{WHAT} eq $b->{WHAT}; } 1; base-7.0.3.1/modules/database/src/tools/DBD/Breaktable.pm0000664000577000060420000000217413557101274021545 0ustar anjaesctlpackage DBD::Breaktable; use DBD::Base; our @ISA = qw(DBD::Base); use Carp; use strict; sub init { my ($this, $name) = @_; $this->SUPER::init($name, "breakpoint table"); $this->{POINT_LIST} = []; $this->{COMMENTS} = []; $this->{POD} = []; return $this; } sub add_point { my ($this, $raw, $eng) = @_; confess "DBD::Breaktable::add_point: Raw value undefined!" unless defined $raw; confess "DBD::Breaktable::add_point: Engineering value undefined!" unless defined $eng; push @{$this->{POINT_LIST}}, [$raw, $eng]; } sub points { return @{shift->{POINT_LIST}}; } sub point { my ($this, $idx) = @_; return $this->{POINT_LIST}[$idx]; } sub add_comment { my $this = shift; push @{$this->{COMMENTS}}, @_; } sub comments { return @{shift->{COMMENTS}}; } sub add_pod { my $this = shift; push @{$this->{POD}}, @_; } sub pod { return @{shift->{POD}}; } sub equals { my ($a, $b) = @_; return $a->SUPER::equals($b) && join(',', map "$_->[0]:$_->[1]", @{$a->{POINT_LIST}}) eq join(',', map "$_->[0]:$_->[1]", @{$b->{POINT_LIST}}); } 1; base-7.0.3.1/modules/database/src/tools/DBD/Device.pm0000664000577000060420000000273513557101274020713 0ustar anjaesctlpackage DBD::Device; use DBD::Base; our @ISA = qw(DBD::Base); use strict; my %link_types = ( CONSTANT => qr/$RXnum/, PV_LINK => qr/$RXname \s+ [.NPCAMS ]*/x, JSON_LINK => qr/\{ .* \}/x, VME_IO => qr/\# (?: \s* [CS] \s* $RXintx)* \s* (?: @ .*)?/x, CAMAC_IO => qr/\# (?: \s* [BCNAF] \s* $RXintx)* \s* (?: @ .*)?/x, RF_IO => qr/\# (?: \s* [RMDE] \s* $RXintx)*/x, AB_IO => qr/\# (?: \s* [LACS] \s* $RXintx)* \s* (?: @ .*)?/x, GPIB_IO => qr/\# (?: \s* [LA] \s* $RXintx)* \s* (?: @ .*)?/x, BITBUS_IO => qr/\# (?: \s* [LNPS] \s* $RXuintx)* \s* (?: @ .*)?/x, BBGPIB_IO => qr/\# (?: \s* [LBG] \s* $RXuintx)* \s* (?: @ .*)?/x, VXI_IO => qr/\# (?: \s* [VCS] \s* $RXintx)* \s* (?: @ .*)?/x, INST_IO => qr/@.*/ ); sub init { my ($this, $link_type, $dset, $choice) = @_; dieContext("Unknown link type '$link_type', valid types are:", sort keys %link_types) unless exists $link_types{$link_type}; $this->SUPER::init($dset, "device support (dset)"); $this->{LINK_TYPE} = $link_type; $this->{CHOICE} = $choice; return $this; } sub link_type { return shift->{LINK_TYPE}; } sub choice { return shift->{CHOICE}; } sub legal_addr { my ($this, $addr) = @_; my $rx = $link_types{$this->{LINK_TYPE}}; return $addr =~ m/^ $rx $/x; } sub equals { my ($a, $b) = @_; return $a->SUPER::equals($b) && $a->{LINK_TYPE} eq $b->{LINK_TYPE} && $a->{CHOICE} eq $b->{CHOICE}; } 1; base-7.0.3.1/modules/database/src/tools/DBD/Driver.pm0000664000577000060420000000023513557101274020740 0ustar anjaesctlpackage DBD::Driver; use DBD::Base; our @ISA = qw(DBD::Base); use strict; sub init { return shift->SUPER::init(shift, "driver support (drvet)"); } 1; base-7.0.3.1/modules/database/src/tools/DBD/Function.pm0000664000577000060420000000022113557101274021265 0ustar anjaesctlpackage DBD::Function; use DBD::Base; our @ISA = qw(DBD::Base); use strict; sub init { return shift->SUPER::init(shift, "function"); } 1; base-7.0.3.1/modules/database/src/tools/DBD/Link.pm0000664000577000060420000000056013557101274020403 0ustar anjaesctlpackage DBD::Link; use DBD::Base; our @ISA = qw(DBD::Base); use strict; sub init { my ($this, $name, $jlif) = @_; $this->SUPER::init($jlif, "link support (jlif)"); $this->{KEY} = $name; return $this; } sub key { return shift->{KEY}; } sub equals { my ($a, $b) = @_; return $a->SUPER::equals($b) && $a->{KEY} eq $b->{KEY}; } 1; base-7.0.3.1/modules/database/src/tools/DBD/Menu.pm0000664000577000060420000000426713557101274020422 0ustar anjaesctlpackage DBD::Menu; use DBD::Base; our @ISA = qw(DBD::Base); use strict; sub init { my ($this, $name) = @_; $this->SUPER::init($name, "menu"); $this->{CHOICE_LIST} = []; $this->{CHOICE_INDEX} = {}; $this->{COMMENTS} = []; return $this; } sub add_choice { my ($this, $name, $value) = @_; $name = $this->identifier($name, "Choice name"); foreach my $pair ($this->choices) { dieContext("Duplicate menu choice name '$name'") if ($pair->[0] eq $name); dieContext("Duplicate menu choice string '$value'") if ($pair->[1] eq $value); } push @{$this->{CHOICE_LIST}}, [$name, $value]; $this->{CHOICE_INDEX}->{$value} = $name; } sub choices { return @{shift->{CHOICE_LIST}}; } sub choice { my ($this, $idx) = @_; return $this->{CHOICE_LIST}[$idx]; } sub legal_choice { my ($this, $value) = @_; return exists $this->{CHOICE_INDEX}->{$value}; } sub add_comment { my $this = shift; push @{$this->{COMMENTS}}, @_; } sub comments { return @{shift->{COMMENTS}}; } sub equals { my ($a, $b) = @_; return $a->SUPER::equals($b) && join(',', map "$_->[0]:$_->[1]", @{$a->{CHOICE_LIST}}) eq join(',', map "$_->[0]:$_->[1]", @{$b->{CHOICE_LIST}}); } sub toDeclaration { my $this = shift; my $name = $this->name; my $macro_name = "${name}_NUM_CHOICES"; my @choices = map { sprintf " %-31s /* %s */", @{$_}[0], escapeCcomment(@{$_}[1]); } $this->choices; my $num = scalar @choices; return "#ifndef $macro_name\n" . "typedef enum {\n" . join(",\n", @choices) . "\n} $name;\n" . "#define $macro_name $num\n" . "#endif\n\n"; } sub toDefinition { my $this = shift; my $name = $this->name; my @strings = map { "\t\"" . escapeCstring(@{$_}[1]) . "\"" } $this->choices; return "static const char * const ${name}ChoiceStrings[] = {\n" . join(",\n", @strings) . "\n};\n" . "const dbMenu ${name}MenuMetaData = {\n" . "\t\"" . escapeCstring($name) . "\",\n" . "\t${name}_NUM_CHOICES,\n" . "\t${name}ChoiceStrings\n};\n\n"; } 1; base-7.0.3.1/modules/database/src/tools/DBD/Output.pm0000664000577000060420000000726613557101274021020 0ustar anjaesctlpackage DBD::Output; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(&OutputDBD &OutputDB); use DBD; use DBD::Base; use DBD::Breaktable; use DBD::Device; use DBD::Driver; use DBD::Link; use DBD::Menu; use DBD::Recordtype; use DBD::Recfield; use DBD::Record; use DBD::Registrar; use DBD::Function; use DBD::Variable; sub OutputDBD { my ($out, $dbd) = @_; OutputMenus($out, $dbd->menus); OutputRecordtypes($out, $dbd->recordtypes); OutputDrivers($out, $dbd->drivers); OutputLinks($out, $dbd->links); OutputRegistrars($out, $dbd->registrars); OutputFunctions($out, $dbd->functions); OutputVariables($out, $dbd->variables); OutputBreaktables($out, $dbd->breaktables); } sub OutputDB { my ($out, $dbd) = @_; OutputRecords($out, $dbd->records); } sub OutputMenus { my ($out, $menus) = @_; while (my ($name, $menu) = each %{$menus}) { printf $out "menu(%s) {\n", $name; printf $out " choice(%s, \"%s\")\n", @{$_} foreach $menu->choices; print $out "}\n"; } } sub OutputRecordtypes { my ($out, $recordtypes) = @_; while (my ($name, $recordtype) = each %{$recordtypes}) { printf $out "recordtype(%s) {\n", $name; print $out " %$_\n" foreach $recordtype->cdefs; foreach my $field ($recordtype->fields) { printf $out " field(%s, %s) {\n", $field->name, $field->dbf_type; while (my ($attr, $val) = each %{$field->attributes}) { $val = "\"$val\"" if $val !~ m/^$RXname$/x || $attr eq 'prompt' || $attr eq 'initial'; printf $out " %s(%s)\n", $attr, $val; } print $out " }\n"; } printf $out "}\n"; printf $out "device(%s, %s, %s, \"%s\")\n", $name, $_->link_type, $_->name, $_->choice foreach $recordtype->devices; } } sub OutputDrivers { my ($out, $drivers) = @_; printf $out "driver(%s)\n", $_ foreach keys %{$drivers}; } sub OutputLinks { my ($out, $links) = @_; while (my ($name, $link) = each %{$links}) { printf $out "link(%s, %s)\n", $link->key, $name; } } sub OutputRegistrars { my ($out, $registrars) = @_; printf $out "registrar(%s)\n", $_ foreach keys %{$registrars}; } sub OutputFunctions { my ($out, $functions) = @_; printf $out "function(%s)\n", $_ foreach keys %{$functions}; } sub OutputVariables { my ($out, $variables) = @_; while (my ($name, $variable) = each %{$variables}) { printf $out "variable(%s, %s)\n", $name, $variable->var_type; } } sub OutputBreaktables { my ($out, $breaktables) = @_; while (my ($name, $breaktable) = each %{$breaktables}) { printf $out "breaktable(\"%s\") {\n", $name; printf $out " %s, %s\n", @{$_} foreach $breaktable->points; print $out "}\n"; } } sub OutputRecords { my ($out, $records) = @_; while (my ($name, $rec) = each %{$records}) { next if $name ne $rec->name; # Alias printf $out "record(%s, \"%s\") {\n", $rec->recordtype->name, $name; printf $out " alias(\"%s\")\n", $_ foreach $rec->aliases; foreach my $recfield ($rec->recfields) { my $field_name = $recfield->name; my $value = $rec->get_field($field_name); printf $out " field(%s, \"%s\")\n", $field_name, $value if defined $value; } printf $out " info(\"%s\", \"%s\")\n", $_, $rec->info_value($_) foreach $rec->info_names; print $out "}\n"; } } 1; base-7.0.3.1/modules/database/src/tools/DBD/Parser.pm0000664000577000060420000002574613557101274020757 0ustar anjaesctlpackage DBD::Parser; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(&ParseDBD); use DBD; use DBD::Base; use DBD::Breaktable; use DBD::Device; use DBD::Driver; use DBD::Link; use DBD::Menu; use DBD::Recordtype; use DBD::Recfield; use DBD::Record; use DBD::Registrar; use DBD::Function; use DBD::Variable; our $debug=0; sub ParseDBD { (my $dbd, $_) = @_; while (1) { parseCommon($dbd); if (m/\G menu \s* \( \s* $RXstr \s* \) \s* \{/xgc) { print "Menu: $1\n" if $debug; my ($menu_name) = unquote($1); parse_menu($dbd, $menu_name); } elsif (m/\G driver \s* \( \s* $RXstr \s* \)/xgc) { print "Driver: $1\n" if $debug; my ($driver_name) = unquote($1); $dbd->add(DBD::Driver->new($driver_name)); } elsif (m/\G link \s* \( \s* $RXstr \s*, \s* $RXstr \s* \)/xgc) { print "Link $1, $2\n" if $debug; my ($key, $lset) = unquote($1, $2); $dbd->add(DBD::Link->new($key, $lset)); } elsif (m/\G registrar \s* \( \s* $RXstr \s* \)/xgc) { print "Registrar: $1\n" if $debug; my ($registrar_name) = unquote($1); $dbd->add(DBD::Registrar->new($registrar_name)); } elsif (m/\G function \s* \( \s* $RXstr \s* \)/xgc) { print "Function: $1\n" if $debug; my ($function_name) = unquote($1); $dbd->add(DBD::Function->new($function_name)); } elsif (m/\G breaktable \s* \( \s* $RXstr \s* \) \s* \{/xgc) { print "Breaktable: $1\n" if $debug; my ($breaktable_name) = unquote($1); parse_breaktable($dbd, $breaktable_name); } elsif (m/\G recordtype \s* \( \s* $RXstr \s* \) \s* \{/xgc) { print "Recordtype: $1\n" if $debug; my ($recordtype_name) = unquote($1); parse_recordtype($dbd, $recordtype_name); } elsif (m/\G g?record \s* \( \s* $RXstr \s*, \s* $RXstr \s* \) \s* \{/xgc) { print "Record: $1, $2\n" if $debug; my ($record_type, $record_name) = unquote($1, $2); parse_record($dbd, $record_type, $record_name); } elsif (m/\G alias \s* \( \s* $RXstr \s*, \s* $RXstr \s* \)/xgc) { print "Alias: $1, $2\n" if $debug; my ($record_name, $alias) = unquote($1, $2); my $rec = $dbd->record($record_name); dieContext("Alias '$alias' refers to unknown record '$record_name'") unless defined $rec; dieContext("Can't create alias '$alias', name already used") if defined $dbd->record($alias); $rec->add_alias($alias); $dbd->add($rec, $alias); } elsif (m/\G variable \s* \( \s* $RXstr \s* \)/xgc) { print "Variable: $1\n" if $debug; my ($variable_name) = unquote($1); $dbd->add(DBD::Variable->new($variable_name)); } elsif (m/\G variable \s* \( \s* $RXstr \s* , \s* $RXstr \s* \)/xgc) { print "Variable: $1, $2\n" if $debug; my ($variable_name, $variable_type) = unquote($1, $2); $dbd->add(DBD::Variable->new($variable_name, $variable_type)); } elsif (m/\G device \s* \( \s* $RXstr \s* , \s* $RXstr \s* , \s* $RXstr \s* , \s*$RXstr \s* \)/xgc) { print "Device: $1, $2, $3, $4\n" if $debug; my ($record_type, $link_type, $dset, $choice) = unquote($1, $2, $3, $4); my $rtyp = $dbd->recordtype($record_type); if (!defined $rtyp) { $rtyp = DBD::Recordtype->new($record_type); warn "Device using unknown record type '$record_type', declaration created\n"; $dbd->add($rtyp); } $rtyp->add_device(DBD::Device->new($link_type, $dset, $choice)); } else { last unless m/\G (.*) $/moxgc; dieContext("Syntax error in '$1'"); } } } sub parseCommon { my ($obj) = @_; while (1) { # Skip leading whitespace m/\G \s* /xgc; # Extract POD if (m/\G ( = [a-zA-Z] )/xgc) { # The above regex was split from the one below for performance. # Using m/\G ( = [a-zA-Z] .* ) \n/ is slow in Perl 5.20 and later. my $directive = $1; m/\G ( .* ) \n/xgc; $directive .= $1; $obj->add_pod($directive, parsePod()); } elsif (m/\G \# /xgc) { if (m/\G \# ! BEGIN \{ ( [^}]* ) \} ! \# \# \n/xgc) { print "File-Begin: $1\n" if $debug; pushContext("file '$1'"); } elsif (m/\G \# ! END \{ ( [^}]* ) \} ! \# \# \n?/xgc) { print "File-End: $1\n" if $debug; popContext("file '$1'"); } else { m/\G (.*) \n/xgc; $obj->add_comment($1); print "Comment: $1\n" if $debug; } } else { return; } } } sub unquote { return map { m/^ ("?) (.*) \1 $/x; $2 } @_; } sub parsePod { pushContext("Pod markup"); my @pod; while (1) { if (m/\G ( =cut .* ) \n?/xgc) { popContext("Pod markup"); return @pod; } elsif (m/\G ( .* ) $/xgc) { dieContext("Unexpected end of input file, Pod block not closed"); } elsif (m/\G ( .* ) \n/xgc) { push @pod, $1 } } } sub parse_menu { my ($dbd, $menu_name) = @_; pushContext("menu($menu_name)"); my $menu = DBD::Menu->new($menu_name); while(1) { parseCommon($menu); if (m/\G choice \s* \( \s* $RXstr \s* , \s* $RXstr \s* \)/xgc) { print " Menu-Choice: $1, $2\n" if $debug; my ($choice_name, $value) = unquote($1, $2); $menu->add_choice($choice_name, $value); } elsif (m/\G \}/xgc) { print " Menu-End:\n" if $debug; $dbd->add($menu); popContext("menu($menu_name)"); return; } else { m/\G (.*) $/moxgc or dieContext("Unexpected end of input"); dieContext("Syntax error in '$1'"); } } } sub parse_breaktable { my ($dbd, $breaktable_name) = @_; pushContext("breaktable($breaktable_name)"); my $bt = DBD::Breaktable->new($breaktable_name); while(1) { parseCommon($bt); if (m/\G point\s* \(\s* $RXstr \s* , \s* $RXstr \s* \)/xgc) { print " Breaktable-Point: $1, $2\n" if $debug; my ($raw, $eng) = unquote($1, $2); $bt->add_point($raw, $eng); } elsif (m/\G $RXstr \s* (?: , \s*)? $RXstr (?: \s* ,)?/xgc) { print " Breaktable-Data: $1, $2\n" if $debug; my ($raw, $eng) = unquote($1, $2); $bt->add_point($raw, $eng); } elsif (m/\G \}/xgc) { print " Breaktable-End:\n" if $debug; $dbd->add($bt); popContext("breaktable($breaktable_name)"); return; } else { m/\G (.*) $/moxgc or dieContext("Unexpected end of input"); dieContext("Syntax error in '$1'"); } } } sub parse_recordtype { my ($dbd, $record_type) = @_; pushContext("recordtype($record_type)"); # Re-use a matching declaration record type if one exists my $rtyp = $dbd->recordtype($record_type); if (!defined($rtyp) || $rtyp->fields) { # Earlier record type is not a declaration, don't re-use it $rtyp = DBD::Recordtype->new($record_type); } while(1) { parseCommon($rtyp); if (m/\G field \s* \( \s* $RXstr \s* , \s* $RXstr \s* \) \s* \{/xgc) { print " Recordtype-Field: $1, $2\n" if $debug; my ($field_name, $field_type) = unquote($1, $2); parse_field($rtyp, $field_name, $field_type); } elsif (m/\G % (.*) \n/xgc) { print " Recordtype-Cdef: $1\n" if $debug; $rtyp->add_cdef($1); } elsif (m/\G \}/xgc) { print " Recordtype-End:\n" if $debug; $dbd->add($rtyp); popContext("recordtype($record_type)"); return; } else { m/\G (.*) $/moxgc or dieContext("Unexpected end of input"); dieContext("Syntax error in '$1'"); } } } sub parse_record { my ($dbd, $record_type, $record_name) = @_; pushContext("record($record_type, $record_name)"); my $rtyp = $dbd->recordtype($record_type); my $rec = $dbd->record($record_name); if (defined $rec) { my $otyp = $rec->recordtype; my $otyp_name = $otyp->name; $rtyp = $otyp if $record_type eq '*'; dieContext("A(n) $otyp_name record '$record_name' already exists") unless $otyp == $rtyp; } else { dieContext("No record exists named '$record_name'") if $record_type eq '*'; dieContext("No recordtype exists named '$record_type'") unless defined $rtyp; $rec = DBD::Record->new($rtyp, $record_name); } while (1) { parseCommon($rec); if (m/\G field \s* \( \s* $RXstr \s* , \s* $RXstr \s* \)/xgc) { print " Record-Field: $1, $2\n" if $debug; my ($field_name, $value) = unquote($1, $2); $rec->put_field($field_name, $value); } elsif (m/\G info \s* \( \s* $RXstr \s* , \s* $RXstr \s* \)/xgc) { print " Record-Info: $1, $2\n" if $debug; my ($info_name, $value) = unquote($1, $2); $rec->add_info($info_name, $value); } elsif (m/\G alias \s* \( \s* $RXstr \s* \)/xgc) { print " Record-Alias: $1\n" if $debug; my ($alias) = unquote($1); dieContext("Can't create alias '$alias', name in use") if defined $dbd->record($1); $rec->add_alias($alias); $dbd->add($rec, $alias); } elsif (m/\G \}/xgc) { print " Record-End:\n" if $debug; $dbd->add($rec); popContext("record($record_type, $record_name)"); return; } else { m/\G (.*) $/moxgc or dieContext("Unexpected end of input"); dieContext("Syntax error in '$1'"); } } } sub parse_field { my ($rtyp, $field_name, $field_type) = @_; my $fld = DBD::Recfield->new($field_name, $field_type); pushContext("field($field_name, $field_type)"); while(1) { parseCommon($fld); if (m/\G (\w+) \s* \( \s* $RXstr \s* \)/xgc) { print " Field-Attribute: $1, $2\n" if $debug; my ($attr, $value) = unquote($1, $2); $fld->add_attribute($attr, $value); } elsif (m/\G \}/xgc) { print " Field-End:\n" if $debug; $rtyp->add_field($fld); popContext("field($field_name, $field_type)"); return; } else { m/\G (.*) $/moxgc or dieContext("Unexpected end of input"); dieContext("Syntax error in '$1'"); } } } 1; base-7.0.3.1/modules/database/src/tools/DBD/Recfield.pm0000664000577000060420000003050713557101274021227 0ustar anjaesctlpackage DBD::Recfield; use DBD::Base; our @ISA = qw(DBD::Base); use strict; # The hash value is a regexp that matches all legal values of this field # NB: The regexps are not currently used, and are wrong for some types. our %field_types = ( DBF_STRING => qr/.{0,40}/, DBF_CHAR => $RXintx, DBF_UCHAR => $RXuintx, DBF_SHORT => $RXintx, DBF_USHORT => $RXuintx, DBF_LONG => $RXintx, DBF_ULONG => $RXuintx, DBF_INT64 => $RXintx, DBF_UINT64 => $RXuintx, DBF_FLOAT => $RXnum, DBF_DOUBLE => $RXnum, DBF_ENUM => qr/.*/, DBF_MENU => qr/.*/, DBF_DEVICE => qr/.*/, DBF_INLINK => qr/.*/, DBF_OUTLINK => qr/.*/, DBF_FWDLINK => qr/.*/, DBF_NOACCESS => qr// ); # The hash value is a regexp that matches all legal values of this attribute our %field_attrs = ( asl => qr/^ASL[01]$/, initial => qr/^.*$/, promptgroup => qr/^.*$/, prompt => qr/^.*$/, special => qr/^(?:SPC_\w+|\d{3,})$/, pp => qr/^(?:TRUE|FALSE)$/, interest => qr/^\d+$/, base => qr/^(?:DECIMAL|HEX)$/, size => qr/^\d+$/, extra => qr/^.*$/, menu => qr/^$RXident$/, prop => qr/^(?:YES|NO)$/ ); # Convert old promptgroups into new-style my %promptgroupMap = ( GUI_COMMON => '10 - Common', GUI_ALARMS => '70 - Alarm', GUI_BITS1 => '41 - Bits (1)', GUI_BITS2 => '42 - Bits (2)', GUI_CALC => '30 - Action', GUI_CLOCK => '30 - Action', GUI_COMPRESS => '30 - Action', GUI_CONVERT => '60 - Convert', GUI_DISPLAY => '80 - Display', GUI_HIST => '30 - Action', GUI_INPUTS => '40 - Input', GUI_LINKS => '40 - Link', GUI_MBB => '30 - Action', GUI_MOTOR => '30 - Action', GUI_OUTPUT => '50 - Output', GUI_PID => '30 - Action', GUI_PULSE => '30 - Action', GUI_SELECT => '40 - Input', GUI_SEQ1 => '51 - Output (1)', GUI_SEQ2 => '52 - Output (2)', GUI_SEQ3 => '53 - Output (3)', GUI_SUB => '30 - Action', GUI_TIMER => '30 - Action', GUI_WAVE => '30 - Action', GUI_SCAN => '20 - Scan', ); sub new { my ($class, $name, $type) = @_; dieContext("Illegal field type '$type', valid field types are:", sort keys %field_types) unless exists $field_types{$type}; my $this = {}; bless $this, "${class}::${type}"; return $this->init($name, $type); } sub init { my ($this, $name, $type) = @_; $this->SUPER::init($name, "record field"); dieContext("Illegal field type '$type', valid field types are:", sort keys %field_types) unless exists $field_types{$type}; $this->{DBF_TYPE} = $type; $this->{ATTR_INDEX} = {}; $this->{COMMENTS} = []; return $this; } sub dbf_type { return shift->{DBF_TYPE}; } sub set_number { my ($this, $number) = @_; $this->{NUMBER} = $number; } sub number { return shift->{NUMBER}; } sub add_attribute { my ($this, $attr, $value) = @_; $value = $promptgroupMap{$value} if $attr eq 'promptgroup' && exists $promptgroupMap{$value}; my $match = $field_attrs{$attr}; if (defined $match) { dieContext("Bad value '$value' for field attribute '$attr'") unless $value =~ m/$match/; } else { warnContext("Unknown field attribute '$attr' with value '$value'; " . "known attributes are:", join(", ", sort keys %field_attrs)); } $this->{ATTR_INDEX}->{$attr} = $value; } sub attributes { return shift->{ATTR_INDEX}; } sub attribute { my ($this, $attr) = @_; return $this->attributes->{$attr}; } sub equals { my ($l, $r) = @_; return 1 if $l eq $r; return 0 if $l->{NAME} ne $r->{NAME} || $l->{DBF_TYPE} ne $r->{DBF_TYPE}; my ($la, $ra) = ($l->{ATTR_INDEX}, $r->{ATTR_INDEX}); my @keys = sort keys %$la; return 0 if join(',', @keys) ne join(',', sort keys %$ra); foreach my $k (@keys) { return 0 if $la->{$k} ne $ra->{$k}; } return 1; } sub check_valid { my ($this) = @_; my $name = $this->name; my $default = $this->attribute("initial"); dieContext("Default value '$default' is invalid for field '$name'") if (defined($default) and !$this->legal_value($default)); } sub add_comment { my $this = shift; push @{$this->{COMMENTS}}, @_; } sub comments { return @{shift->{COMMENTS}}; } # The C structure member name is usually the field name converted to # lower-case. However if that is a reserved word, use the original. sub C_name { my ($this) = @_; my $name = lc $this->name; $name = $this->name if is_reserved($name); return $name; } sub toDeclaration { my ($this, $ctype) = @_; my $name = $this->C_name; my $result = sprintf " %-19s %-12s", $ctype, "$name;"; my $prompt = $this->attribute('prompt'); $result .= "/* $prompt */" if defined $prompt; return $result; } ################################################################################ package DBD::Recfield::DBF_STRING; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { my ($this, $value) = @_; return (length $value < $this->attribute('size')); # NB - we use '<' to allow space for the terminating nil byte } sub check_valid { my ($this) = @_; my $name = $this->name; dieContext("Size missing for DBF_STRING field '$name'") unless exists $this->attributes->{'size'}; $this->SUPER::check_valid; } sub toDeclaration { my ($this) = @_; my $name = lc $this->name; my $size = $this->attribute('size'); my $result = sprintf " %-19s %-12s", 'char', "${name}[${size}];"; my $prompt = $this->attribute('prompt'); $result .= "/* $prompt */" if defined $prompt; return $result; } ################################################################################ package DBD::Recfield::DBF_CHAR; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { my ($this, $value) = @_; $value =~ s/^ ( $RXhex | $RXoct ) $/ oct($1) /xe; return ($value =~ m/^ $RXint $/x and $value >= -128 and $value <= 127); } sub toDeclaration { return shift->SUPER::toDeclaration("epicsInt8"); } ################################################################################ package DBD::Recfield::DBF_UCHAR; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { my ($this, $value) = @_; $value =~ s/^ ( $RXhex | $RXoct ) $/ oct($1) /xe; return ($value =~ m/^ $RXuint $/x and $value >= 0 and $value <= 255); } sub toDeclaration { return shift->SUPER::toDeclaration("epicsUInt8"); } ################################################################################ package DBD::Recfield::DBF_SHORT; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { my ($this, $value) = @_; $value =~ s/^ ( $RXhex | $RXoct ) $/ oct($1) /xe; return ($value =~ m/^ $RXint $/x and $value >= -32768 and $value <= 32767); } sub toDeclaration { return shift->SUPER::toDeclaration("epicsInt16"); } ################################################################################ package DBD::Recfield::DBF_USHORT; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { my ($this, $value) = @_; $value =~ s/^ ( $RXhex | $RXoct ) $/ oct($1) /xe; return ($value =~ m/^ $RXuint $/x and $value >= 0 and $value <= 65535); } sub toDeclaration { return shift->SUPER::toDeclaration("epicsUInt16"); } ################################################################################ package DBD::Recfield::DBF_LONG; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { my ($this, $value) = @_; $value =~ s/^ ( $RXhex | $RXoct ) $/ oct($1) /xe; return ($value =~ m/^ $RXint $/x); } sub toDeclaration { return shift->SUPER::toDeclaration("epicsInt32"); } ################################################################################ package DBD::Recfield::DBF_ULONG; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { my ($this, $value) = @_; $value =~ s/^ ( $RXhex | $RXoct ) $/ oct($1) /xe; return ($value =~ m/^ $RXuint $/x and $value >= 0); } sub toDeclaration { return shift->SUPER::toDeclaration("epicsUInt32"); } ################################################################################ package DBD::Recfield::DBF_INT64; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { my ($this, $value) = @_; $value =~ s/^ ( $RXhex | $RXoct ) $/ oct($1) /xe; return ($value =~ m/^ $RXint $/x); } sub toDeclaration { return shift->SUPER::toDeclaration("epicsInt64"); } ################################################################################ package DBD::Recfield::DBF_UINT64; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { my ($this, $value) = @_; $value =~ s/^ ( $RXhex | $RXoct ) $/ oct($1) /xe; return ($value =~ m/^ $RXuint $/x and $value >= 0); } sub toDeclaration { return shift->SUPER::toDeclaration("epicsUInt64"); } ################################################################################ package DBD::Recfield::DBF_FLOAT; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { my ($this, $value) = @_; return ($value =~ m/^ $RXnum $/x); } sub toDeclaration { return shift->SUPER::toDeclaration("epicsFloat32"); } ################################################################################ package DBD::Recfield::DBF_DOUBLE; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { my ($this, $value) = @_; return ($value =~ m/^ $RXnum $/x); } sub toDeclaration { return shift->SUPER::toDeclaration("epicsFloat64"); } ################################################################################ package DBD::Recfield::DBF_ENUM; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { return 1; } sub toDeclaration { return shift->SUPER::toDeclaration("epicsEnum16"); } ################################################################################ package DBD::Recfield::DBF_MENU; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { # FIXME: If we know the menu name and the menu exists, check further return 1; } sub check_valid { my ($this) = @_; my $name = $this->name; dieContext("Menu name missing for DBF_MENU field '$name'") unless defined($this->attribute("menu")); $this->SUPER::check_valid; } sub toDeclaration { return shift->SUPER::toDeclaration("epicsEnum16"); } ################################################################################ package DBD::Recfield::DBF_DEVICE; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { return 1; } sub toDeclaration { return shift->SUPER::toDeclaration("epicsEnum16"); } ################################################################################ package DBD::Recfield::DBF_INLINK; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { return 1; } sub toDeclaration { return shift->SUPER::toDeclaration("DBLINK"); } ################################################################################ package DBD::Recfield::DBF_OUTLINK; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { return 1; } sub toDeclaration { return shift->SUPER::toDeclaration("DBLINK"); } ################################################################################ package DBD::Recfield::DBF_FWDLINK; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { return 1; } sub toDeclaration { return shift->SUPER::toDeclaration("DBLINK"); } ################################################################################ package DBD::Recfield::DBF_NOACCESS; use DBD::Base; our @ISA = qw(DBD::Recfield); sub legal_value { my ($this, $value) = @_; return ($value eq ''); } sub check_valid { my ($this) = @_; my $name = $this->name; dieContext("Type information missing for DBF_NOACCESS field '$name'") unless defined($this->attribute("extra")); $this->SUPER::check_valid; } sub toDeclaration { my ($this) = @_; my $extra = $this->attribute('extra'); my $result = sprintf " %-31s ", "$extra;"; my $prompt = $this->attribute('prompt'); $result .= "/* $prompt */" if defined $prompt; return $result; } 1; base-7.0.3.1/modules/database/src/tools/DBD/Record.pm0000664000577000060420000000544413557101274020732 0ustar anjaesctlpackage DBD::Record; use strict; use warnings; use DBD::Base; our @ISA = qw(DBD::Base); use Carp; our ($macrosOk); my $warned; sub init { my ($this, $type, $name) = @_; confess "DBD::Record::init: Not a DBD::Recordtype" unless $type->isa('DBD::Recordtype'); $this->SUPER::init($name, "record"); $this->{RECORD_TYPE} = $type; $this->{ALIASES} = []; $this->{RECFIELD_LIST} = []; $this->{FIELD_INDEX} = {}; $this->{INFO_LIST} = []; $this->{INFO_ITEMS} = {}; $this->{COMMENTS} = []; $this->{POD} = []; return $this; } # Override, record names are not as strict as recordtype and menu names sub identifier { my ($this, $id, $what) = @_; confess "DBD::Record::identifier: $what undefined!" unless defined $id; if ($macrosOk) { # FIXME - Check name with macro } elsif ($id !~ m/^$RXname$/) { my @message; push @message, "A $what should contain only letters, digits and these", "special characters: _ - : . [ ] < > ;" unless $warned++; warnContext("Deprecated $what '$id'", @message); } return $id; } sub recordtype { return shift->{RECORD_TYPE}; } sub add_alias { my ($this, $alias) = @_; push @{$this->{ALIASES}}, $this->identifier($alias, "alias name"); } sub aliases { return @{shift->{ALIASES}}; } sub put_field { my ($this, $field_name, $value) = @_; my $recfield = $this->{RECORD_TYPE}->field($field_name); dieContext("No field named '$field_name'") unless defined $recfield; dieContext("Can't set $field_name to '$value'") unless $recfield->legal_value($value); push @{$this->{RECFIELD_LIST}}, $recfield unless exists $this->{FIELD_INDEX}->{$field_name}; $this->{FIELD_INDEX}->{$field_name} = $value; } sub recfields { return @{shift->{RECFIELD_LIST}}; } sub field_names { # In their original order... return map {$_->name} @{shift->{RECFIELD_LIST}}; } sub get_field { my ($this, $field_name) = @_; return $this->{FIELD_INDEX}->{$field_name} if exists $this->{FIELD_INDEX}->{$field_name}; my $recfield = $this->{RECORD_TYPE}->field($field_name); return $recfield->attribute("initial"); } sub add_info { my ($this, $info_name, $value) = @_; push @{$this->{INFO_LIST}}, $info_name unless exists $this->{INFO_ITEMS}->{$info_name}; $this->{INFO_ITEMS}->{$info_name} = $value; } sub info_names { return @{shift->{INFO_LIST}}; } sub info_value { my ($this, $info_name) = @_; return $this->{INFO_ITEMS}->{$info_name}; } sub add_comment { my ($this, $comment) = @_; push @{$this->{COMMENTS}}, $comment; } sub comments { return @{shift->{COMMENTS}}; } sub add_pod { my $this = shift; push @{$this->{POD}}, @_; } sub pod { return @{shift->{POD}}; } 1; base-7.0.3.1/modules/database/src/tools/DBD/Recordtype.pm0000664000577000060420000000627513557101274021637 0ustar anjaesctlpackage DBD::Recordtype; use DBD::Base; our @ISA = qw(DBD::Base); use Carp; use strict; sub init { my ($this, $name) = @_; $this->SUPER::init($name, "record type"); $this->{FIELD_LIST} = []; $this->{FIELD_INDEX} = {}; $this->{DEVICE_LIST} = []; $this->{DEVICE_INDEX} = {}; $this->{CDEFS} = []; $this->{COMMENTS} = []; $this->{POD} = []; return $this; } sub add_field { my ($this, $field) = @_; confess "DBD::Recordtype::add_field: Not a DBD::Recfield" unless $field->isa('DBD::Recfield'); my $field_name = $field->name; dieContext("Duplicate field name '$field_name'") if exists $this->{FIELD_INDEX}->{$field_name}; $field->check_valid; $field->set_number(scalar @{$this->{FIELD_LIST}}); push @{$this->{FIELD_LIST}}, $field; $this->{FIELD_INDEX}->{$field_name} = $field; } sub fields { return @{shift->{FIELD_LIST}}; } sub field_names { # In their original order... return map {$_->name} @{shift->{FIELD_LIST}}; } sub field { my ($this, $field_name) = @_; return $this->{FIELD_INDEX}->{$field_name}; } sub add_device { my ($this, $device) = @_; confess "DBD::Recordtype::add_device: Not a DBD::Device" unless $device->isa('DBD::Device'); my $choice = $device->choice; if (exists $this->{DEVICE_INDEX}->{$choice}) { return if $device->equals($this->{DEVICE_INDEX}->{$choice}); my @warning = ("Two $this->{NAME} device supports '$choice' conflict"); my $old = $this->{DEVICE_INDEX}->{$choice}; push @warning, "Link types differ" if $old->link_type ne $device->link_type; push @warning, "DSETs differ" if $old->name ne $device->name; dieContext(@warning); } push @{$this->{DEVICE_LIST}}, $device; $this->{DEVICE_INDEX}->{$choice} = $device; } sub devices { return @{shift->{DEVICE_LIST}}; } sub device { my ($this, $choice) = @_; return $this->{DEVICE_INDEX}->{$choice}; } sub add_comment { my ($this, $comment) = @_; push @{$this->{COMMENTS}}, $comment; } sub comments { return @{shift->{COMMENTS}}; } sub add_cdef { my ($this, $cdef) = @_; push @{$this->{CDEFS}}, $cdef; } sub cdefs { return @{shift->{CDEFS}}; } sub toCdefs { return join("\n", shift->cdefs) . "\n\n"; } sub add_pod { my $this = shift; push @{$this->{POD}}, @_; } sub pod { return @{shift->{POD}}; } sub equals { my ($new, $known) = @_; return 1 if $new eq $known; return 0 if $new->{NAME} ne $known->{NAME}; return 1 if ! $new->fields; # Later declarations always match # NB: Definition after declaration is handled in parse_recordtype() my @nf = @{$new->{FIELD_LIST}}; my @kf = @{$known->{FIELD_LIST}}; return 0 if scalar @nf != scalar @kf; while (@nf) { my $nf = shift @nf; my $kf = shift @kf; return 0 if ! $nf->equals($kf); } return 1; } sub toDeclaration { my $this = shift; my @fields = map { $_->toDeclaration } $this->fields; my $name = $this->name; $name .= "Record" unless $name eq "dbCommon"; return "typedef struct $name {\n" . join("\n", @fields) . "\n} $name;\n\n"; } 1; base-7.0.3.1/modules/database/src/tools/DBD/Registrar.pm0000664000577000060420000000023413557101274021446 0ustar anjaesctlpackage DBD::Registrar; use DBD::Base; our @ISA = qw(DBD::Base); use strict; sub init { return shift->SUPER::init(shift, "registrar function"); } 1; base-7.0.3.1/modules/database/src/tools/DBD/Variable.pm0000664000577000060420000000146313557101274021236 0ustar anjaesctlpackage DBD::Variable; use DBD::Base; our @ISA = qw(DBD::Base); use strict; my %valid_types = ( # C type name => corresponding iocshArg type identifier int => 'iocshArgInt', double => 'iocshArgDouble' ); sub init { my ($this, $name, $type) = @_; $type = "int" unless defined $type; exists $valid_types{$type} or dieContext("Unknown variable type '$type', valid types are:", sort keys %valid_types); $this->SUPER::init($name, "variable"); $this->{VAR_TYPE} = $type; return $this; } sub var_type { my $this = shift; return $this->{VAR_TYPE}; } sub iocshArg_type { my $this = shift; return $valid_types{$this->{VAR_TYPE}}; } sub equals { my ($a, $b) = @_; return $a->SUPER::equals($b) && $a->{VAR_TYPE} eq $b->{VAR_TYPE}; } 1; base-7.0.3.1/modules/database/src/tools/EPICS/IOC.pm0000664000577000060420000003022713557101274020375 0ustar anjaesctl###################################################################### # EPICS BASE is distributed subject to a Software License Agreement # found in file LICENSE that is included with this distribution. # # Original Author: Shantha Condamoor, SLAC # Creation Date: 1-Sep-2011 # Current Author: Andrew Johnson use strict; use warnings; package EPICS::IOC; require 5.010; =head1 NAME EPICS::IOC - Manage an EPICS IOC =head1 SYNOPSIS use EPICS::IOC; my $ioc = EPICS::IOC->new; $ioc->debug(1); # Show IOC stdio streams $ioc->start('bin/@ARCH@/ioc', 'iocBoot/ioc/st.cmd'); $ioc->cmd; # Wait for the iocsh prompt my @records = $ioc->dbl; my @values = map { $ioc->dbgf($_); } @records; $ioc->kill; =head1 DESCRIPTION This module provides an object-oriented API for starting, interacting with and stopping one or more EPICS IOCs under program control, and is generally intended for writing test programs. The IOC should not be configured to emit unsolicited messages on stdout as this could interfere with the ability of the software to detect an end-of-command from the IOC shell, which is achieved by setting the prompt to a known string (normally C<__END__>). Unsolicited messages on stderr will not cause problems, but can't be seen on Windows systems. =head1 CONSTRUCTOR =over 4 =cut use Symbol 'gensym'; use IPC::Open3; use IO::Select; =item new () Calling C creates an C object that can be used to start and interact with a single IOC. After this IOC has been shut down (by calling its C method) the C object may be reused for another IOC. =back =cut sub new { my $proto = shift; my $class = ref $proto || $proto; my $self = { pid => undef, stdin => gensym, stdout => gensym, stderr => gensym, select => IO::Select->new(), errbuf => '', debug => 0, terminator => '__END__' }; bless $self, $class; } =head1 METHODS =over 4 =item debug ( [FLAG] ) Each C object has its own debug flag which when non-zero causes all IOC console traffic sent or read by other methods to be printed to stdout along with the IOC's pid and a direction indicator. The C method optionally sets and returns the value of this flag. The optional FLAG is treated as a true/false value. If provided this sets the debug flag value. The method's return value is the current (new if given) value of the flag. =cut sub debug { my $self = shift; $self->{debug} = shift if scalar @_; return $self->{debug}; } =item start ( EXECUTABLE [, ARGUMENTS ...] ) Launch an IOC binary given by EXECUTABLE with ARGUMENTS. The method dies if it can't run the program as given, or if the IOC is already running. In most cases the C method should be called next with no command string, which waits for the IOC's boot process to finish and the first iocsh prompt to be displayed. The C method sets two environment variables that control how the IOC shell behaves: C is set to prevent it calling the GNU Readline library, and C is set to a known string which is used as a terminator for the previous command. =cut sub start { my ($self, $exe, @args) = @_; croak("IOC already running") if $self->started; # Turn off readline or its equivalents local $ENV{IOCSH_HISTEDIT_DISABLE} = "TRUE"; # The iocsh prompt marks the end of the previous command local $ENV{IOCSH_PS1} = $self->{terminator} . "\n"; # Run the IOC as a child process $self->{pid} = open3($self->{stdin}, $self->{stdout}, $self->{stderr}, $exe, @args) or die "can't start $exe: $!"; $self->{select}->add($self->{stderr}); printf "#%d running %s\n", $self->{pid}, $exe if $self->{debug}; } =item pid () Returns the process-ID of the IOC process, or undef if the IOC process has not yet been started. =cut sub pid { my $self = shift; return $self->{pid}; } =item started () Returns a true value if the IOC has been started and not yet killed. This state will not change if the IOC dies by itself, it indicates that the start method has been called without the kill method. =cut sub started { my $self = shift; return defined($self->pid); } =item _send ( COMMAND ) The C<_send> method is a primitive for internal use that sends a COMMAND string to the IOC shell, and prints it to stdout if the debug flag is set. =cut sub _send { my ($self, $cmd) = @_; my $stdin = $self->{stdin}; printf "#%d << %s", $self->{pid}, $cmd if $self->{debug}; local $SIG{PIPE} = sub { printf "#%d << \n", $self->{pid} if $self->{debug}; }; print $stdin $cmd; } =item _getline () The C<_getline> method is also designed for internal use, it fetches a single line output by the IOC, and prints it to stdout if the debug flag is set. Any CR/LF is stripped from the line before returning it. If the stream gets closed because the IOC shuts down an C debug message may be shown and an undef value will be returned. =cut sub _getline { my $self = shift; my $line = readline $self->{stdout}; if (defined $line) { $line =~ s/[\r\n]+ $//x; # chomp broken on Windows? printf "#%d >> %s\n", $self->{pid}, $line if $self->{debug}; } elsif (eof($self->{stdout})) { printf "#%d >> \n", $self->{pid} if $self->{debug}; } else { printf "#%d Error: %s\n", $self->{pid}, $! if $self->{debug}; } return $line; } =item _getlines ( [TERM] ) Another internal method C<_getlines> fetches multiple lines from the IOC. It takes an optional TERM string or regexp parameter which is matched against each input line in turn to determine when the IOC's output has been completed. Termination also occurs on an EOF from the output stream. The return value is a list of all the lines received (with the final CR/LF stripped) including the line that matched the terminator. =cut sub _getlines { my ($self, $term) = @_; my @response = (); while (my $line = $self->_getline) { push @response, $line; last if defined $term && $line =~ $term; } return @response; } =item _geterrors ( ) Returns a list of lines output by the IOC to stderr since last called. Only complete lines are included, and trailing newlines have been removed. NOTE: This doesn't work on Windows because it uses select which Perl doesn't support on that OS, but it doesn't seem to cause any problems for short-lived IOCs at least, it just never returns any text from the IOC's stderr output. =cut sub _geterrors { my ($self) = @_; my @errors; while ($self->{select}->can_read(0.01)) { sysread $self->{stderr}, my $errbuf, 1024; push @errors, split m/\n/, $self->{errbuf} . $errbuf, -1; last unless @errors; $self->{errbuf} = pop @errors; } return @errors; } =item cmd ( [COMMAND [, ARGUMENTS ...]] ) If the C method is given an optional COMMAND string along with any number of ARGUMENTS it constructs a command-line, quoting each argument as necessary. This is sent to the IOC and a line read back and discarded if it matches the command-line. With no COMMAND string the method starts here; it then collects lines from the IOC until one matches the terminator. A list of all the lines received prior to the terminator line is returned. =cut sub cmd { my ($self, $cmd, @args) = @_; my @response; my $term = $self->{terminator}; if (defined $cmd) { if (@args) { # FIXME: This quoting stuff isn't quite right my @qargs = map { m/^ (?: -? [0-9.eE+\-]+ ) | (?: " .* " ) $/x ? $_ : "'$_'" } @args; $cmd .= ' ' . join(' ', @qargs); } $self->_send("$cmd\n"); my $echo = $self->_getline; return @response unless defined $echo; # undef => reached EOF if ($echo ne $cmd) { return @response if $echo =~ $term; push @response, $echo; } } push @response, $self->_getlines($term); pop @response if @response and $response[-1] =~ $term; my @errors = $self->_geterrors; if (scalar @errors && $self->{debug}) { my $indent = sprintf "#%d e>", $self->{pid}; print map {"$indent $_\n"} @errors; } return @response; } =item kill () The C method attempts to stop an IOC that is still running in several ways. First it sends an C command to the IOC shell. Next it closes the IOC's stdin stream which will trigger an end-of-file on that stream, and it fetches any remaining lines from the IOC's stdout stream before closing both that and the stderr stream. Finally (unless running on MS-Windows) it sends a SIGTERM signal to the child process and waits for it to clean up. =cut sub kill { my $self = shift; return () unless $self->started; $self->_send("exit\n"); # Don't wait close $self->{stdin}; $self->{stdin} = gensym; my @response = $self->_getlines; # No terminator close $self->{stdout}; $self->{stdout} = gensym; $self->{select}->remove($self->{stderr}); close $self->{stderr}; $self->{stderr} = gensym; if ($^O ne "MSWin32") { kill 'TERM', $self->{pid}; waitpid $self->{pid}, 0; } $self->{pid} = undef; return @response; } =item DESTROY () C objects have a destructor which calls the C method, but it is not recommended that this be relied on to terminate an IOC process. Better to use an C block and/or trap the necessary signals to explicitly kill the IOC. =cut sub DESTROY { shift->kill; } =back =head1 CONVENIENCE METHODS The following methods provide an easy way to perform various common IOC operations. =over 4 =item dbLoadRecords ( FILE [, MACROS] ) Instructs the IOC to load a database (.db) from FILE. If provided, the MACROS parameter is a single string containing one or more comma-separated assignment statements like C for macros that are used in the database file. This method can also be used to load a database definition (.dbd) file. =cut sub dbLoadRecords { my ($self, $file, $macros) = @_; $macros = '' unless defined $macros; $self->cmd('dbLoadRecords', $file, $macros); } =item iocInit () Start the IOC executing. =cut sub iocInit { shift->cmd('iocInit'); } =item dbl ( [RECORDTYPE]) This method uses the C command to fetch a list of all of the record names the IOC has loaded. If a RECORDTYPE name is given, the list will only comprise records of that type. =cut sub dbl { my ($self, $rtyp) = @_; return $self->cmd('dbl', $rtyp) } =item dbgf ( PV ) The C method returns the value of the process variable PV, or C if the PV doesn't exist. This only works when the PV holds a scalar or an array with one element. =cut # Regexps for the output from dbgf, currently supporting scalars only my $RXdbfstr = qr/ DB[FR]_(STRING) : \s* " ( (?> \\. | [^"\\] )* ) " /x; my $RXdbfint = qr/ DB[FR]_(U?(?:CHAR|SHORT|LONG|INT64)) : \s* ( -? [0-9]+ ) /x; my $RXdbfflt = qr/ DB[FR]_(FLOAT|DOUBLE) : \s* ( [0-9.eE+\-]+ ) /x; my $RXdbf = qr/ (?| $RXdbfstr | $RXdbfint | $RXdbfflt ) /x; sub dbgf { my ($self, $pv) = @_; my @res = $self->cmd('dbgf', $pv); return undef unless scalar @res; my ($type, $result) = ($res[0] =~ m/^ $RXdbf /x); $result =~ s/\\([\\"'])/$1/gx if $type eq 'STRING'; return $result; } =item dbpf ( PV, VALUE ) This method sets PV to VALUE, and returns the new value, or C if the PV doesn't exist. If the put fails the return value is the previous value of the PV. As with the C method this only works for scalar or single-element arrays, but PV may be an array field which will be set to one element. =cut sub dbpf { my ($self, $pv, $val) = @_; my @res = $self->cmd('dbpf', $pv, $val); return undef unless scalar @res; my ($type, $result) = ($res[0] =~ m/^ $RXdbf /x); $result =~ s/\\([\\"'])/$1/gx if $type eq 'STRING'; return $result; } =back =head1 COPYRIGHT AND LICENSE Portions Copyright (C) 2011 UChicago Argonne LLC, as Operator of Argonne National Laboratory. This software is distributed under the terms of the EPICS Open License. =cut 1; __END__ base-7.0.3.1/modules/database/src/tools/Makefile0000664000577000060420000000374513557101274020227 0ustar anjaesctl#************************************************************************* # Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* TOP=../../../.. include $(TOP)/configure/CONFIG PERL_MODULES += DBD.pm PERL_MODULES += DBD/Base.pm PERL_MODULES += DBD/Breaktable.pm PERL_MODULES += DBD/Device.pm PERL_MODULES += DBD/Driver.pm PERL_MODULES += DBD/Link.pm PERL_MODULES += DBD/Function.pm PERL_MODULES += DBD/Menu.pm PERL_MODULES += DBD/Output.pm PERL_MODULES += DBD/Parser.pm PERL_MODULES += DBD/Recfield.pm PERL_MODULES += DBD/Recordtype.pm PERL_MODULES += DBD/Record.pm PERL_MODULES += DBD/Registrar.pm PERL_MODULES += DBD/Variable.pm PERL_MODULES += EPICS/IOC.pm HTMLS += EPICS/IOC.html PERL_SCRIPTS += makeIncludeDbd.pl PERL_SCRIPTS += dbdToMenuH.pl PERL_SCRIPTS += dbdToRecordtypeH.pl PERL_SCRIPTS += dbdExpand.pl PERL_SCRIPTS += dbExpand.pl PERL_SCRIPTS += dbdToHtml.pl PERL_SCRIPTS += registerRecordDeviceDriver.pl HTMLS += dbdToHtml.html # Build Package Config Files FINAL_LOCATION ?= $(shell $(PERL) $(TOOLS)/fullPathName.pl $(INSTALL_LOCATION)) C_CFLAGS += $(filter-out -g,$(filter-out -O%,$(filter-out -W%,$(CPPFLAGS)))) C_CFLAGS += $(filter-out -g,$(filter-out -O%,$(filter-out -W%,$(CFLAGS)))) PKGVARS += FINAL_LOCATION OS_CLASS CMPLR_CLASS C_CFLAGS LDFLAGS LDLIBS PKGVARS += EPICS_VERSION EPICS_REVISION EPICS_MODIFICATION EPICS_PATCH_LEVEL PKGVARS += CC CCC CPP AR LD PKGVARS += EPICS_BASE_HOST_LIBS EPICS_BASE_IOC_LIBS EXPANDFLAGS += $(foreach var,$(PKGVARS),-D$(var)="$(strip $($(var)))") PKGCONFIG += epics-base-$(T_A).pc ifeq ($(T_A),$(EPICS_HOST_ARCH)) PKGCONFIG += epics-base.pc endif EXPAND += $(PKGCONFIG:%=%@) CLEANS += epics-base-$(T_A).pc@ include $(TOP)/configure/RULES epics-base-$(T_A).pc@: ../epics-base-arch.pc@ @$(RM) $@ @$(CP) $< $@ base-7.0.3.1/modules/database/src/tools/dbExpand.pl0000664000577000060420000000427513557101274020650 0ustar anjaesctl#!/usr/bin/env perl #************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # $Id$ use strict; use FindBin qw($Bin); use lib ("$Bin/../../lib/perl"); use DBD; use DBD::Parser; use DBD::Output; use EPICS::Getopts; use EPICS::Readfile; use EPICS::macLib; our ($opt_D, @opt_I, @opt_S, $opt_o, $opt_V); getopts('DI@S@o:V') or die "Usage: dbExpand [-D] [-I dir] [-S macro=val] [-o out.db] in.dbd in.db ..."; my @path = map { split /[:;]/ } @opt_I; # FIXME: Broken on Win32? my $macros = EPICS::macLib->new(@opt_S); my $dbd = DBD->new(); $macros->suppressWarning(!$opt_V); $DBD::Record::macrosOk = !$opt_V; # Calculate filename for the dependency warning message below my $dep = $opt_o; my $dot_d = ''; if ($opt_D) { $dep =~ s{\.\./O\.Common/(.*)}{$1\$\(DEP\)}; $dot_d = '.d'; } else { $dep = "\$(COMMON_DIR)/$dep"; } die "dbExpand.pl: No input files for $opt_o\n" if !@ARGV; my $errors = 0; while (@ARGV) { my $file = shift @ARGV; eval { &ParseDBD($dbd, &Readfile($file, $macros, \@opt_I)); }; if ($@) { warn "dbExpand.pl: $@"; my $outfile = $opt_o ? " to create '$opt_o$dot_d'" : ''; warn " while reading '$file'$outfile\n"; warn " Your Makefile may need this dependency rule:\n", " $dep: \$(COMMON_DIR)/$file\n" if $@ =~ m/Can't find file '$file'/; ++$errors; } } if ($opt_D) { # Output dependencies only, ignore errors my %filecount; my @uniqfiles = grep { not $filecount{$_}++ } @inputfiles; print "$opt_o: ", join(" \\\n ", @uniqfiles), "\n\n"; print map { "$_:\n" } @uniqfiles; exit 0; } die "dbExpand.pl: Exiting due to errors\n" if $errors; my $out; if ($opt_o) { open $out, '>', $opt_o or die "Can't create $opt_o: $!\n"; } else { $out = *STDOUT; } &OutputDB($out, $dbd); if ($opt_o) { close $out or die "Closing $opt_o failed: $!\n"; } exit 0; base-7.0.3.1/modules/database/src/tools/dbdExpand.pl0000664000577000060420000000412113557101274021002 0ustar anjaesctl#!/usr/bin/env perl #************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* use strict; use FindBin qw($Bin); use lib ("$Bin/../../lib/perl"); use DBD; use DBD::Parser; use DBD::Output; use EPICS::Getopts; use EPICS::Readfile; use EPICS::macLib; our ($opt_D, @opt_I, @opt_S, $opt_o); getopts('DI@S@o:') or die "Usage: dbdExpand [-D] [-I dir] [-S macro=val] [-o out.dbd] in.dbd ..."; my @path = map { split /[:;]/ } @opt_I; # FIXME: Broken on Win32? my $macros = EPICS::macLib->new(@opt_S); my $dbd = DBD->new(); $macros->suppressWarning(1); # Calculate filename for the dependency warning message below my $dep = $opt_o; my $dot_d = ''; if ($opt_D) { $dep =~ s{\.\./O\.Common/(.*)}{\1\$\(DEP\)}; $dot_d = '.d'; } else { $dep = "\$(COMMON_DIR)/$dep"; } die "dbdExpand.pl: No input files for $opt_o\n" if !@ARGV; my $errors = 0; while (@ARGV) { my $file = shift @ARGV; eval { ParseDBD($dbd, Readfile($file, $macros, \@opt_I)); }; if ($@) { warn "dbdExpand.pl: $@"; warn " while reading '$file' to create '$opt_o$dot_d'\n"; warn " Your Makefile may need this dependency rule:\n", " $dep: \$(COMMON_DIR)/$file\n" if $@ =~ m/Can't find file '$file'/; ++$errors; } } if ($opt_D) { # Output dependencies only, ignore errors my %filecount; my @uniqfiles = grep { not $filecount{$_}++ } @inputfiles; print "$opt_o: ", join(" \\\n ", @uniqfiles), "\n\n"; print map { "$_:\n" } @uniqfiles; exit 0; } die "dbdExpand.pl: Exiting due to errors\n" if $errors; my $out; if ($opt_o) { open $out, '>', $opt_o or die "Can't create $opt_o: $!\n"; } else { $out = *STDOUT; } OutputDBD($out, $dbd); if ($opt_o) { close $out or die "Closing $opt_o failed: $!\n"; } exit 0; base-7.0.3.1/modules/database/src/tools/dbdReport.pl0000664000577000060420000000410313557101274021036 0ustar anjaesctl#!/usr/bin/env perl #************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* use FindBin qw($Bin); use lib ("$Bin/../../lib/perl"); use DBD; use DBD::Parser; use EPICS::Getopts; use EPICS::macLib; use EPICS::Readfile; use Text::Wrap; #$EPICS::Readfile::debug = 1; #$DBD::Parser::debug = 1; getopts('I@S@') or die usage(); sub usage() { "Usage: dbdReport [-I dir:dir2] [-S macro=val,...] file.dbd ..."; } my @path = map { split /[:;]/ } @opt_I; # FIXME: Broken on Win32? my $macros = EPICS::macLib->new(@opt_S); my $dbd = DBD->new(); ParseDBD($dbd, Readfile(shift @ARGV, $macros, \@opt_I)) while @ARGV; $Text::Wrap::columns = 75; my @menus = sort keys %{$dbd->menus}; print wrap("Menus:\t", "\t", join(', ', @menus)), "\n" if @menus; my @drivers = sort keys %{$dbd->drivers}; print wrap("Drivers: ", "\t", join(', ', @drivers)), "\n" if @drivers; my @variables = sort keys %{$dbd->variables}; print wrap("Variables: ", "\t", join(', ', @variables)), "\n" if @variables; my @registrars = sort keys %{$dbd->registrars}; print wrap("Registrars: ", "\t", join(', ', @registrars)), "\n" if @registrars; my @breaktables = sort keys %{$dbd->breaktables}; print wrap("Breaktables: ", "\t", join(', ', @breaktables)), "\n" if @breaktables; my %recordtypes = %{$dbd->recordtypes}; if (%recordtypes) { @rtypes = sort keys %recordtypes; print wrap("Recordtypes: ", "\t", join(', ', @rtypes)), "\n"; foreach my $rtyp (@rtypes) { my @devices = $recordtypes{$rtyp}->devices; print wrap("Devices($rtyp): ", "\t", join(', ', map {$_->choice} @devices)), "\n" if @devices; } } my @records = sort keys %{$dbd->records}; print wrap("Records: ", "\t", join(', ', @records)), "\n" if @records; base-7.0.3.1/modules/database/src/tools/dbdToHtml.pl0000664000577000060420000003072513557101274021003 0ustar anjaesctl#!/usr/bin/env perl #************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* use strict; use FindBin qw($Bin); use lib ("$Bin/../../lib/perl"); use DBD; use DBD::Parser; use EPICS::Getopts; use EPICS::macLib; use EPICS::Readfile; BEGIN { $::XHTML = eval "require Pod::Simple::XHTML; 1"; $::ENTITIES = eval "require HTML::Entities; 1"; if (!$::XHTML) { require Pod::Simple::HTML; } if (!$::ENTITIES) { my %entities = ( q{>} => 'gt', q{<} => 'lt', q{'} => '#39', q{"} => 'quot', q{&} => 'amp', ); sub encode_entities { my $str = shift; my $ents = join '', keys %entities; $str =~ s/([ $ents ])/'&' . ($entities{$1} || sprintf '#x%X', ord $1) . ';'/xge; return $str; } } } use Pod::Usage; =head1 NAME dbdToHtml.pl - Convert DBD file with POD to HTML =head1 SYNOPSIS B [B<-h>] [B<-D>] [B<-I> dir] [B<-o> file] file.dbd.pod =head1 DESCRIPTION Generates HTML 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<-D> Instead of creating the output file as described, read the input file(s) and print a B dependency rule for the output file(s) to stdout. =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<.html>. =cut our ($opt_h, $opt_H, $opt_D, @opt_I, $opt_o); my $tool = 'dbdToHtml.pl'; getopts('hHDI@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$/.html/; $opt_o =~ s/^.*\///; $opt_o =~ s/dbCommonRecord/dbCommon/; } if ($opt_D) { # Output dependencies only my %filecount; my @uniqfiles = grep { not $filecount{$_}++ } @inputfiles; print "$opt_o: ", join(" \\\n ", @uniqfiles), "\n\n"; print map { "$_:\n" } @uniqfiles; exit 0; } (my $title = $opt_o) =~ s/\.html$//; open my $out, '>', $opt_o or die "Can't create $opt_o: $!\n"; my $podHtml; my $idify; my $contentType = ''; if ($::XHTML) { $podHtml = Pod::Simple::XHTML->new(); $podHtml->html_doctype(<< '__END_DOCTYPE'); __END_DOCTYPE if ($podHtml->can('html_charset')) { $podHtml->html_charset('UTF-8'); } else { # Older version of Pod::Simple::XHTML without html_charset() $podHtml->html_header_tags($contentType); } $podHtml->html_header_tags($podHtml->html_header_tags . "\n"); $idify = sub { my $title = shift; return $podHtml->idify($title, 1); } } else { # Fall back to HTML $Pod::Simple::HTML::Content_decl = $contentType; $podHtml = Pod::Simple::HTML->new(); $podHtml->html_css('style.css'); $idify = sub { my $title = shift; return Pod::Simple::HTML::esc($podHtml->section_escape($title)); } } # Parse the Pod text from the root DBD object my $pod = join "\n", '=for html
', '', 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; rtypeToPod($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; menuToPod($menu); } elsif (m/^ =title \s+ (.*)/x) { $title = $1; "=head1 $title"; } else { $_; } } $dbd->pod, '=for html
', ''; $podHtml->force_title(encode_entities($title)); $podHtml->perldoc_url_prefix(''); $podHtml->perldoc_url_postfix('.html'); $podHtml->output_fh($out); $podHtml->parse_string_document($pod); close $out; sub menuToPod { my ($menu) = @_; my $index = 0; return '=begin html', '', '
', '', map({choiceTableRow($_, $index++)} $menu->choices), '
IndexIdentifierChoice String
', '', '=end html'; } sub choiceTableRow { my ($ch, $index) = @_; my ($id, $name) = @{$ch}; return '', "$index", "$id", "$name", ''; } sub rtypeToPod { 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 '=begin html', '', '
', '', '', '', map({fieldTableRow($_, $dbd)} @fields), '
FieldSummaryTypeDCTDefaultReadWriteCA PP
', '', '=end html'; } # 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; menuToPod($menu); } else { # Raw text line $_; } } $rtyp->pod; } sub fieldTableRow { my ($fld, $dbd) = @_; my $html = ''; $html .= $fld->name; $html .= ''; $html .= $fld->attribute('prompt'); $html .= ''; my $type = $fld->public_type; $html .= $type; $html .= ' [' . $fld->attribute('size') . ']' if $type eq 'STRING'; if ($type eq 'MENU') { my $mn = $fld->attribute('menu'); my $menu = $dbd->menu($mn); my $url = $menu ? '#' . &$idify("Menu $mn") : "${mn}.html"; $html .= " ($mn)"; } $html .= ''; $html .= $fld->attribute('promptgroup') ? 'Yes' : 'No'; $html .= ''; $html .= $fld->attribute('initial') || ' '; $html .= ''; $html .= $fld->readable; $html .= ''; $html .= $fld->writable; $html .= ''; $html .= $fld->attribute('pp') eq 'TRUE' ? 'Yes' : 'No'; $html .= "\n"; return $html; } # 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 HTML. 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 HTML 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, so the line =fields DTYP, INP generates all this in the output:
FieldSummaryTypeDCT DefaultReadWriteCA PP
DTYPDevice Type DEVICE Yes   Yes Yes No
INP Input Specification INLINK Yes   Yes Yes No
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 base-7.0.3.1/modules/database/src/tools/dbdToMenuH.pl0000664000577000060420000000470413557101274021111 0ustar anjaesctl#!/usr/bin/env perl #************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* use FindBin qw($Bin); use lib ("$Bin/../../lib/perl"); use EPICS::Getopts; use File::Basename; use DBD; use DBD::Parser; use EPICS::macLib; use EPICS::Readfile; my $tool = 'dbdToMenuH.pl'; our ($opt_D, @opt_I, $opt_o, $opt_s); getopts('DI@o:') or die "Usage: $tool: [-D] [-I dir] [-o menu.h] menu.dbd [menu.h]\n"; my @path = map { split /[:;]/ } @opt_I; # FIXME: Broken on Win32? my $dbd = DBD->new(); my $infile = shift @ARGV; $infile =~ m/\.dbd$/ or die "$tool: Input file '$infile' must have '.dbd' extension\n"; my $inbase = basename($infile); my $outfile; if ($opt_o) { $outfile = $opt_o; } elsif (@ARGV) { $outfile = shift @ARGV; } else { ($outfile = $infile) =~ s/\.dbd$/.h/; $outfile =~ s/^.*\///; } my $outbase = basename($outfile); # Derive a name for the include guard my $guard_name = "INC_$outbase"; $guard_name =~ tr/a-zA-Z0-9_/_/cs; $guard_name =~ s/(_[hH])?$/_H/; ParseDBD($dbd, Readfile($infile, 0, \@opt_I)); if ($opt_D) { my %filecount; my @uniqfiles = grep { not $filecount{$_}++ } @inputfiles; print "$outfile: ", join(" \\\n ", @uniqfiles), "\n\n"; print map { "$_:\n" } @uniqfiles; } else { open OUTFILE, ">$outfile" or die "$tool: Can't open $outfile: $!\n"; print OUTFILE "/* $outbase generated from $inbase */\n\n", "#ifndef $guard_name\n", "#define $guard_name\n\n"; my $menus = $dbd->menus; while (my ($name, $menu) = each %{$menus}) { print OUTFILE $menu->toDeclaration; } # FIXME: Where to put metadata for widely used menus? # In the generated menu.h file is wrong: can't create a list of menu.h files. # Can only rely on registerRecordDeviceDriver output, so we must require that # all such menus be named "menu...", and any other menus must be defined in # the record.dbd file that needs them. # print OUTFILE "\n#ifdef GEN_MENU_METADATA\n\n"; # while (($name, $menu) = each %{$menus}) { # print OUTFILE $menu->toDefinition; # } # print OUTFILE "\n#endif /* GEN_MENU_METADATA */\n"; print OUTFILE "\n#endif /* $guard_name */\n"; close OUTFILE; } base-7.0.3.1/modules/database/src/tools/dbdToRecordtypeH.pl0000664000577000060420000001740213557101274022324 0ustar anjaesctl#!/usr/bin/env perl #************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* use FindBin qw($Bin); use lib ("$Bin/../../lib/perl"); use EPICS::Getopts; use File::Basename; use DBD; use DBD::Parser; use EPICS::macLib; use EPICS::Readfile; my $tool = 'dbdToRecordtypeH.pl'; our ($opt_D, @opt_I, $opt_o, $opt_s); getopts('DI@o:s') or die "Usage: $tool [-D] [-I dir] [-o xRecord.h] xRecord.dbd [xRecord.h]\n"; my @path = map { split /[:;]/ } @opt_I; # FIXME: Broken on Win32? my $dbd = DBD->new(); my $infile = shift @ARGV; $infile =~ m/\.dbd$/ or die "$tool: Input file '$infile' must have '.dbd' extension\n"; my $inbase = basename($infile); my $outfile; if ($opt_o) { $outfile = $opt_o; } elsif (@ARGV) { $outfile = shift @ARGV; } else { ($outfile = $infile) =~ s/\.dbd$/.h/; $outfile =~ s/^.*\///; $outfile =~ s/dbCommonRecord/dbCommon/; } my $outbase = basename($outfile); # Derive a name for the include guard my $guard_name = "INC_$outbase"; $guard_name =~ tr/a-zA-Z0-9_/_/cs; $guard_name =~ s/(_[hH])?$/_H/; ParseDBD($dbd, Readfile($infile, 0, \@opt_I)); my $rtypes = $dbd->recordtypes; die "$tool: Input file must contain a single recordtype definition.\n" unless (1 == keys %{$rtypes}); if ($opt_D) { # Output dependencies only, to stdout my %filecount; my @uniqfiles = grep { not $filecount{$_}++ } @inputfiles; print "$outfile: ", join(" \\\n ", @uniqfiles), "\n\n"; print map { "$_:\n" } @uniqfiles; } else { open OUTFILE, ">$outfile" or die "$tool: Can't open $outfile: $!\n"; print OUTFILE "/* $outbase generated from $inbase */\n\n", "#ifndef $guard_name\n", "#define $guard_name\n\n"; our ($rn, $rtyp) = each %{$rtypes}; print OUTFILE $rtyp->toCdefs; my @menu_fields = grep { $_->dbf_type eq 'DBF_MENU' } $rtyp->fields; my %menu_used; grep { !$menu_used{$_}++ } map { $_->attribute('menu') } @menu_fields; our $menus_defined = $dbd->menus; while (my ($name, $menu) = each %{$menus_defined}) { print OUTFILE $menu->toDeclaration; if ($menu_used{$name}) { delete $menu_used{$name} } } our @menus_external = keys %menu_used; print OUTFILE $rtyp->toDeclaration; unless ($rn eq 'dbCommon') { my $n = 0; print OUTFILE "typedef enum {\n", join(",\n", map { "\t${rn}Record$_ = " . $n++ } $rtyp->field_names), "\n} ${rn}FieldIndex;\n\n"; print OUTFILE "#ifdef GEN_SIZE_OFFSET\n\n"; if ($opt_s) { newtables(); } else { oldtables(); } print OUTFILE "#endif /* GEN_SIZE_OFFSET */\n"; } print OUTFILE "\n", "#endif /* $guard_name */\n"; close OUTFILE; } sub oldtables { # Output compatible with R3.14.x print OUTFILE "#include \n" . "#include \n" . "#ifdef __cplusplus\n" . "extern \"C\" {\n" . "#endif\n" . "static int ${rn}RecordSizeOffset(dbRecordType *prt)\n" . "{\n" . " ${rn}Record *prec = 0;\n\n" . " assert(prt->no_fields == " . scalar($rtyp->fields) . ");\n" . join("\n", map { " prt->papFldDes[${rn}Record" . $_->name . "]->size = " . "sizeof(prec->" . $_->C_name . ");" } $rtyp->fields) . "\n" . join("\n", map { " prt->papFldDes[${rn}Record" . $_->name . "]->offset = (unsigned short)(" . "(char *)&prec->" . $_->C_name . " - (char *)prec);" } $rtyp->fields) . "\n" . " prt->rec_size = sizeof(*prec);\n" . " return 0;\n" . "}\n" . "epicsExportRegistrar(${rn}RecordSizeOffset);\n\n" . "#ifdef __cplusplus\n" . "}\n" . "#endif\n"; } sub newtables { # Output for an eventual DBD-less IOC print OUTFILE (map { "extern const dbMenu ${_}MenuMetaData;\n" } @menus_external), "\n"; while (my ($name, $menu) = each %{$menus_defined}) { print OUTFILE $menu->toDefinition; } print OUTFILE (map { "static const char ${rn}FieldName$_\[] = \"$_\";\n" } $rtyp->field_names), "\n"; my $n = 0; print OUTFILE "static const dbRecordData ${rn}RecordMetaData;\n\n", "static dbFldDes ${rn}FieldMetaData[] = {\n", join(",\n", map { my $fn = $_->name; my $cn = $_->C_name; " { ${rn}FieldName${fn}," . $_->dbf_type . ',"' . $_->attribute('initial') . '",' . ($_->attribute('special') || '0') . ',' . ($_->attribute('pp') || 'FALSE') . ',' . ($_->attribute('interest') || '0') . ',' . ($_->attribute('asl') || 'ASL0') . ',' . $n++ . ",\n\t\&${rn}RecordMetaData," . "GEOMETRY_DATA(${rn}Record,$cn) }"; } $rtyp->fields), "\n};\n\n"; print OUTFILE "static const ${rn}FieldIndex ${rn}RecordLinkFieldIndices[] = {\n", join(",\n", map { " ${rn}Record" . $_->name; } grep { $_->dbf_type =~ m/^DBF_(IN|OUT|FWD)LINK/; } $rtyp->fields), "\n};\n\n"; my @sorted_names = sort $rtyp->field_names; print OUTFILE "static const char * const ${rn}RecordSortedFieldNames[] = {\n", join(",\n", map { " ${rn}FieldName$_" } @sorted_names), "\n};\n\n"; print OUTFILE "static const ${rn}FieldIndex ${rn}RecordSortedFieldIndices[] = {\n", join(",\n", map { " ${rn}Record$_" } @sorted_names), "\n};\n\n"; print OUTFILE "extern rset ${rn}RSET;\n\n", "static const dbRecordData ${rn}RecordMetaData = {\n", " \"$rn\",\n", " sizeof(${rn}Record),\n", " NELEMENTS(${rn}FieldMetaData),\n", " ${rn}FieldMetaData,\n", " ${rn}RecordVAL,\n", " \&${rn}FieldMetaData[${rn}RecordVAL],\n", " NELEMENTS(${rn}RecordLinkFieldIndices),\n", " ${rn}RecordLinkFieldIndices,\n", " ${rn}RecordSortedFieldNames,\n", " ${rn}RecordSortedFieldIndices,\n", " \&${rn}RSET\n", "};\n\n", "#ifdef __cplusplus\n", "extern \"C\" {\n", "#endif\n\n"; print OUTFILE "dbRecordType * epicsShareAPI ${rn}RecordRegistrar(dbBase *pbase, int nDevs)\n", "{\n", " dbRecordType *prt = dbCreateRecordtype(&${rn}RecordMetaData, nDevs);\n"; print OUTFILE " ${rn}FieldMetaData[${rn}RecordDTYP].typDat.pdevMenu = \&prt->devMenu;\n"; while (my ($name, $menu) = each %{$menus_defined}) { print OUTFILE " dbRegisterMenu(pbase, \&${name}MenuMetaData);\n"; } print OUTFILE map { " ${rn}FieldMetaData[${rn}Record" . $_->name . "].typDat.pmenu = \n". " \&" . $_->attribute('menu') . "MenuMetaData;\n"; } @menu_fields; print OUTFILE map { " ${rn}FieldMetaData[${rn}Record" . $_->name . "].typDat.base = CT_HEX;\n"; } grep { $_->attribute('base') eq 'HEX'; } $rtyp->fields; print OUTFILE " dbRegisterRecordtype(pbase, prt);\n"; print OUTFILE " return prt;\n}\n\n", "#ifdef __cplusplus\n", "} /* extern \"C\" */\n", "#endif\n\n"; } base-7.0.3.1/modules/database/src/tools/epics-base-arch.pc@0000664000577000060420000000166113557101274022134 0ustar anjaesctl# standard variables prefix=@FINAL_LOCATION@ exec_prefix=${prefix} bindir=${prefix}/bin/@ARCH@ libdir=${prefix}/lib/@ARCH@ # non-standard variables # EPICS Base install location FINAL_LOCATION=${prefix} ARCH=@ARCH@ OS_CLASS=@OS_CLASS@ CMPLR_CLASS=@CMPLR_CLASS@ EPICS_BASE_HOST_LIBS=@EPICS_BASE_HOST_LIBS@ EPICS_BASE_IOC_LIBS=@EPICS_BASE_IOC_LIBS@ # Directories includedir_osi=${prefix}/include includedir_osd=${prefix}/include/os/@OS_CLASS@ includedir_comp=${prefix}/include/compiler/@CMPLR_CLASS@ includedirs=${includedir_osi} ${includedir_osd} ${includedir_comp} dbddir=${prefix}/dbd dbdir=${prefix}/db # Tool chain CC=@CC@ CXX=@CCC@ CPP=@CPP@ AR=@AR@ LD=@LD@ Name: epics-base-@ARCH@ Version: @EPICS_VERSION@.@EPICS_REVISION@.@EPICS_MODIFICATION@.@EPICS_PATCH_LEVEL@ Description: EPICS Base for @ARCH@ Cflags: -I${includedir_osi} -I${includedir_osd} -I${includedir_comp} @C_CFLAGS@ Libs: -L${libdir} @LDFLAGS@ Libs.private: @LDLIBS@ base-7.0.3.1/modules/database/src/tools/epics-base.pc@0000664000577000060420000000140413557101274021214 0ustar anjaesctl# standard variables prefix=@FINAL_LOCATION@ exec_prefix=${prefix} bindir=${prefix}/bin/@ARCH@ libdir=${prefix}/lib/@ARCH@ # non-standard variables # EPICS Base install location FINAL_LOCATION=${prefix} ARCH=@ARCH@ OS_CLASS=@OS_CLASS@ CMPLR_CLASS=@CMPLR_CLASS@ # Directories includedir_osi=${prefix}/include includedir_osd=${prefix}/include/os/@OS_CLASS@ includedir_comp=${prefix}/include/compiler/@CMPLR_CLASS@ includedirs=${includedir_osi} ${includedir_osd} ${includedir_comp} dbddir=${prefix}/dbd dbdir=${prefix}/db Name: epics-base Version: @EPICS_VERSION@.@EPICS_REVISION@.@EPICS_MODIFICATION@.@EPICS_PATCH_LEVEL@ Description: EPICS Base for the host arch Requires: epics-base-@ARCH@ = @EPICS_VERSION@.@EPICS_REVISION@.@EPICS_MODIFICATION@.@EPICS_PATCH_LEVEL@ base-7.0.3.1/modules/database/src/tools/makeIncludeDbd.pl0000664000577000060420000000156713557101274021757 0ustar anjaesctl#!/usr/bin/env perl #************************************************************************* # Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* use strict; use File::Basename; sub Usage { my $txt = shift; print "Usage: makeIncludeDbd.pl input file list ... outfile\n"; print "Error: $txt\n" if $txt; exit 2; } Usage("No input files specified") unless $#ARGV > 1; my $target = pop @ARGV; my @inputs = map { basename($_); } @ARGV; open(my $OUT, '>', $target) or die "$0: Can't create $target, $!\n"; print $OUT "# Generated file $target\n\n"; print $OUT map { "include \"$_\"\n"; } @inputs; close $OUT; base-7.0.3.1/modules/database/src/tools/registerRecordDeviceDriver.pl0000664000577000060420000002031713557101274024375 0ustar anjaesctl#!/usr/bin/env perl #************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* use strict; use FindBin qw($Bin); use lib ("$Bin/../../lib/perl"); use DBD; use DBD::Parser; use EPICS::Readfile; use EPICS::Path; use EPICS::Getopts; use Text::Wrap; our ($opt_D, @opt_I, $opt_o, $opt_l); getopts('Dlo:I@') or die "Usage: registerRecordDeviceDriver [-D] [-l] [-o out.c] [-I dir] in.dbd subname [TOP]"; my @path = map { split /[:;]/ } @opt_I; # FIXME: Broken on Win32? my ($file, $subname, $bldTop) = @ARGV; my $dbd = DBD->new(); ParseDBD($dbd, Readfile($file, "", \@path)); if ($opt_D) { # Output dependencies only my %filecount; my @uniqfiles = grep { not $filecount{$_}++ } @inputfiles; print "$opt_o: ", join(" \\\n ", @uniqfiles), "\n\n"; print map { "$_:\n" } @uniqfiles; exit 0; } $Text::Wrap::columns = 75; # Eliminate chars not allowed in C symbol names my $c_bad_ident_chars = '[^0-9A-Za-z_]'; $subname =~ s/$c_bad_ident_chars/_/g; # Process bldTop like convertRelease.pl does $bldTop = LocalPath(UnixPath($bldTop)); $bldTop =~ s/([\\"])/\\\1/g; # escape back-slashes and double-quotes # Create output file my $out; if ($opt_o) { open $out, '>', $opt_o or die "Can't create $opt_o: $!\n"; } else { $out = *STDOUT; } print $out (<< "END"); /* THIS IS A GENERATED FILE. DO NOT EDIT! */ /* Generated from $file */ #include #ifndef USE_TYPED_RSET # define USE_TYPED_RSET #endif #include "compilerDependencies.h" #include "epicsStdlib.h" #include "iocsh.h" #include "iocshRegisterCommon.h" #include "registryCommon.h" #include "recSup.h" END print $out (<< "END") if $opt_l; #define epicsExportSharedSymbols #include "shareLib.h" END print $out (<< "END"); extern "C" { END my %rectypes = %{$dbd->recordtypes}; my @rtypnames; my @dsets; if (%rectypes) { my @allrtypnames = sort keys %rectypes; # Record types with no fields defined are declarations, # for building shared libraries containing device support. @rtypnames = grep { scalar $rectypes{$_}->fields } @allrtypnames; if (@rtypnames) { # Declare the record support entry tables print $out wrap('epicsShareExtern typed_rset ', ' ', join(', ', map {"*pvar_rset_${_}RSET"} @rtypnames)), ";\n\n"; # Declare the RecordSizeOffset functions print $out "typedef int (*rso_func)(dbRecordType *pdbRecordType);\n"; print $out wrap('epicsShareExtern rso_func ', ' ', join(', ', map {"pvar_func_${_}RecordSizeOffset"} @rtypnames)), ";\n\n"; # List of record type names print $out "static const char * const recordTypeNames[] = {\n"; print $out wrap(' ', ' ', join(', ', map {"\"$_\""} @rtypnames)); print $out "\n};\n\n"; # List of pointers to each RSET and RecordSizeOffset function print $out "static const recordTypeLocation rtl[] = {\n"; print $out join(",\n", map { " {(struct typed_rset *)pvar_rset_${_}RSET, pvar_func_${_}RecordSizeOffset}" } @rtypnames); print $out "\n};\n\n"; } for my $rtype (@allrtypnames) { my @devices = $rectypes{$rtype}->devices; for my $dtype (@devices) { my $dset = $dtype->name; push @dsets, $dset; } } if (@dsets) { # Declare the device support entry tables print $out wrap('epicsShareExtern dset ', ' ', join(', ', map {"*pvar_dset_$_"} @dsets)), ";\n\n"; # List of dset names print $out "static const char * const deviceSupportNames[] = {\n"; print $out wrap(' ', ' ', join(', ', map {"\"$_\""} @dsets)); print $out "\n};\n\n"; # List of pointers to each dset print $out "static const dset * const devsl[] = {\n"; print $out wrap(' ', ' ', join(", ", map {"pvar_dset_$_"} @dsets)); print $out "\n};\n\n"; } } my %drivers = %{$dbd->drivers}; if (%drivers) { my @drivers = sort keys %drivers; # Declare the driver entry tables print $out wrap('epicsShareExtern drvet ', ' ', join(', ', map {"*pvar_drvet_$_"} @drivers)), ";\n\n"; # List of drvet names print $out "static const char *driverSupportNames[] = {\n"; print $out wrap(' ', ' ', join(', ', map {"\"$_\""} @drivers)); print $out "};\n\n"; # List of pointers to each drvet print $out "static struct drvet *drvsl[] = {\n"; print $out join(",\n", map {" pvar_drvet_$_"} @drivers); print $out "};\n\n"; } my %links = %{$dbd->links}; if (%links) { my @links = sort keys %links; # Declare the link interfaces print $out wrap('epicsShareExtern jlif ', ' ', join(', ', map {"*pvar_jlif_$_"} @links)), ";\n\n"; # List of pointers to each link interface print $out "static struct jlif *jlifsl[] = {\n"; print $out join(",\n", map {" pvar_jlif_$_"} @links); print $out "};\n\n"; } my @registrars = sort keys %{$dbd->registrars}; my @functions = sort keys %{$dbd->functions}; push @registrars, map {"register_func_$_"} @functions; if (@registrars) { # Declare the registrar functions print $out "typedef void (*reg_func)(void);\n"; print $out wrap('epicsShareExtern reg_func ', ' ', join(', ', map {"pvar_func_$_"} @registrars)), ";\n\n"; } my %variables = %{$dbd->variables}; if (%variables) { my @varnames = sort keys %variables; # Declare the variables for my $var (@varnames) { my $vtype = $variables{$var}->var_type; print $out "epicsShareExtern $vtype * const pvar_${vtype}_$var;\n"; } # Generate the structure for registering variables with iocsh print $out "\nstatic struct iocshVarDef vardefs[] = {\n"; for my $var (@varnames) { my $vtype = $variables{$var}->var_type; my $itype = $variables{$var}->iocshArg_type; print $out " {\"$var\", $itype, pvar_${vtype}_$var},\n"; } print $out " {NULL, iocshArgInt, NULL}\n};\n\n"; } # Now for actual registration routine print $out (<< "END"); int $subname(DBBASE *pbase) { static int executed = 0; END print $out (<< "END") if $bldTop ne ''; const char *bldTop = "$bldTop"; const char *envTop = getenv("TOP"); if (envTop && strcmp(envTop, bldTop)) { printf("Warning: IOC is booting with TOP = \\"%s\\"\\n" " but was built with TOP = \\"%s\\"\\n", envTop, bldTop); } END print $out (<< 'END'); if (!pbase) { printf("pdbbase is NULL; you must load a DBD file first.\n"); return -1; } if (executed) { printf("Warning: Registration already done.\n"); } executed = 1; END print $out (<< 'END') if %rectypes && @rtypnames; registerRecordTypes(pbase, NELEMENTS(rtl), recordTypeNames, rtl); END print $out (<< 'END') if @dsets; registerDevices(pbase, NELEMENTS(devsl), deviceSupportNames, devsl); END print $out (<< 'END') if %drivers; registerDrivers(pbase, NELEMENTS(drvsl), driverSupportNames, drvsl); END print $out (<< 'END') if %links; registerJLinks(pbase, NELEMENTS(jlifsl), jlifsl); END print $out (<< "END") for @registrars; pvar_func_$_(); END print $out (<< 'END') if %variables; iocshRegisterVariable(vardefs); END print $out (<< "END"); return 0; } /* $subname */ static const iocshArg rrddArg0 = {"pdbbase", iocshArgPdbbase}; static const iocshArg *rrddArgs[] = {&rrddArg0}; static const iocshFuncDef rrddFuncDef = {"$subname", 1, rrddArgs}; static void rrddCallFunc(const iocshArgBuf *) { iocshSetError($subname(*iocshPpdbbase)); } } // extern "C" /* * Register commands on application startup */ static int Registration() { iocshRegisterCommon(); iocshRegister(&rrddFuncDef, rrddCallFunc); return 0; } static int done EPICS_UNUSED = Registration(); END if ($opt_o) { close $out or die "Closing $opt_o failed: $!\n"; } exit 0; base-7.0.3.1/modules/database/test/Makefile0000664000577000060420000000126413557101274017251 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* TOP = ../../.. include $(TOP)/configure/CONFIG DIRS += ioc/db DIRS += ioc/dbtemplate DIRS += std/rec DIRS += std/link DIRS += std/filters DIRS += tools include $(TOP)/configure/RULES_TOP base-7.0.3.1/modules/database/test/ioc/db/Makefile0000664000577000060420000001522213557101274020407 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* CURDIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) TOP = ../../../../.. include $(TOP)/configure/CONFIG # Allow access to private headers in db/ USR_CPPFLAGS += -I $(CURDIR)/../../../src/ioc/db USR_CPPFLAGS += -DUSE_TYPED_RSET TESTLIBRARY = dbTestIoc dbTestIoc_SRCS += arrRecord.c dbTestIoc_SRCS += xRecord.c dbTestIoc_SRCS += dbLinkdset.c dbTestIoc_SRCS += xLink.c dbTestIoc_SRCS += devx.c dbTestIoc_SRCS += jlinkz.c dbTestIoc_LIBS = dbCore ca Com TARGETS += $(COMMON_DIR)/dbTestIoc.dbd DBDDEPENDS_FILES += dbTestIoc.dbd$(DEP) dbTestIoc_DBD += menuGlobal.dbd dbTestIoc_DBD += menuConvert.dbd dbTestIoc_DBD += menuScan.dbd dbTestIoc_DBD += xRecord.dbd dbTestIoc_DBD += arrRecord.dbd dbTestIoc_DBD += xLink.dbd dbTestIoc_DBD += devx.dbd dbTestIoc_DBD += jlinkz.dbd dbTestIoc_DBD += dbLinkdset.dbd TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db testHarness_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp PROD_LIBS = dbTestIoc dbCore ca Com TESTPROD_HOST += dbScanTest dbScanTest_SRCS += dbScanTest.c dbScanTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbScanTest.c TESTS += dbScanTest TESTPROD_HOST += dbShutdownTest dbShutdownTest_SRCS += dbShutdownTest.c dbShutdownTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbShutdownTest.c TESTS += dbShutdownTest TESTPROD_HOST += dbPutLinkTest dbPutLinkTest_SRCS += dbPutLinkTest.c dbPutLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbPutLinkTest.c TESTS += dbPutLinkTest TESTFILES += ../dbPutLinkTest.db ../dbPutLinkTestJ.db ../dbBadLink.db TESTPROD_HOST += dbLockTest dbLockTest_SRCS += dbLockTest.c dbLockTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbLockTest.c TESTS += dbLockTest TESTFILES += ../dbLockTest.db TESTPROD_HOST += dbStressTest dbStressTest_SRCS += dbStressLock.c dbStressTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp dbStressTest_SYS_LIBS_solaris += rt dbStressTest_SYS_LIBS_Linux += rt TESTS += dbStressTest TESTFILES += ../dbStressLock.db TESTPROD_HOST += testdbConvert testdbConvert_SRCS += testdbConvert.c testHarness_SRCS += testdbConvert.c TESTS += testdbConvert TESTPROD_HOST += callbackTest callbackTest_SRCS += callbackTest.c testHarness_SRCS += callbackTest.c TESTS += callbackTest TESTPROD_HOST += callbackParallelTest callbackParallelTest_SRCS += callbackParallelTest.c testHarness_SRCS += callbackParallelTest.c TESTS += callbackParallelTest TESTPROD_HOST += dbStateTest dbStateTest_SRCS += dbStateTest.c testHarness_SRCS += dbStateTest.c TESTS += dbStateTest TESTPROD_HOST += dbServerTest dbServerTest_SRCS += dbServerTest.c testHarness_SRCS += dbServerTest.c TESTS += dbServerTest TESTPROD_HOST += dbCaStatsTest dbCaStatsTest_SRCS += dbCaStatsTest.c dbCaStatsTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbCaStatsTest.c TESTS += dbCaStatsTest TESTFILES += ../dbCaStats.db TESTPROD_HOST += dbCaLinkTest dbCaLinkTest_SRCS += dbCaLinkTest.c dbCaLinkTest_SRCS += dbCACTest.cpp dbCaLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbCaLinkTest.c testHarness_SRCS += dbCACTest.cpp TESTS += dbCaLinkTest TESTFILES += ../dbCaLinkTest1.db ../dbCaLinkTest2.db ../dbCaLinkTest3.db TESTPROD_HOST += scanIoTest scanIoTest_SRCS += scanIoTest.c scanIoTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += scanIoTest.c TESTFILES += ../scanIoTest.db TESTS += scanIoTest TESTPROD_HOST += dbChannelTest dbChannelTest_SRCS += dbChannelTest.c dbChannelTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbChannelTest.c TESTS += dbChannelTest TARGETS += $(COMMON_DIR)/dbChArrTest.dbd DBDDEPENDS_FILES += dbChArrTest.dbd$(DEP) dbChArrTest_DBD += arrRecord.dbd TESTPROD_HOST += dbChArrTest dbChArrTest_SRCS += dbChArrTest.cpp dbChArrTest_SRCS += dbChArrTest_registerRecordDeviceDriver.cpp testHarness_SRCS += dbChArrTest.cpp testHarness_SRCS += dbChArrTest_registerRecordDeviceDriver.cpp TESTFILES += $(COMMON_DIR)/dbChArrTest.dbd ../dbChArrTest.db TESTS += dbChArrTest TESTPROD_HOST += chfPluginTest chfPluginTest_SRCS += chfPluginTest.c chfPluginTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += chfPluginTest.c TESTS += chfPluginTest TESTPROD_HOST += arrShorthandTest arrShorthandTest_SRCS += arrShorthandTest.c arrShorthandTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += arrShorthandTest.c TESTS += arrShorthandTest TESTPROD_HOST += benchdbConvert benchdbConvert_SRCS += benchdbConvert.c TESTPROD_HOST += recGblCheckDeadbandTest recGblCheckDeadbandTest_SRCS += recGblCheckDeadbandTest.c recGblCheckDeadbandTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += recGblCheckDeadbandTest.c TESTS += recGblCheckDeadbandTest TESTPROD_HOST += testPutGetTest testPutGetTest_SRCS += dbPutGetTest.c testPutGetTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbPutGetTest.c TESTFILES += ../dbPutGetTest.db TESTS += testPutGetTest TESTPROD_HOST += dbStaticTest dbStaticTest_SRCS += dbStaticTest.c dbStaticTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbStaticTest.c TESTFILES += ../dbStaticTest.db TESTS += dbStaticTest # This runs all the test programs in a known working order: testHarness_SRCS += epicsRunDbTests.c dbTestHarness_SRCS += $(testHarness_SRCS) dbTestHarness_SRCS_RTEMS += rtemsTestHarness.c PROD_SRCS_RTEMS += rtemsTestData.c PROD_vxWorks = dbTestHarness PROD_RTEMS = dbTestHarness TESTSPEC_vxWorks = dbTestHarness.munch; epicsRunDbTests TESTSPEC_RTEMS = dbTestHarness.boot; epicsRunDbTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) TESTPROD_RTEMS = $(TESTPROD_HOST) TESTSCRIPTS_RTEMS += $(TESTS:%=%.t) endif include $(TOP)/configure/RULES arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h dbCaLinkTest$(DEP): $(COMMON_DIR)/xRecord.h $(COMMON_DIR)/arrRecord.h dbPutLinkTest$(DEP): $(COMMON_DIR)/xRecord.h dbStressLock$(DEP): $(COMMON_DIR)/xRecord.h devx$(DEP): $(COMMON_DIR)/xRecord.h scanIoTest$(DEP): $(COMMON_DIR)/xRecord.h xRecord$(DEP): $(COMMON_DIR)/xRecord.h rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES) base-7.0.3.1/modules/database/test/ioc/db/arrRecord.c0000664000577000060420000000715013557101274021037 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* arrRecord.c - minimal array record for test purposes: no processing */ /* * Author: Ralph Lange * * vaguely implemented like parts of recWaveform.c by Bob Dalesio * */ #include #include "dbDefs.h" #include "epicsPrint.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "recSup.h" #include "recGbl.h" #include "cantProceed.h" #define GEN_SIZE_OFFSET #include "arrRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); #define special NULL #define get_value NULL static long cvt_dbaddr(DBADDR *); static long get_array_info(DBADDR *, long *, long *); static long put_array_info(DBADDR *, long); #define get_units NULL #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset arrRSET = { RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset, arrRSET); static long init_record(struct dbCommon *pcommon, int pass) { struct arrRecord *prec = (struct arrRecord *)pcommon; if (pass == 0) { if (prec->nelm <= 0) prec->nelm = 1; if (prec->ftvl > DBF_ENUM) prec->ftvl = DBF_UCHAR; prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl), "arr calloc failed"); if (prec->nelm == 1) { prec->nord = 1; } else { prec->nord = 0; } return 0; } return 0; } static long process(struct dbCommon *pcommon) { struct arrRecord *prec = (struct arrRecord *)pcommon; if(prec->clbk) (*prec->clbk)(prec); prec->pact = TRUE; recGblGetTimeStamp(prec); recGblFwdLink(prec); prec->pact = FALSE; return 0; } static long cvt_dbaddr(DBADDR *paddr) { arrRecord *prec = (arrRecord *) paddr->precord; paddr->pfield = prec->bptr; paddr->no_elements = prec->nelm; paddr->field_type = prec->ftvl; paddr->field_size = dbValueSize(prec->ftvl); paddr->dbr_field_type = prec->ftvl; return 0; } static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) { arrRecord *prec = (arrRecord *) paddr->precord; *no_elements = prec->nord; *offset = prec->off; return 0; } static long put_array_info(DBADDR *paddr, long nNew) { arrRecord *prec = (arrRecord *) paddr->precord; prec->nord = nNew; if (prec->nord > prec->nelm) prec->nord = prec->nelm; return 0; } base-7.0.3.1/modules/database/test/ioc/db/arrRecord.dbd0000664000577000060420000000160013557101274021340 0ustar anjaesctlinclude "menuGlobal.dbd" include "menuConvert.dbd" include "menuScan.dbd" recordtype(arr) { include "dbCommon.dbd" field(VAL, DBF_NOACCESS) { prompt("Value") special(SPC_DBADDR) pp(TRUE) extra("void *val") } field(NELM, DBF_ULONG) { prompt("Number of Elements") special(SPC_NOMOD) initial("1") } field(FTVL, DBF_MENU) { prompt("Field Type of Value") special(SPC_NOMOD) menu(menuFtype) } field(NORD, DBF_ULONG) { prompt("Number elements read") special(SPC_NOMOD) } field(OFF, DBF_ULONG) { prompt("Offset into array") } field(BPTR, DBF_NOACCESS) { prompt("Buffer Pointer") special(SPC_NOMOD) extra("void *bptr") } field(INP, DBF_INLINK) { prompt("Input Link") } field(CLBK, DBF_NOACCESS) { prompt("Processing callback") special(SPC_NOMOD) extra("void (*clbk)(struct arrRecord*)") } } base-7.0.3.1/modules/database/test/ioc/db/arrShorthandTest.c0000664000577000060420000000750313557101274022415 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ /* * Test the shorthand array notation [ start : incr : end ] * by registering a thin fake arr plugin * and checking if values are forwarded correctly */ #include #include "chfPlugin.h" #include "dbStaticLib.h" #include "dbAccessDefs.h" #include "registry.h" #include "errlog.h" #include "epicsExit.h" #include "dbUnitTest.h" #include "testMain.h" #include "osiFileName.h" typedef struct myStruct { epicsInt32 start; epicsInt32 incr; epicsInt32 end; } myStruct; static const chfPluginArgDef opts[] = { chfInt32 (myStruct, start, "s", 0, 1), chfInt32 (myStruct, incr, "i", 0, 1), chfInt32 (myStruct, end, "e", 0, 1), chfPluginArgEnd }; static myStruct my; static void * allocPvt(void) { my.start = 0; my.incr = 1; my.end = -1; return &my; } static chfPluginIf myPif = { allocPvt, NULL, /* freePvt, */ NULL, /* parse_error, */ NULL, /* parse_ok, */ NULL, /* channel_open, */ NULL, /* channelRegisterPre, */ NULL, /* channelRegisterPost, */ NULL, /* channel_report, */ NULL /* channel_close */ }; static int checkValues(epicsUInt32 s, epicsUInt32 i, epicsUInt32 e) { if (s == my.start && i == my.incr && e == my.end) return 1; else return 0; } static void testHead (char* title) { testDiag("--------------------------------------------------------"); testDiag("%s", title); testDiag("--------------------------------------------------------"); } void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(arrShorthandTest) { dbChannel *pch; testPlan(26); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("xRecord.db", NULL, NULL); testHead("Register plugin"); testOk(!chfPluginRegister("arr", &myPif, opts), "register fake arr plugin"); eltc(0); testIocInitOk(); eltc(1); #define TESTBAD(Title, Expr) \ testDiag(Title); \ testOk(!(pch = dbChannelCreate("x." Expr)), "dbChannelCreate (" Expr ") fails"); \ if (pch) dbChannelDelete(pch); #define TESTGOOD(Title, Expr, Start, Incr, End) \ testDiag(Title); \ testOk(!!(pch = dbChannelCreate("x." Expr)), "dbChannelCreate (" Expr ")"); \ testOk(checkValues(Start, Incr, End), "parameters set correctly: s=%d i=%d e=%d", Start, Incr, End); \ if (pch) dbChannelDelete(pch); TESTBAD("no parameters []", "[]"); TESTBAD("invalid char at beginning [x", "[x"); TESTBAD("invalid char after 1st arg [2x", "[2x"); TESTBAD("invalid char after 2nd arg [2:3x", "[2:3x"); TESTBAD("invalid char after 3rd arg [2:3:4x", "[2:3:4x"); TESTGOOD("one element [index]", "[2]", 2, 1, 2); TESTGOOD("to end [s:]", "[2:]", 2, 1, -1); TESTGOOD("to end [s::]", "[2::]", 2, 1, -1); TESTGOOD("to end with incr [s:i:]", "[2:3:]", 2, 3, -1); TESTGOOD("from beginning [:e]", "[:2]", 0, 1, 2); TESTGOOD("from beginning [::e]", "[::2]", 0, 1, 2); TESTGOOD("from begin with incr [:i:e]", "[:3:2]", 0, 3, 2); TESTGOOD("range [s:e]", "[2:4]", 2, 1, 4); TESTGOOD("range [s::e]", "[2::4]", 2, 1, 4); TESTGOOD("range with incr [s:i:e]", "[2:3:4]", 2, 3, 4); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/benchdbConvert.c0000664000577000060420000000613713557101274022046 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 Brookhaven Science Assoc, as Operator of Brookhaven * National Laboratory. \*************************************************************************/ #include "string.h" #include "cantProceed.h" #include "dbAddr.h" #include "dbConvert.h" #include "dbDefs.h" #include "epicsTime.h" #include "epicsMath.h" #include "epicsAssert.h" #include "epicsUnitTest.h" #include "testMain.h" typedef struct { size_t nelem, niter; short *output; short *input; GETCONVERTFUNC getter; DBADDR addr; } testData; static long runRep(testData *D) { size_t i; for(i=0; initer; i++) { D->getter(&D->addr, D->output, D->nelem, D->nelem, 0); } return 0; } static void runBench(size_t nelem, size_t niter, size_t nrep) { size_t i; testData tdat; double *reptimes; testDiag("Using %lu element arrays.",(unsigned long)nelem); testDiag("run %lu reps with %lu iterations each", (unsigned long)nrep, (unsigned long)niter); reptimes = callocMustSucceed(nrep, sizeof(*reptimes), "runBench"); tdat.output = callocMustSucceed(nelem, sizeof(*tdat.output), "runBench"); tdat.input = callocMustSucceed(nelem, sizeof(*tdat.input), "runBench"); tdat.nelem = nelem; tdat.niter = niter; tdat.getter = dbGetConvertRoutine[DBF_SHORT][DBF_SHORT]; memset(&tdat.addr, 0, sizeof(tdat.addr)); tdat.addr.field_type = DBF_SHORT; tdat.addr.field_size = nelem*sizeof(*tdat.input); tdat.addr.no_elements = nelem; tdat.addr.pfield = (void*)tdat.input; for(i=0; i #include #include #include #include #include #include "callback.h" #include "cantProceed.h" #include "epicsThread.h" #include "epicsEvent.h" #include "epicsTime.h" #include "epicsUnitTest.h" #include "testMain.h" /* * This test checks both immediate and delayed callbacks in two steps. * In the first step (pass1) NCALLBACKS immediate callbacks are queued. * As each is run it starts a second delayed callback (pass2). * The last delayed callback which runs signals an epicsEvent * to the main thread. * * Two time intervals are measured. The time to queue and run each of * the immediate callbacks, and the actual delay of the delayed callback. * * Slow callbacks no longer fail the test, they just emit a diagnostic. */ #define NCALLBACKS 169 #define DELAY_QUANTUM 0.25 #define TEST_DELAY(i) ((i / NUM_CALLBACK_PRIORITIES) * DELAY_QUANTUM) typedef struct myPvt { epicsCallback cb1; epicsCallback cb2; epicsTimeStamp pass1Time; epicsTimeStamp pass2Time; double delay; int pass; int resultFail; } myPvt; epicsEventId finished; static void myCallback(epicsCallback *pCallback) { myPvt *pmyPvt; callbackGetUser(pmyPvt, pCallback); pmyPvt->pass++; if (pmyPvt->pass == 1) { epicsTimeGetCurrent(&pmyPvt->pass1Time); callbackRequestDelayed(&pmyPvt->cb2, pmyPvt->delay); } else if (pmyPvt->pass == 2) { epicsTimeGetCurrent(&pmyPvt->pass2Time); } else { pmyPvt->resultFail = 1; return; } } static void finalCallback(epicsCallback *pCallback) { myCallback(pCallback); epicsEventSignal(finished); } static void updateStats(double *stats, double val) { if (stats[0] > val) stats[0] = val; if (stats[1] < val) stats[1] = val; stats[2] += val; stats[3] += pow(val, 2.0); stats[4] += 1.; } static void printStats(double *stats, const char* tag) { testDiag("Priority %4s min/avg/max/sigma = %f / %f / %f / %f", tag, stats[0], stats[2]/stats[4], stats[1], sqrt(stats[4]*stats[3]-pow(stats[2], 2.0))/stats[4]); } MAIN(callbackParallelTest) { myPvt *pcbt[NCALLBACKS]; epicsTimeStamp start; int noCpus = epicsThreadGetCPUs(); int i, j, slowups, faults; /* Statistics: min/max/sum/sum^2/n for each priority */ double setupError[NUM_CALLBACK_PRIORITIES][5]; double timeError[NUM_CALLBACK_PRIORITIES][5]; double defaultError[5] = {1,-1,0,0,0}; for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) for (j = 0; j < 5; j++) setupError[i][j] = timeError[i][j] = defaultError[j]; testPlan(2); testDiag("Starting %d parallel callback threads", noCpus); callbackParallelThreads(noCpus, ""); callbackInit(); epicsThreadSleep(1.0); finished = epicsEventMustCreate(epicsEventEmpty); for (i = 0; i < NCALLBACKS ; i++) { pcbt[i] = callocMustSucceed(1, sizeof(myPvt), "pcbt"); callbackSetCallback(myCallback, &pcbt[i]->cb1); callbackSetCallback(myCallback, &pcbt[i]->cb2); callbackSetUser(pcbt[i], &pcbt[i]->cb1); callbackSetUser(pcbt[i], &pcbt[i]->cb2); callbackSetPriority(i % NUM_CALLBACK_PRIORITIES, &pcbt[i]->cb1); callbackSetPriority(i % NUM_CALLBACK_PRIORITIES, &pcbt[i]->cb2); pcbt[i]->delay = TEST_DELAY(i); pcbt[i]->pass = 0; } /* Last callback is special */ callbackSetCallback(finalCallback, &pcbt[NCALLBACKS-1]->cb2); callbackSetPriority(0, &pcbt[NCALLBACKS-1]->cb1); callbackSetPriority(0, &pcbt[NCALLBACKS-1]->cb2); pcbt[NCALLBACKS-1]->delay = TEST_DELAY(NCALLBACKS) + 1.0; pcbt[NCALLBACKS-1]->pass = 0; testOk(epicsTimeGetCurrent(&start)==epicsTimeOK, "Time-of-day clock Ok"); for (i = 0; i < NCALLBACKS ; i++) { callbackRequest(&pcbt[i]->cb1); } testDiag("Waiting %.02f sec", pcbt[NCALLBACKS-1]->delay); epicsEventWait(finished); slowups = 0; faults = 0; for (i = 0; i < NCALLBACKS ; i++) { if(pcbt[i]->resultFail || pcbt[i]->pass!=2) testDiag("callback setup fault #%d: pass = %d for delay = %.02f", ++faults, pcbt[i]->pass, pcbt[i]->delay); else { double delta = epicsTimeDiffInSeconds(&pcbt[i]->pass1Time, &start); if (fabs(delta) >= 0.05) { slowups++; testDiag("callback %.02f setup time |%f| >= 0.05 seconds", pcbt[i]->delay, delta); } updateStats(setupError[i%NUM_CALLBACK_PRIORITIES], delta); } } testOk(faults == 0, "%d faults during callback setup", faults); if (slowups) testDiag("%d slowups during callback setup", slowups); slowups = 0; for (i = 0; i < NCALLBACKS ; i++) { double delta, error; if(pcbt[i]->resultFail || pcbt[i]->pass!=2) continue; delta = epicsTimeDiffInSeconds(&pcbt[i]->pass2Time, &pcbt[i]->pass1Time); error = delta - pcbt[i]->delay; if (fabs(error) >= 0.05) { slowups++; testDiag("delay %.02f seconds, delay error |%.04f| >= 0.05", pcbt[i]->delay, error); } updateStats(timeError[i%NUM_CALLBACK_PRIORITIES], error); } if (slowups) testDiag("%d slowups during callback setup", slowups); testDiag("Setup time statistics"); printStats(setupError[0], "LOW"); printStats(setupError[1], "MID"); printStats(setupError[2], "HIGH"); testDiag("Delay time statistics"); printStats(timeError[0], "LOW"); printStats(timeError[1], "MID"); printStats(timeError[2], "HIGH"); for (i = 0; i < NCALLBACKS ; i++) { free(pcbt[i]); } callbackStop(); callbackCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/callbackTest.c0000664000577000060420000001420413557101274021506 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * Copyright (c) 2013 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Marty Kraimer Date: 26JAN2000 */ #include #include #include #include #include #include #include "callback.h" #include "cantProceed.h" #include "epicsThread.h" #include "epicsEvent.h" #include "epicsTime.h" #include "epicsUnitTest.h" #include "testMain.h" /* * This test checks both immediate and delayed callbacks in two steps. * In the first step (pass1) NCALLBACKS immediate callbacks are queued. * As each is run it starts a second delayed callback (pass2). * The last delayed callback which runs signals an epicsEvent * to the main thread. * * Two time intervals are measured. The time to queue and run each of * the immediate callbacks, and the actual delay of the delayed callback. * * Slow callbacks no longer fail the test, they just emit a diagnostic. */ #define NCALLBACKS 169 #define DELAY_QUANTUM 0.25 #define TEST_DELAY(i) ((i / NUM_CALLBACK_PRIORITIES) * DELAY_QUANTUM) typedef struct myPvt { epicsCallback cb1; epicsCallback cb2; epicsTimeStamp pass1Time; epicsTimeStamp pass2Time; double delay; int pass; int resultFail; } myPvt; epicsEventId finished; static void myCallback(epicsCallback *pCallback) { myPvt *pmyPvt; callbackGetUser(pmyPvt, pCallback); pmyPvt->pass++; if (pmyPvt->pass == 1) { epicsTimeGetCurrent(&pmyPvt->pass1Time); callbackRequestDelayed(&pmyPvt->cb2, pmyPvt->delay); } else if (pmyPvt->pass == 2) { epicsTimeGetCurrent(&pmyPvt->pass2Time); } else { pmyPvt->resultFail = 1; return; } } static void finalCallback(epicsCallback *pCallback) { myCallback(pCallback); epicsEventSignal(finished); } static void updateStats(double *stats, double val) { if (stats[0] > val) stats[0] = val; if (stats[1] < val) stats[1] = val; stats[2] += val; stats[3] += pow(val, 2.0); stats[4] += 1.; } static void printStats(double *stats, const char* tag) { testDiag("Priority %4s min/avg/max/sigma = %f / %f / %f / %f", tag, stats[0], stats[2]/stats[4], stats[1], sqrt(stats[4]*stats[3]-pow(stats[2], 2.0))/stats[4]); } MAIN(callbackTest) { myPvt *pcbt[NCALLBACKS]; epicsTimeStamp start; int i, j, slowups, faults; /* Statistics: min/max/sum/sum^2/n for each priority */ double setupError[NUM_CALLBACK_PRIORITIES][5]; double timeError[NUM_CALLBACK_PRIORITIES][5]; double defaultError[5] = {1,-1,0,0,0}; for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) for (j = 0; j < 5; j++) setupError[i][j] = timeError[i][j] = defaultError[j]; testPlan(2); callbackInit(); epicsThreadSleep(1.0); finished = epicsEventMustCreate(epicsEventEmpty); for (i = 0; i < NCALLBACKS ; i++) { pcbt[i] = callocMustSucceed(1, sizeof(myPvt), "pcbt"); callbackSetCallback(myCallback, &pcbt[i]->cb1); callbackSetCallback(myCallback, &pcbt[i]->cb2); callbackSetUser(pcbt[i], &pcbt[i]->cb1); callbackSetUser(pcbt[i], &pcbt[i]->cb2); callbackSetPriority(i % NUM_CALLBACK_PRIORITIES, &pcbt[i]->cb1); callbackSetPriority(i % NUM_CALLBACK_PRIORITIES, &pcbt[i]->cb2); pcbt[i]->delay = TEST_DELAY(i); pcbt[i]->pass = 0; } /* Last callback is special */ callbackSetCallback(finalCallback, &pcbt[NCALLBACKS-1]->cb2); callbackSetPriority(0, &pcbt[NCALLBACKS-1]->cb1); callbackSetPriority(0, &pcbt[NCALLBACKS-1]->cb2); pcbt[NCALLBACKS-1]->delay = TEST_DELAY(NCALLBACKS) + 1.0; pcbt[NCALLBACKS-1]->pass = 0; testOk(epicsTimeGetCurrent(&start)==epicsTimeOK, "Time-of-day clock Ok"); for (i = 0; i < NCALLBACKS ; i++) { callbackRequest(&pcbt[i]->cb1); } testDiag("Waiting %.02f sec", pcbt[NCALLBACKS-1]->delay); epicsEventWait(finished); slowups = 0; faults = 0; for (i = 0; i < NCALLBACKS ; i++) { if(pcbt[i]->resultFail || pcbt[i]->pass!=2) testDiag("callback setup fault #%d: pass = %d for delay = %.02f", ++faults, pcbt[i]->pass, pcbt[i]->delay); else { double delta = epicsTimeDiffInSeconds(&pcbt[i]->pass1Time, &start); if (fabs(delta) >= 0.05) { slowups++; testDiag("callback %.02f setup time |%f| >= 0.05 seconds", pcbt[i]->delay, delta); } updateStats(setupError[i%NUM_CALLBACK_PRIORITIES], delta); } } testOk(faults == 0, "%d faults during callback setup", faults); if (slowups) testDiag("%d slowups during callback setup", slowups); slowups = 0; for (i = 0; i < NCALLBACKS ; i++) { double delta, error; if(pcbt[i]->resultFail || pcbt[i]->pass!=2) continue; delta = epicsTimeDiffInSeconds(&pcbt[i]->pass2Time, &pcbt[i]->pass1Time); error = delta - pcbt[i]->delay; if (fabs(error) >= 0.05) { slowups++; testDiag("delay %.02f seconds, delay error |%.04f| >= 0.05", pcbt[i]->delay, error); } updateStats(timeError[i%NUM_CALLBACK_PRIORITIES], error); } if (slowups) testDiag("%d slowups during callback setup", slowups); testDiag("Setup time statistics"); printStats(setupError[0], "LOW"); printStats(setupError[1], "MID"); printStats(setupError[2], "HIGH"); testDiag("Delay time statistics"); printStats(timeError[0], "LOW"); printStats(timeError[1], "MID"); printStats(timeError[2], "HIGH"); for (i = 0; i < NCALLBACKS ; i++) { free(pcbt[i]); } callbackStop(); callbackCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/chfPluginTest.c0000664000577000060420000012222613557101274021675 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * für Materialien und Energie GmbH. * Copyright (c) 2014 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #include #include "chfPlugin.h" #include "dbStaticLib.h" #include "dbAccessDefs.h" #include "errlog.h" #include "registry.h" #include "epicsUnitTest.h" #include "testMain.h" #include "osiFileName.h" #define PATTERN 0x55555555 #define TYPE_START 0xAAA #define R_LEVEL 42 /* Expected / actually run callback bit definitions */ #define e_alloc 0x00000001 #define e_free 0x00000002 #define e_error 0x00000004 #define e_ok 0x00000008 #define e_open 0x00000010 #define e_reg_pre 0x00000020 #define e_reg_post 0x00000040 #define e_report 0x00000080 #define e_close 0x00000100 #define e_pre 0x00000200 #define e_post 0x00000400 #define e_dtor 0x00000800 unsigned int e1, e2, c1, c2; unsigned int offset; int drop = -1; db_field_log *dtorpfl; #define e_any (e_alloc | e_free | e_error | e_ok | e_open \ | e_reg_pre | e_reg_post | e_pre | e_post | e_dtor | e_report | e_close) typedef struct myStruct { int sent1; char flag; int sent2; epicsUInt32 ival; int sent3; double dval; int sent4; int enumval; int sent5; char str[20]; int sent6; epicsUInt32 tval; int sent7; char c; char c1[2]; int offpre; int offpost; } myStruct; static const chfPluginEnumType colorEnum[] = { {"R", 1}, {"G", 2}, {"B", 4}, {NULL,0} }; static const chfPluginArgDef sloppyTaggedOpts[] = { chfInt32 (myStruct, tval, "t" , 0, 0), chfTagInt32 (myStruct, ival, "I" , tval, 1, 0, 0), chfTagBoolean(myStruct, flag, "F" , tval, 2, 0, 0), chfTagDouble (myStruct, dval, "D" , tval, 3, 0, 0), chfTagString (myStruct, str, "S" , tval, 4, 0, 0), chfTagEnum (myStruct, enumval, "C" , tval, 5, 0, 0, colorEnum), chfPluginArgEnd }; static const chfPluginArgDef strictTaggedOpts[] = { chfInt32 (myStruct, tval, "t" , 1, 0), chfBoolean (myStruct, flag, "f" , 1, 0), chfTagInt32 (myStruct, ival, "I" , tval, 1, 0, 0), chfTagBoolean(myStruct, flag, "F" , tval, 2, 0, 0), chfTagDouble (myStruct, dval, "D" , tval, 3, 1, 0), chfTagDouble (myStruct, dval, "D2", tval, 4, 1, 0), chfTagEnum (myStruct, enumval, "C" , tval, 5, 0, 0, colorEnum), chfPluginArgEnd }; static const chfPluginArgDef strictOpts[] = { chfInt32 (myStruct, ival, "i" , 1, 0), chfBoolean(myStruct, flag, "f" , 1, 0), chfDouble (myStruct, dval, "d" , 1, 0), chfString (myStruct, str, "s" , 1, 0), chfEnum (myStruct, enumval, "c" , 1, 0, colorEnum), chfPluginArgEnd }; static const chfPluginArgDef noconvOpts[] = { chfInt32 (myStruct, ival, "i" , 0, 0), chfBoolean(myStruct, flag, "f" , 0, 0), chfDouble (myStruct, dval, "d" , 0, 0), chfString (myStruct, str, "s" , 0, 0), chfEnum (myStruct, enumval, "c" , 0, 0, colorEnum), chfPluginArgEnd }; static const chfPluginArgDef sloppyOpts[] = { chfInt32 (myStruct, ival, "i" , 0, 1), chfBoolean(myStruct, flag, "f" , 0, 1), chfDouble (myStruct, dval, "d" , 0, 1), chfString (myStruct, str, "s" , 0, 1), chfEnum (myStruct, enumval, "c" , 0, 1, colorEnum), chfPluginArgEnd }; /* Options defs with not enough room provided */ static const chfPluginArgDef brokenOpts1[] = { chfInt32 (myStruct, c1, "i" , 0, 1), chfPluginArgEnd }; static const chfPluginArgDef brokenOpts2[] = { chfDouble(myStruct, c1, "d" , 0, 1), chfPluginArgEnd }; static const chfPluginArgDef brokenOpts3[] = { chfString(myStruct, c1, "s" , 0, 1), chfPluginArgEnd }; static const chfPluginArgDef brokenOpts4[] = { chfEnum (myStruct, c1, "c" , 0, 1, colorEnum), chfPluginArgEnd }; int p_ok_return = 0; int c_open_return = 0; void *puser1, *puser2; static void clearStruct(void *p) { myStruct *my = (myStruct*) p; if (!my) return; memset(my, 0, sizeof(myStruct)); my->sent1 = my->sent2 = my->sent3 = my->sent4 = my->sent5 = my->sent6 = my->sent7 = PATTERN; my->ival = 12; my->tval = 99; my->flag = 1; my->dval = 1.234e5; strcpy(my->str, "hello"); my->enumval = 4; } static char inst(void* user) { return user == puser1 ? '1' : user == puser2 ? '2' : 'x'; } static void * allocPvt(void) { myStruct *my = (myStruct*) calloc(1, sizeof(myStruct)); if (!puser1) { puser1 = my; testOk(e1 & e_alloc, "allocPvt (1) called"); c1 |= e_alloc; } else if (!puser2) { puser2 = my; testOk(e2 & e_alloc, "allocPvt (2) called"); c2 |= e_alloc; } clearStruct (my); return my; } static void * allocPvtFail(void) { if (!puser1) { testOk(e1 & e_alloc, "allocPvt (1) called"); c1 |= e_alloc; } return NULL; } static void freePvt(void *user) { if (user == puser1) { testOk(e1 & e_free, "freePvt (1) called"); c1 |= e_free; free(user); puser1 = NULL; } else if (user == puser2) { testOk(e2 & e_free, "freePvt (2) called"); c2 |= e_free; free(user); puser2 = NULL; } else testFail("freePvt: user pointer invalid"); } static void parse_error(void *user) { if (user == puser1) { testOk(e1 & e_error, "parse_error (1) called"); c1 |= e_error; } else if (user == puser2) { testOk(e2 & e_error, "parse_error (2) called"); c2 |= e_error; } else testFail("parse_error: user pointer invalid"); } static int parse_ok(void *user) { if (user == puser1) { testOk(e1 & e_ok, "parse_ok (1) called"); c1 |= e_ok; } else if (user == puser2) { testOk(e2 & e_ok, "parse_ok (2) called"); c2 |= e_ok; } else testFail("parse_ok: user pointer invalid"); return p_ok_return; } static long channel_open(dbChannel *chan, void *user) { if (user == puser1) { testOk(e1 & e_open, "channel_open (1) called"); c1 |= e_open; } else if (user == puser2) { testOk(e2 & e_open, "channel_open (2) called"); c2 |= e_open; } else testFail("channel_open: user pointer invalid"); return c_open_return; } static void dbfl_free1(db_field_log *pfl) { testOk(e1 & e_dtor, "dbfl_free (1) called"); testOk(dtorpfl == pfl, "dbfl_free (1): db_field_log pointer correct"); dtorpfl = NULL; c1 |= e_dtor; } static void dbfl_free2(db_field_log *pfl) { testOk(e2 & e_dtor, "dbfl_free (2) called"); testOk(dtorpfl == pfl, "dbfl_free (2): db_field_log pointer correct"); dtorpfl = NULL; c2 |= e_dtor; } static db_field_log * pre(void *user, dbChannel *chan, db_field_log *pLog) { myStruct *my = (myStruct*)user; dbfl_freeFunc *dtor = NULL; if (my == puser1) { testOk(e1 & e_pre, "pre (1) called"); testOk(!(c2 & e_pre), "pre (2) was not called before pre (1)"); c1 |= e_pre; dtor = dbfl_free1; } else if (my == puser2) { testOk(e2 & e_pre, "pre (2) called"); testOk(!(e1 & e_pre) || c1 & e_pre, "pre (1) was called before pre (2)"); c2 |= e_pre; dtor = dbfl_free2; } else { testFail("pre: user pointer invalid"); testSkip(1, "Can't check order of pre(1)/pre(2)"); } testOk(!(c1 & e_post), "post (1) was not called before pre (%c)", inst(user)); testOk(!(c2 & e_post), "post (2) was not called before pre (%c)", inst(user)); if (!testOk(pLog->field_type == TYPE_START + my->offpre, "pre (%c) got field log of expected type", inst(user))) testDiag("expected: %d, got %d", TYPE_START + my->offpre, pLog->field_type); pLog->field_type++; if (my->offpre == 0) { /* The first one registers a dtor and saves pfl */ pLog->u.r.dtor = dtor; dtorpfl = pLog; } if (my->offpre == drop) { testDiag("pre (%c) is dropping the field log", inst(user)); return NULL; } return pLog; } static db_field_log * post(void *user, dbChannel *chan, db_field_log *pLog) { myStruct *my = (myStruct*)user; dbfl_freeFunc *dtor = NULL; if (my == puser1) { testOk(e1 & e_post, "post (1) called"); testOk(!(c2 & e_post), "post (2) was not called before post (1)"); c1 |= e_post; dtor = dbfl_free1; } else if (my == puser2) { testOk(e2 & e_post, "post (2) called"); testOk(!(e1 & e_post) || c1 & e_post, "post (1) was called before post (2)"); c2 |= e_post; dtor = dbfl_free2; } else { testFail("post: user pointer invalid"); testSkip(1, "Can't check order of post(1)/post(2)"); } testOk(!(e1 & e_pre) || c1 & e_pre, "pre (1) was called before post (%c)", inst(user)); testOk(!(e2 & e_pre) || c2 & e_pre, "pre (2) was called before post (%c)", inst(user)); if (!testOk(pLog->field_type == TYPE_START + my->offpost, "post (%c) got field log of expected type", inst(user))) testDiag("expected: %d, got %d", TYPE_START + my->offpost, pLog->field_type); pLog->field_type++; if (my->offpost == 0) { /* The first one registers a dtor and saves pfl */ pLog->u.r.dtor = dtor; dtorpfl = pLog; } if (my->offpost == drop) { testDiag("post (%c) is dropping the field log", inst(user)); return NULL; } return pLog; } static void channelRegisterPre(dbChannel *chan, void *user, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { myStruct *my = (myStruct*)user; if (my == puser1) { testOk(e1 & e_reg_pre, "register_pre (1) called"); testOk(!(c2 & e_reg_pre), "register_pre (2) was not called before register_pre (1)"); c1 |= e_reg_pre; } else if (my == puser2) { testOk(e2 & e_reg_pre, "register_pre (2) called"); testOk(!(e1 & e_reg_pre) || c1 & e_reg_pre, "register_pre (1) was called before register_pre (2)"); c2 |= e_reg_pre; } else { testFail("register_pre: user pointer invalid"); testSkip(1, "Can't check order of register_pre(1)/register_pre(2)"); } testOk(!(c1 & e_reg_post), "register_post (1) was not called before register_pre (%c)", inst(user)); testOk(!(c2 & e_reg_post), "register_post (2) was not called before register_pre (%c)", inst(user)); my->offpre = offset++; probe->field_type++; *cb_out = pre; *arg_out = user; } static void channelRegisterPost(dbChannel *chan, void *user, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { myStruct *my = (myStruct*)user; if (my == puser1) { testOk(e1 & e_reg_post, "register_post (1) called"); testOk(!(c2 & e_reg_post), "register_post (2) was not called before register_post (1)"); c1 |= e_reg_post; } else if (my == puser2) { testOk(e2 & e_reg_post, "register_post (2) called"); testOk(!(e1 & e_reg_post) || c1 & e_reg_post, "register_post (1) was called before register_post (2)"); c2 |= e_reg_post; } else { testFail("register_post: user pointer invalid"); testSkip(1, "Can't check order of register_post(1)/register_post(2)"); } testOk(!(e1 & e_reg_pre) || c1 & e_reg_pre, "register_pre (1) was called before register_post (%c)", inst(user)); testOk(!(e2 & e_reg_pre) || c2 & e_reg_pre, "register_pre (2) was called before register_post (%c)", inst(user)); my->offpost = offset++; probe->field_type++; *cb_out = post; *arg_out = user; } static void channel_report(dbChannel *chan, void *user, int level, const unsigned short indent) { testOk(level == R_LEVEL - 2, "channel_report: level correct %u == %u", level, R_LEVEL-2); if (user == puser1) { testOk(e1 & e_report, "channel_report (1) called"); c1 |= e_report; } else if (user == puser2) { testOk(e2 & e_report, "channel_report (2) called"); c2 |= e_report; } else testFail("channel_report: user pointer invalid"); } static void channel_close(dbChannel *chan, void *user) { if (user == puser1) { testOk(e1 & e_close, "channel_close (1) called"); c1 |= e_close; } else if (user == puser2) { testOk(e2 & e_close, "channel_close (2) called"); c2 |= e_close; } else testFail("channel_close: user pointer invalid"); } static chfPluginIf myPif = { allocPvt, freePvt, parse_error, parse_ok, channel_open, channelRegisterPre, channelRegisterPost, channel_report, channel_close }; static chfPluginIf prePif = { allocPvt, freePvt, parse_error, parse_ok, channel_open, channelRegisterPre, NULL, channel_report, channel_close }; static chfPluginIf postPif = { allocPvt, freePvt, parse_error, parse_ok, channel_open, NULL, channelRegisterPost, channel_report, channel_close }; static chfPluginIf allocFailPif = { allocPvtFail, freePvt, parse_error, parse_ok, channel_open, channelRegisterPre, channelRegisterPost, channel_report, channel_close }; static int checkValues(myStruct *my, char t, epicsUInt32 i, int f, double d, char *s1, char *s2, int c) { int ret = 1; int s1fail, s2fail; int s2valid = (s2 && s2[0] != '\0'); if (!my) return 0; #define CHK(A,B,FMT) if((A)!=(B)) {testDiag("Fail: " #A " (" FMT ") != " #B " (" FMT")", A, B); ret=0;} CHK(my->sent1, PATTERN, "%08x") CHK(my->sent2, PATTERN, "%08x") CHK(my->sent3, PATTERN, "%08x") CHK(my->sent4, PATTERN, "%08x") CHK(my->sent5, PATTERN, "%08x") CHK(my->sent6, PATTERN, "%08x") CHK(my->sent7, PATTERN, "%08x") CHK(my->tval, t, "%08x") CHK(my->ival, i, "%08x") CHK(my->flag, f, "%02x") CHK(my->dval, d, "%f") CHK(my->enumval, c, "%d") #undef CHK s2fail = s1fail = strcmp(s1, my->str); if (s2valid) s2fail = strcmp(s2, my->str); if (s1fail && s2fail) { if (s1fail) testDiag("Fail: my->str (%s) != s (%s)", my->str, s1); if (s2valid && s2fail) testDiag("Fail: my->str (%s) != s (%s)", my->str, s2); ret = 0; } return ret; } static void testHead (char* title) { testDiag("--------------------------------------------------------"); testDiag("%s", title); testDiag("--------------------------------------------------------"); } void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(chfPluginTest) { dbChannel *pch; db_field_log *pfl; #ifdef _WIN32 #if (defined(_MSC_VER) && _MSC_VER < 1900) || \ (defined(_MINGW) && defined(_TWO_DIGIT_EXPONENT)) _set_output_format(_TWO_DIGIT_EXPONENT); #endif #endif testPlan(1433); dbChannelInit(); db_init_events(); /* Enum to string conversion */ testHead("Enum to string conversion"); testOk(strcmp(chfPluginEnumString(colorEnum, 1, "-"), "R") == 0, "Enum to string: R"); testOk(strcmp(chfPluginEnumString(colorEnum, 2, "-"), "G") == 0, "Enum to string: G"); testOk(strcmp(chfPluginEnumString(colorEnum, 4, "-"), "B") == 0, "Enum to string: B"); testOk(strcmp(chfPluginEnumString(colorEnum, 3, "-"), "-") == 0, "Enum to string: invalid index"); if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd", "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)) testAbort("Database description 'dbTestIoc.dbd' not found"); dbTestIoc_registerRecordDeviceDriver(pdbbase); if (dbReadDatabase(&pdbbase, "xRecord.db", "." OSI_PATH_LIST_SEPARATOR "..", NULL)) testAbort("Test database 'xRecord.db' not found"); testHead("Try to register buggy plugins"); eltc(0); testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts1), "not enough storage for integer"); testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts2), "not enough storage for double"); testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts3), "not enough storage for string"); testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts4), "not enough storage for enum"); errlogFlush(); eltc(1); testHead("Register plugins"); testOk(!chfPluginRegister("sloppy-tagged", &myPif, sloppyTaggedOpts), "register plugin sloppy-tagged"); testOk(!chfPluginRegister("strict-tagged", &myPif, strictTaggedOpts), "register plugin strict-tagged"); testOk(!chfPluginRegister("strict", &myPif, strictOpts), "register plugin strict"); testOk(!chfPluginRegister("noconv", &myPif, noconvOpts), "register plugin noconv"); testOk(!chfPluginRegister("sloppy", &myPif, sloppyOpts), "register plugin sloppy"); testOk(!chfPluginRegister("pre", &prePif, sloppyOpts), "register plugin pre"); testOk(!chfPluginRegister("post", &postPif, sloppyOpts), "register plugin post"); testOk(!chfPluginRegister("alloc-fail", &allocFailPif, sloppyOpts), "register plugin alloc-fail"); /* Check failing allocation of plugin private structures */ testHead("Failing allocation of plugin private structures"); /* tag i */ e1 = e_alloc; c1 = 0; testOk(!(pch = dbChannelCreate( "x.{\"alloc-fail\":{\"i\":1}}")), "create channel for alloc-fail: allocPvt returning NULL"); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* TAGGED parsing: shorthand for integer plus other parameter */ /* STRICT TAGGED parsing: mandatory, no conversions */ /* All perfect */ testHead("STRICT TAGGED parsing: all ok"); /* tag D (t and d) and f */ e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate( "x.{\"strict-tagged\":{\"D\":1.2e15,\"f\":false}}")), "create channel for strict-tagged parsing: D (t and d) and f"); testOk(checkValues(puser1, 3, 12, 0, 1.2e15, "hello", 0, 4), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_close | e_free; c1 = 0; if (pch) dbChannelDelete(pch); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* tag D2 (t and d) and f */ e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate( "x.{\"strict-tagged\":{\"D2\":1.2e15,\"f\":false}}")), "create channel for strict-tagged parsing: D2 (t and d) and f"); testOk(checkValues(puser1, 4, 12, 0, 1.2e15, "hello", 0, 4), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_close | e_free; c1 = 0; if (pch) dbChannelDelete(pch); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* tag F: (t and f), d missing) */ e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate( "x.{\"strict-tagged\":{\"F\":false}}")), "create channel for strict-tagged parsing: F (t and f), d missing"); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* tag I: (t and i) and f, d missing) */ e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate( "x.{\"strict-tagged\":{\"I\":1,\"f\":false}}")), "create channel for strict-tagged parsing: I (t and i) and f, d missing"); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* SLOPPY TAGGED parsing: optional, all others have defaults */ testHead("SLOPPY TAGGED parsing: all ok"); /* tag i */ e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate( "x.{\"sloppy-tagged\":{\"I\":1}}")), "create channel for sloppy-tagged parsing: I"); testOk(checkValues(puser1, 1, 1, 1, 1.234e5, "hello", 0, 4), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_close | e_free; c1 = 0; if (pch) dbChannelDelete(pch); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* tag f */ e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate( "x.{\"sloppy-tagged\":{\"F\":false}}")), "create channel for sloppy-tagged parsing: F"); testOk(checkValues(puser1, 2, 12, 0, 1.234e5, "hello", 0, 4), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_close | e_free; c1 = 0; if (pch) dbChannelDelete(pch); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* tag d */ e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate( "x.{\"sloppy-tagged\":{\"D\":1.2e15}}")), "create channel for sloppy-tagged parsing: D"); testOk(checkValues(puser1, 3, 12, 1, 1.2e15, "hello", 0, 4), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_close | e_free; c1 = 0; if (pch) dbChannelDelete(pch); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* tag s */ e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate( "x.{\"sloppy-tagged\":{\"S\":\"bar\"}}")), "create channel for sloppy-tagged parsing: S"); testOk(checkValues(puser1, 4, 12, 1, 1.234e5, "bar", 0, 4), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_close | e_free; c1 = 0; if (pch) dbChannelDelete(pch); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* tag c */ e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate( "x.{\"sloppy-tagged\":{\"C\":\"R\"}}")), "create channel for sloppy-tagged parsing: C"); testOk(checkValues(puser1, 5, 12, 1, 1.234e5, "hello", 0, 1), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_close | e_free; c1 = 0; if (pch) dbChannelDelete(pch); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* STRICT parsing: mandatory, no conversion */ /* All perfect */ testHead("STRICT parsing: all ok"); e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "create channel for strict parsing: JSON correct"); testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "bar", 0, 1), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_close | e_free; c1 = 0; if (pch) dbChannelDelete(pch); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* Any one missing must fail */ testHead("STRICT parsing: any missing parameter must fail"); e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate( "x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), "create channel for strict parsing: c missing"); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate( "x.{\"strict\":{\"f\":false,\"i\":1,\"d\":1.2e15,\"c\":\"R\"}}")), "create channel for strict parsing: s missing"); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate( "x.{\"strict\":{\"i\":1,\"c\":\"R\",\"f\":false,\"s\":\"bar\"}}")), "create channel for strict parsing: d missing"); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate( "x.{\"strict\":{\"d\":1.2e15,\"c\":\"R\",\"i\":1,\"s\":\"bar\"}}")), "create channel for strict parsing: f missing"); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate( "x.{\"strict\":{\"c\":\"R\",\"s\":\"bar\",\"f\":false,\"d\":1.2e15}}")), "create channel for strict parsing: i missing"); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* NOCONV parsing: optional, no conversion */ /* Any one missing must leave the default intact */ testHead("NOCONV parsing: missing parameters get default value"); e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate( "x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), "create channel for noconv parsing: c missing"); testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "bar", 0, 4), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_close | e_free; c1 = 0; if (pch) dbChannelDelete(pch); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_any; testOk(!!(pch = dbChannelCreate( "x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"c\":\"R\"}}")), "create channel for noconv parsing: s missing"); testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "hello", 0, 1), "guards intact, values correct"); if (pch) dbChannelDelete(pch); testOk(!!(pch = dbChannelCreate( "x.{\"noconv\":{\"i\":1,\"f\":false,\"s\":\"bar\",\"c\":\"R\"}}")), "create channel for noconv parsing: d missing"); testOk(checkValues(puser1, 99, 1, 0, 1.234e5, "bar", 0, 1), "guards intact, values correct"); if (pch) dbChannelDelete(pch); testOk(!!(pch = dbChannelCreate( "x.{\"noconv\":{\"i\":1,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "create channel for noconv parsing: f missing"); testOk(checkValues(puser1, 99, 1, 1, 1.2e15, "bar", 0, 1), "guards intact, values correct"); if (pch) dbChannelDelete(pch); testOk(!!(pch = dbChannelCreate( "x.{\"noconv\":{\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "create channel for noconv parsing: i missing"); testOk(checkValues(puser1, 99, 12, 0, 1.2e15, "bar", 0, 1), "guards intact, values correct"); if (pch) dbChannelDelete(pch); /* Reject wrong types */ #define WRONGTYPETEST(Var, Val, Typ) \ e1 = e_alloc | e_error | e_free; c1 = 0; \ testOk(!(pch = dbChannelCreate("x.{\"noconv\":{\""#Var"\":"#Val"}}")), \ "create channel for noconv parsing: wrong type "#Typ" for "#Var); \ testOk(!puser1, "user part cleaned up"); \ if (!testOk(c1 == e1, "all expected calls happened")) \ testDiag("expected %#x - called %#x", e1, c1); testHead("NOCONV parsing: rejection of wrong parameter types"); WRONGTYPETEST(i, 123.0, double); WRONGTYPETEST(i, true, boolean); WRONGTYPETEST(i, "1", string); WRONGTYPETEST(f, "false", string); WRONGTYPETEST(f, 0.0, double); WRONGTYPETEST(f, 1, integer); WRONGTYPETEST(d, "1.2", string); WRONGTYPETEST(d, true, boolean); WRONGTYPETEST(d, 123, integer); WRONGTYPETEST(s, 1.23, double); WRONGTYPETEST(s, true, boolean); WRONGTYPETEST(s, 123, integer); WRONGTYPETEST(c, 1.23, double); WRONGTYPETEST(c, true, boolean); WRONGTYPETEST(c, 2, integer); /* SLOPPY parsing: optional, with conversion */ #define CONVTESTGOOD(Var, Val, Typ, Ival, Fval, Dval, Sval1, Sval2, Cval) \ e1 = e_alloc | e_ok; c1 = 0; \ testDiag("Calling dbChannelCreate x.{\"sloppy\":{\""#Var"\":"#Val"}}"); \ testOk(!!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), \ "create channel for sloppy parsing: "#Typ" (good) for "#Var); \ testOk(checkValues(puser1, 99, Ival, Fval, Dval, Sval1, Sval2, Cval), \ "guards intact, values correct"); \ if (!testOk(c1 == e1, "create channel: all expected calls happened")) \ testDiag("expected %#x - called %#x", e1, c1); \ e1 = e_close | e_free; c1 = 0; \ if (pch) dbChannelDelete(pch); \ testOk(!puser1, "user part cleaned up"); \ if (!testOk(c1 == e1, "delete channel: all expected calls happened")) \ testDiag("expected %#x - called %#x", e1, c1); #define CONVTESTBAD(Var, Val, Typ) \ e1 = e_alloc | e_error | e_free; c1 = 0; \ testDiag("Calling dbChannelCreate x.{\"sloppy\":{\""#Var"\":"#Val"}}"); \ testOk(!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), \ "create channel for sloppy parsing: "#Typ" (bad) for "#Var); \ testOk(!puser1, "user part cleaned up"); \ if (!testOk(c1 == e1, "create channel: all expected calls happened")) \ testDiag("expected %#x - called %#x", e1, c1); /* To integer */ testHead("SLOPPY parsing: conversion to integer"); CONVTESTGOOD(i, "123e4", positive string, 123, 1, 1.234e5, "hello", 0, 4); CONVTESTGOOD(i, "-12345", negative string, -12345, 1, 1.234e5, "hello", 0, 4); CONVTESTBAD(i, "9234567890", out-of-range string); CONVTESTBAD(i, ".4", invalid string); CONVTESTGOOD(i, false, valid boolean, 0, 1, 1.234e5, "hello", 0, 4); CONVTESTGOOD(i, 3456.789, valid double, 3456, 1, 1.234e5, "hello", 0, 4); CONVTESTBAD(i, 34.7e14, out-of-range double); /* To boolean */ testHead("SLOPPY parsing: conversion to boolean"); CONVTESTGOOD(f, "false", valid string, 12, 0, 1.234e5, "hello", 0, 4); CONVTESTGOOD(f, "False", capital valid string, 12, 0, 1.234e5, "hello", 0, 4); CONVTESTGOOD(f, "0", 0 string, 12, 0, 1.234e5, "hello", 0, 4); CONVTESTGOOD(f, "15", 15 string, 12, 1, 1.234e5, "hello", 0, 4); CONVTESTBAD(f, ".4", invalid .4 string); CONVTESTBAD(f, "Flase", misspelled invalid string); CONVTESTGOOD(f, 0, zero integer, 12, 0, 1.234e5, "hello", 0, 4); CONVTESTGOOD(f, 12, positive integer, 12, 1, 1.234e5, "hello", 0, 4); CONVTESTGOOD(f, -1234, negative integer, 12, 1, 1.234e5, "hello", 0, 4); CONVTESTGOOD(f, 0.4, positive non-zero double, 12, 1, 1.234e5, "hello", 0, 4); CONVTESTGOOD(f, 0.0, zero double, 12, 0, 1.234e5, "hello", 0, 4); CONVTESTGOOD(f, -0.0, minus-zero double, 12, 0, 1.234e5, "hello", 0, 4); CONVTESTGOOD(f, -1.24e14, negative double, 12, 1, 1.234e5, "hello", 0, 4); /* To double */ testHead("SLOPPY parsing: conversion to double"); CONVTESTGOOD(d, "123e4", positive double string, 12, 1, 1.23e6, "hello", 0, 4); CONVTESTGOOD(d, "-7.89e-14", negative double string, 12, 1, -7.89e-14, "hello", 0, 4); CONVTESTGOOD(d, "123", positive integer string, 12, 1, 123.0, "hello", 0, 4); CONVTESTGOOD(d, "-1234567", negative integer string, 12, 1, -1.234567e6, "hello", 0, 4); CONVTESTBAD(d, "1.67e407", out-of-range double string); CONVTESTBAD(d, "blubb", invalid blubb string); CONVTESTGOOD(d, 123, positive integer, 12, 1, 123.0, "hello", 0, 4); CONVTESTGOOD(d, -12345, negative integer, 12, 1, -1.2345e4, "hello", 0, 4); CONVTESTGOOD(d, true, true boolean, 12, 1, 1.0, "hello", 0, 4); CONVTESTGOOD(d, false, false boolean, 12, 1, 0.0, "hello", 0, 4); /* To string */ testHead("SLOPPY parsing: conversion to string"); CONVTESTGOOD(s, 12345, positive integer, 12, 1, 1.234e5, "12345", 0, 4); CONVTESTGOOD(s, -1234567891, negative integer, 12, 1, 1.234e5, "-1234567891", 0, 4); CONVTESTGOOD(s, true, true boolean, 12, 1, 1.234e5, "true", 0, 4); CONVTESTGOOD(s, false, false boolean, 12, 1, 1.234e5, "false", 0, 4); CONVTESTGOOD(s, 123e4, small positive double, 12, 1, 1.234e5, "1230000", 0, 4); CONVTESTGOOD(s, -123e24, negative double, 12, 1, 1.234e5, "-1.23e+26", "-1.23e+026", 4); CONVTESTGOOD(s, -1.23456789123e26, large negative double, 12, 1, 1.234e5, "-1.23456789123e+26", "-1.23456789123e+026", 4); /* To Enum */ testHead("SLOPPY parsing: conversion to enum"); CONVTESTGOOD(c, 2, valid integer choice, 12, 1, 1.234e5, "hello", 0, 2); CONVTESTBAD(c, 3, invalid integer choice); CONVTESTBAD(c, 3.2, double); CONVTESTGOOD(c, "R", valid string choice, 12, 1, 1.234e5, "hello", 0, 1); CONVTESTBAD(c, "blubb", invalid string choice); /* Registering and running filter callbacks */ #define CHAINTEST1(Type, Json, ExpReg, ExpRun, DType) \ testHead("Filter chain test, "Type" filter"); \ offset = 0; \ e1 = e_alloc | e_ok; c1 = 0; \ testOk(!!(pch = dbChannelCreate("x."Json)), "filter chains: create channel with "Type" filter"); \ if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ e1 = e_open | ExpReg; c1 = 0; \ testOk(!dbChannelOpen(pch), "dbChannelOpen returned channel"); \ if (!testOk(c1 == e1, "open channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ e1 = ExpRun; c1 = 0; \ testOk(!!(pfl = db_create_read_log(pch)), "create db_field_log"); \ pfl->type = dbfl_type_ref; \ pfl->field_type = TYPE_START; \ testOk(!!(pfl = dbChannelRunPreChain(pch, pfl)), "run pre eventq chain"); \ testOk(!!(pfl = dbChannelRunPostChain(pch, pfl)), "run post eventq chain"); \ testOk(pfl->field_type == TYPE_START + DType, "final data type is correct"); \ db_delete_field_log(pfl); \ if (!testOk(c1 == e1, "run filter chains: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ e1 = e_report; c1 = 0; \ dbChannelShow(pch, R_LEVEL, 0); \ if (!testOk(c1 == e1, "report: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ e1 = e_close | e_free; c1 = 0; \ if (pch) dbChannelDelete(pch); \ if (!testOk(c1 == e1, "delete channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); #define CHAINTEST2(Type, Json, ExpReg1, ExpRun1, ExpReg2, ExpRun2, DType) \ testHead("Filter chain test, "Type" filters"); \ offset = 0; \ e1 = e_alloc | e_ok; c1 = 0; \ e2 = e_alloc | e_ok; c2 = 0; \ testOk(!!(pch = dbChannelCreate("x."Json)), "filter chains: create channel with "Type" filters"); \ if (!testOk(c1 == e1, "create channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ if (!testOk(c2 == e2, "create channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ e1 = e_open | ExpReg1; c1 = 0; \ e2 = e_open | ExpReg2; c2 = 0; \ if (pch) testOk(!dbChannelOpen(pch), "dbChannelOpen returned channel"); \ if (!testOk(c1 == e1, "open channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ if (!testOk(c2 == e2, "open channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ e1 = ExpRun1; c1 = 0; \ e2 = ExpRun2; c2 = 0; \ if (pch) testOk(!!(pfl = db_create_read_log(pch)), "create db_field_log"); \ pfl->type = dbfl_type_ref; \ pfl->field_type = TYPE_START; \ if (pch) testOk(!!(pfl = dbChannelRunPreChain(pch, pfl)) || (drop >=0 && drop <= 1), "run pre eventq chain"); \ if (pch && (drop < 0 || drop >= 2)) testOk(!!(pfl = dbChannelRunPostChain(pch, pfl)) || drop >=2, "run post eventq chain"); \ if (pfl) testOk(pfl->field_type == TYPE_START + DType, "final data type is correct"); \ if (pfl) db_delete_field_log(pfl); \ if (!testOk(c1 == e1, "run filter chains (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ if (!testOk(c2 == e2, "run filter chains (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ e1 = e_report; c1 = 0; \ e2 = e_report; c2 = 0; \ dbChannelShow(pch, R_LEVEL, 0); \ if (!testOk(c1 == e1, "report (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ if (!testOk(c2 == e2, "report (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ e1 = e_close | e_free; c1 = 0; \ e2 = e_close | e_free; c2 = 0; \ if (pch) dbChannelDelete(pch); \ if (!testOk(c1 == e1, "delete channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ if (!testOk(c2 == e2, "delete channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); CHAINTEST1("1 pre", "{\"pre\":{}}", e_reg_pre, e_pre | e_dtor, 1); /* One filter, pre chain */ CHAINTEST1("1 post", "{\"post\":{}}", e_reg_post, e_post | e_dtor, 1); /* One filter, post chain */ CHAINTEST1("1 both", "{\"sloppy\":{}}", e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, 2); /* One, both chains */ CHAINTEST2("2 pre", "{\"pre\":{},\"pre\":{}}", e_reg_pre, e_pre | e_dtor, e_reg_pre, e_pre, 2); /* Two filters, pre chain */ CHAINTEST2("2 post", "{\"post\":{},\"post\":{}}", e_reg_post, e_post | e_dtor, e_reg_post, e_post, 2); /* Two filters, post chain */ CHAINTEST2("2 both", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains */ e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_pre | e_reg_post, e_pre | e_post, 4); CHAINTEST2("1 pre, 1 post", "{\"pre\":{},\"post\":{}}", e_reg_pre, e_pre | e_dtor, e_reg_post, e_post, 2); /* Two, pre then post */ CHAINTEST2("1 post, 1 pre", "{\"post\":{},\"pre\":{}}", e_reg_post, e_post, e_reg_pre, e_pre | e_dtor, 2); /* Two, post then pre */ CHAINTEST2("1 pre, 1 both", "{\"pre\":{},\"sloppy\":{}}", /* Two, pre then both */ e_reg_pre, e_pre | e_dtor, e_reg_pre | e_reg_post, e_pre | e_post, 3); CHAINTEST2("1 both, 1 pre", "{\"sloppy\":{},\"pre\":{}}", /* Two, both then pre */ e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_pre, e_pre, 3); CHAINTEST2("1 post, 1 both", "{\"post\":{},\"sloppy\":{}}", /* Two, post then both */ e_reg_post, e_post, e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, 3); CHAINTEST2("1 both, 1 post", "{\"sloppy\":{},\"post\":{}}", /* Two, both then post */ e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_post, e_post, 3); /* Plugins dropping updates */ drop = 0; CHAINTEST2("2 both (drop at 0)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 0 */ e_reg_pre | e_reg_post, e_pre, e_reg_pre | e_reg_post, 0, -1); drop = 1; CHAINTEST2("2 both (drop at 1)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 1 */ e_reg_pre | e_reg_post, e_pre, e_reg_pre | e_reg_post, e_pre, -1); drop = 2; CHAINTEST2("2 both (drop at 2)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 2 */ e_reg_pre | e_reg_post, e_pre | e_post, e_reg_pre | e_reg_post, e_pre, -1); drop = 3; CHAINTEST2("2 both (drop at 3)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 3 */ e_reg_pre | e_reg_post, e_pre | e_post, e_reg_pre | e_reg_post, e_pre | e_post, -1); drop = -1; dbFreeBase(pdbbase); registryFree(); pdbbase = NULL; return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/dbBadLink.db0000664000577000060420000000070113557101274021064 0ustar anjaesctl# The records in this file have intentional # syntax error in their input links record(x, "eVME_IO1") { field(DTYP, "Unit Test VME_IO") field(INP, "C100 S101 @parm VME_IO") } record(x, "eVME_IO2") { field(DTYP, "Unit Test VME_IO") field(INP, "#C200 201 @parm VME_IO") } record(x, "eINST_IO") { field(DTYP, "Unit Test INST_IO") field(INP, "hello") } record(x, "eINST_IO2") { field(DTYP, "Unit Test INST_IO") field(INP, "RL @no") } base-7.0.3.1/modules/database/test/ioc/db/dbCACTest.cpp0000664000577000060420000000420013557101274021201 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Part of dbCaLinkTest, compiled seperately to avoid * dbAccess.h vs. db_access.h conflicts */ #include #include #include #include #include "epicsUnitTest.h" #include "cadef.h" #define testECA(OP) if((OP)!=ECA_NORMAL) {testAbort("%s", #OP);} else {testPass("%s", #OP);} void putgetarray(chid chanid, double first, size_t count) { testDiag("putgetarray(%f,%u)", first, (unsigned)count); std::vector buf(count); for(size_t i=0; i buf2(count); testECA(ca_array_get(DBR_DOUBLE, count, chanid, &buf2[0])); testECA(ca_pend_io(1.0)); for(size_t i=0; i */ #include #include #include #define EPICS_DBCA_PRIVATE_API #include "epicsString.h" #include "dbUnitTest.h" #include "epicsThread.h" #include "cantProceed.h" #include "epicsEvent.h" #include "iocInit.h" #include "dbBase.h" #include "link.h" #include "dbAccess.h" #include "epicsStdio.h" #include "dbEvent.h" /* Declarations from cadef.h and db_access.h which we can't include here */ typedef void * chid; typedef void * evid; epicsShareExtern const unsigned short dbr_value_size[]; epicsShareExtern short epicsShareAPI ca_field_type (chid chan); #define MAX_UNITS_SIZE 8 #include "dbCaPvt.h" #include "errlog.h" #include "testMain.h" #include "xRecord.h" #include "arrRecord.h" #define testOp(FMT,A,OP,B) testOk((A)OP(B), #A " ("FMT") " #OP " " #B " ("FMT")", A,B) void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); static epicsEventId waitEvent; static unsigned waitCounter; static void waitForUpdateN(DBLINK *plink, unsigned long n) { while(dbCaGetUpdateCount(plink)val = val; db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE); dbScanUnlock((dbCommon*)ptarg); epicsThreadSleep(0.1); } if (waitEvent) epicsEventMustTrigger(waitEvent); } static void testNativeLink(void) { xRecord *psrc, *ptarg; DBLINK *psrclnk; epicsInt32 temp; long nReq; testDiag("Link to a scalar numeric field"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target CA"); eltc(0); testIocInitOk(); eltc(1); psrc = (xRecord*)testdbRecordPtr("source"); ptarg= (xRecord*)testdbRecordPtr("target"); psrclnk = &psrc->lnk; /* ensure this is really a CA link */ testOk1(dbLockGetLockId((dbCommon*)psrc)!=dbLockGetLockId((dbCommon*)ptarg)); testOk1(psrclnk->type==CA_LINK); waitForUpdateN(psrclnk, 1); dbScanLock((dbCommon*)ptarg); ptarg->val = 42; db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE); dbScanUnlock((dbCommon*)ptarg); waitForUpdateN(psrclnk, 2); dbScanLock((dbCommon*)psrc); /* local CA_LINK connects immediately */ testOk1(dbCaIsLinkConnected(psrclnk)); nReq = 422; testOk1(dbCaGetNelements(psrclnk, &nReq)==0); testOp("%ld",nReq,==,1l); nReq = 1; temp = 0x0f0f0f0f; testOk1(dbGetLink(psrclnk, DBR_LONG, (void*)&temp, NULL, &nReq)==0); testOp("%d",temp,==,42); dbScanUnlock((dbCommon*)psrc); temp = 1010; nReq = 1; putLink(psrclnk, DBR_LONG, (void*)&temp, nReq); dbScanLock((dbCommon*)ptarg); testOk1(ptarg->val==1010); dbScanUnlock((dbCommon*)ptarg); assert(!waitEvent); waitEvent = epicsEventMustCreate(epicsEventEmpty); /* Start counter */ epicsThreadCreate("countUp", epicsThreadPriorityHigh, epicsThreadGetStackSize(epicsThreadStackSmall), countUp, ptarg); dbScanLock((dbCommon*)psrc); /* Check that unlocked gets change */ temp = getTwice(psrclnk, NULL); testOk(temp == -1, "unlocked, getTwice returned %d (-1)", temp); /* Check locked gets are atomic */ temp = dbLinkDoLocked(psrclnk, getTwice, NULL); testOk(temp == 0, "locked, getTwice returned %d (0)", temp); dbScanUnlock((dbCommon*)psrc); epicsEventMustWait(waitEvent); epicsEventDestroy(waitEvent); waitEvent = NULL; testIocShutdownOk(); testdbCleanup(); } static void testStringLink(void) { xRecord *psrc, *ptarg; DBLINK *psrclnk; char temp[MAX_STRING_SIZE]; long nReq; testDiag("Link to a string field"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target.DESC CA"); eltc(0); testIocInitOk(); eltc(1); psrc = (xRecord*)testdbRecordPtr("source"); ptarg= (xRecord*)testdbRecordPtr("target"); psrclnk = &psrc->lnk; /* ensure this is really a CA link */ testOk1(dbLockGetLockId((dbCommon*)psrc)!=dbLockGetLockId((dbCommon*)ptarg)); testOk1(psrclnk->type==CA_LINK); waitForUpdateN(psrclnk, 1); dbScanLock((dbCommon*)ptarg); strcpy(ptarg->desc, "hello"); db_post_events(ptarg, &ptarg->desc, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE); dbScanUnlock((dbCommon*)ptarg); waitForUpdateN(psrclnk, 2); dbScanLock((dbCommon*)psrc); /* local CA_LINK connects immediately */ testOk1(dbCaIsLinkConnected(psrclnk)); nReq = 1; memset(temp, '!', sizeof(temp)); testOk1(dbGetLink(psrclnk, DBR_STRING, (void*)&temp, NULL, &nReq)==0); testOk(strcmp(temp, "hello")==0, "%s == hello", temp); dbScanUnlock((dbCommon*)psrc); strcpy(temp, "world"); putLink(psrclnk, DBR_STRING, (void*)&temp, nReq); dbScanLock((dbCommon*)ptarg); testOk(strcmp(ptarg->desc, "world")==0, "%s == world", ptarg->desc); dbScanUnlock((dbCommon*)ptarg); testIocShutdownOk(); testdbCleanup(); } static void wasproc(xRecord *prec) { waitCounter++; epicsEventTrigger(waitEvent); } static void testCP(void) { xRecord *psrc, *ptarg; testDiag("Link CP modifier"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target CP"); psrc = (xRecord*)testdbRecordPtr("source"); ptarg= (xRecord*)testdbRecordPtr("target"); /* hook in before IOC init */ waitCounter=0; psrc->clbk = &wasproc; assert(!waitEvent); waitEvent = epicsEventMustCreate(epicsEventEmpty); eltc(0); testIocInitOk(); eltc(1); dbCaSync(); epicsEventMustWait(waitEvent); dbScanLock((dbCommon*)psrc); testOp("%u",waitCounter,==,1); /* initial processing */ dbScanUnlock((dbCommon*)psrc); dbScanLock((dbCommon*)ptarg); ptarg->val = 42; db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE); dbScanUnlock((dbCommon*)ptarg); epicsEventMustWait(waitEvent); dbScanLock((dbCommon*)psrc); testOp("%u",waitCounter,==,2); /* process due to monitor update */ dbScanUnlock((dbCommon*)psrc); testIocShutdownOk(); testdbCleanup(); epicsEventDestroy(waitEvent); waitEvent = NULL; } static void fillArray(epicsInt32 *buf, unsigned count, epicsInt32 first) { for(;count;count--,first++) *buf++ = first; } static void fillArrayDouble(double *buf, unsigned count, double first) { for(;count;count--,first++) *buf++ = first; } static void checkArray(const char *msg, epicsInt32 *buf, epicsInt32 first, unsigned used) { int match = 1; unsigned i; epicsInt32 x, *b; for(b=buf,x=first,i=0;ivalue.pv_link.pvt; if(lnk->type!=CA_LINK || !pca->pputNative) return; epicsMutexMustLock(pca->lock); memset(pca->pputNative, '!', pca->nelements*dbr_value_size[ca_field_type(pca->chid)]); epicsMutexUnlock(pca->lock); } static void testArrayLink(unsigned nsrc, unsigned ntarg) { char buf[100]; arrRecord *psrc, *ptarg; DBLINK *psrclnk; epicsInt32 *bufsrc, *buftarg, *tmpbuf; long nReq; unsigned num_min, num_max; testDiag("Link to a array numeric field"); /* source.INP = "target CA" */ epicsSnprintf(buf, sizeof(buf), "TARGET=target CA,FTVL=LONG,SNELM=%u,TNELM=%u", nsrc, ntarg); testDiag("%s", buf); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbCaLinkTest2.db", NULL, buf); psrc = (arrRecord*)testdbRecordPtr("source"); ptarg= (arrRecord*)testdbRecordPtr("target"); psrclnk = &psrc->inp; eltc(0); testIocInitOk(); eltc(1); waitForUpdateN(psrclnk, 1); bufsrc = psrc->bptr; buftarg= ptarg->bptr; num_max=num_min=psrc->nelm; if(num_min>ptarg->nelm) num_min=ptarg->nelm; if(num_maxnelm) num_max=ptarg->nelm; /* always request more than can possibly be filled */ num_max += 2; tmpbuf = callocMustSucceed(num_max, sizeof(*tmpbuf), "tmpbuf"); dbScanLock((dbCommon*)ptarg); fillArray(buftarg, ptarg->nelm, 1); ptarg->nord = ptarg->nelm; db_post_events(ptarg, ptarg->bptr, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE); dbScanUnlock((dbCommon*)ptarg); waitForUpdateN(psrclnk, 2); dbScanLock((dbCommon*)psrc); testDiag("fetch source.INP into source.BPTR"); nReq = psrc->nelm; if(dbGetLink(psrclnk, DBR_LONG, bufsrc, NULL, &nReq)==0) { testPass("dbGetLink"); testOp("%ld",nReq,==,(long)num_min); checkArray("array update", bufsrc, 1, nReq); } else { testFail("dbGetLink"); testSkip(2, "dbGetLink fails"); } testDiag("fetch source.INP into temp buffer w/ larger capacity"); nReq = num_max; if(dbGetLink(psrclnk, DBR_LONG, tmpbuf, NULL, &nReq)==0) { testPass("dbGetLink"); testOp("%ld",nReq,==,(long)ntarg); checkArray("array update", tmpbuf, 1, nReq); } else { testFail("dbGetLink"); testSkip(2, "dbGetLink fails"); } dbScanUnlock((dbCommon*)psrc); fillArray(bufsrc, psrc->nelm, 2); /* write buffer allocated on first put */ putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm); dbScanLock((dbCommon*)ptarg); testOp("%ld",(long)ptarg->nord,==,(long)num_min); dbScanUnlock((dbCommon*)ptarg); /* write again to ensure that buffer is completely updated */ spoilputbuf(psrclnk); fillArray(bufsrc, psrc->nelm, 3); putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm); dbScanLock((dbCommon*)ptarg); testOp("%ld",(long)ptarg->nord,==,(long)num_min); checkArray("array update", buftarg, 3, num_min); dbScanUnlock((dbCommon*)ptarg); testIocShutdownOk(); testdbCleanup(); free(tmpbuf); /* records don't cleanup after themselves * so do here to silence valgrind */ free(bufsrc); free(buftarg); } static void softarr(arrRecord *prec) { long nReq = prec->nelm; long status = dbGetLink(&prec->inp, DBR_DOUBLE, prec->bptr, NULL, &nReq); if(status) { testFail("dbGetLink() -> %ld", status); } else { testPass("dbGetLink() succeeds"); prec->nord = nReq; if(nReq>0) testDiag("%s.VAL[0] - %f", prec->name, *(double*)prec->bptr); } waitCounter++; epicsEventTrigger(waitEvent); } static void testreTargetTypeChange(void) { arrRecord *psrc, *ptarg1, *ptarg2; double *bufsrc, *buftarg1; epicsInt32 *buftarg2; testDiag("Retarget an link to a PV with a different type DOUBLE->LONG"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbCaLinkTest3.db", NULL, "NELM=5,TARGET=target1 CP"); psrc = (arrRecord*)testdbRecordPtr("source"); ptarg1= (arrRecord*)testdbRecordPtr("target1"); ptarg2= (arrRecord*)testdbRecordPtr("target2"); /* hook in before IOC init */ waitCounter=0; psrc->clbk = &softarr; assert(!waitEvent); waitEvent = epicsEventMustCreate(epicsEventEmpty); eltc(0); testIocInitOk(); eltc(1); epicsEventMustWait(waitEvent); /* wait for initial processing */ bufsrc = psrc->bptr; buftarg1= ptarg1->bptr; buftarg2= ptarg2->bptr; testDiag("Update one with original target"); dbScanLock((dbCommon*)ptarg2); fillArray(buftarg2, ptarg2->nelm, 2); ptarg2->nord = ptarg2->nelm; dbScanUnlock((dbCommon*)ptarg2); /* initialize buffers */ dbScanLock((dbCommon*)ptarg1); fillArrayDouble(buftarg1, ptarg1->nelm, 1); ptarg1->nord = ptarg1->nelm; db_post_events(ptarg1, ptarg1->bptr, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE); dbScanUnlock((dbCommon*)ptarg1); epicsEventMustWait(waitEvent); /* wait for update */ dbScanLock((dbCommon*)psrc); testOp("%ld",(long)psrc->nord,==,(long)5); checkArrayDouble("array update", bufsrc, 1, 5); dbScanUnlock((dbCommon*)psrc); testDiag("Retarget"); testdbPutFieldOk("source.INP", DBR_STRING, "target2 CP"); epicsEventMustWait(waitEvent); /* wait for update */ dbScanLock((dbCommon*)psrc); testOp("%ld",(long)psrc->nord,==,(long)5); checkArrayDouble("array update", bufsrc, 2, 5); dbScanUnlock((dbCommon*)psrc); testIocShutdownOk(); testdbCleanup(); /* records don't cleanup after themselves * so do here to silence valgrind */ free(bufsrc); free(buftarg1); free(buftarg2); } void dbCaLinkTest_testCAC(void); static void testCAC(void) { arrRecord *psrc, *ptarg1, *ptarg2; double *bufsrc, *buftarg1; epicsInt32 *buftarg2; testDiag("Check local CA through libca"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbCaLinkTest3.db", NULL, "NELM=5,TARGET=target1 CP"); psrc = (arrRecord*)testdbRecordPtr("source"); ptarg1= (arrRecord*)testdbRecordPtr("target1"); ptarg2= (arrRecord*)testdbRecordPtr("target2"); eltc(0); testIocInitOk(); eltc(1); bufsrc = psrc->bptr; buftarg1= ptarg1->bptr; buftarg2= ptarg2->bptr; dbCaLinkTest_testCAC(); testIocShutdownOk(); testdbCleanup(); /* records don't cleanup after themselves * so do here to silence valgrind */ free(bufsrc); free(buftarg1); free(buftarg2); } MAIN(dbCaLinkTest) { testPlan(101); testNativeLink(); testStringLink(); testCP(); testArrayLink(1,1); testArrayLink(10,1); testArrayLink(1,10); testArrayLink(10,10); testreTargetTypeChange(); testCAC(); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/dbCaLinkTest1.db0000664000577000060420000000011213557101274021636 0ustar anjaesctlrecord(x, "target") {} record(x, "source") { field(LNK, "$(TARGET)") } base-7.0.3.1/modules/database/test/ioc/db/dbCaLinkTest2.db0000664000577000060420000000033313557101274021644 0ustar anjaesctlrecord(arr, "target") { field(FTVL, "$(TFTVL=$(FTVL=))") field(NELM, "$(TNELM=$(NELM=))") } record(arr, "source") { field(INP, "$(TARGET)") field(FTVL, "$(SFTVL=$(FTVL=))") field(NELM, "$(SNELM=$(NELM=))") } base-7.0.3.1/modules/database/test/ioc/db/dbCaLinkTest3.db0000664000577000060420000000043213557101274021645 0ustar anjaesctlrecord(arr, "target1") { field(FTVL, "DOUBLE") field(NELM, "$(TNELM=$(NELM=))") } record(arr, "target2") { field(FTVL, "LONG") field(NELM, "$(TNELM=$(NELM=))") } record(arr, "source") { field(INP, "$(TARGET)") field(FTVL, "DOUBLE") field(NELM, "$(SNELM=$(NELM=))") } base-7.0.3.1/modules/database/test/ioc/db/dbCaStats.db0000664000577000060420000000005013557101274021117 0ustar anjaesctlrecord(x, "e1") { } record(x, "e2") { } base-7.0.3.1/modules/database/test/ioc/db/dbCaStatsTest.c0000664000577000060420000000344713557101274021631 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include "dbCaTest.h" #include "dbAccess.h" #include "dbUnitTest.h" #include "testMain.h" #include "dbAccess.h" #include "errlog.h" void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); static void testCaStats(void) { int channels; int disconnected; testDiag("Check dbcaStats"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbCaStats.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); testDiag("No CA links"); channels = disconnected = -1; dbcaStats(&channels, &disconnected); testOk(channels==0, "channels==0 (got %d)", channels); testOk(disconnected==0, "disconnected==0 (got %d)", disconnected); testDiag("One CA link, disconnected"); testdbPutFieldOk("e2.INP", DBR_STRING, "e12 CA", 1); channels = disconnected = -1; dbcaStats(&channels, &disconnected); testOk(channels==1, "channels==1 (got %d)", channels); testOk(disconnected==1, "disconnected==1 (got %d)", disconnected); /* Connected CA links can not be tested without fully starting the IOC which we will skip for the moment as it is not allowed inside the vxWorks/RTEMS test harness. The above is good enough to check for bug lp:1394212 */ testIocShutdownOk(); testdbCleanup(); } MAIN(dbCaStatsTest) { testPlan(5); testCaStats(); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/dbChArrTest.cpp0000664000577000060420000001666213557101274021631 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2003 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to the Software License Agreement * found in the file LICENSE that is included with this distribution. \*************************************************************************/ /* * Authors: Ralph Lange , * Andrew Johnson */ #include #include #include #include #include #include "registryFunction.h" #include "epicsThread.h" #include "epicsExit.h" #include "epicsStdio.h" #include "epicsString.h" #include "envDefs.h" #include "dbStaticLib.h" #include "dbmf.h" #include "registry.h" #include "dbAddr.h" #include "dbAccess.h" #include "asDbLib.h" #include "iocInit.h" #include "iocsh.h" #include "dbChannel.h" #include "dbUnitTest.h" #include "testMain.h" #include "osiFileName.h" extern "C" { int dbChArrTest_registerRecordDeviceDriver(struct dbBase *pdbbase); } #define CA_SERVER_PORT "65535" const char *server_port = CA_SERVER_PORT; static void createAndOpen(const char *name, dbChannel**pch) { testOk(!!(*pch = dbChannelCreate(name)), "dbChannel %s created", name); testOk(!(dbChannelOpen(*pch)), "dbChannel opened"); testOk((ellCount(&(*pch)->pre_chain) == 0), "no filters in pre chain"); testOk((ellCount(&(*pch)->post_chain) == 0), "no filters in post chain"); } extern "C" { static void freeArray(db_field_log *pfl) { if (pfl->type == dbfl_type_ref) { free(pfl->u.r.field); } } } static void testHead (const char *title, const char *typ = "") { const char *line = "------------------------------------------------------------------------------"; testDiag("%s", line); testDiag(title, typ); testDiag("%s", line); } static void check(short dbr_type) { dbChannel *pch; db_field_log *pfl; dbAddr valaddr; dbAddr offaddr; const char *offname = NULL, *valname = NULL, *typname = NULL; epicsInt32 buf[26]; long off, req; int i; switch (dbr_type) { case DBR_LONG: offname = "i32.OFF"; valname = "i32.VAL"; typname = "long"; break; case DBR_DOUBLE: offname = "f64.OFF"; valname = "f64.VAL"; typname = "double"; break; case DBR_STRING: offname = "c40.OFF"; valname = "c40.VAL"; typname = "string"; break; default: testDiag("Invalid data type %d", dbr_type); } (void) dbNameToAddr(offname, &offaddr); (void) dbNameToAddr(valname, &valaddr); testHead("Ten %s elements", typname); /* Fill the record's array field with data, 10..19 */ epicsInt32 ar[10] = {10,11,12,13,14,15,16,17,18,19}; (void) dbPutField(&valaddr, DBR_LONG, ar, 10); /* Open a channel to it, make sure no filters present */ createAndOpen(valname, &pch); testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); /* TEST1 sets the record's OFF field, then requests 10 elements from the channel, * passing in a transparent db_field_log and converting the data to LONG on the way in. * It checks that it got back the expected data and the right number of elements. */ #define TEST1(Size, Offset, Text, Expected) \ testDiag("Reading from offset = %d (%s)", Offset, Text); \ off = Offset; req = 10; \ memset(buf, 0, sizeof(buf)); \ (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \ pfl = db_create_read_log(pch); \ testOk(pfl && pfl->type == dbfl_type_rec, "Valid pfl, type = rec"); \ testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \ testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \ if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \ for (i=0; ifinal_type == valaddr.field_type, "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); const epicsInt32 res_5_0[] = {15,16,17,18,19}; TEST1(5, 0, "no offset", res_5_0); const epicsInt32 res_5_3[] = {18,19,15,16,17}; TEST1(5, 3, "wrapped", res_5_3); /* TEST2 sets the record's OFF field, then requests 15 elements from the channel * but passes in a db_field_log with alternate data, converting that data to LONG. * It checks that it got back the expected data and the right number of elements. */ #define TEST2(Size, Offset, Text, Expected) \ testDiag("Reading from offset = %d (%s)", Offset, Text); \ off = Offset; req = 15; \ memset(buf, 0, sizeof(buf)); \ (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \ pfl = db_create_read_log(pch); \ pfl->type = dbfl_type_ref; \ pfl->field_type = DBF_CHAR; \ pfl->field_size = 1; \ pfl->no_elements = 26; \ pfl->u.r.dtor = freeArray; \ pfl->u.r.field = epicsStrDup("abcdefghijklmnopqrsstuvwxyz"); \ testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \ testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \ if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \ for (i=0; i * Ralph Lange */ #include "dbChannel.h" #include "dbStaticLib.h" #include "dbAccessDefs.h" #include "registry.h" #include "recSup.h" #include "dbUnitTest.h" #include "testMain.h" #include "osiFileName.h" #include "errlog.h" /* Expected call bit definitions */ #define e_start 0x00000001 #define e_abort 0x00000002 #define e_end 0x00000004 #define e_null 0x00000008 #define e_boolean 0x00000010 #define e_integer 0x00000020 #define e_double 0x00000040 #define e_string 0x00000080 #define e_start_map 0x00000100 #define e_map_key 0x00000200 #define e_end_map 0x00000400 #define e_start_array 0x00000800 #define e_end_array 0x00001000 #define e_open 0x00002000 #define e_reg_pre 0x00004000 #define e_reg_post 0x00008000 #define e_report 0x00010000 #define e_close 0x00020000 #define r_any (e_start | e_abort | e_end | \ e_null | e_boolean | e_integer | e_double | e_string | \ e_start_map | e_map_key | e_end_map | e_start_array | e_end_array) #define r_scalar (e_start | e_abort | e_end | \ e_null | e_boolean | e_integer | e_double | e_string) unsigned int e, r; #define p_ret(x) return r & x ? parse_continue : parse_stop parse_result p_start(chFilter *filter) { testOk(e & e_start, "parse_start called"); p_ret(e_start); } void p_abort(chFilter *filter) { testOk(e & e_abort, "parse_abort called"); } parse_result p_end(chFilter *filter) { testOk(e & e_end, "parse_end called"); p_ret(e_end); } parse_result p_null(chFilter *filter) { testOk(e & e_null, "parse_null called"); p_ret(e_null); } parse_result p_boolean(chFilter *filter, int boolVal) { testOk(e & e_boolean, "parse_boolean called, val = %d", boolVal); p_ret(e_boolean); } parse_result p_integer(chFilter *filter, long integerVal) { testOk(e & e_integer, "parse_integer called, val = %ld", integerVal); p_ret(e_integer); } parse_result p_double(chFilter *filter, double doubleVal) { testOk(e & e_double, "parse_double called, val = %g", doubleVal); p_ret(e_double); } parse_result p_string(chFilter *filter, const char *stringVal, size_t stringLen) { testOk(e & e_string, "parse_string called, val = '%.*s'", (int) stringLen, stringVal); p_ret(e_string); } parse_result p_start_map(chFilter *filter) { testOk(e & e_start_map, "parse_start_map called"); p_ret(e_start_map); } parse_result p_map_key(chFilter *filter, const char *key, size_t stringLen) { testOk(e & e_map_key, "parse_map_key called, key = '%.*s'", (int) stringLen, key); p_ret(e_map_key); } parse_result p_end_map(chFilter *filter) { testOk(e & e_end_map, "parse_end_map called"); p_ret(e_end_map); } parse_result p_start_array(chFilter *filter) { testOk(e & e_start_array, "parse_start_array called"); p_ret(e_start_array); } parse_result p_end_array(chFilter *filter) { testOk(e & e_end_array, "parse_end_array called"); p_ret(e_end_array); } long c_open(chFilter *filter) { testOk(e & e_open, "channel_open called"); return 0; } void c_reg_pre(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { testOk(e & e_reg_pre, "channel_register_pre called"); } void c_reg_post(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { testOk(e & e_reg_post, "channel_register_post called"); } void c_report(chFilter *filter, int level, const unsigned short indent) { testOk(e & e_report, "channel_report called, level = %d", level); } void c_close(chFilter *filter) { testOk(e & e_close, "channel_close called"); } chFilterIf testIf = { NULL, p_start, p_abort, p_end, p_null, p_boolean, p_integer, p_double, p_string, p_start_map, p_map_key, p_end_map, p_start_array, p_end_array, c_open, c_reg_pre, c_reg_post, c_report, c_close }; void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(testDbChannel) /* dbChannelTest is an API routine... */ { dbChannel *pch; testPlan(76); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("xRecord.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); r = e = 0; /* dbChannelTest() checks record and field names */ testOk1(!dbChannelTest("x.NAME")); testOk1(!dbChannelTest("x.INP")); testOk1(!dbChannelTest("x.VAL")); testOk1(!dbChannelTest("x.")); testOk1(!dbChannelTest("x")); testOk(dbChannelTest("y"), "Test, nonexistent record"); testOk(dbChannelTest("x.NOFIELD"), "Test, nonexistent field"); /* dbChannelTest() allows but ignores field modifiers */ testOk1(!dbChannelTest("x.NAME$")); testOk1(!dbChannelTest("x.{}")); testOk1(!dbChannelTest("x.VAL{\"json\":true}")); /* dbChannelCreate() accepts field modifiers */ testOk1(!!(pch = dbChannelCreate("x.{}"))); if (pch) dbChannelDelete(pch); testOk1(!!(pch = dbChannelCreate("x.VAL{}"))); testOk1(pch && dbChannelElements(pch) == 1); if (pch) dbChannelDelete(pch); testOk1(!!(pch = dbChannelCreate("x.NAME$"))); testOk1(pch && dbChannelFieldType(pch) == DBF_CHAR); testOk1(pch && dbChannelExportType(pch) == DBR_CHAR); testOk1(pch && dbChannelElements(pch) == PVNAME_STRINGSZ); if (pch) dbChannelDelete(pch); testOk1(!!(pch = dbChannelCreate("x.INP$"))); testOk1(pch && dbChannelFieldType(pch) == DBF_INLINK); testOk1(pch && dbChannelExportType(pch) == DBR_CHAR); testOk1(pch && dbChannelElements(pch) > PVNAME_STRINGSZ); if (pch) dbChannelDelete(pch); testOk1(!!(pch = dbChannelCreate("x.NAME${}"))); testOk1(pch && dbChannelFieldType(pch) == DBF_CHAR); testOk1(pch && dbChannelExportType(pch) == DBR_CHAR); testOk1(pch && dbChannelElements(pch) == PVNAME_STRINGSZ); if (pch) dbChannelDelete(pch); /* dbChannelCreate() rejects bad PVs */ testOk(!dbChannelCreate("y"), "Create, bad record"); testOk(!dbChannelCreate("x.NOFIELD"), "Create, bad field"); testOk(!dbChannelCreate("x.{not-json}"), "Create, bad JSON"); eltc(0); testOk(!dbChannelCreate("x.{\"none\":null}"), "Create, bad filter"); eltc(1); dbRegisterFilter("any", &testIf, NULL); /* Parser event rejection by filter */ e = e_start; testOk1(!dbChannelCreate("x.{\"any\":null}")); r = e_start; e = e_start | e_null | e_abort; testOk1(!dbChannelCreate("x.{\"any\":null}")); r = e_start | e_null; e = e_start | e_null | e_end; testOk1(!dbChannelCreate("x.{\"any\":null}")); /* Successful parsing... */ r = r_any; e = e_start | e_null | e_end; testOk1(!!(pch = dbChannelCreate("x.{\"any\":null}"))); e = e_close; if (pch) dbChannelDelete(pch); dbRegisterFilter("scalar", &testIf, NULL); e = e_start | e_null | e_end; testOk1(!!(pch = dbChannelCreate("x.{\"scalar\":null}"))); e = e_report; dbChannelShow(pch, 2, 2); e = e_close; if (pch) dbChannelDelete(pch); e = e_start | e_start_array | e_boolean | e_integer | e_end_array | e_end; testOk1(!!(pch = dbChannelCreate("x.{\"any\":[true,1]}"))); e = e_close; if (pch) dbChannelDelete(pch); e = e_start | e_start_map | e_map_key | e_double | e_string | e_end_map | e_end; testOk1(!!(pch = dbChannelCreate("x.{\"any\":{\"a\":2.7183,\"b\":\"c\"}}"))); e = e_close; if (pch) dbChannelDelete(pch); /* More event rejection */ r = r_scalar; e = e_start | e_start_array | e_abort; testOk1(!dbChannelCreate("x.{\"scalar\":[null]}")); e = e_start | e_start_map | e_abort; testOk1(!dbChannelCreate("x.{\"scalar\":{}}")); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/dbLinkdset.c0000664000577000060420000000131113557101274021170 0ustar anjaesctl #include #include #include static long link_test_extend(struct dbCommon *junk) { return 0; } static dsxt xrecextend = {&link_test_extend, &link_test_extend}; static long link_test_init(int pass) { if (pass == 0) devExtend(&xrecextend); return 0; } static long link_test_noop(void *junk) { return 0; } #define DEFDSET(LTYPE) \ static dset devxLTest ## LTYPE = {4, NULL, &link_test_init, &link_test_noop, &link_test_noop}; \ epicsExportAddress(dset, devxLTest ## LTYPE); DEFDSET(JSON_LINK) DEFDSET(VME_IO) DEFDSET(CAMAC_IO) DEFDSET(AB_IO) DEFDSET(GPIB_IO) DEFDSET(BITBUS_IO) DEFDSET(INST_IO) DEFDSET(BBGPIB_IO) DEFDSET(RF_IO) DEFDSET(VXI_IO) base-7.0.3.1/modules/database/test/ioc/db/dbLinkdset.dbd0000664000577000060420000000110713557101274021502 0ustar anjaesctldevice(x, JSON_LINK, devxLTestJSON_LINK, "Unit Test JSON_LINK") device(x, VME_IO, devxLTestVME_IO, "Unit Test VME_IO") device(x, CAMAC_IO, devxLTestCAMAC_IO, "Unit Test CAMAC_IO") device(x, AB_IO, devxLTestAB_IO, "Unit Test AB_IO") device(x, GPIB_IO, devxLTestGPIB_IO, "Unit Test GPIB_IO") device(x, BITBUS_IO, devxLTestBITBUS_IO, "Unit Test BITBUS_IO") device(x, INST_IO, devxLTestINST_IO, "Unit Test INST_IO") device(x, BBGPIB_IO, devxLTestBBGPIB_IO, "Unit Test BBGPIB_IO") device(x, RF_IO, devxLTestRF_IO, "Unit Test RF_IO") device(x, VXI_IO, devxLTestVXI_IO, "Unit Test VXI_IO") base-7.0.3.1/modules/database/test/ioc/db/dbLockTest.c0000664000577000060420000003015613557101274021154 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Assoc. as operator of Brookhaven * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver */ #include #include "epicsSpin.h" #include "epicsMutex.h" #include "dbCommon.h" #include "epicsThread.h" #include "dbLockPvt.h" #include "dbStaticLib.h" #include "dbUnitTest.h" #include "testMain.h" #include "dbAccess.h" #include "errlog.h" void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); static void compareSets(int match, const char *A, const char *B) { int actual; dbCommon *rA, *rB; rA = testdbRecordPtr(A); rB = testdbRecordPtr(B); actual = rA->lset->plockSet==rB->lset->plockSet; testOk(match==actual, "dbLockGetLockId(\"%s\")%c=dbLockGetLockId(\"%s\")", A, match?'=':'!', B); } #define testIntOk1(A, OP, B) testOk((A) OP (B), "%s (%d) %s %s (%d)", #A, A, #OP, #B, B); #define testPtrOk1(A, OP, B) testOk((A) OP (B), "%s (%p) %s %s (%p)", #A, A, #OP, #B, B); static void testSets(void) { DBENTRY entry; long status; testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbLockTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); testDiag("Check that all records have initialized lockRecord and lockSet"); dbInitEntry(pdbbase, &entry); for(status = dbFirstRecordType(&entry); !status; status = dbNextRecordType(&entry)) { for(status = dbFirstRecord(&entry); !status; status = dbNextRecord(&entry)) { dbCommon *prec = entry.precnode->precord; testOk(prec->lset!=NULL, "%s.LSET != NULL", prec->name); if(prec->lset!=NULL) testOk(prec->lset->plockSet!=NULL, "%s.LSET.plockSet != NULL", prec->name); else testSkip(1, "lockRecord missing"); } } dbFinishEntry(&entry); testDiag("Check initial creation of DB links"); /* reca is by itself */ compareSets(0, "reca", "recb"); compareSets(0, "reca", "recc"); compareSets(0, "reca", "recd"); compareSets(0, "reca", "rece"); compareSets(0, "reca", "recf"); /* recb and recc should be in a lockset */ compareSets(1, "recb", "recc"); compareSets(0, "recb", "recd"); compareSets(0, "recb", "rece"); compareSets(0, "recb", "recf"); compareSets(0, "recc", "recd"); compareSets(0, "recc", "rece"); compareSets(0, "recc", "recf"); /* recd, e, and f should be in a lockset */ compareSets(1, "recd", "rece"); compareSets(1, "recd", "recf"); compareSets(1, "rece", "recf"); testOk1(testdbRecordPtr("reca")->lset->plockSet->refcount==1); testOk1(testdbRecordPtr("recb")->lset->plockSet->refcount==2); testOk1(testdbRecordPtr("recd")->lset->plockSet->refcount==3); testIocShutdownOk(); testdbCleanup(); } static void testSingleLock(void) { dbCommon *prec; testDiag("testing dbScanLock()/dbScanUnlock()"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbLockTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); prec = testdbRecordPtr("reca"); testOk1(prec->lset->plockSet->refcount==1); dbScanLock(prec); /* scan lock does not keep a reference to the lockSet */ testOk1(prec->lset->plockSet->refcount==1); dbScanUnlock(prec); dbScanLock(prec); dbScanLock(prec); /* scan lock can be recursive */ testOk1(prec->lset->plockSet->refcount==1); dbScanUnlock(prec); dbScanUnlock(prec); testIocShutdownOk(); testdbCleanup(); } static void testMultiLock(void) { dbCommon *prec[8]; dbLocker *plockA; #ifdef LOCKSET_DEBUG epicsThreadId myself = epicsThreadGetIdSelf(); #endif testDiag("Test multi-locker function (lock everything)"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbLockTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); prec[0] = testdbRecordPtr("reca"); prec[1] = testdbRecordPtr("recb"); prec[2] = testdbRecordPtr("recc"); prec[3] = NULL; prec[4] = testdbRecordPtr("recd"); prec[5] = testdbRecordPtr("rece"); prec[6] = testdbRecordPtr("recf"); prec[7] = testdbRecordPtr("recg"); testDiag("Test init refcounts"); testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,1); testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,2); testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,3); testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,1); plockA = dbLockerAlloc(prec, 8, 0); if(!plockA) testAbort("dbLockerAlloc() failed"); testDiag("After locker created"); /* locker takes 7 references, one for each lockRecord. */ testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,2); testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,4); testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,6); testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,2); #ifdef LOCKSET_DEBUG testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,NULL); testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,NULL); testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,NULL); testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,NULL); #endif dbScanLockMany(plockA); testDiag("After locker locked"); /* locker takes 4 references, one for each lockSet. */ testIntOk1(ellCount(&plockA->locked),==,4); testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,3); testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,5); testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,7); testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,3); #ifdef LOCKSET_DEBUG testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,myself); testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,myself); testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,myself); testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,myself); #endif /* recursive locking of individual records is allowed */ dbScanLock(prec[0]); testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,3); dbScanUnlock(prec[0]); /* recursive locking with dbScanLockMany() isn't * dbScanLockMany(plockA); <-- would fail */ dbScanUnlockMany(plockA); testDiag("After locker unlocked"); testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,2); testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,4); testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,6); testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,2); #ifdef LOCKSET_DEBUG testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,NULL); testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,NULL); testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,NULL); testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,NULL); #endif dbLockerFree(plockA); testDiag("After locker free'd"); testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,1); testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,2); testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,3); testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,1); testIocShutdownOk(); testdbCleanup(); } static void testLinkBreak(void) { dbCommon *precB, *precC; testDiag("Test break link"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbLockTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); precB = testdbRecordPtr("recb"); precC = testdbRecordPtr("recc"); testOk1(precB->lset->plockSet==precC->lset->plockSet); testOk1(precB->lset->plockSet->refcount==2); /* break the link between B and C */ testdbPutFieldOk("recb.SDIS", DBR_STRING, ""); testOk1(precB->lset->plockSet!=precC->lset->plockSet); testIntOk1(precB->lset->plockSet->refcount, ==, 1); testIntOk1(precC->lset->plockSet->refcount, ==, 1); testIocShutdownOk(); testdbCleanup(); } static void testLinkMake(void) { dbCommon *precA, *precG; lockSet *lA, *lG; testDiag("Test make link"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbLockTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); precA = testdbRecordPtr("reca"); lA = dbLockGetRef(precA->lset); precG = testdbRecordPtr("recg"); lG = dbLockGetRef(precG->lset); testPtrOk1(precA->lset->plockSet, !=, precG->lset->plockSet); testIntOk1(precA->lset->plockSet->refcount, ==, 2); testIntOk1(precG->lset->plockSet->refcount, ==, 2); /* make a link between A and G */ testdbPutFieldOk("reca.SDIS", DBR_STRING, "recg"); testPtrOk1(precA->lset->plockSet, ==, precG->lset->plockSet); testIntOk1(precA->lset->plockSet->refcount, ==, 3); if(precA->lset->plockSet==lG) { testIntOk1(lA->refcount, ==, 1); testIntOk1(lG->refcount, ==, 3); } else { testIntOk1(lA->refcount, ==, 3); testIntOk1(lG->refcount, ==, 1); } dbLockDecRef(lG); dbLockDecRef(lA); testIocShutdownOk(); testdbCleanup(); } static void testLinkChange(void) { dbCommon *precB, *precC, *precG; lockSet *lB, *lG; testDiag("Test re-target link"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbLockTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); precB = testdbRecordPtr("recb"); precC = testdbRecordPtr("recc"); precG = testdbRecordPtr("recg"); lB = dbLockGetRef(precB->lset); lG = dbLockGetRef(precG->lset); testPtrOk1(lB,==,precC->lset->plockSet); testPtrOk1(lB,!=,lG); testIntOk1(lB->refcount,==,3); testIntOk1(lG->refcount,==,2); /* break the link between B and C and replace it * with a link between B and G */ testdbPutFieldOk("recb.SDIS", DBR_STRING, "recg"); testPtrOk1(precB->lset->plockSet,==,lB); testPtrOk1(precG->lset->plockSet,==,lB); testPtrOk1(precC->lset->plockSet,!=,lB); testPtrOk1(precC->lset->plockSet,!=,lG); testIntOk1(lB->refcount,==,3); testIntOk1(lG->refcount,==,1); testIntOk1(precC->lset->plockSet->refcount,==,1); dbLockDecRef(lB); dbLockDecRef(lG); testIocShutdownOk(); testdbCleanup(); } static void testLinkNOP(void) { dbCommon *precB, *precC; testDiag("Test re-target link to the same destination"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbLockTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); precB = testdbRecordPtr("recb"); precC = testdbRecordPtr("recc"); testOk1(precB->lset->plockSet==precC->lset->plockSet); testOk1(precB->lset->plockSet->refcount==2); /* renew link between B and C */ testdbPutFieldOk("recb.SDIS", DBR_STRING, "recc"); testOk1(precB->lset->plockSet==precC->lset->plockSet); testOk1(precB->lset->plockSet->refcount==2); testIocShutdownOk(); testdbCleanup(); } MAIN(dbLockTest) { #ifdef LOCKSET_DEBUG testPlan(100); #else testPlan(88); #endif testSets(); testSingleLock(); testMultiLock(); testLinkBreak(); testLinkMake(); testLinkChange(); testLinkNOP(); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/dbLockTest.db0000664000577000060420000000040013557101274021304 0ustar anjaesctlrecord(x, "reca") { } record(x, "recb") { field(SDIS, "recc") } record(x, "recc") { field(SDIS, "recc") } record(x, "recd") { field(SDIS, "rece") } record(x, "rece") { field(SDIS, "recf") } record(x, "recf") { } record(x, "recg") { } base-7.0.3.1/modules/database/test/ioc/db/dbPutGetTest.c0000664000577000060420000001045113557101274021470 0ustar anjaesctl #include #include #include #include #include #include #include static void testdbGetStringEqual(const char *pv, const char *expected) { const char *actual; int ok; DBENTRY ent; dbInitEntry(pdbbase, &ent); if(dbFindRecord(&ent, pv)) { testFail("Failed to find record '%s'", pv); return; } actual = dbGetString(&ent); ok = (!actual && !expected) || (actual && expected && strcmp(actual, expected)==0); testOk(ok, "dbGetString(\"%s\") -> \"%s\" == \"%s\"", pv, actual, expected); dbFinishEntry(&ent); } static void testGetString(void) { testDiag("testGetString()"); testdbGetStringEqual("recempty.DTYP", "Soft Channel"); testdbGetStringEqual("recempty.DESC", ""); testdbGetStringEqual("recempty.PHAS", "0"); testdbGetStringEqual("recempty.TSE" , "0"); testdbGetStringEqual("recempty.DISA", "0"); testdbGetStringEqual("recempty.DISV", "0"); testdbGetStringEqual("recoverwrite.DTYP", "Soft Channel"); testdbGetStringEqual("recoverwrite.DESC", ""); testdbGetStringEqual("recoverwrite.PHAS", "0"); testdbGetStringEqual("recoverwrite.TSE" , "0"); testdbGetStringEqual("recoverwrite.DISA", "0"); testdbGetStringEqual("recoverwrite.DISV", "0"); } static void testStringMax(void) { testDiag("testStringMax()"); testdbGetStringEqual("recmax.DISA", "-1"); } static void testLongLink(void) { testDiag("testLonkLink()"); testdbGetFieldEqual("lnktest.INP", DBR_STRING, "lnktarget NPP NMS"); testdbGetFieldEqual("lnktest.INP$", DBR_STRING, "lnktarget NPP NMS"); testDiag("dbGet() w/ nRequest==1 gets only trailing nil"); testdbGetFieldEqual("lnktest.INP$", DBR_CHAR, '\0'); testdbGetArrFieldEqual("lnktest.INP$", DBR_CHAR, 19, 18, "lnktarget NPP NMS"); testDiag("get w/ truncation"); testdbGetArrFieldEqual("lnktest.INP$", DBR_CHAR, 0, 0, NULL); testdbGetArrFieldEqual("lnktest.INP$", DBR_CHAR, 1, 1, ""); testdbGetArrFieldEqual("lnktest.INP$", DBR_CHAR, 2, 2, "l"); testdbGetArrFieldEqual("lnktest.INP$", DBR_CHAR, 3, 3, "ln"); testdbGetArrFieldEqual("lnktest.INP$", DBR_CHAR, 17, 17, "lnktarget NPP NM"); testdbGetArrFieldEqual("lnktest.INP$", DBR_CHAR, 18, 18, "lnktarget NPP NMS"); testdbGetArrFieldEqual("lnktest.INP", DBR_STRING, 2, 1, "lnktarget NPP NMS"); } static void testLongAttr(void) { testDiag("testLongAttr()"); testdbGetFieldEqual("lnktest.RTYP", DBR_STRING, "x"); testdbGetFieldEqual("lnktest.RTYP$", DBR_STRING, "x"); testDiag("dbGet() w/ nRequest==1 gets only trailing nil"); testdbGetFieldEqual("lnktest.RTYP$", DBR_CHAR, '\0'); testdbGetArrFieldEqual("lnktest.RTYP$", DBR_CHAR, 4, 2, "x"); testDiag("get w/ truncation"); testdbGetArrFieldEqual("lnktest.RTYP$", DBR_CHAR, 0, 0, NULL); testdbGetArrFieldEqual("lnktest.RTYP$", DBR_CHAR, 1, 1, ""); testdbGetArrFieldEqual("lnktest.RTYP$", DBR_CHAR, 2, 2, "x"); } static void testLongField(void) { testDiag("testLongField()"); testdbGetFieldEqual("lnktest.NAME", DBR_STRING, "lnktest"); testdbGetFieldEqual("lnktest.NAME$", DBR_STRING, "108"); testDiag("dbGet() w/ nRequest==1 gets only trailing nil"); testdbGetFieldEqual("lnktest.NAME$", DBR_CHAR, '\0'); testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 10, 8, "lnktest"); testDiag("get w/ truncation"); testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 0, 0, NULL); testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 1, 1, ""); testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 2, 2, "l"); testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 3, 3, "ln"); testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 7, 7, "lnktes"); testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 8, 8, "lnktest"); } void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(dbPutGet) { testPlan(41); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbPutGetTest.db", NULL, NULL); testGetString(); testStringMax(); eltc(0); testIocInitOk(); eltc(1); testLongLink(); testLongAttr(); testLongField(); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/dbPutGetTest.db0000664000577000060420000000134513557101274021635 0ustar anjaesctl record(x, "recempty") { # empty string is the same "0" for numeric fields field(DTYP, "") field(DESC, "") field(PHAS, "") field(TSE , "") field(DISA, "") field(DISV, "") } record(x, "recoverwrite") { # first with non-default values field(DTYP, "Scan I/O") field(DESC, "hello") field(PHAS, "2") field(TSE , "5") field(DISA, "6") field(DISV, "7") } record(x, "recoverwrite") { # now restore default values field(DTYP, "") field(DESC, "") field(PHAS, "") field(TSE , "") field(TSEL, "") field(DISA, "") field(DISV, "") } record(x, "recmax") { field(DISA, "0xffffffff") } record(x, "lnktarget") {} record(x, "lnktest") { field(INP, "lnktarget NPP NMS") } base-7.0.3.1/modules/database/test/ioc/db/dbPutLinkTest.c0000664000577000060420000005337513557101274021662 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Assoc. as operator of Brookhaven * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver */ #include "string.h" #include "epicsString.h" #include "dbUnitTest.h" #include "epicsThread.h" #include "iocInit.h" #include "dbBase.h" #include "dbDefs.h" #include "link.h" #include "dbAccess.h" #include "registry.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "osiFileName.h" #include "dbmf.h" #include "errlog.h" #include #include "xRecord.h" #include "jlinkz.h" #include "testMain.h" static int testStrcmp(int expect, const char *A, const char *B) { static const char op[] = "<=>"; int ret = strcmp(A,B); testOk(ret==expect, "\"%s\" %c= \"%s\"", A, op[expect+1], B); return ret==expect; } void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); #define TEST_CONSTANT(SET, EXPECT) {SET, {CONSTANT, EXPECT}} #define TEST_PV_LINK(SET, PV, MOD) {SET, {PV_LINK, PV, MOD}} static const struct testParseDataT { const char * const str; dbLinkInfo info; } testParseData[] = { TEST_CONSTANT("", ""), TEST_CONSTANT("0.1", "0.1"), TEST_CONSTANT(" 0.2\t ", "0.2"), TEST_PV_LINK("0.1a", "0.1a", 0), TEST_PV_LINK(" hello ", "hello", 0), TEST_PV_LINK(" hellox MSI", "hellox", pvlOptMSI), TEST_PV_LINK(" world MSICP", "world", pvlOptMSI|pvlOptCP), {"#C14 S145 @testing", {VME_IO, "testing", 0, "CS", {14, 145}}}, {"#C14 S145", {VME_IO, "", 0, "CS", {14, 145}}}, {"#B11 C12 N13 A14 F15 @cparam", {CAMAC_IO, "cparam", 0, "BCNAF", {11, 12, 13, 14, 15}}}, {"#B11 C12 N13 A14 F15", {CAMAC_IO, "", 0, "BCNAF", {11, 12, 13, 14, 15}}}, {" #B111 C112 N113 @cparam", {CAMAC_IO, "cparam", 0, "BCN", {111, 112, 113}}}, {" @hello world ", {INST_IO, "hello world", 0, "", /*{}*/}}, {" {\"x\":true} ", {JSON_LINK, "{\"x\":true}", 0, "", /*{}*/}}, {NULL} }; static void testLinkParse(void) { const struct testParseDataT *td = testParseData; dbLinkInfo info; testDiag("\n# Checking link parsing\n#"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); for (;td->str; td++) { int i, N; testDiag("Parsing \"%s\"", td->str); testOk(dbParseLink(td->str, DBF_INLINK, &info) == 0, "Parser returned OK"); if (!testOk(info.ltype == td->info.ltype, "Link type value")) testDiag("Expected %d, got %d", td->info.ltype, info.ltype); if (td->info.target && info.target) testStrcmp(0, info.target, td->info.target); else if(!!td->info.target ^ !!info.target) testFail("info target NULL mis-match %s %s", info.target, td->info.target); else testPass("info target NULL as expected"); if (info.ltype == td->info.ltype) { switch (info.ltype) { case PV_LINK: if (!testOk(info.modifiers == td->info.modifiers, "PV Link modifier flags")) testDiag("Expected %d, got %d", td->info.modifiers, info.modifiers); break; case VME_IO: case CAMAC_IO: testStrcmp(0, info.hwid, td->info.hwid); N = strlen(td->info.hwid); for (i=0; iinfo.hwnums[i], "%d == %d", info.hwnums[i], td->info.hwnums[i]); } } dbFreeLinkInfo(&info); } testIocShutdownOk(); testdbCleanup(); } static const char *testParseFailData[] = { "#", "#S", "#ABC", "#A0 B", "#A0 B @", "#A0 B C @", "#R1 M2 D3 E4 @oops", /* RF_IO has no parm */ NULL }; static void testLinkFailParse(void) { const char * const *td = testParseFailData; dbLinkInfo info; testDiag("\n# Check parsing of invalid inputs\n#"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); for(;*td; td++) { testOk(dbParseLink(*td, DBF_INLINK, &info) == S_dbLib_badField, "dbParseLink correctly rejected \"%s\"", *td); } testIocShutdownOk(); testdbCleanup(); } static const struct testDataT { const char * const linkstring; short linkType; unsigned int pvlMask; const char * const linkback; } testSetData[] = { {"", CONSTANT, 0}, {"0", CONSTANT, 0}, {"42", CONSTANT, 0}, {"0x1", CONSTANT, 0}, {"x1", DB_LINK, 0, "x1 NPP NMS"}, {"x1.VAL", DB_LINK, 0, "x1.VAL NPP NMS"}, {"x1.TIME", DB_LINK, 0, "x1.TIME NPP NMS"}, {"x1 PP", DB_LINK, pvlOptPP, "x1 PP NMS"}, {"x1 PP MSS", DB_LINK, pvlOptPP|pvlOptMSS, "x1 PP MSS"}, {"x1 PPMSS", DB_LINK, pvlOptPP|pvlOptMSS, "x1 PP MSS"}, {"x1 PPMSI", DB_LINK, pvlOptPP|pvlOptMSI, "x1 PP MSI"}, {"qq", CA_LINK, pvlOptInpNative, "qq NPP NMS"}, {"qq MSI", CA_LINK, pvlOptInpNative|pvlOptMSI, "qq NPP MSI"}, {"qq MSICA", CA_LINK, pvlOptInpNative|pvlOptCA|pvlOptMSI, "qq CA MSI"}, {"x1 CA", CA_LINK, pvlOptInpNative|pvlOptCA, "x1 CA NMS"}, {NULL} }; static void testCADBSet(void) { const struct testDataT *td = testSetData; xRecord *prec; DBLINK *plink; testDiag("\n# Checking DB/CA link retargeting\n#"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); prec = (xRecord*)testdbRecordPtr("x1"); plink = &prec->lnk; for (;td->linkstring;td++) { testDiag("Trying field value \"%s\"", td->linkstring); testdbPutFieldOk("x1.LNK", DBF_STRING, td->linkstring); if (td->linkback) testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkback); else testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkstring); if (!testOk(plink->type == td->linkType, "Link type")) testDiag("Expected %d, got %d", td->linkType, plink->type); if (plink->type == td->linkType) { switch (td->linkType) { case CONSTANT: if (plink->value.constantStr) testOk1(strcmp(plink->value.constantStr, td->linkstring) == 0); else if (td->linkstring[0]=='\0') testPass("Empty String"); else testFail("oops"); break; case DB_LINK: case CA_LINK: testOk(plink->value.pv_link.pvlMask == td->pvlMask, "pvlMask %x == %x", plink->value.pv_link.pvlMask, td->pvlMask); break; } } } testIocShutdownOk(); testdbCleanup(); } typedef struct { const char * const recname; short ltype; const char * const wval; short vals[5]; const char * const parm; } testHWDataT; static const testHWDataT testHWData[] = { {"rJSON_LINK", JSON_LINK, "{\"x\":true}", {0}, "{\"x\":true}"}, {"rVME_IO", VME_IO, "#C100 S101 @parm VME_IO", {100, 101}, "parm VME_IO"}, {"rCAMAC_IO", CAMAC_IO, "#B11 C12 N13 A14 F15 @parm CAMAC_IO", {11, 12, 13, 14, 15}, "parm CAMAC_IO"}, {"rAB_IO", AB_IO, "#L21 A22 C23 S24 @parm AB_IO", {21, 22, 23, 24}, "parm AB_IO"}, {"rGPIB_IO", GPIB_IO, "#L31 A32 @parm GPIB_IO", {31, 32}, "parm GPIB_IO"}, {"rBITBUS_IO", BITBUS_IO, "#L41 N42 P43 S44 @parm BITBUS_IO", {41, 42, 43, 44}, "parm BITBUS_IO"}, {"rINST_IO", INST_IO, "@parm INST_IO", {0}, "parm INST_IO"}, {"rBBGPIB_IO", BBGPIB_IO, "#L51 B52 G53 @parm BBGPIB_IO", {51, 52, 53}, "parm BBGPIB_IO"}, {"rRF_IO", RF_IO, "#R61 M62 D63 E64", {61, 62, 63, 64}, NULL}, {"rVXI_IO1", VXI_IO, "#V71 C72 S73 @parm1 VXI_IO", {71, 72, 73}, "parm1 VXI_IO"}, {"rVXI_IO2", VXI_IO, "#V81 S82 @parm2 VXI_IO", {81, 82}, "parm2 VXI_IO"}, {NULL} }; static void testLink(DBLINK *plink, const testHWDataT *td) { switch(td->ltype) { case JSON_LINK: testOk1(strcmp(plink->value.json.string, td->parm) == 0); break; case VME_IO: testOk1(plink->value.vmeio.card == td->vals[0]); testOk1(plink->value.vmeio.signal == td->vals[1]); testOk1(strcmp(plink->value.vmeio.parm, td->parm) == 0); break; case CAMAC_IO: testOk1(plink->value.camacio.b == td->vals[0]); testOk1(plink->value.camacio.c == td->vals[1]); testOk1(plink->value.camacio.n == td->vals[2]); testOk1(plink->value.camacio.a == td->vals[3]); testOk1(plink->value.camacio.f == td->vals[4]); testOk1(strcmp(plink->value.camacio.parm, td->parm) == 0); break; case AB_IO: testOk1(plink->value.abio.link == td->vals[0]); testOk1(plink->value.abio.adapter == td->vals[1]); testOk1(plink->value.abio.card == td->vals[2]); testOk1(plink->value.abio.signal == td->vals[3]); testOk1(strcmp(plink->value.abio.parm, td->parm) == 0); break; case GPIB_IO: testOk1(plink->value.gpibio.link == td->vals[0]); testOk1(plink->value.gpibio.addr == td->vals[1]); testOk1(strcmp(plink->value.gpibio.parm, td->parm) == 0); break; case BITBUS_IO: testOk1(plink->value.bitbusio.link == td->vals[0]); testOk1(plink->value.bitbusio.node == td->vals[1]); testOk1(plink->value.bitbusio.port == td->vals[2]); testOk1(plink->value.bitbusio.signal == td->vals[3]); testOk1(strcmp(plink->value.bitbusio.parm, td->parm) == 0); break; case INST_IO: testOk1(strcmp(plink->value.instio.string, td->parm) == 0); break; case BBGPIB_IO: testOk1(plink->value.bbgpibio.link == td->vals[0]); testOk1(plink->value.bbgpibio.bbaddr == td->vals[1]); testOk1(plink->value.bbgpibio.gpibaddr == td->vals[2]); testOk1(strcmp(plink->value.bbgpibio.parm, td->parm) == 0); break; case RF_IO: testOk1(plink->value.rfio.cryo == td->vals[0]); testOk1(plink->value.rfio.micro == td->vals[1]); testOk1(plink->value.rfio.dataset == td->vals[2]); testOk1(plink->value.rfio.element == td->vals[3]); break; case VXI_IO: if(plink->value.vxiio.flag == VXIDYNAMIC) { testOk1(plink->value.vxiio.frame == td->vals[0]); testOk1(plink->value.vxiio.slot == td->vals[1]); testOk1(plink->value.vxiio.signal == td->vals[2]); } else { testOk1(plink->value.vxiio.la == td->vals[0]); testOk1(plink->value.vxiio.signal == td->vals[1]); } testOk1(strcmp(plink->value.vxiio.parm, td->parm) == 0); break; } } static void testHWInitSet(void) { const testHWDataT *td = testHWData; testDiag("\n# Checking HW link parsing during initialization\n#"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); for (;td->recname; td++) { char buf[MAX_STRING_SIZE]; xRecord *prec; DBLINK *plink; testDiag("%s == \"%s\"", td->recname, td->wval); prec = (xRecord *) testdbRecordPtr(td->recname); plink = &prec->inp; strcpy(buf, td->recname); strcat(buf, ".INP"); testdbGetFieldEqual(buf, DBR_STRING, td->wval); if (!testOk(plink->type == td->ltype, "Link type")) { testDiag("Expected %d, got %d", td->ltype, plink->type); } else { testLink(plink, td); } } testIocShutdownOk(); testdbCleanup(); } static const testHWDataT testHWData2[] = { {"rJSON_LINK", JSON_LINK, "{\"x\":true}", {0}, "{\"x\":true}"}, {"rVME_IO", VME_IO, "#C200 S201 @another VME_IO", {200, 201}, "another VME_IO"}, {"rCAMAC_IO", CAMAC_IO, "#B111 C112 N113 A114 F115 @CAMAC_IO", {111, 112, 113, 114, 115}, "CAMAC_IO"}, {"rAB_IO", AB_IO, "#L121 A122 C123 S124 @another AB_IO", {121, 122, 123, 124}, "another AB_IO"}, {"rGPIB_IO", GPIB_IO, "#L131 A132 @another GPIB_IO", {131, 132}, "another GPIB_IO"}, {"rBITBUS_IO", BITBUS_IO, "#L141 N142 P143 S144 @BITBUS_IO", {141, 142, 143, 144}, "BITBUS_IO"}, {"rINST_IO", INST_IO, "@another INST_IO", {0}, "another INST_IO"}, {"rBBGPIB_IO", BBGPIB_IO, "#L151 B152 G153 @another BBGPIB_IO", {151, 152, 153}, "another BBGPIB_IO"}, {"rRF_IO", RF_IO, "#R161 M162 D163 E164", {161, 162, 163, 164}, NULL}, {"rVXI_IO1", VXI_IO, "#V171 C172 S173 @another1 VXI_IO", {171, 172, 173}, "another1 VXI_IO"}, {"rVXI_IO2", VXI_IO, "#V181 S182 @another2 VXI_IO", {181, 182}, "another2 VXI_IO"}, {NULL} }; static void testHWMod(void) { const testHWDataT *td = testHWData2; testDiag("\n# Checking HW link parsing during retarget\n#"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); for(;td->recname;td++) { char buf[MAX_STRING_SIZE]; xRecord *prec; DBLINK *plink; testDiag("%s -> \"%s\"", td->recname, td->wval); prec = (xRecord*)testdbRecordPtr(td->recname); plink = &prec->inp; strcpy(buf, td->recname); strcat(buf, ".INP"); testdbPutFieldOk(buf, DBR_STRING, td->wval); testdbGetFieldEqual(buf, DBR_STRING, td->wval); if (!testOk(plink->type == td->ltype, "Link type")) { testDiag("Expected %d, got %d", td->ltype, plink->type); } else { testLink(plink, td); } } testIocShutdownOk(); testdbCleanup(); } static void testLinkInitFail(void) { xRecord *prec; DBLINK *plink; testDiag("\n# Checking link parse failures at iocInit\n#"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); /* this load will fail */ eltc(0); testOk(dbReadDatabase(&pdbbase, "dbBadLink.db", "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL) != 0, "dbReadDatabase returned error (expected)"); testIocInitOk(); eltc(1); testdbGetFieldEqual("eVME_IO1.INP", DBR_STRING, "#C0 S0 @"); prec = (xRecord *) testdbRecordPtr("eVME_IO1"); plink = &prec->inp; testOk1(plink->type == VME_IO); testOk1(plink->value.vmeio.parm != NULL); testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C0 S0 @"); prec = (xRecord *) testdbRecordPtr("eVME_IO2"); plink = &prec->inp; testOk1(plink->type == VME_IO); testOk1(plink->value.vmeio.parm != NULL); testdbGetFieldEqual("eINST_IO.INP", DBR_STRING, "@"); prec = (xRecord *) testdbRecordPtr("eINST_IO"); plink = &prec->inp; testOk1(plink->type == INST_IO); testOk1(plink->value.instio.string != NULL); testdbGetFieldEqual("eINST_IO2.INP", DBR_STRING, "@"); prec = (xRecord *) testdbRecordPtr("eINST_IO2"); plink = &prec->inp; testOk1(plink->type == INST_IO); testOk1(plink->value.instio.string != NULL); testIocShutdownOk(); testdbCleanup(); } static void testLinkFail(void) { testDiag("\n# Checking runtime link parse failures\n#"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); /* INST_IO doesn't accept empty string */ testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, ""); /* INST_IO doesn't accept string without @ */ testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "abc"); /* JSON_LINK dies when expected */ testdbPutFieldOk("rJSON_LINK.INP", DBR_STRING, "{\"x\":true}"); testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":false}"); testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":null}"); testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":1}"); testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":1.1}"); testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":\"x\"}"); testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":[]}"); testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":{}}"); /* JSON_LINK syntax errors */ testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":bbbb}"); /* syntax errors */ testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "#S201 C200 @another VME_IO"); testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "C200 #S201"); /* VME_IO doesn't accept empty string */ testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, ""); testdbPutFieldOk("rVME_IO.INP", DBR_STRING, "#C1 S2 @hello"); /* VME_IO fails invalid input */ testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "abc"); testIocShutdownOk(); testdbCleanup(); } static void testNumZ(int expect) { int numz = epicsAtomicGetIntT(&numzalloc); testOk(numz==expect, "numzalloc==%d (%d)", expect, numz); } static void testJLink(void) { testDiag("Test json link setup/retarget"); testNumZ(0); testDiag("Link parsing failures"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); testdbReadDatabase("dbPutLinkTestJ.db", NULL, NULL); testNumZ(0); eltc(0); testIocInitOk(); eltc(1); testNumZ(6); testdbPutFieldOk("j1.PROC", DBF_LONG, 1); testdbPutFieldOk("j2.PROC", DBF_LONG, 1); testdbPutFieldOk("j3.PROC", DBF_LONG, 1); testdbGetFieldEqual("j1.INP", DBF_STRING, "{\"z\":{\"good\":1}}"); testdbGetFieldEqual("j1.VAL", DBF_LONG, 1); testdbGetFieldEqual("j2.VAL", DBF_LONG, 2); testdbGetFieldEqual("j3.VAL", DBF_LONG, 3); testNumZ(6); testdbPutFieldOk("j1.INP", DBF_STRING, "{\"z\":{\"good\":4}}"); testdbPutFieldOk("j1.PROC", DBF_LONG, 1); testdbGetFieldEqual("j1.VAL", DBF_LONG, 4); testdbPutFieldOk("j2.TSEL", DBF_STRING, "{\"z\":{\"good\":0}}"); testdbPutFieldOk("j2.PROC", DBF_LONG, 1); testNumZ(7); testdbPutFieldFail(S_dbLib_badField, "j1.INP", DBF_STRING, "{\"z\":{\"fail\":5}}"); testdbPutFieldFail(S_dbLib_badField, "j1.INP", DBF_STRING, "{\"z\":{\"good\":6}"); testdbPutFieldOk("j1.PROC", DBF_LONG, 1); testdbGetFieldEqual("j1.VAL", DBF_LONG, 4); /* put failures in parsing stage don't modify link */ testdbGetFieldEqual("j1.INP", DBF_STRING, "{\"z\":{\"good\":4}}"); testNumZ(7); /* Check SDIS using a JSON link prevents processing */ testdbPutFieldOk("j1.SDIS", DBF_STRING, "{\"z\":{\"good\":1}}"); testdbPutFieldOk("j1.INP", DBF_STRING, "{\"z\":{\"good\":1}}"); testdbPutFieldOk("j1.PROC", DBF_LONG, 1); testdbGetFieldEqual("j1.VAL", DBF_LONG, 4); testIocShutdownOk(); testNumZ(0); testdbCleanup(); } static void testTSEL(void) { dbCommon *rec[2]; dbLocker *locker; testDiag("Test TSEL link to .TIME"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); rec[0] = testdbRecordPtr("time:one"); rec[1] = testdbRecordPtr("time:two"); eltc(0); testIocInitOk(); eltc(1); locker = dbLockerAlloc(rec, NELEMENTS(rec), 0); if(!locker) testAbort("dbLockerAlloc() fails"); testdbPutFieldOk("time:one.PROC", DBF_LONG, 1); /* wait a bit so that we would get different timestamps */ epicsThreadSleep(0.001); testdbPutFieldOk("time:two.PROC", DBF_LONG, 1); #define COMPARE(MSG, C, TS1, TS2) testOk((C)^((TS1)->secPastEpoch == (TS2)->secPastEpoch && (TS1)->nsec == (TS2)->nsec), \ MSG " %u:%u == %u:%u", (unsigned)(TS1)->secPastEpoch, (unsigned)(TS1)->nsec, \ (unsigned)(TS2)->secPastEpoch, (unsigned)(TS2)->nsec) testDiag("Check initially connected TSEL link"); dbScanLockMany(locker); COMPARE("first", 0, &rec[0]->time, &rec[1]->time); testOk1(rec[1]->tsel.flags & DBLINK_FLAG_TSELisTIME); dbScanUnlockMany(locker); testdbPutFieldOk("time:two.TSEL", DBF_STRING, ""); testdbPutFieldOk("time:two.PROC", DBF_LONG, 1); testDiag("Check no TSEL link"); dbScanLockMany(locker); COMPARE("second", 1, &rec[0]->time, &rec[1]->time); testOk1(!(rec[1]->tsel.flags & DBLINK_FLAG_TSELisTIME)); dbScanUnlockMany(locker); testdbPutFieldOk("time:two.TSEL", DBF_STRING, "time:one.TIME"); testdbPutFieldOk("time:two.PROC", DBF_LONG, 1); testDiag("Check re-connected TSEL link"); dbScanLockMany(locker); COMPARE("third", 0, &rec[0]->time, &rec[1]->time); testOk1(rec[1]->tsel.flags & DBLINK_FLAG_TSELisTIME); dbScanUnlockMany(locker); dbLockerFree(locker); testIocShutdownOk(); testdbCleanup(); #undef COMPARE } MAIN(dbPutLinkTest) { testPlan(337); testLinkParse(); testLinkFailParse(); testCADBSet(); testHWInitSet(); testHWMod(); testLinkInitFail(); testLinkFail(); testJLink(); testTSEL(); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/dbPutLinkTest.db0000664000577000060420000000237113557101274022013 0ustar anjaesctlrecord(x, "x1") {} record(x, "x2") {} record(x, "x3") {} record(x, "x4") {} record(x, "rJSON_LINK") { field(DTYP, "Unit Test JSON_LINK") field(INP, {x:true}) } record(x, "rVME_IO") { field(DTYP, "Unit Test VME_IO") field(INP, "#C100 S101 @parm VME_IO") } record(x, "rCAMAC_IO") { field(DTYP, "Unit Test CAMAC_IO") field(INP, "#B11 C12 N13 A14 F15 @parm CAMAC_IO") } record(x, "rAB_IO") { field(DTYP, "Unit Test AB_IO") field(INP, "#L21 A22 C23 S24 @parm AB_IO") } record(x, "rGPIB_IO") { field(DTYP, "Unit Test GPIB_IO") field(INP, "#L31 A32 @parm GPIB_IO") } record(x, "rBITBUS_IO") { field(DTYP, "Unit Test BITBUS_IO") field(INP, "#L41 N42 P43 S44 @parm BITBUS_IO") } record(x, "rINST_IO") { field(DTYP, "Unit Test INST_IO") field(INP, "@parm INST_IO") } record(x, "rBBGPIB_IO") { field(DTYP, "Unit Test BBGPIB_IO") field(INP, "#L51 B52 G53 @parm BBGPIB_IO") } record(x, "rRF_IO") { field(DTYP, "Unit Test RF_IO") field(INP, "#R61 M62 D63 E64") } record(x, "rVXI_IO1") { field(DTYP, "Unit Test VXI_IO") field(INP, "#V71 C72 S73 @parm1 VXI_IO") } record(x, "rVXI_IO2") { field(DTYP, "Unit Test VXI_IO") field(INP, "#V81 S82 @parm2 VXI_IO") } record(x, "time:one") {} record(x, "time:two") { field(TSEL, "time:one.TIME") } base-7.0.3.1/modules/database/test/ioc/db/dbPutLinkTestJ.db0000664000577000060420000000037513557101274022127 0ustar anjaesctl record(x, "j1") { field(INP, {z:{good:1}}) field(TSEL, {z:{good:0}}) field(SDIS, {z:{good:0}}) field(FLNK, {z:{good:0}}) } record(x, "j2") { field(INP, {z:{good:2}}) field(TSEL, "j1.TIME") } record(x, "j3") { field(INP, {z:{good:3}}) } base-7.0.3.1/modules/database/test/ioc/db/dbScanTest.c0000664000577000060420000000326713557101274021153 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver */ #include #include "dbScan.h" #include "epicsEvent.h" #include "dbUnitTest.h" #include "testMain.h" #include "dbAccess.h" #include "errlog.h" void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); static epicsEventId waiter; static int called; static dbCommon *prec; static void onceComp(void *junk, dbCommon *prec) { testOk1(junk==(void*)&waiter); testOk1(strcmp(prec->name, "reca")==0); called = 1; epicsEventMustTrigger(waiter); } static void testOnce(void) { testDiag("check scanOnceCallback() callback"); waiter = epicsEventMustCreate(epicsEventEmpty); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbLockTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); prec = testdbRecordPtr("reca"); testDiag("scanOnce %s", prec->name); scanOnceCallback(prec, onceComp, &waiter); testDiag("Waiting"); epicsEventMustWait(waiter); testOk1(called==1); if(!called) testSkip(2, "callback failed to run"); testIocShutdownOk(); testdbCleanup(); epicsEventDestroy(waiter); } MAIN(dbScanTest) { testPlan(3); testOnce(); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/dbServerTest.c0000664000577000060420000001133013557101274021523 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Andrew Johnson */ #include #include "dbServer.h" #include #include #include enum { NOTHING_CALLED, REPORT_CALLED, CLIENT_CALLED_UNKNOWN, CLIENT_CALLED_KNOWN, STATS_CALLED, INIT_CALLED, RUN_CALLED, PAUSE_CALLED, STOP_CALLED } oneState; char *oneSim; void oneReport(unsigned level) { oneState = REPORT_CALLED; } void oneStats(unsigned *channels, unsigned *clients) { oneState = STATS_CALLED; } int oneClient(char *pbuf, size_t len) { if (oneSim) { strncpy(pbuf, oneSim, len); oneState = CLIENT_CALLED_KNOWN; oneSim = NULL; return 0; } oneState = CLIENT_CALLED_UNKNOWN; return -1; } void oneInit(void) { oneState = INIT_CALLED; } void oneRun(void) { oneState = RUN_CALLED; } void onePause(void) { oneState = PAUSE_CALLED; } void oneStop(void) { oneState = STOP_CALLED; } dbServer one = { ELLNODE_INIT, "one", oneReport, oneStats, oneClient, oneInit, oneRun, onePause, oneStop }; dbServer one2 = { ELLNODE_INIT, "one", oneReport, oneStats, oneClient, oneInit, oneRun, onePause, oneStop }; /* Server layer for testing NULL methods */ dbServer no_routines = { ELLNODE_INIT, "no-routines", NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /* Server layer which should be disabled */ int disInitialized = 0; void disInit(void) { disInitialized = 1; } dbServer disabled = { ELLNODE_INIT, "disabled", NULL, NULL, NULL, disInit, NULL, NULL, NULL }; dbServer illegal = { ELLNODE_INIT, "bad name", NULL, NULL, NULL, disInit, NULL, NULL, NULL }; dbServer toolate = { ELLNODE_INIT, "toolate", NULL, NULL, NULL, disInit, NULL, NULL, NULL }; MAIN(dbServerTest) { char name[16]; char *theName = "The One"; int status; testPlan(25); /* Prove that we handle substring names properly */ epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); testDiag("Registering dbServer 'one'"); testOk(dbRegisterServer(&one) == 0, "Registered 'one'"); testOk1(oneState == NOTHING_CALLED); testOk(dbRegisterServer(&one) == 0, "Duplicate registration accepted"); testOk(dbRegisterServer(&one2) != 0, "change registration rejected"); testOk(dbRegisterServer(&illegal) != 0, "Illegal registration rejected"); testDiag("Registering dbServer 'no-routines'"); testOk(dbRegisterServer(&no_routines) == 0, "Registered 'no-routines'"); testOk(dbUnregisterServer(&no_routines) == 0, "'no-routines' unreg'd"); testOk(dbRegisterServer(&no_routines) == 0, "Re-registered 'no-routines'"); epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "disabled nonexistent"); testDiag("Registering dbServer 'disabled'"); testOk(dbRegisterServer(&disabled) == 0, "Registration accepted"); testDiag("Changing server state"); dbInitServers(); testOk(oneState == INIT_CALLED, "dbInitServers"); testOk(disInitialized == 0, "Disabled server not initialized"); testOk(dbRegisterServer(&toolate) != 0, "No registration while active"); dbRunServers(); testOk(oneState == RUN_CALLED, "dbRunServers"); testOk(dbUnregisterServer(&one) != 0, "No unregistration while active"); testDiag("Checking server methods called"); dbsr(0); testOk(oneState == REPORT_CALLED, "dbsr called report()"); oneSim = NULL; name[0] = 0; status = dbServerClient(name, sizeof(name)); testOk(oneState == CLIENT_CALLED_UNKNOWN, "Client unknown"); testOk(status == -1 && name[0] == 0, "dbServerClient mismatch"); oneSim = theName; name[0] = 0; status = dbServerClient(name, sizeof(name)); testOk(oneState == CLIENT_CALLED_KNOWN, "Client known"); testOk(status == 0 && strcmp(name, theName) == 0, "dbServerClient match"); dbPauseServers(); testOk(oneState == PAUSE_CALLED, "dbPauseServers"); dbsr(0); testOk(oneState != REPORT_CALLED, "No call to report() when paused"); status = dbServerClient(name, sizeof(name)); testOk(oneState != CLIENT_CALLED_KNOWN, "No call to client() when paused"); dbStopServers(); testOk(oneState == STOP_CALLED, "dbStopServers"); testOk(dbUnregisterServer(&toolate) != 0, "No unreg' if not reg'ed"); testOk(dbUnregisterServer(&no_routines) != 0, "No unreg' of 'no-routines'"); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/dbShutdownTest.c0000664000577000060420000000371213557101274022075 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 Brookhaven National Laboratory. * Copyright (c) 2013 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver * Ralph Lange */ #include "epicsString.h" #include "dbUnitTest.h" #include "epicsThread.h" #include "iocInit.h" #include "dbBase.h" #include "dbAccess.h" #include "registry.h" #include "dbStaticLib.h" #include "osiFileName.h" #include "dbmf.h" #include "errlog.h" #include "testMain.h" void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); static struct threadItem { char *name; char found; } commonThreads[] = { { "errlog", 0 }, { "taskwd", 0 }, { "timerQueue", 0 }, { "cbLow", 0 }, { "scanOnce", 0 }, { NULL, 0 } }; static void findCommonThread (epicsThreadId id) { struct threadItem *thr; char name[32]; epicsThreadGetName(id, name, 32); for (thr = commonThreads; thr->name; thr++) { if (epicsStrCaseCmp(thr->name, name) == 0) { thr->found = 1; } } } static void checkCommonThreads (void) { struct threadItem *thr; for (thr = commonThreads; thr->name; thr++) { testOk(thr->found, "Thread %s is running", thr->name); thr->found = 0; } } static void cycle(void) { testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("xRecord.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); epicsThreadMap(findCommonThread); checkCommonThreads(); testIocShutdownOk(); testdbCleanup(); } MAIN(dbShutdownTest) { testPlan(10); cycle(); cycle(); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/dbStateTest.c0000664000577000060420000000430413557101274021340 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #include "dbState.h" #include "epicsUnitTest.h" #include "testMain.h" MAIN(dbStateTest) { dbStateId red, red2, blue, blue2; int i; testPlan(20); testOk(!dbStateFind("y"), "Finding nonexisting state fails"); testOk(!!(red = dbStateCreate("red")), "Create state 'red'"); testOk((red2 = dbStateFind("red")) == red, "Find 'red' returns correct id"); testOk((red2 = dbStateCreate("red")) == red, "Create existing 'red' returns correct id"); testOk(!dbStateFind("y"), "Finding nonexisting state still fails"); testOk(!!(blue = dbStateCreate("blue")), "Create state 'blue'"); testOk((blue2 = dbStateFind("blue")) == blue, "Find 'blue' returns correct id"); testOk((blue2 = dbStateCreate("blue")) == blue, "Create existing 'blue' returns correct id"); testOk(!dbStateFind("y"), "Finding nonexisting state still fails"); testOk((i = dbStateGet(red)) == 0, "Default 'red' state is 0"); testOk((i = dbStateGet(blue)) == 0, "Default 'blue' state is 0"); dbStateSet(red); testOk((i = dbStateGet(red)) == 1, "After setting, 'red' state is 1"); testOk((i = dbStateGet(blue)) == 0, "'blue' state is 0"); dbStateSet(blue); testOk((i = dbStateGet(blue)) == 1, "After setting, 'blue' state is 1"); testOk((i = dbStateGet(red)) == 1, "'red' state is 1"); dbStateClear(blue); testOk((i = dbStateGet(blue)) == 0, "After clearing, 'blue' state is 0"); testOk((i = dbStateGet(red)) == 1, "'red' state is 1"); dbStateClear(red); testOk((i = dbStateGet(red)) == 0, "After clearing, 'red' state is 0"); testOk((i = dbStateGet(blue)) == 0, "'red' state is 0"); testOk(!dbStateFind("y"), "Finding nonexisting state still fails"); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/dbStaticTest.c0000664000577000060420000001503513557101274021512 0ustar anjaesctl#include #include #include #include #include #include #include static void testEntry(const char *pv) { DBENTRY entry; testDiag("testEntry(\"%s\")", pv); dbInitEntry(pdbbase, &entry); testOk1(dbFindRecord(&entry, pv)==0); testDiag("precordType=%p precnode=%p", entry.precordType, entry.precnode); testOk(entry.precordType && strcmp(entry.precordType->name, "x")==0, "Record type is '%s' ('x')", entry.precordType->name); testOk(entry.precnode && strcmp(((dbCommon*)entry.precnode->precord)->name, "testrec")==0, "Record name is '%s' ('testrec')", ((dbCommon*)entry.precnode->precord)->name); testOk(entry.pflddes && strcmp(entry.pflddes->name, "VAL")==0, "Field name is '%s' ('VAL')", entry.pflddes->name); /* all tested PVs are either a record with aliases, or an alias */ testOk(entry.precnode && (!(entry.precnode->flags & DBRN_FLAGS_ISALIAS) ^ !(entry.precnode->flags & DBRN_FLAGS_HASALIAS)), "Recnode flags %d", entry.precnode->flags); testOk1(dbFollowAlias(&entry)==0); testOk(dbFindInfo(&entry, "A")==0 && strcmp(dbGetInfoString(&entry), "B")==0, "Info item is set"); dbFinishEntry(&entry); } static void testAddr2Entry(const char *pv) { DBENTRY entry, entry2; dbAddr addr; testDiag("testAddr2Entry(\"%s\")", pv); memset(&entry, 0, sizeof(entry)); memset(&entry2, 0, sizeof(entry2)); dbInitEntry(pdbbase, &entry); if(dbFindRecord(&entry, pv)!=0) testAbort("no entry for %s", pv); if(dbFollowAlias(&entry)) testAbort("Can't follow alias"); if(dbNameToAddr(pv, &addr)) testAbort("no addr for %s", pv); dbInitEntryFromAddr(&addr, &entry2); testOk1(entry.pdbbase==entry2.pdbbase); testOk1(entry.precordType==entry2.precordType); testOk1(entry.pflddes==entry2.pflddes); testOk1(entry.precnode==entry2.precnode); testOk1(entry.pfield==entry2.pfield); testOk1(entry.indfield==entry2.indfield); testOk1(!entry2.pinfonode); testOk1(!entry2.message); testOk(memcmp(&entry, &entry2, sizeof(entry))==0, "dbEntries identical"); dbFinishEntry(&entry); dbFinishEntry(&entry2); } static void testRec2Entry(const char *recname) { DBENTRY entry, entry2; dbCommon *prec; testDiag("testRec2Entry(\"%s\")", recname); memset(&entry, 0, sizeof(entry)); memset(&entry2, 0, sizeof(entry2)); prec = testdbRecordPtr(recname); dbInitEntry(pdbbase, &entry); if(dbFindRecord(&entry, recname)!=0) testAbort("no entry for %s", recname); if(dbFollowAlias(&entry)) testAbort("Can't follow alias"); dbInitEntryFromRecord(prec, &entry2); testOk1(entry.pdbbase==entry2.pdbbase); testOk1(entry.precordType==entry2.precordType); testOk1(entry.pflddes==entry2.pflddes); testOk1(entry.precnode==entry2.precnode); testOk1(entry.pfield==entry2.pfield); testOk1(entry.indfield==entry2.indfield); testOk1(!entry2.pinfonode); testOk1(!entry2.message); testOk(memcmp(&entry, &entry2, sizeof(entry))==0, "dbEntries identical"); dbFinishEntry(&entry); dbFinishEntry(&entry2); } static void verify(DBENTRY *pentry, const char *put, const char *exp) { const char *msg; int result; msg = dbVerify(pentry, put); result = (!msg && !exp) || (msg && exp && strcmp(msg, exp) == 0); if (!testOk(result, "dbVerify('%s.%s', '%s') => '%s'", (char *) pentry->precnode->precord, pentry->pflddes->name, put, msg ? msg : "OK")) testDiag("Expected => '%s'", exp ? exp : "OK"); } static void testDbVerify(const char *record) { DBENTRY entry; testDiag("# # # # # # # testDbVerify('%s') # # # # # # # #", record); dbInitEntry(pdbbase, &entry); if (dbFindRecord(&entry, record) != 0) testAbort("Can't find record '%s'", record); dbFindField(&entry, "UDF"); verify(&entry, "0", NULL); verify(&entry, "255", NULL); verify(&entry, "256", "Number too large for field type"); verify(&entry, "0x100", "Number too large for field type"); dbFindField(&entry, "PHAS"); verify(&entry, "0", NULL); verify(&entry, "-32768", NULL); verify(&entry, "-32769", "Number too large for field type"); verify(&entry, "0x7fff", NULL); verify(&entry, "32768", "Number too large for field type"); dbFindField(&entry, "VAL"); verify(&entry, "0", NULL); verify(&entry, "-123456789", NULL); verify(&entry, "123456789", NULL); verify(&entry, "0x1234FEDC", NULL); verify(&entry, "0x100000000", "Number too large for field type"); verify(&entry, "1.2345", "Extraneous characters after number"); dbFindField(&entry, "DESC"); verify(&entry, "", NULL); verify(&entry, "abcdefghijklmnopqrstuvwxyz", NULL); verify(&entry, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "String too long, max 40 characters"); dbFindField(&entry, "DTYP"); verify(&entry, "Soft Channel", NULL); verify(&entry, "zzzz", "Not a valid device type"); dbFindField(&entry, "SCAN"); verify(&entry, "1 second", NULL); verify(&entry, "zzzz", "Not a valid menu choice"); dbFindField(&entry, "FLNK"); verify(&entry, "Anything works here!", NULL); dbFinishEntry(&entry); } void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(dbStaticTest) { testPlan(223); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbStaticTest.db", NULL, NULL); testEntry("testrec.VAL"); testEntry("testalias.VAL"); testEntry("testalias2.VAL"); testEntry("testalias3.VAL"); testAddr2Entry("testrec.VAL"); testAddr2Entry("testalias.VAL"); testAddr2Entry("testalias2.VAL"); testAddr2Entry("testalias3.VAL"); testRec2Entry("testrec"); testRec2Entry("testalias"); testRec2Entry("testalias2"); testRec2Entry("testalias3"); eltc(0); testIocInitOk(); eltc(1); testEntry("testrec.VAL"); testEntry("testalias.VAL"); testEntry("testalias2.VAL"); testEntry("testalias3.VAL"); testAddr2Entry("testrec.VAL"); testAddr2Entry("testalias.VAL"); testAddr2Entry("testalias2.VAL"); testAddr2Entry("testalias3.VAL"); testRec2Entry("testrec"); testRec2Entry("testalias"); testRec2Entry("testalias2"); testRec2Entry("testalias3"); testDbVerify("testrec"); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/dbStaticTest.db0000664000577000060420000000020613557101274021647 0ustar anjaesctl record(x, "testrec") { alias("testalias") info("A", "B") } alias("testrec", "testalias2") alias("testalias2", "testalias3") base-7.0.3.1/modules/database/test/ioc/db/dbStressLock.c0000664000577000060420000002136313557101274021520 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Assoc. as operator of Brookhaven * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Lockset stress test. * * The test stratagy is for N threads to contend for M records. * Each thread will perform one of three operations: * 1) Lock a single record. * 2) Lock several records. * 3) Retarget the TSEL link of a record * * Author: Michael Davidsaver */ #include #include #include #include "envDefs.h" #include "epicsEvent.h" #include "epicsStdlib.h" #include "epicsSpin.h" #include "epicsThread.h" #include "epicsMutex.h" #include "epicsEvent.h" #include "epicsMath.h" #include "dbCommon.h" #include "epicsTime.h" #include "dbLockPvt.h" #include "dbStaticLib.h" #include "dbUnitTest.h" #include "testMain.h" #include "dbAccess.h" #include "errlog.h" #include "xRecord.h" #define testIntOk1(A, OP, B) testOk((A) OP (B), "%s (%d) %s %s (%d)", #A, A, #OP, #B, B); #define testPtrOk1(A, OP, B) testOk((A) OP (B), "%s (%p) %s %s (%p)", #A, A, #OP, #B, B); void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); /* number of seconds for the test to run */ static double runningtime = 18.0; /* number of worker threads */ static unsigned int nworkers = 5; static unsigned int nrecords; #define MAXLOCK 20 static dbCommon **precords; typedef struct { int id; unsigned long N[3]; double X[3]; double X2[3]; double min[3], max[3]; unsigned int done; epicsEventId donevent; dbCommon *prec[MAXLOCK]; } workerPriv; /* hopefully a uniform random number in [0.0, 1.0] */ static double getRand(void) { return rand()/(double)RAND_MAX; } static void doSingle(workerPriv *p) { size_t recn = (size_t)(getRand()*(nrecords-1)); dbCommon *prec = precords[recn]; xRecord *px = (xRecord*)prec; dbScanLock(prec); px->val++; dbScanUnlock(prec); } static void doMulti(workerPriv *p) { int sum = 0; size_t i; size_t nlock = 2 + (size_t)(getRand()*(MAXLOCK-3)); size_t nrec = (size_t)(getRand()*(nrecords-1)); dbLocker *locker; assert(nlock>=2); assert(nlockprec[i] = precords[nrec]; } locker = dbLockerAlloc(p->prec, nlock, 0); if(!locker) testAbort("locker allocation fails"); dbScanLockMany(locker); for(i=0; iprec[i]; sum += px->val; } dbScanUnlockMany(locker); dbLockerFree(locker); } static void doreTarget(workerPriv *p) { char scratchsrc[60]; char scratchdst[MAX_STRING_SIZE]; long ret; DBADDR dbaddr; double action = getRand(); size_t nsrc = (size_t)(getRand()*(nrecords-1)); size_t ntarg = (size_t)(getRand()*(nrecords-1)); xRecord *psrc = (xRecord*)precords[nsrc]; xRecord *ptarg = (xRecord*)precords[ntarg]; strcpy(scratchsrc, psrc->name); strcat(scratchsrc, ".TSEL"); ret = dbNameToAddr(scratchsrc, &dbaddr); if(ret) testAbort("bad record name? %ld", ret); if(action<=0.6) { scratchdst[0] = '\0'; } else { strcpy(scratchdst, ptarg->name); } ret = dbPutField(&dbaddr, DBR_STRING, ptarg->name, 1); if(ret) testAbort("put fails with %ld", ret); } static void worker(void *raw) { unsigned init = 1; workerPriv *priv = raw; testDiag("worker %d is %p", priv->id, epicsThreadGetIdSelf()); while(!priv->done) { epicsUInt64 after, before; double sel = getRand(); double duration; int act; before = epicsMonotonicGet(); if(sel<0.33) { doSingle(priv); act = 0; } else if(sel<0.66) { doMulti(priv); act = 1; } else { doreTarget(priv); act = 2; } after = epicsMonotonicGet(); duration = (after-before)*1e-9; priv->N[act]++; priv->X[act] += duration; priv->X2[act] += duration*duration; if(durationmin[act] || init) { priv->min[act] = duration; init = 0; } if(duration>priv->max[act]) priv->max[act] = duration; } epicsEventMustTrigger(priv->donevent); } MAIN(dbStressTest) { DBENTRY ent; long status; unsigned int i; workerPriv *priv; char *nwork=getenv("NWORK"); epicsTimeStamp seed; epicsTimeGetCurrent(&seed); srand(seed.nsec); if(nwork) { long val = 0; epicsParseLong(nwork, &val, 0, NULL); if(val>2) nworkers = val; } testPlan(80+nworkers*3); #if defined(__rtems__) testSkip(80+nworkers*3, "Test assumes time sliced preempting scheduling"); return testDone(); #endif priv = callocMustSucceed(nworkers, sizeof(*priv), "no memory"); testDiag("lock set stress test"); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("dbStressLock.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); /* collect an array of all records */ dbInitEntry(pdbbase, &ent); for(status = dbFirstRecordType(&ent); !status; status = dbNextRecordType(&ent)) { for(status = dbFirstRecord(&ent); !status; status = dbNextRecord(&ent)) { if(ent.precnode->flags&DBRN_FLAGS_ISALIAS) continue; nrecords++; } } if(nrecords<2) testAbort("where are the records!"); precords = callocMustSucceed(nrecords, sizeof(*precords), "no mem"); for(status = dbFirstRecordType(&ent), i = 0; !status; status = dbNextRecordType(&ent)) { for(status = dbFirstRecord(&ent); !status; status = dbNextRecord(&ent)) { if(ent.precnode->flags&DBRN_FLAGS_ISALIAS) continue; precords[i++] = ent.precnode->precord; } } dbFinishEntry(&ent); testDiag("Running with %u workers and %u records", nworkers, nrecords); for(i=0; iprecord; lockSet *ls; if(ent.precnode->flags&DBRN_FLAGS_ISALIAS) continue; ls = prec->lset->plockSet; testOk(ellCount(&ls->lockRecordList)==ls->refcount, "%s only lockRecords hold refs. %d == %d", prec->name,ellCount(&ls->lockRecordList),ls->refcount); testOk1(ls->ownerlocker==NULL); } } dbFinishEntry(&ent); testDiag("Statistics"); for(i=0; i0); testOk1(priv[i].N[1]>0); testOk1(priv[i].N[2]>0); } testIocShutdownOk(); testdbCleanup(); free(priv); free(precords); return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/dbStressLock.db0000664000577000060420000000156013557101274021660 0ustar anjaesctlrecord(x, "rec01") {} record(x, "rec02") {} record(x, "rec03") {} record(x, "rec04") {} record(x, "rec05") {} record(x, "rec06") {} record(x, "rec07") {} record(x, "rec08") {} record(x, "rec09") {} record(x, "rec10") {} record(x, "rec11") {} record(x, "rec12") {} record(x, "rec13") {} record(x, "rec14") {} record(x, "rec15") {} record(x, "rec16") {} record(x, "rec17") {} record(x, "rec18") {} record(x, "rec19") {} record(x, "rec20") {} record(x, "rec21") {} record(x, "rec22") {} record(x, "rec23") {} record(x, "rec24") {} record(x, "rec25") {} record(x, "rec26") {} record(x, "rec27") {} record(x, "rec28") {} record(x, "rec29") {} record(x, "rec30") {} record(x, "rec31") {} record(x, "rec32") {} record(x, "rec33") {} record(x, "rec34") {} record(x, "rec35") {} record(x, "rec36") {} record(x, "rec37") {} record(x, "rec38") {} record(x, "rec39") {} record(x, "rec40") {} base-7.0.3.1/modules/database/test/ioc/db/devx.c0000664000577000060420000000734713557101274020072 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Assoc. as Operator of Brookhaven * National Lab * EPICS BASE 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 "devx.h" /* xRecord DTYP="Scan I/O" * * dset to test I/O Intr scanning. * INP="@drvname" names a "driver" which * provides a IOSCANPVT. * The driver also defines a callback function which * is invoked when the record is processed. */ struct ELLLIST xdrivers; /* Add a new "driver" with the given group id * and processing callback */ xdrv* xdrv_add(int group, xdrvcb cb, void *arg) { xdrv *drv=callocMustSucceed(1, sizeof(*drv), "xdrv_add"); drv->cb = cb; drv->arg = arg; drv->group = group; scanIoInit(&drv->scan); ellAdd(&xdrivers, &drv->drvnode); return drv; } /* Trigger the named "driver" group to scan */ xdrv *xdrv_get(int group) { ELLNODE *cur; for(cur=ellFirst(&xdrivers); cur; cur=ellNext(cur)) { xdrv *curd = CONTAINER(cur, xdrv, drvnode); if(curd->group==group) { return curd; } } cantProceed("xdrv_get() for non-existant group"); return NULL; } /* Free all "driver" groups and record private structures. * Call after testdbCleanup() */ void xdrv_reset() { ELLNODE *cur; while((cur=ellGet(&xdrivers))!=NULL) { ELLNODE *cur2; xdrv *curd = CONTAINER(cur, xdrv, drvnode); while((cur2=ellGet(&curd->privlist))!=NULL) { xpriv *priv = CONTAINER(cur2, xpriv, privnode); free(priv); } free(curd); } } static long xscanio_init_record(xRecord *prec) { ELLNODE *cur; xpriv *priv; xdrv *drv = NULL; int group, member; assert(prec->inp.type==INST_IO); if(sscanf(prec->inp.value.instio.string, "%d %d", &group, &member)!=2) cantProceed("xscanio_init_record invalid INP string"); for(cur=ellFirst(&xdrivers); cur; cur=ellNext(cur)) { xdrv *curd = CONTAINER(cur, xdrv, drvnode); if(curd->group==group) { drv = curd; break; } } assert(drv!=NULL); priv = mallocMustSucceed(sizeof(*priv), "xscanio_init_record"); priv->prec = prec; priv->drv = drv; priv->member = member; ellAdd(&drv->privlist, &priv->privnode); prec->dpvt = priv; return 0; } static long xscanio_get_ioint_info(int cmd, xRecord *prec, IOSCANPVT *ppvt) { xpriv *priv = prec->dpvt; if(!priv || !priv->drv) return 0; *ppvt = priv->drv->scan; return 0; } static long xscanio_read(xRecord *prec) { xpriv *priv = prec->dpvt; if(!priv || !priv->drv) return 0; if(priv->drv->cb) (*priv->drv->cb)(priv, priv->drv->arg); return 0; } static xdset devxScanIO = { 5, NULL, NULL, &xscanio_init_record, &xscanio_get_ioint_info, &xscanio_read }; epicsExportAddress(dset, devxScanIO); /* basic DTYP="Soft Channel" */ static long xsoft_init_record(xRecord *prec) { recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val); return 0; } static long xsoft_read(xRecord *prec) { return dbGetLink(&prec->inp, DBR_LONG, &prec->val, NULL, NULL); } static struct xdset devxSoft = { 5, NULL, NULL, &xsoft_init_record, NULL, &xsoft_read }; epicsExportAddress(dset, devxSoft); base-7.0.3.1/modules/database/test/ioc/db/devx.dbd0000664000577000060420000000013213557101274020362 0ustar anjaesctldevice(x, CONSTANT, devxSoft, "Soft Channel") device(x, INST_IO , devxScanIO, "Scan I/O") base-7.0.3.1/modules/database/test/ioc/db/devx.h0000664000577000060420000000234013557101274020063 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Assoc. as Operator of Brookhaven * National Lab * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef DEVXSCANIO_H #define DEVXSCANIO_H #include #include #include struct xRecord; struct xpriv; epicsShareExtern struct ELLLIST xdrivers; typedef void (*xdrvcb)(struct xpriv *, void *); typedef struct { ELLNODE drvnode; IOSCANPVT scan; xdrvcb cb; void *arg; ELLLIST privlist; int group; } xdrv; typedef struct xpriv { ELLNODE privnode; xdrv *drv; struct xRecord *prec; int member; } xpriv; epicsShareFunc xdrv *xdrv_add(int group, xdrvcb cb, void *arg); epicsShareFunc xdrv *xdrv_get(int group); epicsShareFunc void xdrv_reset(); typedef struct xdset { long number; long (*report)(int); long (*init)(int); long (*init_record)(struct xRecord *); long (*get_ioint_info)(int, struct xRecord*, IOSCANPVT*); long (*process)(struct xRecord *); } xdset; #endif /* DEVXSCANIO_H */ base-7.0.3.1/modules/database/test/ioc/db/epicsRunDbTests.c0000664000577000060420000000317213557101274022175 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Run Db tests as a batch. * * Do not include performance measurements here, they don't prove * functionality (which is the purpose of this convenience routine). */ #include "epicsUnitTest.h" #include "epicsExit.h" #include "dbmf.h" int testdbConvert(void); int callbackTest(void); int callbackParallelTest(void); int dbStateTest(void); int dbServerTest(void); int dbCaStatsTest(void); int dbShutdownTest(void); int dbScanTest(void); int scanIoTest(void); int dbLockTest(void); int dbPutLinkTest(void); int dbStaticTest(void); int dbCaLinkTest(void); int testDbChannel(void); int chfPluginTest(void); int arrShorthandTest(void); int recGblCheckDeadbandTest(void); void epicsRunDbTests(void) { testHarness(); runTest(testdbConvert); runTest(callbackTest); runTest(callbackParallelTest); runTest(dbStateTest); runTest(dbServerTest); runTest(dbCaStatsTest); runTest(dbShutdownTest); runTest(dbScanTest); runTest(scanIoTest); runTest(dbLockTest); runTest(dbPutLinkTest); runTest(dbStaticTest); runTest(dbCaLinkTest); runTest(testDbChannel); runTest(arrShorthandTest); runTest(recGblCheckDeadbandTest); runTest(chfPluginTest); dbmfFreeChunks(); epicsExit(0); /* Trigger test harness */ } base-7.0.3.1/modules/database/test/ioc/db/jlinkz.c0000664000577000060420000001227213557101274020416 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 Michael Davidsaver * EPICS BASE 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 #define epicsExportSharedSymbols #include "jlinkz.h" #include int numzalloc; static void z_open(struct link *plink) { zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base); if(priv->isopen) testDiag("lsetZ re-open"); priv->isopen = 1; testDiag("Open jlinkz %p", priv); } static void z_remove(struct dbLocker *locker, struct link *plink) { zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base); epicsMutexLock(priv->lock); if(!priv->isopen) testDiag("lsetZ remove without open"); epicsMutexUnlock(priv->lock); testDiag("Remove/free jlinkz %p", priv); epicsAtomicDecrIntT(&numzalloc); epicsMutexDestroy(priv->lock); free(priv); plink->value.json.jlink = NULL; /* paranoia */ } static int z_connected(const struct link *plink) { return 1; /* TODO: not provided should be connected */ } static int z_dbftype(const struct link *plink) { return DBF_LONG; } static long z_elements(const struct link *plink, long *nelements) { *nelements = 1; return 0; } static 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]; zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base); if(pnRequest && *pnRequest==0) return 0; epicsMutexLock(priv->lock); ret = (*pconv)(&priv->value, pbuffer, NULL); epicsMutexUnlock(priv->lock); if(ret==0 && pnRequest) *pnRequest = 1; return ret; } /* TODO: atomicly get value and alarm */ static long z_getalarm(const struct link *plink, epicsEnum16 *status, epicsEnum16 *severity) { zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base); epicsEnum16 sevr, stat; epicsMutexLock(priv->lock); sevr = priv->isset ? 0 : INVALID_ALARM; stat = priv->isset ? 0 : LINK_ALARM; epicsMutexUnlock(priv->lock); if(status) *status = stat; if(severity) *severity = sevr; return 0; } static long z_putval(struct link *plink, short dbrType, const void *pbuffer, long nRequest) { long ret; long (*pconv)(epicsInt32 *, const void *, const dbAddr *) = dbFastPutConvertRoutine[DBF_LONG][dbrType]; zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base); if(nRequest==0) return 0; epicsMutexLock(priv->lock); ret = (*pconv)(&priv->value, pbuffer, NULL); epicsMutexUnlock(priv->lock); return ret; } static lset lsetZ = { 0, 0, /* non-const, non-volatile */ &z_open, &z_remove, NULL, NULL, NULL, /* load */ &z_connected, &z_dbftype, &z_elements, &z_getval, NULL, /* control limits */ NULL, /* display limits */ NULL, /* alarm limits */ NULL, /* prec */ NULL, /* units */ &z_getalarm, NULL, /* time */ &z_putval, NULL, /* putasync */ NULL, /* forward */ NULL, /* doLocked */ }; static jlink* z_alloc(short dbfType) { zpriv *priv; priv = calloc(1, sizeof(*priv)); if(!priv) goto fail; priv->lock = epicsMutexCreate(); if(!priv->lock) goto fail; epicsAtomicIncrIntT(&numzalloc); testDiag("Alloc jlinkz %p", priv); return &priv->base; fail: if(priv && priv->lock) epicsMutexDestroy(priv->lock); free(priv); return NULL; } static void z_free(jlink *pj) { zpriv *priv = CONTAINER(pj, zpriv, base); if(priv->isopen) testDiag("lsetZ jlink free after open()"); testDiag("Free jlinkz %p", priv); epicsAtomicDecrIntT(&numzalloc); epicsMutexDestroy(priv->lock); free(priv); } static jlif_result z_int(jlink *pj, long long num) { zpriv *priv = CONTAINER(pj, zpriv, base); priv->value = num; priv->isset = 1; return jlif_continue; } static jlif_key_result z_start(jlink *pj) { return jlif_key_continue; } static jlif_result z_key(jlink *pj, const char *key, size_t len) { zpriv *priv = CONTAINER(pj, zpriv, base); if(len==4 && strncmp(key,"fail", len)==0) { testDiag("Found fail key jlinkz %p", priv); return jlif_stop; } else { return jlif_continue; } } static jlif_result z_end(jlink *pj) { return jlif_continue; } static struct lset* z_lset(const jlink *pj) { return &lsetZ; } static jlif jlifZ = { "z", &z_alloc, &z_free, NULL, /* null */ NULL, /* bool */ &z_int, NULL, /* double */ NULL, /* string */ &z_start, &z_key, &z_end, NULL, /* start array */ NULL, /* end array */ NULL, /* end child */ &z_lset, NULL, /* report */ NULL, /* map child */ NULL /* start child */ }; epicsExportAddress(jlif, jlifZ); base-7.0.3.1/modules/database/test/ioc/db/jlinkz.dbd0000664000577000060420000000002313557101274020714 0ustar anjaesctllink("z", "jlifZ") base-7.0.3.1/modules/database/test/ioc/db/jlinkz.h0000664000577000060420000000120313557101274020413 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 Michael Davidsaver * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef JLINKZ_H #define JLINKZ_H #include #include #include #include epicsShareExtern int numzalloc; typedef struct { jlink base; epicsMutexId lock; unsigned isset:1; unsigned isopen:1; epicsInt32 value; } zpriv; #endif /* JLINKZ_H */ base-7.0.3.1/modules/database/test/ioc/db/recGblCheckDeadbandTest.c0000664000577000060420000001015013557101274023505 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #include "recGbl.h" #include "dbBase.h" #include "epicsMath.h" #include "epicsUnitTest.h" #include "testMain.h" /* Test parameters */ #define NO_OF_DEADBANDS 3 #define NO_OF_PATTERNS 19 void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); /* Indices for deadband value, test number, val in sequence */ static int idbnd, itest; /* Different deadbands to test with */ static double t_Deadband[NO_OF_DEADBANDS] = { -1, 0, 1.5 }; /* Value sequences for each of the 16 tests */ static double t_SetValues[NO_OF_PATTERNS][2]; /* Expected updates (1=yes) for each sequence of each test of each deadband */ static int t_ExpectedUpdates[NO_OF_DEADBANDS][NO_OF_PATTERNS] = { { /* deadband = -1 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, { /* deadband = 0 */ 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, }, { /* deadband = 1.5 */ 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, }, }; MAIN(recGblCheckDeadbandTest) { unsigned mask; double oldval, newval; /* Test patterns: * 0: step less than deadband (of 1.5) * 1: step larger than deadband (of 1.5) * 2: no change * 3: -0.0 -> +0.0 * ... all possible combinations of steps * between: finite / NaN / -inf / +inf */ t_SetValues[ 0][0] = 1.0; t_SetValues[ 0][1] = 2.0; t_SetValues[ 1][0] = 0.0; t_SetValues[ 1][1] = 2.0; t_SetValues[ 2][0] = 0.0; t_SetValues[ 2][1] = 0.0; t_SetValues[ 3][0] = -0.0; t_SetValues[ 3][1] = 0.0; t_SetValues[ 4][0] = 1.0; t_SetValues[ 4][1] = epicsNAN; t_SetValues[ 5][0] = 1.0; t_SetValues[ 5][1] = epicsINF; t_SetValues[ 6][0] = 1.0; t_SetValues[ 6][1] = -epicsINF; t_SetValues[ 7][0] = epicsNAN; t_SetValues[ 7][1] = 1.0; t_SetValues[ 8][0] = epicsNAN; t_SetValues[ 8][1] = epicsNAN; t_SetValues[ 9][0] = epicsNAN; t_SetValues[ 9][1] = epicsINF; t_SetValues[10][0] = epicsNAN; t_SetValues[10][1] = -epicsINF; t_SetValues[11][0] = epicsINF; t_SetValues[11][1] = 1.0; t_SetValues[12][0] = epicsINF; t_SetValues[12][1] = epicsNAN; t_SetValues[13][0] = epicsINF; t_SetValues[13][1] = epicsINF; t_SetValues[14][0] = epicsINF; t_SetValues[14][1] = -epicsINF; t_SetValues[15][0] = -epicsINF; t_SetValues[15][1] = 1.0; t_SetValues[16][0] = -epicsINF; t_SetValues[16][1] = epicsNAN; t_SetValues[17][0] = -epicsINF; t_SetValues[17][1] = epicsINF; t_SetValues[18][0] = -epicsINF; t_SetValues[18][1] = -epicsINF; testPlan(114); /* Loop over all tested deadband values */ for (idbnd = 0; idbnd < NO_OF_DEADBANDS; idbnd++) { /* Loop over all test patterns */ for (itest = 0; itest < NO_OF_PATTERNS; itest++) { oldval = t_SetValues[itest][0]; newval = t_SetValues[itest][1]; mask = 0; recGblCheckDeadband(&oldval, newval, t_Deadband[idbnd], &mask, 1); /* Check expected vs. actual test result */ testOk(t_ExpectedUpdates[idbnd][itest] == mask, "deadband=%2.1f: check for oldvalue=%f newvalue=%f (expected %d, got %d)", t_Deadband[idbnd], t_SetValues[itest][0], t_SetValues[itest][1], t_ExpectedUpdates[idbnd][itest], mask); if (mask) { testOk((oldval == newval) || (isnan(oldval) && isnan(newval)), "mask set, oldval equals newval"); } else { testOk((oldval == t_SetValues[itest][0]) || (isnan(oldval) && isnan(t_SetValues[itest][0])), "mask not set, oldval unchanged"); } } } return testDone(); } base-7.0.3.1/modules/database/test/ioc/db/rtemsTestHarness.c0000664000577000060420000000100213557101274022420 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ extern void epicsRunDbTests(void); int main(int argc, char **argv) { epicsRunDbTests(); /* calls epicsExit(0) */ return 0; } base-7.0.3.1/modules/database/test/ioc/db/scanIoTest.c0000664000577000060420000002036413557101274021172 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2013 Helmholtz-Zentrum Berlin * für Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #include #include #include "epicsEvent.h" #include "epicsMessageQueue.h" #include "epicsPrint.h" #include "epicsMath.h" #include "alarm.h" #include "menuPriority.h" #include "dbChannel.h" #include "dbStaticLib.h" #include "dbAccessDefs.h" #include "dbScan.h" #include "dbLock.h" #include "dbUnitTest.h" #include "dbCommon.h" #include "recSup.h" #include "devSup.h" #include "iocInit.h" #include "callback.h" #include "ellLib.h" #include "epicsUnitTest.h" #include "testMain.h" #include "osiFileName.h" #include "epicsExport.h" #include "devx.h" #include "xRecord.h" STATIC_ASSERT(NUM_CALLBACK_PRIORITIES==3); void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); static void loadRecord(int group, int member, const char *prio) { char buf[40]; sprintf(buf, "GROUP=%d,MEMBER=%d,PRIO=%s", group, member, prio); testdbReadDatabase("scanIoTest.db", NULL, buf); } typedef struct { int hasprocd[NUM_CALLBACK_PRIORITIES]; int getcomplete[NUM_CALLBACK_PRIORITIES]; epicsEventId wait[NUM_CALLBACK_PRIORITIES]; epicsEventId wake[NUM_CALLBACK_PRIORITIES]; } testsingle; static void testcb(xpriv *priv, void *raw) { testsingle *td = raw; int prio = priv->prec->prio; testOk1(td->hasprocd[prio]==0); td->hasprocd[prio] = 1; } static void testcomp(void *raw, IOSCANPVT scan, int prio) { testsingle *td = raw; testOk1(td->hasprocd[prio]==1); testOk1(td->getcomplete[prio]==0); td->getcomplete[prio] = 1; epicsEventMustTrigger(td->wait[prio]); epicsEventMustWait(td->wake[prio]); } static void testSingleThreading(void) { int i; testsingle data[2]; xdrv *drvs[2]; memset(data, 0, sizeof(data)); for(i=0; i<2; i++) { int p; for(p=0; pscan, &testcomp, &data[0]); scanIoSetComplete(drvs[1]->scan, &testcomp, &data[1]); eltc(0); testIocInitOk(); eltc(1); testDiag("Scan first list"); scanIoRequest(drvs[0]->scan); testDiag("Scan second list"); scanIoRequest(drvs[1]->scan); testDiag("Wait for first list to complete"); for(i=0; imember; testOk1(td->hasprocd==0); td->hasprocd = 1; epicsEventMustTrigger(td->wait); epicsEventMustWait(td->wake); td->getcomplete = 1; } static void testcompmulti(void *raw, IOSCANPVT scan, int prio) { int *mask = raw; testOk(((*mask)&(1<scan, &testcompmulti, &masks[0]); scanIoSetComplete(drvs[1]->scan, &testcompmulti, &masks[1]); /* just enough workers to process all records concurrently */ callbackParallelThreads(2, "LOW"); callbackParallelThreads(2, "MEDIUM"); callbackParallelThreads(2, "HIGH"); eltc(0); testIocInitOk(); eltc(1); testDiag("Scan first list"); testOk1(scanIoRequest(drvs[0]->scan)==0x7); testDiag("Scan second list"); testOk1(scanIoRequest(drvs[1]->scan)==0x7); testDiag("Wait for everything to start"); for(i=0; ijlink; } static void xlink_free(jlink *pjlink) { xlink *xlink = CONTAINER(pjlink, struct xlink, jlink); free(xlink); } static jlif_result xlink_boolean(jlink *pjlink, int val) { return val; /* False triggers a parse failure */ } static struct lset* xlink_get_lset(const jlink *pjlink) { return &xlink_lset; } static void xlink_remove(struct dbLocker *locker, struct link *plink) { xlink_free(plink->value.json.jlink); } static long xlink_getNelements(const struct link *plink, long *nelements) { *nelements = 0; return 0; } static long xlink_getValue(struct link *plink, short dbrType, void *pbuffer, long *pnRequest) { if (pnRequest) *pnRequest = 0; return 0; } static lset xlink_lset = { 1, 0, /* Constant, not Volatile */ NULL, xlink_remove, NULL, NULL, NULL, NULL, NULL, xlink_getNelements, xlink_getValue, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static jlif xlinkIf = { "x", xlink_alloc, xlink_free, NULL, xlink_boolean, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, xlink_get_lset, NULL, NULL, NULL }; epicsExportAddress(jlif, xlinkIf); base-7.0.3.1/modules/database/test/ioc/db/xLink.dbd0000664000577000060420000000002113557101274020476 0ustar anjaesctllink(x, xlinkIf) base-7.0.3.1/modules/database/test/ioc/db/xRecord.c0000664000577000060420000000327113557101274020522 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Andrew Johnson * Ralph Lange */ #include "dbAccessDefs.h" #include "recSup.h" #include "recGbl.h" #include "devSup.h" #include "dbScan.h" #define GEN_SIZE_OFFSET #include "xRecord.h" #include #include "devx.h" static long init_record(struct dbCommon *pcommon, int pass) { struct xRecord *prec = (struct xRecord *)pcommon; long ret = 0; xdset *xset = (xdset*)prec->dset; if(!pass) return 0; if(!xset) { recGblRecordError(S_dev_noDSET, prec, "x: init_record"); return S_dev_noDSET; } if(xset->init_record) ret = (*xset->init_record)(prec); return ret; } static long process(struct dbCommon *pcommon) { struct xRecord *prec = (struct xRecord *)pcommon; long ret = 0; xdset *xset = (xdset*)prec->dset; if(prec->clbk) (*prec->clbk)(prec); prec->pact = TRUE; if(xset && xset->process) ret = (*xset->process)(prec); recGblGetTimeStamp(prec); recGblFwdLink(prec); prec->pact = FALSE; return ret; } static rset xRSET = { RSETNUMBER, NULL, NULL, init_record, process }; epicsExportAddress(rset,xRSET); base-7.0.3.1/modules/database/test/ioc/db/xRecord.db0000664000577000060420000000002113557101274020653 0ustar anjaesctlrecord(x, x) {} base-7.0.3.1/modules/database/test/ioc/db/xRecord.dbd0000664000577000060420000000056313557101274021032 0ustar anjaesctl# This is a minimal record definition recordtype(x) { include "dbCommon.dbd" field(VAL, DBF_LONG) { prompt("Value") } field(LNK, DBF_INLINK) { prompt("Link") } field(INP, DBF_INLINK) { prompt("Input Link") } field(CLBK, DBF_NOACCESS) { prompt("Processing callback") special(SPC_NOMOD) extra("void (*clbk)(struct xRecord*)") } } base-7.0.3.1/modules/database/test/ioc/dbtemplate/Makefile0000664000577000060420000000120613557101274022140 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* TOP = ../../../../.. include $(TOP)/configure/CONFIG TESTPROD_HOST_DEFAULT = dbltExpand TESTPROD_HOST_WIN32 = -nil- dbltExpand_SRCS += dbltExpand.c dbltExpand_LIBS += dbCore ca Com TESTS += msi TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES base-7.0.3.1/modules/database/test/ioc/dbtemplate/dbltExpand.c0000664000577000060420000000510513557101274022733 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS Base is distributed subject to a Software License Agreement found * in the file LICENSE that is included with this distribution. \*************************************************************************/ /* This is a simple version of msi for testing the dbLoadTemplate() code. * * It calls dbLoadTemplate() to parse the substitution file, but replaces * dbLoadRecords() with its own version that reads the template file, * expands any macros in the text and prints the result to stdout. * * This technique won't work on Windows, dbLoadRecords() has to be * epicsShare... decorated and loaded from a shared library. */ #include #include #include #include #include "macLib.h" #include "dbLoadTemplate.h" #define BUFFER_SIZE 0x10000 static char *input_buffer, *output_buffer; int dbLoadRecords(const char *file, const char *macros) { MAC_HANDLE *macHandle = NULL; char **macPairs; FILE *fp; size_t input_len; if (macCreateHandle(&macHandle, NULL)) { fprintf(stderr, "macCreateHandle failed\n"); exit(1); } macSuppressWarning(macHandle, 1); macParseDefns(macHandle, macros, &macPairs); if (!macPairs) { macDeleteHandle(macHandle); macHandle = NULL; } else { macInstallMacros(macHandle, macPairs); free(macPairs); } fp = fopen(file, "r"); if (!fp) { fprintf(stderr, "fopen('%s') failed: %s\n", file, strerror(errno)); exit(1); } input_len = fread(input_buffer, 1, BUFFER_SIZE, fp); if (!feof(fp)) { fprintf(stderr, "input file > 64K!\n"); fclose(fp); exit(1); } input_buffer[input_len] = 0; if (fclose(fp)) { fprintf(stderr, "fclose('%s') failed: %s\n", file, strerror(errno)); exit(1); } macExpandString(macHandle, input_buffer, output_buffer, BUFFER_SIZE-1); printf("%s", output_buffer); if (macHandle) macDeleteHandle(macHandle); return 0; } int main(int argc, char **argv) { input_buffer = malloc(BUFFER_SIZE); output_buffer = malloc(BUFFER_SIZE); if (!input_buffer || !output_buffer) { fprintf(stderr, "malloc(%d) failed\n", BUFFER_SIZE); exit(1); } if (argc != 2) { fprintf(stderr, "Usage: %s file.sub\n", argv[0]); exit(1); } dbLoadTemplate(argv[1], NULL); free(output_buffer); free(input_buffer); return 0; } base-7.0.3.1/modules/database/test/ioc/dbtemplate/msi.plt0000664000577000060420000000477113557101274022023 0ustar anjaesctl#!/usr/bin/perl #************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # Script to run tests on the msi program use strict; use Test; BEGIN {plan tests => 12} # Check include/substitute command model ok(msi('-I .. ../t1-template.txt'), slurp('../t1-result.txt')); # Substitution file, dbLoadTemplate format ok(msi('-I.. -S ../t2-substitution.txt'), slurp('../t2-result.txt')); # Macro scoping ok(msi('-I. -I.. -S ../t3-substitution.txt'), slurp('../t3-result.txt')); # Global scope (backwards compatibility check) ok(msi('-g -I.. -S ../t4-substitution.txt'), slurp('../t4-result.txt')); # Substitution file, regular format ok(msi('-S ../t5-substitute.txt ../t5-template.txt'), slurp('../t5-result.txt')); # Substitution file, pattern format ok(msi('-S../t6-substitute.txt ../t6-template.txt'), slurp('../t6-result.txt')); # Output option -o and verbose option -V my $out = 't7-output.txt'; unlink $out; msi("-I.. -V -o $out ../t1-template.txt"); ok(slurp($out), slurp('../t7-result.txt')); # Dependency generation, include/substitute model ok(msi('-I.. -D -o t8.txt ../t1-template.txt'), slurp('../t8-result.txt')); # Dependency generation, dbLoadTemplate format ok(msi('-I.. -D -ot9.txt -S ../t2-substitution.txt'), slurp('../t9-result.txt')); # Substitution file, variable format, with 0 variable definitions ok(msi('-I. -I.. -S ../t10-substitute.txt'), slurp('../t10-result.txt')); # Substitution file, pattern format, with 0 pattern definitions ok(msi('-I. -I.. -S ../t11-substitute.txt'), slurp('../t11-result.txt')); # Substitution file, environment variable macros in template filename my %envs = (TEST_NO => 12, PREFIX => 't'); @ENV{ keys %envs } = values %envs; ok(msi('-I. -I.. -S ../t12-substitute.txt'), slurp('../t12-result.txt')); delete @ENV{ keys %envs }; # Not really needed # Test support routines sub slurp { my ($file) = @_; open my $in, '<', $file or die "Can't open file $file: $!\n"; my $contents = do { local $/; <$in> }; return $contents; } sub msi { my ($args) = @_; my $nul = $^O eq 'MSWin32' ? 'NUL' : '/dev/null'; my $msi = '@TOP@/bin/@ARCH@/msi'; $msi =~ tr(/)(\\) if $^O eq 'MSWin32'; return `$msi $args 2>$nul`; } base-7.0.3.1/modules/database/test/ioc/dbtemplate/t1-include.txt0000664000577000060420000000031613557101274023207 0ustar anjaesctlThis is t1-include.txt $(include-file-again=) a = $(a=default value used when a is undefined) b = $(b=default value used when b is undefined) substitute "include-file-again=again" End of t1-include.txt base-7.0.3.1/modules/database/test/ioc/dbtemplate/t1-result.txt0000664000577000060420000000061213557101274023101 0ustar anjaesctlThis is t1-template.txt With $(a) & $(b): This is t1-include.txt a = default value used when a is undefined b = default value used when b is undefined End of t1-include.txt On defining a=aaa & b=bbb: This is t1-include.txt again a = aaa b = bbb End of t1-include.txt On setting a="aa": This is t1-include.txt again a = "aa" b = bbb End of t1-include.txt End of t1-template.txt base-7.0.3.1/modules/database/test/ioc/dbtemplate/t1-template.txt0000664000577000060420000000035713557101274023404 0ustar anjaesctlThis is t1-template.txt With $(a) & ${b}: include "t1-include.txt" substitute "a=aaa,b=bbb" On defining a=$(a) & b=${b}: include "t1-include.txt" substitute "a=\"aa\"" On setting a=$(a): include "t1-include.txt" End of t1-template.txt base-7.0.3.1/modules/database/test/ioc/dbtemplate/t10-result.txt0000664000577000060420000000005313557101274023160 0ustar anjaesctl# comment line a=$(a) # comment line a=gbl base-7.0.3.1/modules/database/test/ioc/dbtemplate/t10-substitute.txt0000664000577000060420000000012413557101274024054 0ustar anjaesctlfile t10-template.txt { {} } global { a=gbl } file t10-template.txt { {} } base-7.0.3.1/modules/database/test/ioc/dbtemplate/t10-template.txt0000664000577000060420000000002613557101274023455 0ustar anjaesctl# comment line a=$(a) base-7.0.3.1/modules/database/test/ioc/dbtemplate/t11-result.txt0000664000577000060420000000005313557101274023161 0ustar anjaesctl# comment line a=$(a) # comment line a=gbl base-7.0.3.1/modules/database/test/ioc/dbtemplate/t11-substitute.txt0000664000577000060420000000016213557101274024057 0ustar anjaesctlfile t11-template.txt { pattern {} {} } global { a=gbl } file t11-template.txt { pattern {} {} } base-7.0.3.1/modules/database/test/ioc/dbtemplate/t11-template.txt0000664000577000060420000000002613557101274023456 0ustar anjaesctl# comment line a=$(a) base-7.0.3.1/modules/database/test/ioc/dbtemplate/t12-result.txt0000664000577000060420000000002513557101274023161 0ustar anjaesctl# comment line a=foo base-7.0.3.1/modules/database/test/ioc/dbtemplate/t12-substitute.txt0000664000577000060420000000007013557101274024056 0ustar anjaesctlfile $(PREFIX)$(TEST_NO)-template.txt { { a=foo } } base-7.0.3.1/modules/database/test/ioc/dbtemplate/t12-template.txt0000664000577000060420000000002613557101274023457 0ustar anjaesctl# comment line a=$(a) base-7.0.3.1/modules/database/test/ioc/dbtemplate/t2-result.txt0000664000577000060420000000037413557101274023107 0ustar anjaesctla = va1-a b = def-b c = def-c d = $(d) a = va2-a b = va2-b c = def-c d = $(d) a = va3-a b = va3-b c = va3-c d = $(d) a = va4-a b = va4-b c = def-c d = $(d) a = va5-a b = def-b c = def-c d = $(d) a = pt3-a b = pt3-b c = pt3-c d = $(d) base-7.0.3.1/modules/database/test/ioc/dbtemplate/t2-substitution.txt0000664000577000060420000000031413557101274024337 0ustar anjaesctlfile t2-template.txt { {a=va1-a} {a=va2-a, b=va2-b} {a=va3-a, b=va3-b, c=va3-c} {a=va4-a, b=va4-b} {a=va5-a} } file t2-template.txt { pattern {a, b, c} {pt3-a, pt3-b, pt3-c} } base-7.0.3.1/modules/database/test/ioc/dbtemplate/t2-template.txt0000664000577000060420000000007713557101274023404 0ustar anjaesctla = $(a=def-a) b = $(b=def-b) c = $(c=def-c) d = $(d,undef) base-7.0.3.1/modules/database/test/ioc/dbtemplate/t3-result.txt0000664000577000060420000000223013557101274023101 0ustar anjaesctla = gb1-a b = gb1-b c = def-c d = $(d) a = va1-a b = gb1-b c = def-c d = $(d) a = va2-a b = va2-b c = def-c d = $(d) a = va3-a b = va3-b c = va3-c d = $(d) a = va4-a b = va4-b c = def-c d = $(d) a = va5-a b = gb1-b c = def-c d = $(d) a = gb1-a b = gb1-b c = def-c d = $(d) a = gb2-a b = gb2-b c = def-c d = $(d) a = va1-a b = gb2-b c = def-c d = $(d) a = va2-a b = va2-b c = def-c d = $(d) a = va3-a b = va3-b c = va3-c d = $(d) a = va4-a b = va4-b c = def-c d = $(d) a = va5-a b = gb2-b c = def-c d = $(d) a = gb2-a b = gb2-b c = def-c d = $(d) a = gb3-a b = gb3-b c = def-c d = $(d) a = pt1-a b = gb3-b c = def-c d = $(d) a = pt2-a b = pt2-b c = def-c d = $(d) a = pt3-a b = pt3-b c = pt3-c d = $(d) a = pt4-a b = pt4-b c = def-c d = $(d) a = pt5-a b = gb3-b c = def-c d = $(d) a = gb3-a b = gb3-b c = def-c d = $(d) a = gb4-a b = gb4-b c = def-c d = $(d) a = pt1-a b = gb4-b c = def-c d = $(d) a = pt2-a b = pt2-b c = def-c d = $(d) a = pt3-a b = pt3-b c = pt3-c d = $(d) a = pt4-a b = pt4-b c = def-c d = $(d) a = pt5-a b = gb4-b c = def-c d = $(d) a = gb4-a b = gb4-b c = def-c d = $(d) base-7.0.3.1/modules/database/test/ioc/dbtemplate/t3-substitution.txt0000664000577000060420000000116413557101274024344 0ustar anjaesctlglobal {a=gb1-a, b=gb1-b} file t3-template.txt { {} {a=va1-a} {a=va2-a, b=va2-b} {a=va3-a, b=va3-b, c=va3-c} {a=va4-a, b=va4-b} {a=va5-a} {} global {a=gb2-a, b=gb2-b} {} {a=va1-a} {a=va2-a, b=va2-b} {a=va3-a, b=va3-b, c=va3-c} {a=va4-a, b=va4-b} {a=va5-a} {} } global {b=gb3-b, a=gb3-a} file t3-template.txt { pattern {a, b, c} {} {pt1-a} {pt2-a, pt2-b} {pt3-a, pt3-b, pt3-c} {pt4-a, pt4-b} {pt5-a} {} global {b=gb4-b, a=gb4-a} {} {pt1-a} {pt2-a, pt2-b} {pt3-a, pt3-b, pt3-c} {pt4-a, pt4-b} {pt5-a} {} } base-7.0.3.1/modules/database/test/ioc/dbtemplate/t3-template.txt0000664000577000060420000000007713557101274023405 0ustar anjaesctla = $(a=def-a) b = $(b=def-b) c = $(c=def-c) d = $(d,undef) base-7.0.3.1/modules/database/test/ioc/dbtemplate/t4-result.txt0000664000577000060420000000037413557101274023111 0ustar anjaesctla = va1-a b = def-b c = def-c d = $(d) a = va2-a b = va2-b c = def-c d = $(d) a = va3-a b = va3-b c = va3-c d = $(d) a = va4-a b = va4-b c = va3-c d = $(d) a = va5-a b = va4-b c = va3-c d = $(d) a = pt3-a b = pt3-b c = pt3-c d = $(d) base-7.0.3.1/modules/database/test/ioc/dbtemplate/t4-substitution.txt0000664000577000060420000000031413557101274024341 0ustar anjaesctlfile t2-template.txt { {a=va1-a} {a=va2-a, b=va2-b} {a=va3-a, b=va3-b, c=va3-c} {a=va4-a, b=va4-b} {a=va5-a} } file t2-template.txt { pattern {a, b, c} {pt3-a, pt3-b, pt3-c} } base-7.0.3.1/modules/database/test/ioc/dbtemplate/t5-result.txt0000664000577000060420000000027313557101274023110 0ustar anjaesctl# comment line a = 111 b = 222 c = xx d = $(d) # comment line a = aaa b = bbb c = ccc d = $(d) # comment line a = AA b = BB c = xx d = $(d) # comment line a = aaa b = bbb c = yy d = $(d) base-7.0.3.1/modules/database/test/ioc/dbtemplate/t5-substitute.txt0000664000577000060420000000017613557101274024007 0ustar anjaesctlglobal {c=xx} {a=111,b="222"} { a = aaa , b=bbb , c = ccc} {a=AA,b='BB'} global { c = yy } { a= aaa b= bbb } base-7.0.3.1/modules/database/test/ioc/dbtemplate/t5-template.txt0000664000577000060420000000006313557101274023402 0ustar anjaesctl# comment line a = $(a) b = $(b) c = $(c) d = $(d) base-7.0.3.1/modules/database/test/ioc/dbtemplate/t6-result.txt0000664000577000060420000000027313557101274023111 0ustar anjaesctl# comment line a = 111 b = 222 c = xx d = $(d) # comment line a = aaa b = bbb c = ccc d = $(d) # comment line a = AA b = BB c = xx d = $(d) # comment line a = aaa b = bbb c = yy d = $(d) base-7.0.3.1/modules/database/test/ioc/dbtemplate/t6-substitute.txt0000664000577000060420000000024013557101274024000 0ustar anjaesctlglobal {c=xx} pattern {b,a} {"222",111} pattern {a b c} { aaa , bbb , ccc} pattern { a , b } {AA,'BB'} global { c = yy } pattern { a , b } { aaa bbb } base-7.0.3.1/modules/database/test/ioc/dbtemplate/t6-template.txt0000664000577000060420000000006313557101274023403 0ustar anjaesctl# comment line a = $(a) b = $(b) c = $(c) d = $(d) base-7.0.3.1/modules/database/test/ioc/dbtemplate/t7-result.txt0000664000577000060420000000063613557101274023115 0ustar anjaesctlThis is t1-template.txt With $(a,undefined) & $(b,undefined): This is t1-include.txt a = default value used when a is undefined b = default value used when b is undefined End of t1-include.txt On defining a=aaa & b=bbb: This is t1-include.txt again a = aaa b = bbb End of t1-include.txt On setting a="aa": This is t1-include.txt again a = "aa" b = bbb End of t1-include.txt End of t1-template.txt base-7.0.3.1/modules/database/test/ioc/dbtemplate/t8-result.txt0000664000577000060420000000006013557101274023105 0ustar anjaesctlt8.txt: ../t1-template.txt \ ../t1-include.txt base-7.0.3.1/modules/database/test/ioc/dbtemplate/t9-result.txt0000664000577000060420000000003313557101274023106 0ustar anjaesctlt9.txt: ../t2-template.txt base-7.0.3.1/modules/database/test/std/filters/Makefile0000664000577000060420000000562713557101274021522 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* TOP = ../../../../.. include $(TOP)/configure/CONFIG USR_CPPFLAGS += -DUSE_TYPED_RSET TESTLIBRARY = Recs Recs_SRCS += xRecord.c Recs_SRCS += arrRecord.c Recs_LIBS += dbCore ca Com PROD_LIBS = Recs dbRecStd dbCore ca Com DBDDEPENDS_FILES += filterTest.dbd$(DEP) TARGETS += $(COMMON_DIR)/filterTest.dbd filterTest_DBD += menuGlobal.dbd filterTest_DBD += menuConvert.dbd filterTest_DBD += menuScan.dbd filterTest_DBD += filters.dbd filterTest_DBD += xRecord.dbd filterTest_DBD += arrRecord.dbd TESTFILES += $(COMMON_DIR)/filterTest.dbd testHarness_SRCS += filterTest_registerRecordDeviceDriver.cpp TESTPROD_HOST += tsTest tsTest_SRCS += tsTest.c tsTest_SRCS += filterTest_registerRecordDeviceDriver.cpp testHarness_SRCS += tsTest.c TESTFILES += ../xRecord.db TESTS += tsTest TESTPROD_HOST += dbndTest dbndTest_SRCS += dbndTest.c dbndTest_SRCS += filterTest_registerRecordDeviceDriver.cpp testHarness_SRCS += dbndTest.c TESTS += dbndTest TESTPROD_HOST += arrTest arrTest_SRCS += arrTest.cpp arrTest_SRCS += filterTest_registerRecordDeviceDriver.cpp testHarness_SRCS += arrTest.cpp TESTFILES += ../arrTest.db TESTS += arrTest TESTPROD_HOST += syncTest syncTest_SRCS += syncTest.c syncTest_SRCS += filterTest_registerRecordDeviceDriver.cpp testHarness_SRCS += syncTest.c TESTS += syncTest TESTPROD_HOST += decTest decTest_SRCS += decTest.c decTest_SRCS += filterTest_registerRecordDeviceDriver.cpp testHarness_SRCS += decTest.c TESTS += decTest # epicsRunFilterTests runs all the test programs in a known working order. testHarness_SRCS += epicsRunFilterTests.c filterTestHarness_SRCS += $(testHarness_SRCS) filterTestHarness_SRCS_RTEMS += rtemsTestHarness.c PROD_SRCS_RTEMS += rtemsTestData.c PROD_vxWorks = filterTestHarness PROD_RTEMS = filterTestHarness TESTSPEC_vxWorks = filterTestHarness.munch; epicsRunFilterTests TESTSPEC_RTEMS = filterTestHarness.boot; epicsRunFilterTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) TESTPROD_RTEMS = $(TESTPROD_HOST) TESTSCRIPTS_RTEMS += $(TESTS:%=%.t) endif include $(TOP)/configure/RULES xRecord$(DEP): $(COMMON_DIR)/xRecord.h tsTest$(DEP): $(COMMON_DIR)/xRecord.h dbndTest$(DEP): $(COMMON_DIR)/xRecord.h syncTest$(DEP): $(COMMON_DIR)/xRecord.h arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h arrTest$(DEP): $(COMMON_DIR)/arrRecord.h rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES) base-7.0.3.1/modules/database/test/std/filters/arrRecord.c0000664000577000060420000000715113557101274022143 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* arrRecord.c - minimal array record for test purposes: no processing */ /* * Author: Ralph Lange * * vaguely implemented like parts of recWaveform.c by Bob Dalesio * */ #include #include "dbDefs.h" #include "epicsPrint.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" #include "recSup.h" #include "recGbl.h" #include "cantProceed.h" #define GEN_SIZE_OFFSET #include "arrRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); #define special NULL #define get_value NULL static long cvt_dbaddr(DBADDR *); static long get_array_info(DBADDR *, long *, long *); static long put_array_info(DBADDR *, long); #define get_units NULL #define get_precision NULL #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL #define get_graphic_double NULL #define get_control_double NULL #define get_alarm_double NULL rset arrRSET = { RSETNUMBER, report, initialize, init_record, process, special, get_value, cvt_dbaddr, get_array_info, put_array_info, get_units, get_precision, get_enum_str, get_enum_strs, put_enum_str, get_graphic_double, get_control_double, get_alarm_double }; epicsExportAddress(rset, arrRSET); static long init_record(struct dbCommon *pcommon, int pass) { struct arrRecord *prec = (struct arrRecord *)pcommon; if (pass == 0) { if (prec->nelm <= 0) prec->nelm = 1; if (prec->ftvl > DBF_ENUM) prec->ftvl = DBF_UCHAR; prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl), "arr calloc failed"); if (prec->nelm == 1) { prec->nord = 1; } else { prec->nord = 0; } return 0; } return 0; } static long process(struct dbCommon *pcommon) { struct arrRecord *prec = (struct arrRecord *)pcommon; if(prec->clbk) (*prec->clbk)(prec); prec->pact = TRUE; recGblGetTimeStamp(prec); recGblFwdLink(prec); prec->pact = FALSE; return 0; } static long cvt_dbaddr(DBADDR *paddr) { arrRecord *prec = (arrRecord *) paddr->precord; paddr->pfield = prec->bptr; paddr->no_elements = prec->nelm; paddr->field_type = prec->ftvl; paddr->field_size = dbValueSize(prec->ftvl); paddr->dbr_field_type = prec->ftvl; return 0; } static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) { arrRecord *prec = (arrRecord *) paddr->precord; *no_elements = prec->nord; *offset = prec->off; return 0; } static long put_array_info(DBADDR *paddr, long nNew) { arrRecord *prec = (arrRecord *) paddr->precord; prec->nord = nNew; if (prec->nord > prec->nelm) prec->nord = prec->nelm; return 0; } base-7.0.3.1/modules/database/test/std/filters/arrRecord.dbd0000664000577000060420000000160013557101274022443 0ustar anjaesctlinclude "menuGlobal.dbd" include "menuConvert.dbd" include "menuScan.dbd" recordtype(arr) { include "dbCommon.dbd" field(VAL, DBF_NOACCESS) { prompt("Value") special(SPC_DBADDR) pp(TRUE) extra("void *val") } field(NELM, DBF_ULONG) { prompt("Number of Elements") special(SPC_NOMOD) initial("1") } field(FTVL, DBF_MENU) { prompt("Field Type of Value") special(SPC_NOMOD) menu(menuFtype) } field(NORD, DBF_ULONG) { prompt("Number elements read") special(SPC_NOMOD) } field(OFF, DBF_ULONG) { prompt("Offset into array") } field(BPTR, DBF_NOACCESS) { prompt("Buffer Pointer") special(SPC_NOMOD) extra("void *bptr") } field(INP, DBF_INLINK) { prompt("Input Link") } field(CLBK, DBF_NOACCESS) { prompt("Processing callback") special(SPC_NOMOD) extra("void (*clbk)(struct arrRecord*)") } } base-7.0.3.1/modules/database/test/std/filters/arrTest.cpp0000664000577000060420000002742113557101274022206 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2003 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to the Software License Agreement * found in the file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ /* using stuff from softIoc.cpp by Andrew Johnson */ #include #include #include #include #include #include "registryFunction.h" #include "epicsThread.h" #include "epicsExit.h" #include "epicsStdio.h" #include "envDefs.h" #include "dbStaticLib.h" #include "dbmf.h" #include "errlog.h" #include "registry.h" #include "dbAddr.h" #include "dbAccess.h" #include "asDbLib.h" #include "iocInit.h" #include "iocsh.h" #include "dbChannel.h" #include "epicsUnitTest.h" #include "dbUnitTest.h" #include "testMain.h" #include "osiFileName.h" #include "arrRecord.h" extern "C" { void filterTest_registerRecordDeviceDriver(struct dbBase *); } #define CA_SERVER_PORT "65535" #define PATTERN 0x55 const char *server_port = CA_SERVER_PORT; static int fl_equals_array(short type, const db_field_log *pfl1, void *p2) { for (int i = 0; i < pfl1->no_elements; i++) { switch (type) { case DBR_DOUBLE: if (((epicsFloat64*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) { testDiag("at index=%d: field log has %g, should be %d", i, ((epicsFloat64*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]); return 0; } break; case DBR_LONG: if (((epicsInt32*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) { testDiag("at index=%d: field log has %d, should be %d", i, ((epicsInt32*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]); return 0; } break; case DBR_STRING: if (strtol(&((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], NULL, 0) != ((epicsInt32*)p2)[i]) { testDiag("at index=%d: field log has '%s', should be '%d'", i, &((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], ((epicsInt32*)p2)[i]); return 0; } break; default: return 0; } } return 1; } static void createAndOpen(const char *chan, const char *json, const char *type, dbChannel**pch, short no) { ELLNODE *node; char name[80]; strncpy(name, chan, sizeof(name)-1); strncat(name, json, sizeof(name)-strlen(name)-1); testOk(!!(*pch = dbChannelCreate(name)), "dbChannel with plugin arr %s created", type); testOk((ellCount(&(*pch)->filters) == no), "channel has %d filter(s) in filter list", no); testOk(!(dbChannelOpen(*pch)), "dbChannel with plugin arr opened"); node = ellFirst(&(*pch)->pre_chain); (void) CONTAINER(node, chFilter, pre_node); testOk((ellCount(&(*pch)->pre_chain) == 0), "arr has no filter in pre chain"); node = ellFirst(&(*pch)->post_chain); (void) CONTAINER(node, chFilter, post_node); testOk((ellCount(&(*pch)->post_chain) == no), "arr has %d filter(s) in post chain", no); } static void testHead (const char *title, const char *typ = "") { const char *line = "------------------------------------------------------------------------------"; testDiag("%s", line); testDiag(title, typ); testDiag("%s", line); } #define TEST1(Size, Offset, Incr, Text) \ testDiag("Offset: %d (%s)", Offset, Text); \ off = Offset; \ (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \ pfl = db_create_read_log(pch); \ testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \ pfl2 = dbChannelRunPostChain(pch, pfl); \ testOk(pfl2 == pfl, "call does not drop or replace field_log"); \ testOk(pfl->type == dbfl_type_ref, "filtered field log has type ref"); \ testOk(fl_equals_array(dbr_type, pfl2, ar##Size##_##Offset##_##Incr), "array data correct"); \ db_delete_field_log(pfl); static void check(short dbr_type) { dbChannel *pch; db_field_log *pfl, *pfl2; dbAddr valaddr; dbAddr offaddr; const char *offname = NULL, *valname = NULL, *typname = NULL; epicsInt32 ar[10] = {10,11,12,13,14,15,16,17,18,19}; epicsInt32 *ar10_0_1 = ar; epicsInt32 ar10_4_1[10] = {14,15,16,17,18,19,10,11,12,13}; epicsInt32 ar5_0_1[10] = {12,13,14,15,16}; epicsInt32 ar5_3_1[10] = {15,16,17,18,19}; epicsInt32 ar5_5_1[10] = {17,18,19,10,11}; epicsInt32 ar5_9_1[10] = {11,12,13,14,15}; epicsInt32 ar5_0_2[10] = {12,14,16}; epicsInt32 ar5_3_2[10] = {15,17,19}; epicsInt32 ar5_5_2[10] = {17,19,11}; epicsInt32 ar5_9_2[10] = {11,13,15}; epicsInt32 ar5_0_3[10] = {12,15}; epicsInt32 ar5_3_3[10] = {15,18}; epicsInt32 ar5_5_3[10] = {17,10}; epicsInt32 ar5_9_3[10] = {11,14}; epicsInt32 off = 0; switch (dbr_type) { case DBR_LONG: offname = "x.OFF"; valname = "x.VAL"; typname = "long"; break; case DBR_DOUBLE: offname = "y.OFF"; valname = "y.VAL"; typname = "double"; break; case DBR_STRING: offname = "z.OFF"; valname = "z.VAL"; typname = "string"; break; default: testDiag("Invalid data type %d", dbr_type); } (void) dbNameToAddr(offname, &offaddr); (void) dbNameToAddr(valname, &valaddr); (void) dbPutField(&valaddr, DBR_LONG, ar, 10); /* Default: should not change anything */ testHead("Ten %s elements from rec, increment 1, full size (default)", typname); createAndOpen(valname, "{\"arr\":{}}", "(default)", &pch, 1); testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); TEST1(10, 0, 1, "no offset"); TEST1(10, 4, 1, "wrapped"); dbChannelDelete(pch); testHead("Ten %s elements from rec, increment 1, out-of-bound start parameter", typname); createAndOpen(valname, "{\"arr\":{\"s\":-500}}", "out-of-bound start", &pch, 1); testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); TEST1(10, 4, 1, "wrapped"); dbChannelDelete(pch); testHead("Ten %s elements from rec, increment 1, out-of-bound end parameter", typname); createAndOpen(valname, "{\"arr\":{\"e\":500}}", "out-of-bound end", &pch, 1); testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); TEST1(10, 4, 1, "wrapped"); dbChannelDelete(pch); testHead("Ten %s elements from rec, increment 1, zero increment parameter", typname); createAndOpen(valname, "{\"arr\":{\"i\":0}}", "zero increment", &pch, 1); testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); TEST1(10, 4, 1, "wrapped"); dbChannelDelete(pch); testHead("Ten %s elements from rec, increment 1, invalid increment parameter", typname); createAndOpen(valname, "{\"arr\":{\"i\":-30}}", "invalid increment", &pch, 1); testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); TEST1(10, 4, 1, "wrapped"); dbChannelDelete(pch); #define TEST5(Incr, Left, Right, Type) \ testHead("Five %s elements from rec, increment " #Incr ", " Type " addressing", typname); \ createAndOpen(valname, "{\"arr\":{\"s\":" #Left ",\"e\":" #Right ",\"i\":" #Incr "}}", \ "(" #Left ":" #Incr ":" #Right ")", &pch, 1); \ testOk(pch->final_type == valaddr.field_type, \ "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); \ testOk(pch->final_no_elements == 4 / Incr + 1, \ "final no_elements correct (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); \ TEST1(5, 0, Incr, "no offset"); \ TEST1(5, 3, Incr, "from upper block"); \ TEST1(5, 5, Incr, "wrapped"); \ TEST1(5, 9, Incr, "from lower block"); \ dbChannelDelete(pch); /* Contiguous block of 5 */ TEST5(1, 2, 6, "regular"); TEST5(1, -8, 6, "left side from-end"); TEST5(1, 2, -4, "right side from-end"); TEST5(1, -8, -4, "both sides from-end"); /* 5 elements with increment 2 */ TEST5(2, 2, 6, "regular"); TEST5(2, -8, 6, "left side from-end"); TEST5(2, 2, -4, "right side from-end"); TEST5(2, -8, -4, "both sides from-end"); /* 5 elements with increment 3 */ TEST5(3, 2, 6, "regular"); TEST5(3, -8, 6, "left side from-end"); TEST5(3, 2, -4, "right side from-end"); TEST5(3, -8, -4, "both sides from-end"); /* From buffer (plugin chain) */ #define TEST5B(Incr, Left, Right, Type) \ testHead("Five %s elements from buffer, increment " #Incr ", " Type " addressing", typname); \ createAndOpen(valname, "{\"arr\":{},\"arr\":{\"s\":" #Left ",\"e\":" #Right ",\"i\":" #Incr "}}", \ "(" #Left ":" #Incr ":" #Right ")", &pch, 2); \ testOk(pch->final_type == valaddr.field_type, \ "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); \ testOk(pch->final_no_elements == 4 / Incr + 1, \ "final no_elements correct (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); \ TEST1(5, 0, Incr, "no offset"); \ dbChannelDelete(pch); /* Contiguous block of 5 */ TEST5B(1, 2, 6, "regular"); TEST5B(1, -8, 6, "left side from-end"); TEST5B(1, 2, -4, "right side from-end"); TEST5B(1, -8, -4, "both sides from-end"); /* 5 elements with increment 2 */ TEST5B(2, 2, 6, "regular"); TEST5B(2, -8, 6, "left side from-end"); TEST5B(2, 2, -4, "right side from-end"); TEST5B(2, -8, -4, "both sides from-end"); /* 5 elements with increment 3 */ TEST5B(3, 2, 6, "regular"); TEST5B(3, -8, 6, "left side from-end"); TEST5B(3, 2, -4, "right side from-end"); TEST5B(3, -8, -4, "both sides from-end"); } MAIN(arrTest) { dbEventCtx evtctx; const chFilterPlugin *plug; char arr[] = "arr"; testPlan(1402); /* Prepare the IOC */ epicsEnvSet("EPICS_CA_SERVER_PORT", server_port); testdbPrepare(); testdbReadDatabase("filterTest.dbd", NULL, NULL); filterTest_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("arrTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); /* Start the IOC */ evtctx = db_init_events(); testOk(!!(plug = dbFindFilter(arr, strlen(arr))), "plugin arr registered correctly"); check(DBR_LONG); check(DBR_DOUBLE); check(DBR_STRING); db_close_events(evtctx); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/std/filters/arrTest.db0000664000577000060420000000047413557101274022010 0ustar anjaesctlrecord(arr, "x") { field(DESC, "test array record") field(NELM, "10") field(FTVL, "LONG") } record(arr, "y") { field(DESC, "test array record") field(NELM, "10") field(FTVL, "DOUBLE") } record(arr, "z") { field(DESC, "test array record") field(NELM, "10") field(FTVL, "STRING") } base-7.0.3.1/modules/database/test/std/filters/dbndTest.c0000664000577000060420000002262113557101274021766 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #include "dbStaticLib.h" #include "dbAccessDefs.h" #include "db_field_log.h" #include "dbCommon.h" #include "registry.h" #include "errlog.h" #include "chfPlugin.h" #include "epicsUnitTest.h" #include "dbUnitTest.h" #include "epicsTime.h" #include "dbmf.h" #include "testMain.h" #include "osiFileName.h" #define PATTERN 0x55 void filterTest_registerRecordDeviceDriver(struct dbBase *); static db_field_log fl; static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) { return !(memcmp(pfl1, pfl2, sizeof(db_field_log))); } static void fl_setup(dbChannel *chan, db_field_log *pfl) { struct dbCommon *prec = dbChannelRecord(chan); memset(pfl, 0, sizeof(db_field_log)); pfl->ctx = dbfl_context_read; pfl->type = dbfl_type_val; pfl->stat = prec->stat; pfl->sevr = prec->sevr; pfl->time = prec->time; pfl->field_type = dbChannelFieldType(chan); pfl->field_size = dbChannelFieldSize(chan); pfl->no_elements = dbChannelElements(chan); /* * use memcpy to avoid a bus error on * union copy of char in the db at an odd * address */ memcpy(&pfl->u.v.field, dbChannelField(chan), dbChannelFieldSize(chan)); } static void changeValue(db_field_log *pfl2, long val) { pfl2->u.v.field.dbf_long = val; testDiag("new value: %ld", val); } static void mustPassOnce(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { int oldFree = db_available_logs(), newFree; db_field_log *pfl; changeValue(pfl2, val); testDiag("mode=%s delta=%g filter must pass once", m, d); pfl = dbChannelRunPreChain(pch, pfl2); testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); testOk(fl_equal(pfl, pfl2), "call 1 does not change field_log data"); pfl = dbChannelRunPreChain(pch, pfl2); testOk(NULL == pfl, "call 2 drops field_log"); newFree = db_available_logs(); testOk(newFree == oldFree + 1, "field_log was freed - %d+1 => %d", oldFree, newFree); } static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { int oldFree = db_available_logs(), newFree; db_field_log *pfl; changeValue(pfl2, val); testDiag("mode=%s delta=%g filter must drop", m, d); pfl = dbChannelRunPreChain(pch, pfl2); testOk(NULL == pfl, "call 1 drops field_log"); newFree = db_available_logs(); testOk(newFree == oldFree + 1, "field_log was freed - %d+1 => %d", oldFree, newFree); } static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { int oldFree = db_available_logs(), newFree; db_field_log *pfl; changeValue(pfl2, val); testDiag("mode=%s delta=%g filter must pass twice", m, d); pfl = dbChannelRunPreChain(pch, pfl2); testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); testOk(fl_equal(pfl, pfl2), "call 1 does not change field_log data"); pfl = dbChannelRunPreChain(pch, pfl2); testOk(pfl2 == pfl, "call 2 does not drop or replace field_log"); testOk(fl_equal(pfl, pfl2), "call 2 does not change field_log data"); newFree = db_available_logs(); testOk(newFree == oldFree, "field_log was not freed - %d => %d", oldFree, newFree); } static void testHead (char* title) { testDiag("--------------------------------------------------------"); testDiag("%s", title); testDiag("--------------------------------------------------------"); } MAIN(dbndTest) { dbChannel *pch; chFilter *filter; const chFilterPlugin *plug; char dbnd[] = "dbnd"; ELLNODE *node; chPostEventFunc *cb_out = NULL; void *arg_out = NULL; db_field_log *pfl2; db_field_log fl1; dbEventCtx evtctx; int logsFree, logsFinal; testPlan(77); testdbPrepare(); testdbReadDatabase("filterTest.dbd", NULL, NULL); filterTest_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("xRecord.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); evtctx = db_init_events(); testOk(!!(plug = dbFindFilter(dbnd, strlen(dbnd))), "plugin dbnd registered correctly"); testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{}}")), "dbChannel with plugin dbnd (delta=0) created"); testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); /* Start the free-list */ db_delete_field_log(db_create_read_log(pch)); logsFree = db_available_logs(); testDiag("%d field_logs on free-list", logsFree); memset(&fl, PATTERN, sizeof(fl)); fl1 = fl; node = ellFirst(&pch->filters); filter = CONTAINER(node, chFilter, list_node); plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1); testOk(!!(cb_out) && !!(arg_out), "register_pre registers one filter with argument"); testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type"); testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened"); node = ellFirst(&pch->pre_chain); filter = CONTAINER(node, chFilter, pre_node); testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg != NULL), "dbnd has one filter with argument in pre chain"); testOk((ellCount(&pch->post_chain) == 0), "dbnd has no filter in post chain"); /* Field logs of type ref and rec: pass any update */ testHead("Field logs of type ref and rec"); fl1.type = dbfl_type_rec; mustPassTwice(pch, &fl1, "abs field_log=rec", 0., 0); fl1.type = dbfl_type_ref; mustPassTwice(pch, &fl1, "abs field_log=ref", 0., 0); /* Delta = 0: pass any change */ testHead("Delta = 0: pass any change"); pfl2 = db_create_read_log(pch); testDiag("new field_log from record"); fl_setup(pch, pfl2); mustPassOnce(pch, pfl2, "abs", 0., 0); pfl2 = db_create_read_log(pch); testDiag("new field_log from record"); fl_setup(pch, pfl2); mustPassOnce(pch, pfl2, "abs", 0., 1); dbChannelDelete(pch); testDiag("%d field_logs on free-list", db_available_logs()); /* Delta = -1: pass any update */ testHead("Delta = -1: pass any update"); testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"d\":-1.0}}")), "dbChannel with plugin dbnd (delta=-1) created"); testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened"); pfl2 = db_create_read_log(pch); testDiag("new field_log from record"); fl_setup(pch, pfl2); mustPassTwice(pch, pfl2, "abs", -1., 0); mustPassTwice(pch, pfl2, "abs", -1., 1); db_delete_field_log(pfl2); dbChannelDelete(pch); testDiag("%d field_logs on free-list", db_available_logs()); /* Delta = absolute */ testHead("Delta = absolute"); testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"d\":3}}")), "dbChannel with plugin dbnd (delta=3) created"); testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened"); pfl2 = db_create_read_log(pch); testDiag("new field_log from record"); fl_setup(pch, pfl2); mustPassOnce(pch, pfl2, "abs", 3., 1); pfl2 = db_create_read_log(pch); testDiag("new field_log from record"); fl_setup(pch, pfl2); mustDrop(pch, pfl2, "abs", 3., 3); pfl2 = db_create_read_log(pch); testDiag("new field_log from record"); fl_setup(pch, pfl2); mustDrop(pch, pfl2, "abs", 3., 4); pfl2 = db_create_read_log(pch); testDiag("new field_log from record"); fl_setup(pch, pfl2); mustPassOnce(pch, pfl2, "abs", 3., 5); dbChannelDelete(pch); testDiag("%d field_logs on free-list", db_available_logs()); /* Delta = relative */ testHead("Delta = relative"); testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"m\":\"rel\",\"d\":50}}")), "dbChannel with plugin dbnd (mode=rel, delta=50) created"); testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened"); pfl2 = db_create_read_log(pch); testDiag("new field_log from record"); fl_setup(pch, pfl2); mustPassOnce(pch, pfl2, "rel", 50., 1); pfl2 = db_create_read_log(pch); testDiag("new field_log from record"); fl_setup(pch, pfl2); mustPassOnce(pch, pfl2, "rel", 50., 2); pfl2 = db_create_read_log(pch); testDiag("new field_log from record"); fl_setup(pch, pfl2); mustDrop(pch, pfl2, "rel", 50., 3); pfl2 = db_create_read_log(pch); testDiag("new field_log from record"); fl_setup(pch, pfl2); mustPassOnce(pch, pfl2, "rel", 50., 4); pfl2 = db_create_read_log(pch); testDiag("new field_log from record"); fl_setup(pch, pfl2); mustDrop(pch, pfl2, "rel", 50., 5); pfl2 = db_create_read_log(pch); testDiag("new field_log from record"); fl_setup(pch, pfl2); mustDrop(pch, pfl2, "rel", 50., 6); pfl2 = db_create_read_log(pch); testDiag("new field_log from record"); fl_setup(pch, pfl2); mustPassOnce(pch, pfl2, "rel", 50., 7); dbChannelDelete(pch); logsFinal = db_available_logs(); testOk(logsFree == logsFinal, "%d field_logs on free-list", logsFinal); db_close_events(evtctx); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/std/filters/decTest.c0000664000577000060420000002031713557101274021612 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2019 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Authors: Ralph Lange , * Andrew Johnson */ #include #include "dbStaticLib.h" #include "dbAccessDefs.h" #include "db_field_log.h" #include "dbCommon.h" #include "dbChannel.h" #include "registry.h" #include "chfPlugin.h" #include "errlog.h" #include "dbmf.h" #include "epicsUnitTest.h" #include "dbUnitTest.h" #include "epicsTime.h" #include "testMain.h" #include "osiFileName.h" void filterTest_registerRecordDeviceDriver(struct dbBase *); static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) { return !(memcmp(pfl1, pfl2, sizeof(db_field_log))); } static void fl_setup(dbChannel *chan, db_field_log *pfl, long val) { struct dbCommon *prec = dbChannelRecord(chan); memset(pfl, 0, sizeof(db_field_log)); pfl->ctx = dbfl_context_event; pfl->type = dbfl_type_val; pfl->stat = prec->stat; pfl->sevr = prec->sevr; pfl->time = prec->time; pfl->field_type = DBF_LONG; pfl->field_size = sizeof(epicsInt32); pfl->no_elements = 1; /* * use memcpy to avoid a bus error on * union copy of char in the db at an odd * address */ memcpy(&pfl->u.v.field, dbChannelField(chan), dbChannelFieldSize(chan)); pfl->u.v.field.dbf_long = val; } static void testHead (char* title) { testDiag("--------------------------------------------------------"); testDiag("%s", title); testDiag("--------------------------------------------------------"); } static void mustDrop(dbChannel *pch, db_field_log *pfl, char* m) { int oldFree = db_available_logs(); db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl); int newFree = db_available_logs(); testOk(NULL == pfl2, "filter drops field_log (%s)", m); testOk(newFree == oldFree + 1, "field_log was freed - %d+1 => %d", oldFree, newFree); db_delete_field_log(pfl2); } static void mustPass(dbChannel *pch, db_field_log *pfl, char* m) { int oldFree = db_available_logs(); db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl); int newFree = db_available_logs(); testOk(pfl == pfl2, "filter passes field_log (%s)", m); testOk(newFree == oldFree, "field_log was not freed - %d => %d", oldFree, newFree); db_delete_field_log(pfl2); } static void checkAndOpenChannel(dbChannel *pch, const chFilterPlugin *plug) { ELLNODE *node; chFilter *filter; chPostEventFunc *cb_out = NULL; void *arg_out = NULL; db_field_log fl, fl1; testDiag("Test filter structure and open channel"); testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); fl_setup(pch, &fl, 1); fl1 = fl; node = ellFirst(&pch->filters); filter = CONTAINER(node, chFilter, list_node); plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1); testOk(cb_out && arg_out, "register_pre registers one filter with argument"); testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type"); testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dec opened"); node = ellFirst(&pch->pre_chain); filter = CONTAINER(node, chFilter, pre_node); testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg != NULL), "dec has one filter with argument in pre chain"); testOk((ellCount(&pch->post_chain) == 0), "sync has no filter in post chain"); } MAIN(decTest) { dbChannel *pch; const chFilterPlugin *plug; char myname[] = "dec"; db_field_log *pfl[10]; int i, logsFree, logsFinal; dbEventCtx evtctx; testPlan(104); testdbPrepare(); testdbReadDatabase("filterTest.dbd", NULL, NULL); filterTest_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("xRecord.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); evtctx = db_init_events(); testOk(!!(plug = dbFindFilter(myname, strlen(myname))), "plugin '%s' registered correctly", myname); /* N < 1 */ testOk(!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":-1}}")), "dbChannel with dec (n=-1) failed"); testOk(!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":0}}")), "dbChannel with dec (n=0) failed"); /* Bad parms */ testOk(!(pch = dbChannelCreate("x.VAL{\"dec\":{}}")), "dbChannel with dec (no parm) failed"); testOk(!(pch = dbChannelCreate("x.VAL{\"dec\":{\"x\":true}}")), "dbChannel with dec (x=true) failed"); /* No Decimation (N=1) */ testHead("No Decimation (n=1)"); testOk(!!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":1}}")), "dbChannel with plugin dec (n=1) created"); /* Start the free-list */ db_delete_field_log(db_create_read_log(pch)); logsFree = db_available_logs(); testDiag("%d field_logs on free-list", logsFree); checkAndOpenChannel(pch, plug); for (i = 0; i < 5; i++) { pfl[i] = db_create_read_log(pch); fl_setup(pch, pfl[i], 10 + i); } testDiag("Test event stream"); mustPass(pch, pfl[0], "i=0"); mustPass(pch, pfl[1], "i=1"); mustPass(pch, pfl[2], "i=2"); mustPass(pch, pfl[3], "i=3"); mustPass(pch, pfl[4], "i=4"); dbChannelDelete(pch); testDiag("%d field_logs on free-list", db_available_logs()); /* Decimation (N=2) */ testHead("Decimation (n=2)"); testOk(!!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":2}}")), "dbChannel with plugin dec (n=2) created"); checkAndOpenChannel(pch, plug); for (i = 0; i < 10; i++) { pfl[i] = db_create_read_log(pch); fl_setup(pch, pfl[i], 20 + i); } testDiag("Test event stream"); mustPass(pch, pfl[0], "i=0"); mustDrop(pch, pfl[1], "i=1"); mustPass(pch, pfl[2], "i=2"); mustDrop(pch, pfl[3], "i=3"); mustPass(pch, pfl[4], "i=4"); mustDrop(pch, pfl[5], "i=5"); mustPass(pch, pfl[6], "i=6"); mustDrop(pch, pfl[7], "i=7"); mustPass(pch, pfl[8], "i=8"); mustDrop(pch, pfl[9], "i=9"); dbChannelDelete(pch); testDiag("%d field_logs on free-list", db_available_logs()); /* Decimation (N=3) */ testHead("Decimation (n=3)"); testOk(!!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":3}}")), "dbChannel with plugin dec (n=3) created"); checkAndOpenChannel(pch, plug); for (i = 0; i < 10; i++) { pfl[i] = db_create_read_log(pch); fl_setup(pch, pfl[i], 30 + i); } testDiag("Test event stream"); mustPass(pch, pfl[0], "i=0"); mustDrop(pch, pfl[1], "i=1"); mustDrop(pch, pfl[2], "i=2"); mustPass(pch, pfl[3], "i=3"); mustDrop(pch, pfl[4], "i=4"); mustDrop(pch, pfl[5], "i=5"); mustPass(pch, pfl[6], "i=6"); mustDrop(pch, pfl[7], "i=7"); mustDrop(pch, pfl[8], "i=8"); mustPass(pch, pfl[9], "i=9"); dbChannelDelete(pch); testDiag("%d field_logs on free-list", db_available_logs()); /* Decimation (N=4) */ testHead("Decimation (n=4)"); testOk(!!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":4}}")), "dbChannel with plugin dec (n=4) created"); checkAndOpenChannel(pch, plug); for (i = 0; i < 10; i++) { pfl[i] = db_create_read_log(pch); fl_setup(pch, pfl[i], 40 + i); } testDiag("Test event stream"); mustPass(pch, pfl[0], "i=0"); mustDrop(pch, pfl[1], "i=1"); mustDrop(pch, pfl[2], "i=2"); mustDrop(pch, pfl[3], "i=3"); mustPass(pch, pfl[4], "i=4"); mustDrop(pch, pfl[5], "i=5"); mustDrop(pch, pfl[6], "i=6"); mustDrop(pch, pfl[7], "i=7"); mustPass(pch, pfl[8], "i=8"); mustDrop(pch, pfl[9], "i=9"); dbChannelDelete(pch); logsFinal = db_available_logs(); testOk(logsFree == logsFinal, "%d field_logs on free-list", logsFinal); db_close_events(evtctx); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/std/filters/epicsRunFilterTests.c0000664000577000060420000000146613557101274024204 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Run filter tests as a batch. */ #include "epicsUnitTest.h" #include "epicsExit.h" #include "dbmf.h" int tsTest(void); int dbndTest(void); int syncTest(void); int arrTest(void); int decTest(void); void epicsRunFilterTests(void) { testHarness(); runTest(tsTest); runTest(dbndTest); runTest(syncTest); runTest(arrTest); runTest(decTest); dbmfFreeChunks(); epicsExit(0); /* Trigger test harness */ } base-7.0.3.1/modules/database/test/std/filters/rtemsTestHarness.c0000664000577000060420000000101213557101274023524 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ extern void epicsRunFilterTests(void); int main(int argc, char **argv) { epicsRunFilterTests(); /* calls epicsExit(0) */ return 0; } base-7.0.3.1/modules/database/test/std/filters/syncTest.c0000664000577000060420000003342013557101274022032 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #include "dbStaticLib.h" #include "dbAccessDefs.h" #include "db_field_log.h" #include "dbCommon.h" #include "dbChannel.h" #include "registry.h" #include "chfPlugin.h" #include "errlog.h" #include "dbmf.h" #include "epicsUnitTest.h" #include "dbUnitTest.h" #include "epicsTime.h" #include "dbState.h" #include "testMain.h" #include "osiFileName.h" #define PATTERN 0x55 void filterTest_registerRecordDeviceDriver(struct dbBase *); static db_field_log fl; static dbStateId red; static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) { return !(memcmp(pfl1, pfl2, sizeof(db_field_log))); } static void fl_setup(dbChannel *chan, db_field_log *pfl, long val) { struct dbCommon *prec = dbChannelRecord(chan); memset(pfl, 0, sizeof(db_field_log)); pfl->ctx = dbfl_context_event; pfl->type = dbfl_type_val; pfl->stat = prec->stat; pfl->sevr = prec->sevr; pfl->time = prec->time; pfl->field_type = DBF_LONG; pfl->field_size = sizeof(epicsInt32); pfl->no_elements = 1; /* * use memcpy to avoid a bus error on * union copy of char in the db at an odd * address */ memcpy(&pfl->u.v.field, dbChannelField(chan), dbChannelFieldSize(chan)); pfl->u.v.field.dbf_long = val; } static void testHead (char* title) { testDiag("--------------------------------------------------------"); testDiag("%s", title); testDiag("--------------------------------------------------------"); } /* * Use mustDrop() and mustPass() to test filters with no memory * of previous field_log pointers. */ static void mustDrop(dbChannel *pch, db_field_log *pfl, char* m) { int oldFree = db_available_logs(); db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl); int newFree = db_available_logs(); testOk(NULL == pfl2, "filter drops field_log (%s)", m); testOk(newFree == oldFree + 1, "a field_log was freed - %d+1 => %d", oldFree, newFree); db_delete_field_log(pfl2); } static void mustPass(dbChannel *pch, db_field_log *pfl, char* m) { int oldFree = db_available_logs(); db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl); int newFree = db_available_logs(); testOk(pfl == pfl2, "filter passes field_log (%s)", m); testOk(newFree == oldFree, "no field_logs were freed - %d => %d", oldFree, newFree); db_delete_field_log(pfl2); } /* * Use mustStash() and mustSwap() to test filters that save * field_log pointers and return them later. * * mustStash() expects the filter to save the current pointer * (freeing any previously saved pointer) and return NULL. * mustSwap() expects the filter to return the previously * saved pointer and save the current pointer. */ static db_field_log *stashed; static void streamReset(void) { stashed = NULL; } static void mustStash(dbChannel *pch, db_field_log *pfl, char* m) { int oldFree = db_available_logs(); db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl); int newFree = db_available_logs(); testOk(NULL == pfl2, "filter stashes field_log (%s)", m); if (stashed) { testOk(newFree == oldFree + 1, "a field_log was freed - %d+1 => %d", oldFree, newFree); } else { testOk(newFree == oldFree, "no field_logs were freed - %d => %d", oldFree, newFree); } stashed = pfl; db_delete_field_log(pfl2); } static void mustSwap(dbChannel *pch, db_field_log *pfl, char* m) { int oldFree = db_available_logs(); db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl); int newFree = db_available_logs(); testOk(stashed == pfl2, "filter returns stashed field log (%s)", m); testOk(newFree == oldFree, "no field_logs were freed - %d => %d", oldFree, newFree); stashed = pfl; db_delete_field_log(pfl2); } static void mustPassTwice(dbChannel *pch, db_field_log *pfl, char* m) { int oldFree = db_available_logs(), newFree; db_field_log *pfl2; testDiag("%s: filter must pass twice", m); pfl2 = dbChannelRunPreChain(pch, pfl); testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); pfl2 = dbChannelRunPreChain(pch, pfl); testOk(pfl2 == pfl, "call 2 does not drop or replace field_log"); newFree = db_available_logs(); testOk(newFree == oldFree, "no field_logs were freed - %d => %d", oldFree, newFree); } static void checkCtxRead(dbChannel *pch, dbStateId id) { fl.ctx = dbfl_context_read; dbStateClear(id); mustPassTwice(pch, &fl, "ctx='read', state=FALSE"); dbStateSet(id); mustPassTwice(pch, &fl, "ctx='read', state=TRUE"); dbStateClear(id); mustPassTwice(pch, &fl, "ctx='read', state=FALSE"); fl.ctx = dbfl_context_event; } static void checkAndOpenChannel(dbChannel *pch, const chFilterPlugin *plug) { ELLNODE *node; chFilter *filter; chPostEventFunc *cb_out = NULL; void *arg_out = NULL; db_field_log fl1; testDiag("Test filter structure and open channel"); testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); fl1 = fl; node = ellFirst(&pch->filters); filter = CONTAINER(node, chFilter, list_node); plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1); testOk(!!(cb_out) && !!(arg_out), "register_pre registers one filter with argument"); testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type"); testOk(!(dbChannelOpen(pch)), "dbChannel with plugin sync opened"); node = ellFirst(&pch->pre_chain); filter = CONTAINER(node, chFilter, pre_node); testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg != NULL), "sync has one filter with argument in pre chain"); testOk((ellCount(&pch->post_chain) == 0), "sync has no filter in post chain"); checkCtxRead(pch, red); } MAIN(syncTest) { dbChannel *pch; const chFilterPlugin *plug; char myname[] = "sync"; db_field_log *pfl[10]; int i, logsFree, logsFinal; dbEventCtx evtctx; testPlan(214); testdbPrepare(); testdbReadDatabase("filterTest.dbd", NULL, NULL); filterTest_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("xRecord.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); evtctx = db_init_events(); testOk(!!(plug = dbFindFilter(myname, strlen(myname))), "plugin %s registered correctly", myname); testOk(!!(red = dbStateCreate("red")), "state 'red' created successfully"); /* nonexisting state */ testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\",\"s\":\"blue\"}}")), "dbChannel with sync (m='while' s='blue') (nonex state) failed"); /* missing state */ testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\"}}")), "dbChannel with sync (m='while') (no state) failed"); /* missing mode */ testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"s\":\"red\"}}")), "dbChannel with sync (s='red') (no mode) failed"); /* mode WHILE */ testHead("Mode WHILE (m='while', s='red')"); testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\",\"s\":\"red\"}}")), "dbChannel with plugin sync (m='while' s='red') created"); /* Start the free-list */ db_delete_field_log(db_create_read_log(pch)); logsFree = db_available_logs(); testDiag("%d field_logs on free-list", logsFree); checkAndOpenChannel(pch, plug); for (i = 0; i < 9; i++) { pfl[i] = db_create_read_log(pch); fl_setup(pch, pfl[i], 120 + i); } testDiag("Test event stream"); dbStateClear(red); mustDrop(pch, pfl[0], "state=FALSE, log0"); mustDrop(pch, pfl[1], "state=FALSE, log1"); mustDrop(pch, pfl[2], "state=FALSE, log2"); dbStateSet(red); mustPass(pch, pfl[3], "state=TRUE, log3"); mustPass(pch, pfl[4], "state=TRUE, log4"); mustPass(pch, pfl[5], "state=TRUE, log5"); dbStateClear(red); mustDrop(pch, pfl[6], "state=FALSE, log6"); mustDrop(pch, pfl[7], "state=FALSE, log7"); mustDrop(pch, pfl[8], "state=FALSE, log8"); dbChannelDelete(pch); testDiag("%d field_logs on free-list", db_available_logs()); /* mode UNLESS */ testHead("Mode UNLESS (m='unless', s='red')"); testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"unless\",\"s\":\"red\"}}")), "dbChannel with plugin sync (m='unless' s='red') created"); checkAndOpenChannel(pch, plug); for (i = 0; i < 9; i++) { pfl[i] = db_create_read_log(pch); fl_setup(pch, pfl[i], 120 + i); } testDiag("Test event stream"); dbStateClear(red); mustPass(pch, pfl[0], "state=FALSE, log0"); mustPass(pch, pfl[1], "state=FALSE, log1"); mustPass(pch, pfl[2], "state=FALSE, log2"); dbStateSet(red); mustDrop(pch, pfl[3], "state=TRUE, log3"); mustDrop(pch, pfl[4], "state=TRUE, log4"); mustDrop(pch, pfl[5], "state=TRUE, log5"); dbStateClear(red); mustPass(pch, pfl[6], "state=FALSE, log6"); mustPass(pch, pfl[7], "state=FALSE, log7"); mustPass(pch, pfl[8], "state=FALSE, log8"); dbChannelDelete(pch); testDiag("%d field_logs on free-list", db_available_logs()); /* mode BEFORE */ testHead("Mode BEFORE (m='before', s='red')"); testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"before\",\"s\":\"red\"}}")), "dbChannel with plugin sync (m='before' s='red') created"); checkAndOpenChannel(pch, plug); for (i = 0; i < 10; i++) { pfl[i] = db_create_read_log(pch); fl_setup(pch, pfl[i], 120 + i); } testDiag("Test event stream"); streamReset(); dbStateClear(red); mustStash(pch, pfl[0], "state=FALSE, log0"); mustStash(pch, pfl[1], "state=FALSE, log1"); mustStash(pch, pfl[2], "state=FALSE, log2"); dbStateSet(red); mustSwap(pch, pfl[3], "state=TRUE, log3"); mustStash(pch, pfl[4], "state=TRUE, log4"); mustStash(pch, pfl[5], "state=TRUE, log5"); mustStash(pch, pfl[6], "state=TRUE, log6"); dbStateClear(red); mustStash(pch, pfl[7], "state=FALSE, log7"); mustStash(pch, pfl[8], "state=FALSE, log8"); mustStash(pch, pfl[9], "state=FALSE, log9"); dbChannelDelete(pch); testDiag("%d field_logs on free-list", db_available_logs()); /* mode FIRST */ testHead("Mode FIRST (m='first', s='red')"); testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"first\",\"s\":\"red\"}}")), "dbChannel with plugin sync (m='first' s='red') created"); checkAndOpenChannel(pch, plug); for (i = 0; i < 9; i++) { pfl[i] = db_create_read_log(pch); fl_setup(pch, pfl[i], 120 + i); } testDiag("Test event stream"); streamReset(); dbStateClear(red); mustDrop(pch, pfl[0], "state=FALSE, log0"); mustDrop(pch, pfl[1], "state=FALSE, log1"); mustDrop(pch, pfl[2], "state=FALSE, log2"); dbStateSet(red); mustPass(pch, pfl[3], "state=TRUE, log3"); mustDrop(pch, pfl[4], "state=TRUE, log4"); mustDrop(pch, pfl[5], "state=TRUE, log5"); dbStateClear(red); mustDrop(pch, pfl[6], "state=FALSE, log6"); mustDrop(pch, pfl[7], "state=FALSE, log7"); mustDrop(pch, pfl[8], "state=FALSE, log8"); dbChannelDelete(pch); testDiag("%d field_logs on free-list", db_available_logs()); /* mode LAST */ testHead("Mode LAST (m='last', s='red')"); testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"last\",\"s\":\"red\"}}")), "dbChannel with plugin sync (m='last' s='red') created"); checkAndOpenChannel(pch, plug); for (i = 0; i < 10; i++) { pfl[i] = db_create_read_log(pch); fl_setup(pch, pfl[i], 120 + i); } testDiag("Test event stream"); streamReset(); dbStateClear(red); mustStash(pch, pfl[0], "state=FALSE, log0"); mustStash(pch, pfl[1], "state=FALSE, log1"); mustStash(pch, pfl[2], "state=FALSE, log2"); dbStateSet(red); mustStash(pch, pfl[3], "state=TRUE, log3"); mustStash(pch, pfl[4], "state=TRUE, log4"); mustStash(pch, pfl[5], "state=TRUE, log5"); dbStateClear(red); mustSwap(pch, pfl[6], "state=TRUE, log6"); mustStash(pch, pfl[7], "state=FALSE, log7"); mustStash(pch, pfl[8], "state=FALSE, log8"); mustStash(pch, pfl[9], "state=FALSE, log9"); dbChannelDelete(pch); testDiag("%d field_logs on free-list", db_available_logs()); /* mode AFTER */ testHead("Mode AFTER (m='after', s='red')"); testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"after\",\"s\":\"red\"}}")), "dbChannel with plugin sync (m='after' s='red') created"); checkAndOpenChannel(pch, plug); for (i = 0; i < 9; i++) { pfl[i] = db_create_read_log(pch); fl_setup(pch, pfl[i], 120 + i); } testDiag("Test event stream"); streamReset(); dbStateClear(red); mustDrop(pch, pfl[0], "state=FALSE, log0"); mustDrop(pch, pfl[1], "state=FALSE, log1"); mustDrop(pch, pfl[2], "state=FALSE, log2"); dbStateSet(red); mustDrop(pch, pfl[3], "state=TRUE, log3"); mustDrop(pch, pfl[4], "state=TRUE, log4"); mustDrop(pch, pfl[5], "state=TRUE, log5"); dbStateClear(red); mustPass(pch, pfl[6], "state=FALSE, log6"); mustDrop(pch, pfl[7], "state=FALSE, log7"); mustDrop(pch, pfl[8], "state=FALSE, log8"); dbChannelDelete(pch); logsFinal = db_available_logs(); testOk(logsFree == logsFinal, "%d field_logs on free-list", logsFinal); db_close_events(evtctx); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/std/filters/tsTest.c0000664000577000060420000000670113557101274021506 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #include "dbStaticLib.h" #include "dbAccessDefs.h" #include "chfPlugin.h" #include "errlog.h" #include "epicsUnitTest.h" #include "dbUnitTest.h" #include "registry.h" #include "dbmf.h" #include "epicsTime.h" #include "testMain.h" #include "osiFileName.h" #define PATTERN 0x55 void filterTest_registerRecordDeviceDriver(struct dbBase *); static db_field_log fl; static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) { return !(memcmp(pfl1, pfl2, sizeof(db_field_log))); } static int fl_equal_ex_ts(const db_field_log *pfl1, const db_field_log *pfl2) { db_field_log fl1 = *pfl1; fl1.time = pfl2->time; return fl_equal(&fl1, pfl2); } MAIN(tsTest) { dbChannel *pch; chFilter *filter; const chFilterPlugin *plug; char ts[] = "ts"; ELLNODE *node; chPostEventFunc *cb_out = NULL; void *arg_out = NULL; db_field_log fl1; db_field_log *pfl2; epicsTimeStamp stamp, now; dbEventCtx evtctx; testPlan(12); testdbPrepare(); testdbReadDatabase("filterTest.dbd", NULL, NULL); filterTest_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("xRecord.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); evtctx = db_init_events(); testOk(!!(plug = dbFindFilter(ts, strlen(ts))), "plugin ts registered correctly"); testOk(!!(pch = dbChannelCreate("x.VAL{\"ts\":{}}")), "dbChannel with plugin ts created"); testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); memset(&fl, PATTERN, sizeof(fl)); fl1 = fl; node = ellFirst(&pch->filters); filter = CONTAINER(node, chFilter, list_node); plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1); testOk(!!(cb_out) && !(arg_out), "register_pre registers one filter w/o argument"); testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type"); testOk(!(dbChannelOpen(pch)), "dbChannel with plugin ts opened"); node = ellFirst(&pch->pre_chain); filter = CONTAINER(node, chFilter, pre_node); testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg == NULL), "ts has one filter w/o argument in pre chain"); testOk((ellCount(&pch->post_chain) == 0), "ts has no filter in post chain"); memset(&fl, PATTERN, sizeof(fl)); fl1 = fl; pfl2 = dbChannelRunPreChain(pch, &fl1); testOk(pfl2 == &fl1, "ts filter does not drop or replace field_log"); testOk(fl_equal_ex_ts(&fl1, pfl2), "ts filter does not change field_log data"); testOk(!!(pfl2 = db_create_read_log(pch)), "create field log from channel"); stamp = pfl2->time; db_delete_field_log(pfl2); pfl2 = dbChannelRunPreChain(pch, &fl1); epicsTimeGetCurrent(&now); testOk(epicsTimeDiffInSeconds(&pfl2->time, &stamp) >= 0 && epicsTimeDiffInSeconds(&now, &pfl2->time) >= 0, "ts filter sets time stamp to \"now\""); dbChannelDelete(pch); db_close_events(evtctx); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/std/filters/xRecord.c0000664000577000060420000000142613557101274021625 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Andrew Johnson * Ralph Lange */ #include "dbAccessDefs.h" #include #define GEN_SIZE_OFFSET #include "xRecord.h" #include static rset xRSET; epicsExportAddress(rset,xRSET); base-7.0.3.1/modules/database/test/std/filters/xRecord.db0000664000577000060420000000002113557101274021756 0ustar anjaesctlrecord(x, x) {} base-7.0.3.1/modules/database/test/std/filters/xRecord.dbd0000664000577000060420000000021213557101274022124 0ustar anjaesctl# This is a combined minimal DBD and DB file recordtype(x) { include "dbCommon.dbd" field(VAL, DBF_LONG) { prompt("Value") } } base-7.0.3.1/modules/database/test/std/link/Makefile0000664000577000060420000000353313557101274021001 0ustar anjaesctl#************************************************************************* # Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* TOP=../../../../.. include $(TOP)/configure/CONFIG USR_CPPFLAGS += -DUSE_TYPED_RSET TESTLIBRARY = Recs Recs_SRCS += ioRecord.c Recs_LIBS += dbCore ca Com PROD_LIBS = Recs dbRecStd dbCore ca Com DBDDEPENDS_FILES += linkTest.dbd$(DEP) TARGETS += $(COMMON_DIR)/linkTest.dbd linkTest_DBD += menuGlobal.dbd linkTest_DBD += menuConvert.dbd linkTest_DBD += menuScan.dbd linkTest_DBD += links.dbd linkTest_DBD += ioRecord.dbd TESTFILES += $(COMMON_DIR)/linkTest.dbd TESTFILES += ../ioRecord.db testHarness_SRCS += linkTest_registerRecordDeviceDriver.cpp TESTPROD_HOST += lnkStateTest lnkStateTest_SRCS += lnkStateTest.c lnkStateTest_SRCS += linkTest_registerRecordDeviceDriver.cpp testHarness_SRCS += lnkStateTest.c TESTS += lnkStateTest TESTPROD_HOST += lnkCalcTest lnkCalcTest_SRCS += lnkCalcTest.c lnkCalcTest_SRCS += linkTest_registerRecordDeviceDriver.cpp testHarness_SRCS += lnkCalcTest.c TESTS += lnkCalcTest # epicsRunLinkTests runs all the test programs in a known working order. testHarness_SRCS += epicsRunLinkTests.c linkTestHarness_SRCS += $(testHarness_SRCS) linkTestHarness_SRCS_RTEMS += rtemsTestHarness.c PROD_vxWorks = linkTestHarness PROD_RTEMS = linkTestHarness TESTSPEC_vxWorks = linkTestHarness.munch; epicsRunLinkTests TESTSPEC_RTEMS = linkTestHarness.boot; epicsRunLinkTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES ioRecord$(DEP): $(COMMON_DIR)/ioRecord.h lnkStateTest$(DEP): $(COMMON_DIR)/ioRecord.h lnkCalcTest$(DEP): $(COMMON_DIR)/ioRecord.h base-7.0.3.1/modules/database/test/std/link/epicsRunLinkTests.c0000664000577000060420000000131513557101274023132 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in the file LICENSE that is included with this distribution. \*************************************************************************/ /* * Run filter tests as a batch. */ #include "epicsUnitTest.h" #include "epicsExit.h" #include "dbmf.h" int lnkStateTest(void); int lnkCalcTest(void); void epicsRunLinkTests(void) { testHarness(); runTest(lnkStateTest); runTest(lnkCalcTest); dbmfFreeChunks(); epicsExit(0); /* Trigger test harness */ } base-7.0.3.1/modules/database/test/std/link/ioRecord.c0000664000577000060420000000117013557101274021246 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in the file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Andrew Johnson */ #include #include #define GEN_SIZE_OFFSET #include "ioRecord.h" #undef GEN_SIZE_OFFSET #include static rset ioRSET; epicsExportAddress(rset,ioRSET); base-7.0.3.1/modules/database/test/std/link/ioRecord.db0000664000577000060420000000002213557101274021404 0ustar anjaesctlrecord(io, io) {} base-7.0.3.1/modules/database/test/std/link/ioRecord.dbd0000664000577000060420000000042313557101274021555 0ustar anjaesctl# This is a soft record type with both input and output links recordtype(io) { include "dbCommon.dbd" field(VAL, DBF_LONG) { prompt("Value") } field(INPUT, DBF_INLINK) { prompt("Input Link") } field(OUTPUT, DBF_OUTLINK) { prompt("Output Link") } } base-7.0.3.1/modules/database/test/std/link/lnkCalcTest.c0000664000577000060420000001205713557101274021715 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2018 Andrew Johnson * EPICS BASE is distributed subject to a Software License Agreement found * in the file LICENSE that is included with this distribution. \*************************************************************************/ #include #include "dbAccess.h" #include "alarm.h" #include "dbUnitTest.h" #include "errlog.h" #include "epicsThread.h" #include "dbLink.h" #include "dbState.h" #include "recGbl.h" #include "testMain.h" #include "ioRecord.h" #define testPutLongStr(PV, VAL) \ testdbPutArrFieldOk(PV, DBF_CHAR, sizeof(VAL), VAL); void linkTest_registerRecordDeviceDriver(struct dbBase *); static void startTestIoc(const char *dbfile) { testdbPrepare(); testdbReadDatabase("linkTest.dbd", NULL, NULL); linkTest_registerRecordDeviceDriver(pdbbase); testdbReadDatabase(dbfile, NULL, NULL); eltc(0); testIocInitOk(); eltc(1); } static void testCalc() { ioRecord *pio; DBLINK *pinp, *pout; long status; epicsFloat64 f64; startTestIoc("ioRecord.db"); pio = (ioRecord *) testdbRecordPtr("io"); pinp = &pio->input; pout = &pio->output; testDiag("testing lnkCalc input"); { dbStateId red; testPutLongStr("io.INPUT", "{\"calc\":{" "\"expr\":\"a\"," "\"args\":[{\"state\":\"red\"}]" "}}"); if (testOk1(pinp->type == JSON_LINK)) testDiag("Link was set to '%s'", pinp->value.json.string); red = dbStateFind("red"); testOk(!!red, "State red was created"); dbStateSet(red); status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL); testOk(!status, "dbGetLink succeeded (status = %ld)", status); testOk(f64, "Got TRUE (%g)", f64); dbStateClear(red); status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL); testOk(!status, "dbGetLink succeeded (status = %ld)", status); testOk(!f64, "Got FALSE (%g)", f64); } { dbStateId major = dbStateCreate("major"); dbStateId minor = dbStateCreate("minor"); epicsEnum16 stat, sevr; testPutLongStr("io.INPUT", "{\"calc\":{" "\"expr\":\"0\"," "\"major\":\"A\"," "\"minor\":\"B\"," "\"args\":[{\"state\":\"major\"},{\"state\":\"minor\"}]" "}}"); if (testOk1(pinp->type == JSON_LINK)) testDiag("Link was set to '%s'", pinp->value.json.string); dbStateSet(major); dbStateSet(minor); status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL); testOk(!status, "dbGetLink succeeded (status = %ld)", status); testOk(f64 == 0.0, "Got zero (%g)", f64); testOk(recGblResetAlarms(pio) & DBE_ALARM, "Record alarm was raised"); status = dbGetAlarm(pinp, &stat, &sevr); testOk(!status, "dbGetAlarm succeeded (status = %ld)", status); testOk(stat == LINK_ALARM, "Alarm status = LINK (%d)", stat); testOk(sevr == MAJOR_ALARM, "Alarm severity = MAJOR (%d)", sevr); dbStateClear(major); status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL); testOk(!status, "dbGetLink succeeded (status = %ld)", status); testOk(recGblResetAlarms(pio) & DBE_ALARM, "Record alarm was raised"); status = dbGetAlarm(pinp, &stat, &sevr); testOk(!status, "dbGetAlarm succeeded (status = %ld)", status); testOk(stat == LINK_ALARM, "Alarm status = LINK (%d)", stat); testOk(sevr == MINOR_ALARM, "Alarm severity = MINOR (%d)", sevr); } testDiag("testing lnkCalc output"); { dbStateId red = dbStateFind("red"); dbStateId out = dbStateCreate("out"); testPutLongStr("io.OUTPUT", "{\"calc\":{" "\"expr\":\"!a\"," "\"out\":{\"state\":\"out\"}," "\"args\":[{\"state\":\"red\"}]," "\"units\":\"things\"," "\"prec\":3" "}}"); if (testOk1(pout->type == JSON_LINK)) testDiag("Link was set to '%s'", pout->value.json.string); dbStateSet(red); f64 = 1.0; status = dbPutLink(pout, DBF_DOUBLE, &f64, 1); testOk(!status, "dbPutLink succeeded (status = %ld)", status); testOk(!dbStateGet(out), "output was cleared"); dbStateClear(red); status = dbPutLink(pout, DBF_DOUBLE, &f64, 1); testOk(!status, "dbGetLink succeeded (status = %ld)", status); testOk(dbStateGet(out), "output was set"); } { char units[20] = {0}; short prec = 0; status = dbGetUnits(pout, units, sizeof(units)); testOk(!status, "dbGetUnits succeeded (status = %ld)", status); testOk(!strcmp(units, "things"), "Units string correct (%s)", units); status = dbGetPrecision(pout, &prec); testOk(!status, "dbGetPrecision succeeded (status = %ld)", status); testOk(prec == 3, "Precision correct (%d)", prec); } testIocShutdownOk(); testdbCleanup(); } MAIN(lnkCalcTest) { testPlan(0); testCalc(); return testDone(); } base-7.0.3.1/modules/database/test/std/link/lnkStateTest.c0000664000577000060420000000755313557101274022140 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2018 Andrew Johnson * EPICS BASE is distributed subject to a Software License Agreement found * in the file LICENSE that is included with this distribution. \*************************************************************************/ #include #include "dbAccess.h" #include "alarm.h" #include "dbUnitTest.h" #include "errlog.h" #include "epicsThread.h" #include "dbLink.h" #include "dbState.h" #include "ioRecord.h" #include "testMain.h" void linkTest_registerRecordDeviceDriver(struct dbBase *); static void startTestIoc(const char *dbfile) { testdbPrepare(); testdbReadDatabase("linkTest.dbd", NULL, NULL); linkTest_registerRecordDeviceDriver(pdbbase); testdbReadDatabase(dbfile, NULL, NULL); eltc(0); testIocInitOk(); eltc(1); } static void testState() { dbStateId red; ioRecord *pio; DBLINK *pinp, *pout; long status; testDiag("testing lnkState"); startTestIoc("ioRecord.db"); pio = (ioRecord *) testdbRecordPtr("io"); pinp = &pio->input; pout = &pio->output; red = dbStateFind("red"); testOk(!red, "No state red exists"); testdbPutFieldOk("io.INPUT", DBF_STRING, "{\"state\":\"red\"}"); if (testOk1(pinp->type == JSON_LINK)) testDiag("Link was set to '%s'", pinp->value.json.string); red = dbStateFind("red"); testOk(!!red, "state red exists"); { epicsInt16 i16; dbStateSet(red); status = dbGetLink(pinp, DBF_SHORT, &i16, NULL, NULL); testOk(!status, "dbGetLink succeeded (status = %ld)", status); testOk(i16, "Got TRUE"); testdbPutFieldOk("io.INPUT", DBF_STRING, "{\"state\":\"!red\"}"); if (testOk1(pinp->type == JSON_LINK)) testDiag("Link was set to '%s'", pinp->value.json.string); status = dbGetLink(pinp, DBF_SHORT, &i16, NULL, NULL); testOk(!status, "dbGetLink succeeded (status = %ld)", status); testOk(!i16, "Got FALSE"); testdbPutFieldOk("io.OUTPUT", DBF_STRING, "{\"state\":\"red\"}"); if (testOk1(pout->type == JSON_LINK)) testDiag("Link was set to '%s'", pout->value.json.string); i16 = 0; status = dbPutLink(pout, DBF_SHORT, &i16, 1); testOk(!status, "dbPutLink %d succeeded (status = %ld)", i16, status); testOk(!dbStateGet(red), "state was cleared"); i16 = 0x8000; status = dbPutLink(pout, DBF_SHORT, &i16, 1); testOk(!status, "dbPutLink 0x%hx succeeded (status = %ld)", i16, status); testOk(dbStateGet(red), "state was set"); } status = dbPutLink(pout, DBF_STRING, "", 1); testOk(!status, "dbPutLink '' succeeded (status = %ld)", status); testOk(!dbStateGet(red), "state was cleared"); status = dbPutLink(pout, DBF_STRING, "FALSE", 1); /* Not really... */ testOk(!status, "dbPutLink 'FALSE' succeeded (status = %ld)", status); testOk(dbStateGet(red), "state was set"); status = dbPutLink(pout, DBF_STRING, "0", 1); testOk(!status, "dbPutLink '0' succeeded (status = %ld)", status); testOk(!dbStateGet(red), "state was cleared"); { epicsFloat64 f64 = 0.1; status = dbPutLink(pout, DBF_DOUBLE, &f64, 1); testOk(!status, "dbPutLink %g succeeded (status = %ld)", f64, status); testOk(dbStateGet(red), "state was set"); testdbPutFieldOk("io.OUTPUT", DBF_STRING, "{\"state\":\"!red\"}"); if (testOk1(pout->type == JSON_LINK)) testDiag("Link was set to '%s'", pout->value.json.string); status = dbPutLink(pout, DBF_DOUBLE, &f64, 1); testOk(!status, "dbPutLink %g succeeded (status = %ld)", f64, status); testOk(!dbStateGet(red), "state was cleared"); } testIocShutdownOk(); testdbCleanup(); } MAIN(lnkStateTest) { testPlan(28); testState(); return testDone(); } base-7.0.3.1/modules/database/test/std/link/rtemsTestHarness.c0000664000577000060420000000101213557101274023011 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in the file LICENSE that is included with this distribution. \*************************************************************************/ extern void epicsRunLinkTests(void); int main(int argc, char **argv) { epicsRunLinkTests(); /* calls epicsExit(0) */ return 0; } base-7.0.3.1/modules/database/test/std/rec/Makefile0000664000577000060420000001403613557101274020615 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* TOP = ../../../../.. include $(TOP)/configure/CONFIG USR_CPPFLAGS += -DUSE_TYPED_RSET TESTLIBRARY = dbRecStdTest dbRecStdTest_SRCS += asTestLib.c dbRecStdTest_LIBS += dbRecStd dbCore ca Com PROD_LIBS = dbRecStdTest dbRecStd dbCore ca Com TARGETS += $(COMMON_DIR)/recTestIoc.dbd DBDDEPENDS_FILES += recTestIoc.dbd$(DEP) recTestIoc_DBD = base.dbd TESTFILES += $(COMMON_DIR)/recTestIoc.dbd testHarness_SRCS += recTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += asTestIoc_registerRecordDeviceDriver.cpp TESTPROD_HOST += arrayOpTest arrayOpTest_SRCS += arrayOpTest.c arrayOpTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += arrayOpTest.c TESTFILES += ../arrayOpTest.db TESTS += arrayOpTest TESTPROD_HOST += recMiscTest recMiscTest_SRCS += recMiscTest.c recMiscTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += recMiscTest.c TESTFILES += ../recMiscTest.db TESTS += recMiscTest TESTPROD_HOST += linkRetargetLinkTest linkRetargetLinkTest_SRCS += linkRetargetLinkTest.c linkRetargetLinkTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += linkRetargetLinkTest.c TESTFILES += ../linkRetargetLink.db TESTS += linkRetargetLinkTest TESTPROD_HOST += linkInitTest linkInitTest_SRCS += linkInitTest.c linkInitTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += linkInitTest.c TESTFILES += ../linkInitTest.db TESTS += linkInitTest TESTPROD_HOST += compressTest compressTest_SRCS += compressTest.c compressTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += compressTest.c TESTFILES += ../compressTest.db TESTS += compressTest TESTPROD_HOST += asyncSoftTest asyncSoftTest_SRCS += asyncSoftTest.c asyncSoftTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += asyncSoftTest.c TESTFILES += ../asyncSoftTest.db TESTS += asyncSoftTest TESTPROD_HOST += softTest softTest_SRCS += softTest.c softTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += softTest.c TESTFILES += ../softTest.db TESTS += softTest TARGETS += $(COMMON_DIR)/asTestIoc.dbd DBDDEPENDS_FILES += asTestIoc.dbd$(DEP) asTestIoc_DBD += base.dbd asTestIoc_DBD += asTest.dbd TESTPROD_HOST += asTest asTest_SRCS += asTest.c asTest_SRCS += asTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += asTest.c TESTFILES += $(COMMON_DIR)/asTestIoc.dbd ../asTest.db TESTS += asTest TARGETS += $(COMMON_DIR)/analogMonitorTest.dbd DBDDEPENDS_FILES += analogMonitorTest.dbd$(DEP) analogMonitorTest_DBD += base.dbd TESTPROD_HOST += analogMonitorTest analogMonitorTest_SRCS += analogMonitorTest.c analogMonitorTest_SRCS += analogMonitorTest_registerRecordDeviceDriver.cpp testHarness_SRCS += analogMonitorTest.c testHarness_SRCS += analogMonitorTest_registerRecordDeviceDriver.cpp TESTFILES += $(COMMON_DIR)/analogMonitorTest.dbd ../analogMonitorTest.db TESTS += analogMonitorTest TARGETS += $(COMMON_DIR)/scanEventTest.dbd DBDDEPENDS_FILES += scanEventTest.dbd$(DEP) scanEventTest_DBD += base.dbd TESTPROD_HOST += scanEventTest scanEventTest_SRCS += scanEventTest.c scanEventTest_SRCS += scanEventTest_registerRecordDeviceDriver.cpp testHarness_SRCS += scanEventTest.c testHarness_SRCS += scanEventTest_registerRecordDeviceDriver.cpp TESTFILES += $(COMMON_DIR)/scanEventTest.dbd ../scanEventTest.db TESTS += scanEventTest TARGETS += $(COMMON_DIR)/regressTest.dbd DBDDEPENDS_FILES += regressTest.dbd$(DEP) regressTest_DBD += base.dbd TESTPROD_HOST += regressTest regressTest_SRCS += regressTest.c regressTest_SRCS += regressTest_registerRecordDeviceDriver.cpp TESTFILES += $(COMMON_DIR)/regressTest.dbd ../regressArray1.db ../regressHex.db ../regressLinkMS.db TESTFILES += ../badCaLink.db TESTS += regressTest TARGETS += $(COMMON_DIR)/simmTest.dbd TARGETS += $(COMMON_DIR)/simmTest.db DBDDEPENDS_FILES += simmTest.dbd$(DEP) DBDDEPENDS_FILES += simmTest.db$(DEP) simmTest_DBD += base.dbd TESTPROD_HOST += simmTest simmTest_SRCS += simmTest.c simmTest_SRCS += simmTest_registerRecordDeviceDriver.cpp testHarness_SRCS += simmTest.c testHarness_SRCS += simmTest_registerRecordDeviceDriver.cpp TESTFILES += $(COMMON_DIR)/simmTest.dbd $(COMMON_DIR)/simmTest.db TESTS += simmTest TESTPROD_HOST += mbbioDirectTest mbbioDirectTest_SRCS += mbbioDirectTest.c mbbioDirectTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += mbbioDirectTest.c TESTFILES += ../mbbioDirectTest.db TESTS += mbbioDirectTest TARGETS += $(COMMON_DIR)/asyncproctest.dbd DBDDEPENDS_FILES += asyncproctest.dbd$(DEP) asyncproctest_DBD += base.dbd TESTPROD_HOST += asyncproctest asyncproctest_SRCS += asyncproctest.c asyncproctest_SRCS += asyncproctest_registerRecordDeviceDriver.cpp TESTFILES += $(COMMON_DIR)/asyncproctest.dbd ../asyncproctest.db TESTS += asyncproctest ifeq ($(T_A),$(EPICS_HOST_ARCH)) # Host-only tests of softIoc/softIocPVA, caget and pvget (if present) TESTS += netget endif # epicsRunRecordTests runs all the test programs in a known working order. testHarness_SRCS += epicsRunRecordTests.c recordTestHarness_SRCS += $(testHarness_SRCS) recordTestHarness_SRCS_RTEMS += rtemsTestHarness.c PROD_SRCS_RTEMS += rtemsTestData.c PROD_vxWorks = recordTestHarness PROD_RTEMS = recordTestHarness TESTSPEC_vxWorks = recordTestHarness.munch; epicsRunRecordTests TESTSPEC_RTEMS = recordTestHarness.boot; epicsRunRecordTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) TESTPROD_RTEMS = $(TESTPROD_HOST) TESTSCRIPTS_RTEMS += $(TESTS:%=%.t) endif include $(TOP)/configure/RULES rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES) base-7.0.3.1/modules/database/test/std/rec/analogMonitorTest.c0000664000577000060420000002154213557101274022772 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #include "registryFunction.h" #include "osiFileName.h" #include "epicsThread.h" #include "epicsMath.h" #include "epicsUnitTest.h" #include "dbAccessDefs.h" #include "dbStaticLib.h" #include "dbEvent.h" #include "caeventmask.h" #include "db_field_log.h" #include "chfPlugin.h" #include "iocInit.h" #include "testMain.h" #include "epicsExport.h" /* Test parameters */ #define NO_OF_RECORD_TYPES 7 #define NO_OF_DEADBANDS 3 #define NO_OF_PATTERNS 16 #define NO_OF_VALUES_PER_SEQUENCE 2 void analogMonitorTest_registerRecordDeviceDriver(struct dbBase *); /* Indices for record type, deadband type, deadband value, test number, val in sequence */ static int irec, ityp, idbnd, itest, iseq; /* Records to test with */ static const char t_Record[NO_OF_RECORD_TYPES][10] = { {"ai"}, {"ao"}, {"calc"}, {"calcout"}, {"dfanout"}, {"sel"}, {"sub"}, }; /* Deadband types to test */ static const char t_DbndType[2][6] = { {".MDEL"}, {".ADEL"} }; /* Different deadbands to test with */ static double t_Deadband[NO_OF_DEADBANDS] = { -1, 0, 1.5 }; /* Value sequences for each of the 16 tests */ static double t_SetValues[NO_OF_PATTERNS][NO_OF_VALUES_PER_SEQUENCE]; /* Expected updates (1=yes) for each sequence of each test of each deadband */ static int t_ExpectedUpdates[NO_OF_DEADBANDS][NO_OF_PATTERNS][NO_OF_VALUES_PER_SEQUENCE] = { { /* deadband = -1 */ {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, }, { /* deadband = 0 */ {1, 1}, {0, 1}, {0, 0}, {0, 0}, {1, 1}, {1, 0}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 0}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 0}, }, { /* deadband = 1.5 */ {0, 1}, {0, 1}, {0, 0}, {0, 0}, {1, 1}, {1, 0}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 0}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 0}, }, }; static int t_ReceivedUpdates[NO_OF_PATTERNS][NO_OF_VALUES_PER_SEQUENCE]; /* Dummy subroutine needed for sub record */ static long myTestSub(void *p) { return 0; } /* Minimal pre-chain plugin to divert all monitors back into the test (before they hit the queue) */ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { double val = *((double*)chan->addr.pfield); /* iseq == -1 is the value reset before the test pattern -> do not count */ if (iseq >= 0) { t_ReceivedUpdates[itest][iseq] = 1; testOk((val == t_SetValues[itest][iseq]) || (isnan(val) && isnan(t_SetValues[itest][iseq])), "update %d pattern %2d with %s = %2.1f (expected %f, got %f)", iseq, itest, (ityp==0?"MDEL":"ADEL"), t_Deadband[idbnd], t_SetValues[itest][iseq], val); } db_delete_field_log(pfl); return NULL; } static void channelRegisterPre(dbChannel *chan, void *pvt, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { *cb_out = filter; } static chfPluginIf pif = { NULL, /* allocPvt, */ NULL, /* freePvt, */ NULL, /* parse_error, */ NULL, /* parse_ok, */ NULL, /* channel_open, */ channelRegisterPre, NULL, /* channelRegisterPost, */ NULL, /* channel_report, */ NULL /* channel_close */ }; MAIN(analogMonitorTest) { dbChannel *pch; const chFilterPlugin *plug; const char test[] = "test"; dbEventCtx evtctx; dbEventSubscription subscr; unsigned mask; struct dbAddr vaddr, daddr; double val; char chan[50]; /* Channel name */ char cval[50]; /* Name for test values */ char cdel[50]; /* Name for deadband values */ /* Test patterns: * 0: step less than deadband (of 1.5) * 1: step larger than deadband (of 1.5) * 2: no change * 3: -0.0 -> +0.0 * ... all possible combinations of steps * between: finite / NaN / -inf / +inf */ t_SetValues[ 0][0] = 1.0; t_SetValues[ 0][1] = 2.0; t_SetValues[ 1][0] = 0.0; t_SetValues[ 1][1] = 2.0; t_SetValues[ 2][0] = 0.0; t_SetValues[ 2][1] = 0.0; t_SetValues[ 3][0] = -0.0; t_SetValues[ 3][1] = 0.0; t_SetValues[ 4][0] = epicsNAN; t_SetValues[ 4][1] = 1.0; t_SetValues[ 5][0] = epicsNAN; t_SetValues[ 5][1] = epicsNAN; t_SetValues[ 6][0] = epicsNAN; t_SetValues[ 6][1] = epicsINF; t_SetValues[ 7][0] = epicsNAN; t_SetValues[ 7][1] = -epicsINF; t_SetValues[ 8][0] = epicsINF; t_SetValues[ 8][1] = 1.0; t_SetValues[ 9][0] = epicsINF; t_SetValues[ 9][1] = epicsNAN; t_SetValues[10][0] = epicsINF; t_SetValues[10][1] = epicsINF; t_SetValues[11][0] = epicsINF; t_SetValues[11][1] = -epicsINF; t_SetValues[12][0] = -epicsINF; t_SetValues[12][1] = 1.0; t_SetValues[13][0] = -epicsINF; t_SetValues[13][1] = epicsNAN; t_SetValues[14][0] = -epicsINF; t_SetValues[14][1] = epicsINF; t_SetValues[15][0] = -epicsINF; t_SetValues[15][1] = -epicsINF; registryFunctionAdd("myTestSub", (REGISTRYFUNCTION) myTestSub); testPlan(1793); if (dbReadDatabase(&pdbbase, "analogMonitorTest.dbd", "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)) testAbort("Error reading database description 'analogMonitorTest.dbd'"); analogMonitorTest_registerRecordDeviceDriver(pdbbase); if (dbReadDatabase(&pdbbase, "analogMonitorTest.db", "." OSI_PATH_LIST_SEPARATOR "..", NULL)) testAbort("Error reading test database 'analogMonitorTest.db'"); /* Start the core IOC (no CA) */ iocBuildIsolated(); evtctx = db_init_events(); chfPluginRegister(test, &pif, NULL); plug = dbFindFilter(test, strlen(test)); testOk(!!plug, "interceptor plugin registered"); /* Loop over all analog record types (one instance each) */ for (irec = 0; irec < NO_OF_RECORD_TYPES; irec++) { strcpy(cval, t_Record[irec]); strcpy(chan, cval); strcat(chan, ".VAL{\"test\":{}}"); if ((strcmp(t_Record[irec], "sel") == 0) || (strcmp(t_Record[irec], "calc") == 0) || (strcmp(t_Record[irec], "calcout") == 0)) { strcat(cval, ".A"); } else { strcat(cval, ".VAL"); } testDiag("--------------------------------------------------------"); testDiag("Testing the %s record", t_Record[irec]); testDiag("--------------------------------------------------------"); pch = dbChannelCreate(chan); testOk(!!pch, "dbChannel with test plugin created"); testOk(!dbChannelOpen(pch), "dbChannel opened"); dbNameToAddr(cval, &vaddr); /* Loop over both tested deadband types */ for (ityp = 0; ityp < 2; ityp++) { strcpy(cdel, t_Record[irec]); strcat(cdel, t_DbndType[ityp]); dbNameToAddr(cdel, &daddr); mask = (ityp==0?DBE_VALUE:DBE_ARCHIVE); subscr = db_add_event(evtctx, pch, NULL, NULL, mask); db_event_enable(subscr); /* Loop over all tested deadband values */ for (idbnd = 0; idbnd < NO_OF_DEADBANDS; idbnd++) { testDiag("Test %s%s = %g", t_Record[irec], t_DbndType[ityp], t_Deadband[idbnd]); dbPutField(&daddr, DBR_DOUBLE, (void*) &t_Deadband[idbnd], 1); memset(t_ReceivedUpdates, 0, sizeof(t_ReceivedUpdates)); /* Loop over all test patterns */ for (itest = 0; itest < NO_OF_PATTERNS; itest++) { iseq = -1; val = 0.0; dbPutField(&vaddr, DBR_DOUBLE, (void*) &val, 1); /* Loop over the test sequence */ for (iseq = 0; iseq < NO_OF_VALUES_PER_SEQUENCE; iseq++) { dbPutField(&vaddr, DBR_DOUBLE, (void*) &t_SetValues[itest][iseq], 1); } /* Check expected vs. actual monitors */ testOk( (t_ExpectedUpdates[idbnd][itest][0] == t_ReceivedUpdates[itest][0]) && (t_ExpectedUpdates[idbnd][itest][1] == t_ReceivedUpdates[itest][1]), "pattern %2d with %s = %2.1f (expected %d-%d, got %d-%d)", itest, (ityp==0?"MDEL":"ADEL"), t_Deadband[idbnd], t_ExpectedUpdates[idbnd][itest][0], t_ExpectedUpdates[idbnd][itest][1], t_ReceivedUpdates[itest][0], t_ReceivedUpdates[itest][1]); } } db_cancel_event(subscr); } } iocShutdown(); return testDone(); } base-7.0.3.1/modules/database/test/std/rec/analogMonitorTest.db0000664000577000060420000000035413557101274023133 0ustar anjaesctlrecord(ai, "ai") {} record(ao, "ao") {} record(calc, "calc") { field(CALC, "A") } record(calcout, "calcout") { field(CALC, "A") } record(dfanout, "dfanout") {} record(sel, "sel") {} record(sub, "sub") { field(SNAM, "myTestSub") } base-7.0.3.1/modules/database/test/std/rec/arrayOpTest.c0000664000577000060420000001040013557101274021565 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Assoc. as operator of Brookhaven * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include "dbAccess.h" #include "dbTest.h" #include "dbUnitTest.h" #include "errlog.h" #include "waveformRecord.h" #include "testMain.h" void recTestIoc_registerRecordDeviceDriver(struct dbBase *); static void testGetPutArray(void) { double data[4] = {11, 12, 13, 14}; DBADDR addr, save; long nreq; epicsInt32 *pbtr; waveformRecord *prec; testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); recTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("arrayOpTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); testDiag("Test dbGet() and dbPut() from/to an array"); prec = (waveformRecord*)testdbRecordPtr("wfrec"); if(!prec || dbNameToAddr("wfrec", &addr)) testAbort("Failed to find record wfrec"); memcpy(&save, &addr, sizeof(save)); testDiag("Fetch initial value of %s", prec->name); dbScanLock(addr.precord); testOk(prec->nord==3, "prec->nord==3 (got %d)", prec->nord); nreq = NELEMENTS(data); if(dbGet(&addr, DBF_DOUBLE, &data, NULL, &nreq, NULL)) { testFail("dbGet fails"); testSkip(1, "failed get"); } else { testOk(nreq==3, "nreq==3 (got %ld)", nreq); testOk1(data[0]==1.0 && data[1]==2.0 && data[2]==3.0); } dbScanUnlock(addr.precord); testOk1(memcmp(&addr, &save, sizeof(save))==0); addr=save; testDiag("Write a new value"); data[0] = 4.0; data[1] = 5.0; data[2] = 6.0; data[3] = 7.0; dbScanLock(addr.precord); testOk1(dbPut(&addr, DBF_DOUBLE, &data, NELEMENTS(data))==0); pbtr = prec->bptr; testOk(prec->nord==4, "prec->nord==4 (got %u)", prec->nord); testOk1(pbtr[0]==4 && pbtr[1]==5 && pbtr[2]==6 && pbtr[3]==7); dbScanUnlock(addr.precord); testOk1(memcmp(&addr, &save, sizeof(save))==0); addr=save; memset(&data, 0, sizeof(data)); testDiag("Reread the value"); dbScanLock(addr.precord); nreq = NELEMENTS(data); if(dbGet(&addr, DBF_DOUBLE, &data, NULL, &nreq, NULL)) testFail("dbGet fails"); else { testOk1(nreq==NELEMENTS(data)); testOk1(data[0]==4.0 && data[1]==5.0 && data[2]==6.0 && data[3]==7.0); } dbScanUnlock(addr.precord); testOk1(memcmp(&addr, &save, sizeof(save))==0); testDiag("Test dbGet() and dbPut() from/to an array of size 1"); prec = (waveformRecord*)testdbRecordPtr("wfrec1"); if(!prec || dbNameToAddr("wfrec1", &addr)) testAbort("Failed to find record wfrec1"); memcpy(&save, &addr, sizeof(save)); testDiag("Fetch initial value of %s", prec->name); dbScanLock(addr.precord); testOk(prec->nord==0, "prec->nord==0 (got %d)", prec->nord); nreq = NELEMENTS(data); if(dbGet(&addr, DBF_DOUBLE, &data, NULL, &nreq, NULL)) testFail("dbGet fails"); else { testOk(nreq==0, "nreq==0 (got %ld)", nreq); } dbScanUnlock(addr.precord); testOk1(memcmp(&addr, &save, sizeof(save))==0); addr=save; testDiag("Write a new value"); data[0] = 4.0; data[1] = 5.0; data[2] = 6.0; data[3] = 7.0; dbScanLock(addr.precord); testOk1(dbPut(&addr, DBF_DOUBLE, &data, 1)==0); pbtr = prec->bptr; testOk(prec->nord==1, "prec->nord==1 (got %u)", prec->nord); testOk1(pbtr[0]==4); dbScanUnlock(addr.precord); testOk1(memcmp(&addr, &save, sizeof(save))==0); addr=save; memset(&data, 0, sizeof(data)); testDiag("Reread the value"); dbScanLock(addr.precord); nreq = NELEMENTS(data); if(dbGet(&addr, DBF_DOUBLE, &data, NULL, &nreq, NULL)) testFail("dbGet fails"); else { testOk1(nreq==1); testOk1(data[0]==4.0); } dbScanUnlock(addr.precord); testOk1(memcmp(&addr, &save, sizeof(save))==0); testIocShutdownOk(); testdbCleanup(); } MAIN(arrayOpTest) { testPlan(21); testGetPutArray(); return testDone(); } base-7.0.3.1/modules/database/test/std/rec/arrayOpTest.db0000664000577000060420000000026213557101274021735 0ustar anjaesctlrecord(waveform, "wfrec") { field(NELM, "10") field(FTVL, "LONG") field(INP, [1, 2, 3]) } record(waveform, "wfrec1") { field(NELM, "1") field(FTVL, "LONG") } base-7.0.3.1/modules/database/test/std/rec/asTest.c0000664000577000060420000000216613557101274020565 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver * * Test the hooks that autosave uses during initialization */ #include "string.h" #include "epicsString.h" #include "dbUnitTest.h" #include "epicsThread.h" #include "iocInit.h" #include "dbBase.h" #include "link.h" #include "recSup.h" #include "dbAccess.h" #include "dbConvert.h" #include "dbStaticLib.h" #include "registry.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "osiFileName.h" #include "initHooks.h" #include "devSup.h" #include "errlog.h" #include "aoRecord.h" #include "waveformRecord.h" #include "testMain.h" epicsShareFunc void testRestore(void); #include "epicsExport.h" MAIN(asTest) { testPlan(42); testRestore(); return testDone(); } base-7.0.3.1/modules/database/test/std/rec/asTest.db0000664000577000060420000000032413557101274020722 0ustar anjaesctlrecord(ao, "rec0") { field(DESC, "foobar") field(DTYP, "asTest") field(VAL, "1") field(OUT, "rec0.DISV") } record(waveform, "rec1") { field(DTYP, "asTest") field(FTVL, "DOUBLE") field(NELM, "5") } base-7.0.3.1/modules/database/test/std/rec/asTest.dbd0000664000577000060420000000013613557101274021067 0ustar anjaesctldevice(ao, CONSTANT, devAOasTest, "asTest") device(waveform, CONSTANT, devWFasTest, "asTest") base-7.0.3.1/modules/database/test/std/rec/asTestLib.c0000664000577000060420000002044213557101274021211 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver * * Test the hooks that autosave uses during initialization */ #include "string.h" #include "epicsString.h" #include "dbUnitTest.h" #include "epicsThread.h" #include "iocInit.h" #include "dbBase.h" #include "link.h" #include "recSup.h" #include "iocsh.h" #include "dbAccess.h" #include "dbConvert.h" #include "dbStaticLib.h" #include "registry.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "osiFileName.h" #include "initHooks.h" #include "devSup.h" #include "errlog.h" #include "aoRecord.h" #include "waveformRecord.h" #include "epicsExport.h" static unsigned iran; static int checkGetString(DBENTRY *pent, const char *expect) { dbCommon *prec = pent->precnode->precord; const char *actual = dbGetString(pent); int ret = strcmp(actual, expect); testOk(ret==0, "dbGetString(\"%s.%s\") -> '%s' == '%s'", prec->name, pent->pflddes->name, actual, expect); return ret; } static void hookPass0(initHookState state) { DBENTRY entry; if(state!=initHookAfterInitDevSup) return; testDiag("initHookAfterInitDevSup"); dbInitEntry(pdbbase, &entry); testDiag("restore integer pass0"); /* rec0.VAL is initially 1, set it to 2 */ if(dbFindRecord(&entry, "rec0.VAL")==0) { aoRecord *prec = entry.precnode->precord; testOk(prec->val==1, "VAL %d==1 (initial value from .db)", (int)prec->val); checkGetString(&entry, "1"); testOk1(dbPutString(&entry, "2")==0); testOk(prec->val==2, "VAL %d==2", (int)prec->val); checkGetString(&entry, "2"); } else { testFail("Missing rec0"); testSkip(4, "missing record"); } testDiag("restore string pass0"); if(dbFindRecord(&entry, "rec0.DESC")==0) { aoRecord *prec = entry.precnode->precord; testOk1(strcmp(prec->desc, "foobar")==0); checkGetString(&entry, "foobar"); testOk1(dbPutString(&entry, "hello")==0); testOk1(strcmp(prec->desc, "hello")==0); checkGetString(&entry, "hello"); } else { testFail("Missing rec0"); testSkip(4, "missing record"); } if(dbFindRecord(&entry, "rec1.DESC")==0) { aoRecord *prec = entry.precnode->precord; testOk1(strcmp(prec->desc, "")==0); checkGetString(&entry, ""); testOk1(dbPutString(&entry, "world")==0); testOk1(strcmp(prec->desc, "world")==0); checkGetString(&entry, "world"); } else { testFail("Missing rec1"); testSkip(4, "missing record"); } testDiag("restore link pass0"); /* rec0.OUT is initially "rec0.DISV", set it to "rec0.SEVR" */ if(dbFindRecord(&entry, "rec0.OUT")==0) { aoRecord *prec = entry.precnode->precord; if(prec->out.type==CONSTANT) testOk(strcmp(prec->out.text,"rec0.DISV")==0, "%s==rec0.DISV (initial value from .db)", prec->out.text); else testFail("Wrong link type: %d", (int)prec->out.type); checkGetString(&entry, "rec0.DISV"); testOk1(dbPutString(&entry, "rec0.SEVR")==0); } else{ testFail("Missing rec0"); testSkip(1, "missing record"); } /* rec0.SDIS is initially NULL, set it to "rec0.STAT" */ if(dbFindRecord(&entry, "rec0.SDIS")==0) { aoRecord *prec = entry.precnode->precord; if(prec->sdis.type==CONSTANT) testOk1(prec->sdis.value.constantStr==NULL); else testFail("Wrong link type: %d", (int)prec->sdis.type); testOk1(dbPutString(&entry, "rec0.STAT")==0); } else{ testFail("Missing rec0"); testSkip(1, "missing record"); } /* can't restore array field in pass0 */ dbFinishEntry(&entry); } static long initRec0(aoRecord *prec) { DBLINK *plink = &prec->out; testDiag("init_record(%s)", prec->name); testOk(prec->val==2, "VAL %d==2 (pass0 value)", (int)prec->val); prec->val = 3; testOk(prec->val==3, "VAL %d==3", (int)prec->val); testOk1(plink->type==DB_LINK); if(plink->type==DB_LINK) testOk(strcmp(plink->value.pv_link.pvname,"rec0.SEVR")==0, "%s==rec0.SEVR (pass0 value)", plink->value.pv_link.pvname); else testFail("Wrong link type"); plink = &prec->sdis; testOk1(plink->type==DB_LINK); if(plink->type==DB_LINK) testOk(strcmp(plink->value.pv_link.pvname,"rec0.STAT")==0, "%s==rec0.STAT (pass0 value)", plink->value.pv_link.pvname); else testFail("Wrong link type"); iran |= 1; return 2; /* we set .VAL, so don't use RVAL */ } static long initRec1(waveformRecord *prec) { testDiag("init_record(%s)", prec->name); testOk(prec->nord==0, "NORD %d==0", (int)prec->nord); iran |= 2; return 0; } static double values[] = {1,2,3,4,5}; static void hookPass1(initHookState state) { DBENTRY entry; DBADDR addr; if(state!=initHookAfterInitDatabase) return; testDiag("initHookAfterInitDatabase"); dbInitEntry(pdbbase, &entry); if(dbFindRecord(&entry, "rec0.VAL")==0) { aoRecord *prec = entry.precnode->precord; testOk(prec->val==3, "VAL %d==3 (init_record value)", (int)prec->val); testOk1(dbPutString(&entry, "4")==0); testOk(prec->val==4, "VAL %d==4", (int)prec->val); } else{ testFail("Missing rec0"); testSkip(1, "missing record"); } /* Can't restore links in pass 1 */ if(dbNameToAddr("rec1.VAL", &addr)) { testFail("missing rec1"); testSkip(3, "missing record"); } else { rset *prset = dbGetRset(&addr); dbfType ftype = addr.field_type; long count=-1, offset=-1, maxcount = addr.no_elements; testOk1(prset && prset->get_array_info && prset->put_array_info); testOk1((*prset->get_array_info)(&addr, &count, &offset)==0); /* count is ignored */ testOk1((*dbPutConvertRoutine[DBF_DOUBLE][ftype])(&addr, values, NELEMENTS(values), maxcount,offset)==0); testOk1((*prset->put_array_info)(&addr, NELEMENTS(values))==0); } dbFinishEntry(&entry); } #if defined(__rtems__) || defined(vxWorks) void asTestIoc_registerRecordDeviceDriver(struct dbBase *); #endif epicsShareFunc void testRestore(void) { aoRecord *rec0; waveformRecord *rec1; testDiag("test Restore"); initHookRegister(hookPass0); initHookRegister(hookPass1); testdbPrepare(); testdbReadDatabase("asTestIoc.dbd", NULL, NULL); /* since this test has device support it must appear in a * DLL for windows dynamic builds. * However, the rRDD function is in the executable, * and not accessible here. So use iocsh. * For rtems/vxworks the test harness clears * iocsh registrations, so iocsh can't work here. */ #if defined(__rtems__) || defined(vxWorks) asTestIoc_registerRecordDeviceDriver(pdbbase); #else iocshCmd("asTestIoc_registerRecordDeviceDriver(pdbbase)"); #endif testdbReadDatabase("asTest.db", NULL, NULL); rec0 = (aoRecord*)testdbRecordPtr("rec0"); rec1 = (waveformRecord*)testdbRecordPtr("rec1"); eltc(0); testIocInitOk(); eltc(1); testDiag("Post initialization"); testOk1(iran==3); testOk1(rec0->val==4); testOk1(rec1->nord==5); { double *buf = rec1->bptr; testOk(buf[0]==1, "buf[0] %f==1", buf[0]); testOk1(buf[1]==2); testOk1(buf[2]==3); testOk1(buf[3]==4); testOk1(buf[4]==5); } testIocShutdownOk(); /* recSup doesn't cleanup after itself */ free(rec1->bptr); testdbCleanup(); } struct dset6 { dset common; DEVSUPFUN proc; DEVSUPFUN linconv; }; static long noop() {return 0;} static struct dset6 devAOasTest = { {6, NULL, NULL, (DEVSUPFUN)initRec0, NULL}, (DEVSUPFUN)noop, NULL}; static struct dset6 devWFasTest = { {6, NULL, NULL, (DEVSUPFUN)initRec1, NULL}, (DEVSUPFUN)noop, NULL}; epicsExportAddress(dset, devAOasTest); epicsExportAddress(dset, devWFasTest); base-7.0.3.1/modules/database/test/std/rec/asyncSoftTest.c0000664000577000060420000001135013557101274022126 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2017 UChicago Argonne LLC, as operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include "dbAccess.h" #include "dbStaticLib.h" #include "dbTest.h" #include "dbUnitTest.h" #include "epicsEvent.h" #include "errlog.h" #include "registryFunction.h" #include "subRecord.h" #include "testMain.h" static int startCounter, doneCounter; static epicsEventId asyncEvent, doneEvent; static long asyncSubr(subRecord *prec) { testDiag("Processing %s, pact=%d", prec->name, prec->pact); if (!prec->pact) { epicsEventTrigger(asyncEvent); prec->pact = 1; /* Make asynchronous */ } return 0; } static long doneSubr(subRecord *prec) { epicsEventTrigger(doneEvent); return 0; } static void checkAsyncInput(const char *rec, int init, dbCommon *async) { char inp[16], proc[16]; testDiag("Checking record '%s'", rec); strcpy(inp, rec); strcat(inp, ".INP"); strcpy(proc, rec); strcat(proc, ".PROC"); if (init) { testdbGetFieldEqual(rec, DBF_LONG, init); testdbPutFieldOk(inp, DBF_STRING, "async"); } testdbPutFieldOk(proc, DBF_CHAR, 1); epicsEventWait(asyncEvent); testdbGetFieldEqual("startCounter", DBF_LONG, ++startCounter); testdbGetFieldEqual("doneCounter", DBF_LONG, doneCounter); dbScanLock(async); async->rset->process(async); dbScanUnlock(async); epicsEventWait(doneEvent); testdbGetFieldEqual("startCounter", DBF_LONG, startCounter); testdbGetFieldEqual("doneCounter", DBF_LONG, ++doneCounter); } static void testAsynInputs(dbCommon *async) { const char * records[] = { "ai0", "bi0", "di0", "ii0", "li0", "mi0", "si0", NULL, "bi1", /* bi1 must be first in this group */ "ai1", "di1", "ii1", "li1", "mi1", "si1", NULL, }; const char ** rec = &records[0]; int init = 1; /* bi1 initializes to 1 */ testDiag("============ Starting %s ============", EPICS_FUNCTION); startCounter = doneCounter = 0; testdbPutFieldOk("startCounter", DBF_LONG, startCounter); testdbPutFieldOk("doneCounter", DBF_LONG, doneCounter); epicsEventTryWait(asyncEvent); epicsEventTryWait(doneEvent); while (*rec) { /* 1st group don't need initializing */ checkAsyncInput(*rec++, 0, async); } rec++; while (*rec) { checkAsyncInput(*rec++, init, async); init = 9; /* remainder initialize to 9 */ } testDiag("============= Ending %s =============", EPICS_FUNCTION); } static void checkAsyncOutput(const char *rec, dbCommon *async) { char proc[16]; testDiag("Checking record '%s'", rec); strcpy(proc, rec); strcat(proc, ".PROC"); testdbPutFieldOk(proc, DBF_CHAR, 1); epicsEventWait(asyncEvent); testdbGetFieldEqual("startCounter", DBF_LONG, ++startCounter); testdbGetFieldEqual("doneCounter", DBF_LONG, doneCounter); dbScanLock(async); async->rset->process(async); dbScanUnlock(async); epicsEventWait(doneEvent); testdbGetFieldEqual("startCounter", DBF_LONG, startCounter); testdbGetFieldEqual("doneCounter", DBF_LONG, ++doneCounter); } static void testAsyncOutputs(dbCommon *async) { const char * records[] = { "ao1", "bo1", "do1", "io1", "lo1", "lso1", "mo1", "so1", NULL, }; const char ** rec = &records[0]; testDiag("============ Starting %s ============", EPICS_FUNCTION); startCounter = doneCounter = 0; testdbPutFieldOk("startCounter", DBF_LONG, startCounter); testdbPutFieldOk("doneCounter", DBF_LONG, doneCounter); epicsEventTryWait(asyncEvent); epicsEventTryWait(doneEvent); while (*rec) { checkAsyncOutput(*rec++, async); } testDiag("============= Ending %s =============", EPICS_FUNCTION); } void recTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(asyncSoftTest) { dbCommon *async; testPlan(128); testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); recTestIoc_registerRecordDeviceDriver(pdbbase); registryFunctionAdd("asyncSubr", (REGISTRYFUNCTION) asyncSubr); registryFunctionAdd("doneSubr", (REGISTRYFUNCTION) doneSubr); testdbReadDatabase("asyncSoftTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); async = testdbRecordPtr("async"); asyncEvent = epicsEventCreate(epicsEventEmpty); doneEvent = epicsEventCreate(epicsEventEmpty); testAsynInputs(async); testAsyncOutputs(async); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/std/rec/asyncSoftTest.db0000664000577000060420000000770013557101274022275 0ustar anjaesctlrecord(ai, "ai0") { field(DTYP, "Async Soft Channel") field(INP, "async") field(FLNK, "done") } record(bi, "bi0") { field(DTYP, "Async Soft Channel") field(INP, "async") field(FLNK, "done") field(ZNAM, "Zero") field(ONAM, "One") } record(int64in, "ii0") { field(DTYP, "Async Soft Channel") field(INP, "async") field(FLNK, "done") } record(longin, "li0") { field(DTYP, "Async Soft Channel") field(INP, "async") field(FLNK, "done") } record(mbbiDirect, "di0") { field(DTYP, "Async Soft Channel") field(NOBT, 4) field(INP, "async") field(FLNK, "done") } record(mbbi, "mi0") { field(DTYP, "Async Soft Channel") field(NOBT, 4) field(INP, "async") field(FLNK, "done") field(ZRST, "Zero") field(ONST, "One") field(TWST, "Two") field(THST, "Three") field(FRST, "Four") field(FVST, "Five") field(SXST, "Six") field(SVST, "Seven") field(EIST, "Eight") field(NIST, "Nine") field(TEST, "Ten") field(ELST, "Eleven") field(TWST, "Twelve") field(TTST, "Thirteen") field(FTST, "Fourteen") field(FFST, "Fifteen") } record(stringin, "si0") { field(DTYP, "Async Soft Channel") field(INP, "async") field(FLNK, "done") } record(ai, "ai1") { field(DTYP, "Async Soft Channel") field(INP, {const:9}) field(FLNK, "done") } record(bi, "bi1") { field(DTYP, "Async Soft Channel") field(INP, {const:1}) field(FLNK, "done") field(ZNAM, "Zero") field(ONAM, "One") } record(int64in, "ii1") { field(DTYP, "Async Soft Channel") field(INP, {const:9}) field(FLNK, "done") } record(longin, "li1") { field(DTYP, "Async Soft Channel") field(INP, {const:9}) field(FLNK, "done") } record(mbbiDirect, "di1") { field(DTYP, "Async Soft Channel") field(NOBT, 4) field(INP, {const:9}) field(FLNK, "done") } record(mbbi, "mi1") { field(DTYP, "Async Soft Channel") field(NOBT, 4) field(INP, {const:9}) field(FLNK, "done") field(ZRST, "Zero") field(ONST, "One") field(TWST, "Two") field(THST, "Three") field(FRST, "Four") field(FVST, "Five") field(SXST, "Six") field(SVST, "Seven") field(EIST, "Eight") field(NIST, "Nine") field(TEST, "Ten") field(ELST, "Eleven") field(TWST, "Twelve") field(TTST, "Thirteen") field(FTST, "Fourteen") field(FFST, "Fifteen") } record(stringin, "si1") { field(DTYP, "Async Soft Channel") field(INP, {const:"9"}) field(FLNK, "done") } record(sub, "async") { field(INPA, "startCounter PP") field(SNAM, "asyncSubr") } record(calc, "startCounter") { field(CALC, "VAL+1") } record(sub, "done") { field(INPA, "doneCounter PP") field(SNAM, "doneSubr") } record(calc, "doneCounter") { field(CALC, "VAL+1") } record(ao, "ao1") { field(DTYP, "Async Soft Channel") field(OUT, "async.PROC CA") field(FLNK, "done") } record(bo, "bo1") { field(DTYP, "Async Soft Channel") field(OUT, "async.PROC CA") field(FLNK, "done") field(ZNAM, "Zero") field(ONAM, "One") } record(int64out, "io1") { field(DTYP, "Async Soft Channel") field(OUT, "async.PROC CA") field(FLNK, "done") } record(longout, "lo1") { field(DTYP, "Async Soft Channel") field(OUT, "async.PROC CA") field(FLNK, "done") } record(mbboDirect, "do1") { field(DTYP, "Async Soft Channel") field(NOBT, 4) field(OUT, "async.PROC CA") field(FLNK, "done") } record(mbbo, "mo1") { field(DTYP, "Async Soft Channel") field(NOBT, 4) field(OUT, "async.PROC CA") field(FLNK, "done") field(ZRST, "Zero") field(ONST, "One") field(TWST, "Two") field(THST, "Three") field(FRST, "Four") field(FVST, "Five") field(SXST, "Six") field(SVST, "Seven") field(EIST, "Eight") field(NIST, "Nine") field(TEST, "Ten") field(ELST, "Eleven") field(TWST, "Twelve") field(TTST, "Thirteen") field(FTST, "Fourteen") field(FFST, "Fifteen") } record(lso, "lso1") { field(DTYP, "Async Soft Channel") field(OUT, "async.PROC CA") field(FLNK, "done") field(SIZV, 40) } record(stringout, "so1") { field(DTYP, "Async Soft Channel") field(OUT, "async.PROC CA") field(FLNK, "done") } base-7.0.3.1/modules/database/test/std/rec/asyncproctest.c0000664000577000060420000001014513557101274022217 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2018 Michael Davidsaver * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* This test covers some situations where asynchronous records are * dbProcess()'d while busy (PACT==1). */ #include #include #include #include #include #include #include #include #include "registryFunction.h" #include #include epicsEventId done; static int waitFor; static long doneSubr(subRecord *prec) { if (--waitFor <= 0) epicsEventMustTrigger(done); return 0; } static void dummydone(void *usr, struct dbCommon* prec) { epicsEventMustTrigger(done); } void asyncproctest_registerRecordDeviceDriver(struct dbBase *); MAIN(asyncproctest) { testPlan(27); done = epicsEventMustCreate(epicsEventEmpty); testdbPrepare(); testdbReadDatabase("asyncproctest.dbd", NULL, NULL); asyncproctest_registerRecordDeviceDriver(pdbbase); registryFunctionAdd("doneSubr", (REGISTRYFUNCTION) doneSubr); testdbReadDatabase("asyncproctest.db", NULL, "TPRO=0"); dbAccessDebugPUTF = 1; testIocInitOk(); testDiag("===== Chain 1 ======"); waitFor = 2; testdbPutFieldOk("chain1.B", DBF_LONG, 6); testdbPutFieldOk("chain1.B", DBF_LONG, 7); if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK) testAbort("Processing timed out"); testdbGetFieldEqual("chain1", DBF_LONG, 7); testdbGetFieldEqual("chain1.A", DBF_LONG, 2); testDiag("===== Chain 2 ======"); waitFor = 2; testdbPutFieldOk("chain2:1.B", DBF_LONG, 6); testdbPutFieldOk("chain2:1.B", DBF_LONG, 7); if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK) testAbort("Processing timed out"); testdbGetFieldEqual("chain2:1", DBF_LONG, 7); testdbGetFieldEqual("chain2:2", DBF_LONG, 7); testdbGetFieldEqual("chain2:1.A", DBF_LONG, 2); testdbGetFieldEqual("chain2:2.A", DBF_LONG, 2); testDiag("===== Chain 2 again ======"); waitFor = 2; testdbPutFieldOk("chain2:1.B", DBF_LONG, 6); testdbPutFieldOk("chain2:1.B", DBF_LONG, 7); testdbPutFieldOk("chain2:1.B", DBF_LONG, 8); if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK) testAbort("Processing timed out"); testdbGetFieldEqual("chain2:1", DBF_LONG, 8); testdbGetFieldEqual("chain2:2", DBF_LONG, 8); testdbGetFieldEqual("chain2:1.A", DBF_LONG, 5); testdbGetFieldEqual("chain2:2.A", DBF_LONG, 4); testDiag("===== Chain 3 ======"); waitFor = 2; testdbPutFieldOk("chain3.B", DBF_LONG, 6); testdbPutFieldOk("chain3.B", DBF_LONG, 7); if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK) testAbort("Processing timed out"); testdbGetFieldEqual("chain3", DBF_LONG, 7); testdbGetFieldEqual("chain3.A", DBF_LONG, 2); testDiag("===== Chain 4 ======"); { dbCommon *dummy=testdbRecordPtr("chain4_dummy"); testdbPutFieldOk("chain4_pos.PROC", DBF_LONG, 0); /* sync once queue to wait for any queued RPRO */ scanOnceCallback(dummy, dummydone, NULL); if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK) testAbort("Processing timed out"); testdbGetFieldEqual("chain4_pos", DBF_SHORT, 1); testdbGetFieldEqual("chain4_rel", DBF_SHORT, 1); testdbGetFieldEqual("chain4_lim", DBF_SHORT, 1); } testDiag("===== Chain 5 ======"); { dbCommon *dummy=testdbRecordPtr("chain4_dummy"); testdbPutFieldOk("chain5_cnt.PROC", DBF_LONG, 0); /* sync once queue to wait for any queued RPRO */ scanOnceCallback(dummy, dummydone, NULL); if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK) testAbort("Processing timed out"); testdbGetFieldEqual("chain5_cnt", DBF_SHORT, 1); } testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/std/rec/asyncproctest.db0000664000577000060420000000361213557101274022363 0ustar anjaesctl # simple case # stand alone async record record(calcout, "chain1") { field(CALC, "A:=A+1;B") field(ODLY, "0.1") field(TPRO, "$(TPRO=)") field(FLNK, "done1") } record(sub, "done1") { field(SNAM, "doneSubr") field(TPRO, "$(TPRO=)") } # original problem case # async record chained after syncronous record record(calcout, "chain2:1") { field(CALC, "A:=A+1;B") # ODLY=0 synchronous field(OUT , "chain2:2.B PP") field(TPRO, "$(TPRO=)") } record(ai, "chain2:3") { field(INP, "chain2:1 CPP") field(FLNK, "chain2:2") field(TPRO, "$(TPRO=)") } record(calcout, "chain2:2") { field(CALC, "A:=A+1;B") field(ODLY, "0.5") field(TPRO, "$(TPRO=)") field(FLNK, "done2") } record(sub, "done2") { field(SNAM, "doneSubr") field(TPRO, "$(TPRO=)") } # ANJ's error case # async record FLNK's to itself (via done3) record(calcout, "chain3") { field(CALC, "A:=A+1;B") field(ODLY, "0.1") field(FLNK, "done3") field(TPRO, "$(TPRO=)") } record(sub, "done3") { field(SNAM, "doneSubr") field(FLNK, "chain3") field(TPRO, "$(TPRO=)") } # loop breaking regression # should _not_ RPRO record(calcout,"chain4_pos") { field(CALC, "E:=E+1;E") field(OUT,"chain4_rel.A PP") field(TPRO, "$(TPRO=)") } record(calc,"chain4_rel") { field(CALC, "E:=E+1;E") field(FLNK,"chain4_lim") field(TPRO, "$(TPRO=)") } record(calc,"chain4_lim") { field(CALC, "E:=E+1;E") field(INPA,"chain4_pos PP") field(INPB,"chain4_pos.HIGH PP") field(INPC,"chain4_pos.LOW PP") field(FLNK,"chain4_pos") field(TPRO, "$(TPRO=)") } record(bo, "chain4_dummy") { field(TPRO, "$(TPRO=)") } # loop breaking regression part 2 # selft link should _not_ RPRO record(calcout,"chain5_cnt") { field(CALC, "E:=E+1;E") field(INPA,"chain5_cnt.A PP") field(OUT,"chain5_cnt.A PP") field(FLNK,"chain5_cnt") field(TPRO, "$(TPRO=)") } base-7.0.3.1/modules/database/test/std/rec/badCaLink.db0000664000577000060420000000012013557101274021261 0ustar anjaesctlrecord(ai, "ai:disconn") { field(INP , "invalid CA") field(UDF , "0") } base-7.0.3.1/modules/database/test/std/rec/compressTest.c0000664000577000060420000002114113557101274022007 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 Michael Davidsaver * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "dbUnitTest.h" #include "testMain.h" #include "dbLock.h" #include "errlog.h" #include "dbAccess.h" #include "epicsMath.h" #include "aiRecord.h" #include "compressRecord.h" #define testDEq(A,B,D) testOk(fabs((A)-(B))<(D), #A " (%f) ~= " #B " (%f)", A, B) void recTestIoc_registerRecordDeviceDriver(struct dbBase *); static void checkArrD(const char *pv, long elen, double a, double b, double c, double d) { double buf[4]; double expect[4]; long nReq = NELEMENTS(buf), i; unsigned match; DBADDR addr; expect[0] = a; expect[1] = b; expect[2] = c; expect[3] = d; if (dbNameToAddr(pv, &addr)) testAbort("Unknown PV '%s'", pv); if (dbGet(&addr, DBR_DOUBLE, buf, NULL, &nReq, NULL)) testAbort("Failed to get '%s'", pv); match = elen==nReq; for (i=0; i=0.01) testDiag("[%ld] -> %f != %f", i, expect[i], buf[i]); } } static void checkArrI(const char *pv, long elen, epicsInt32 a, epicsInt32 b, epicsInt32 c, epicsInt32 d) { epicsInt32 buf[4]; epicsInt32 expect[4]; long nReq = NELEMENTS(buf), i; unsigned match; DBADDR addr; expect[0] = a; expect[1] = b; expect[2] = c; expect[3] = d; if (dbNameToAddr(pv, &addr)) testAbort("Unknown PV '%s'", pv); if (dbGet(&addr, DBR_LONG, buf, NULL, &nReq, NULL)) testAbort("Failed to get '%s'", pv); match = elen==nReq; for (i=0; i %d != %d", i, (int)expect[i], (int)buf[i]); } } static void testFIFOCirc(void) { aiRecord *vrec; compressRecord *crec; double *cbuf; testDiag("Test FIFO"); testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); recTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("compressTest.db", NULL, "ALG=Circular Buffer,BALG=FIFO Buffer,NSAM=4"); vrec = (aiRecord*)testdbRecordPtr("val"); crec = (compressRecord*)testdbRecordPtr("comp"); eltc(0); testIocInitOk(); eltc(1); dbScanLock((dbCommon*)crec); cbuf = crec->bptr; testOk1(crec->off==0); testOk1(crec->inx==0); testOk1(crec->nuse==0); testDiag("Push 1.1"); vrec->val = 1.1; dbProcess((dbCommon*)crec); /* In FIFO mode the valid elements are * cbuf[(off-nuse-1) % size] through cbuf[(off-1) % size] */ testOk1(crec->off==1); testOk1(crec->inx==0); testOk1(crec->nuse==1); testDEq(cbuf[0], 1.1, 0.1); testDEq(cbuf[1], 0.0, 0.1); testDEq(cbuf[2], 0.0, 0.1); testDEq(cbuf[3], 0.0, 0.1); checkArrD("comp", 1, 1.1, 0, 0, 0); testDiag("Push 2.1"); vrec->val = 2.1; dbProcess((dbCommon*)crec); testOk1(crec->off==2); testOk1(crec->inx==0); testOk1(crec->nuse==2); testDEq(cbuf[0], 1.1, 0.1); testDEq(cbuf[1], 2.1, 0.1); testDEq(cbuf[2], 0.0, 0.1); testDEq(cbuf[3], 0.0, 0.1); checkArrD("comp", 2, 1.1, 2.1, 0, 0); testDiag("Push 3.1"); vrec->val = 3.1; dbProcess((dbCommon*)crec); testOk1(crec->off==3); testOk1(crec->inx==0); testOk1(crec->nuse==3); testDEq(cbuf[0], 1.1, 0.1); testDEq(cbuf[1], 2.1, 0.1); testDEq(cbuf[2], 3.1, 0.1); testDEq(cbuf[3], 0.0, 0.1); checkArrD("comp", 3, 1.1, 2.1, 3.1, 0); testDiag("Push 4.1"); vrec->val = 4.1; dbProcess((dbCommon*)crec); testOk1(crec->off==0); testOk1(crec->inx==0); testOk1(crec->nuse==4); testDEq(cbuf[0], 1.1, 0.1); testDEq(cbuf[1], 2.1, 0.1); testDEq(cbuf[2], 3.1, 0.1); testDEq(cbuf[3], 4.1, 0.1); checkArrD("comp", 4, 1.1, 2.1, 3.1, 4.1); testDiag("Push 5.1"); vrec->val = 5.1; dbProcess((dbCommon*)crec); testOk1(crec->off==1); testOk1(crec->inx==0); testOk1(crec->nuse==4); testDEq(cbuf[0], 5.1, 0.1); testDEq(cbuf[1], 2.1, 0.1); testDEq(cbuf[2], 3.1, 0.1); testDEq(cbuf[3], 4.1, 0.1); checkArrD("comp", 4, 2.1, 3.1, 4.1, 5.1); testDiag("Push 6.1"); vrec->val = 6.1; dbProcess((dbCommon*)crec); testOk1(crec->off==2); testOk1(crec->inx==0); testOk1(crec->nuse==4); testDEq(cbuf[0], 5.1, 0.1); testDEq(cbuf[1], 6.1, 0.1); testDEq(cbuf[2], 3.1, 0.1); testDEq(cbuf[3], 4.1, 0.1); checkArrD("comp", 4, 3.1, 4.1, 5.1, 6.1); dbScanUnlock((dbCommon*)crec); testDiag("Reset"); testdbPutFieldOk("comp.RES", DBF_LONG, 0); dbScanLock((dbCommon*)crec); testOk1(crec->off==0); testOk1(crec->inx==0); testOk1(crec->nuse==0); checkArrD("comp", 0, 0, 0, 0, 0); dbScanUnlock((dbCommon*)crec); testIocShutdownOk(); testdbCleanup(); } static void testLIFOCirc(void) { aiRecord *vrec; compressRecord *crec; double *cbuf; testDiag("Test LIFO"); testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); recTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("compressTest.db", NULL, "ALG=Circular Buffer,BALG=LIFO Buffer,NSAM=4"); vrec = (aiRecord*)testdbRecordPtr("val"); crec = (compressRecord*)testdbRecordPtr("comp"); eltc(0); testIocInitOk(); eltc(1); dbScanLock((dbCommon*)crec); cbuf = crec->bptr; testOk1(crec->off==0); testOk1(crec->inx==0); testOk1(crec->nuse==0); testDiag("Push 1.1"); vrec->val = 1.1; dbProcess((dbCommon*)crec); testDiag("off %u", crec->off); testOk1(crec->off==3); testOk1(crec->inx==0); testOk1(crec->nuse==1); testDEq(cbuf[0], 0.0, 0.1); testDEq(cbuf[1], 0.0, 0.1); testDEq(cbuf[2], 0.0, 0.1); testDEq(cbuf[3], 1.1, 0.1); checkArrD("comp", 1, 1.1, 0, 0, 0); testDiag("Push 2.1"); vrec->val = 2.1; dbProcess((dbCommon*)crec); testOk1(crec->off==2); testOk1(crec->inx==0); testOk1(crec->nuse==2); testDEq(cbuf[0], 0.0, 0.1); testDEq(cbuf[1], 0.0, 0.1); testDEq(cbuf[2], 2.1, 0.1); testDEq(cbuf[3], 1.1, 0.1); checkArrD("comp", 2, 2.1, 1.1, 0, 0); checkArrI("comp", 2, 2, 1, 0, 0); testDiag("Push 3.1"); vrec->val = 3.1; dbProcess((dbCommon*)crec); testOk1(crec->off==1); testOk1(crec->inx==0); testOk1(crec->nuse==3); testDEq(cbuf[0], 0.0, 0.1); testDEq(cbuf[1], 3.1, 0.1); testDEq(cbuf[2], 2.1, 0.1); testDEq(cbuf[3], 1.1, 0.1); checkArrD("comp", 3, 3.1, 2.1, 1.1, 0); checkArrI("comp", 3, 3, 2, 1, 0); testDiag("Push 4.1"); vrec->val = 4.1; dbProcess((dbCommon*)crec); testOk1(crec->off==0); testOk1(crec->inx==0); testOk1(crec->nuse==4); testDEq(cbuf[0], 4.1, 0.1); testDEq(cbuf[1], 3.1, 0.1); testDEq(cbuf[2], 2.1, 0.1); testDEq(cbuf[3], 1.1, 0.1); checkArrD("comp", 4, 4.1, 3.1, 2.1, 1.1); checkArrI("comp", 4, 4, 3, 2, 1); testDiag("Push 5.1"); vrec->val = 5.1; dbProcess((dbCommon*)crec); testOk1(crec->off==3); testOk1(crec->inx==0); testOk1(crec->nuse==4); testDEq(cbuf[0], 4.1, 0.1); testDEq(cbuf[1], 3.1, 0.1); testDEq(cbuf[2], 2.1, 0.1); testDEq(cbuf[3], 5.1, 0.1); checkArrD("comp", 4, 5.1, 4.1, 3.1, 2.1); checkArrI("comp", 4, 5, 4, 3, 2); testDiag("Push 6.1"); vrec->val = 6.1; dbProcess((dbCommon*)crec); testOk1(crec->off==2); testOk1(crec->inx==0); testOk1(crec->nuse==4); testDEq(cbuf[0], 4.1, 0.1); testDEq(cbuf[1], 3.1, 0.1); testDEq(cbuf[2], 6.1, 0.1); testDEq(cbuf[3], 5.1, 0.1); checkArrD("comp", 4, 6.1, 5.1, 4.1, 3.1); dbScanUnlock((dbCommon*)crec); testDiag("Reset"); testdbPutFieldOk("comp.RES", DBF_LONG, 0); dbScanLock((dbCommon*)crec); testOk1(crec->off==0); testOk1(crec->inx==0); testOk1(crec->nuse==0); checkArrD("comp", 0, 0, 0, 0, 0); dbScanUnlock((dbCommon*)crec); testIocShutdownOk(); testdbCleanup(); } MAIN(compressTest) { testPlan(116); testFIFOCirc(); testLIFOCirc(); return testDone(); } base-7.0.3.1/modules/database/test/std/rec/compressTest.db0000664000577000060420000000022113557101274022146 0ustar anjaesctlrecord(ai, "val") {} record(compress, "comp") { field(INP, "val NPP") field(ALG, "$(ALG)") field(BALG,"$(BALG)") field(NSAM,"$(NSAM)") } base-7.0.3.1/modules/database/test/std/rec/epicsRunRecordTests.c0000664000577000060420000000217613557101274023275 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Run tests as a batch. */ #include "epicsUnitTest.h" #include "epicsExit.h" int analogMonitorTest(void); int compressTest(void); int recMiscTest(void); int arrayOpTest(void); int asTest(void); int linkRetargetLinkTest(void); int linkInitTest(void); int asyncSoftTest(void); int simmTest(void); int mbbioDirectTest(void); int scanEventTest(void); void epicsRunRecordTests(void) { testHarness(); runTest(analogMonitorTest); runTest(compressTest); runTest(recMiscTest); runTest(arrayOpTest); runTest(asTest); runTest(linkRetargetLinkTest); runTest(linkInitTest); runTest(asyncSoftTest); runTest(simmTest); runTest(mbbioDirectTest); runTest(scanEventTest); epicsExit(0); /* Trigger test harness */ } base-7.0.3.1/modules/database/test/std/rec/linkInitTest.c0000664000577000060420000001770113557101274021744 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Michael Davidsaver * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include "dbAccess.h" #include "devSup.h" #include "alarm.h" #include "dbUnitTest.h" #include "errlog.h" #include "epicsThread.h" #include "longinRecord.h" #include "testMain.h" void recTestIoc_registerRecordDeviceDriver(struct dbBase *); static void startTestIoc(const char *dbfile) { testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); recTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase(dbfile, NULL, NULL); eltc(0); testIocInitOk(); eltc(1); } /* testing here instead of ioc/db/test as xRecord has no INP/OUT */ static void testdbGetDevLink(void) { longinRecord *rec = (longinRecord*)testdbRecordPtr("li1"); testOk1(dbGetDevLink((dbCommon*)rec) == &rec->inp); } static void testLongStringInit() { testDiag("testLongStringInit"); startTestIoc("linkInitTest.db"); testdbGetDevLink(); { const char buf[] = "!----------------------------------------------!"; testdbGetArrFieldEqual("longstr1.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf); testdbGetFieldEqual("longstr1.VAL", DBR_STRING, "!--------------------------------------"); } { const char buf[] = "!----------------------------------------------!"; testdbGetArrFieldEqual("longstr2.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf); testdbGetFieldEqual("longstr2.VAL", DBR_STRING, "!--------------------------------------"); } { const char buf[] = "!----------------------------------------------!"; testdbGetArrFieldEqual("longstr3.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf); testdbGetFieldEqual("longstr3.VAL", DBR_STRING, "!--------------------------------------"); } testdbGetFieldEqual("longstr4.VAL", DBR_STRING, "One"); testIocShutdownOk(); testdbCleanup(); } static void testCalcInit() { testDiag("testCalcInit"); startTestIoc("linkInitTest.db"); testdbGetFieldEqual("emptylink.VAL", DBR_DOUBLE, 0.0); testdbGetFieldEqual("emptylink.SEVR", DBR_LONG, INVALID_ALARM); testdbPutFieldOk("emptylink.PROC", DBF_LONG, 1); testdbGetFieldEqual("emptylink.VAL", DBR_DOUBLE, 0.0); testdbGetFieldEqual("emptylink.SEVR", DBR_LONG, 0); testdbGetFieldEqual("emptylink1.VAL", DBR_DOUBLE, 0.0); testdbGetFieldEqual("emptylink1.SEVR", DBR_LONG, INVALID_ALARM); testdbPutFieldOk("emptylink1.PROC", DBF_LONG, 1); testdbGetFieldEqual("emptylink1.VAL", DBR_DOUBLE, 1.0); testdbGetFieldEqual("emptylink1.SEVR", DBR_LONG, 0); testIocShutdownOk(); testdbCleanup(); } static void testPrintfStrings() { testDiag("testPrintfStrings"); startTestIoc("linkInitTest.db"); { const char buf1[] = "Test string, exactly 40 characters long"; const char buf2[] = "Longer test string, more that 40 characters long"; const char buf2t[] = "Longer test string, more that 40 charac"; /* The FMT field is pp(TRUE), so this put triggers processing */ testdbPutFieldOk("printf1.FMT", DBF_STRING, "%s"); testdbGetArrFieldEqual("printf1.VAL$", DBF_CHAR, NELEMENTS(buf1)+2, NELEMENTS(buf1), buf1); testdbGetFieldEqual("printf1.VAL", DBR_STRING, buf1); testdbPutFieldOk("printf1.FMT", DBF_STRING, "%ls"); testdbGetArrFieldEqual("printf1.VAL$", DBF_CHAR, NELEMENTS(buf1)+2, NELEMENTS(buf1), buf1); testdbGetFieldEqual("printf1.VAL", DBR_STRING, buf1); testdbPutFieldOk("printf2.FMT", DBF_STRING, "%s"); testdbGetArrFieldEqual("printf2.VAL$", DBF_CHAR, NELEMENTS(buf2)+2, NELEMENTS(buf2t), buf2t); testdbGetFieldEqual("printf2.VAL", DBR_STRING, buf2t); testdbPutFieldOk("printf2.FMT", DBF_STRING, "%ls"); testdbGetArrFieldEqual("printf2.VAL$", DBF_CHAR, NELEMENTS(buf2)+2, NELEMENTS(buf2), buf2); testdbGetFieldEqual("printf2.VAL", DBR_STRING, buf2t); testdbPutFieldOk("printf2.FMT", DBF_STRING, "%.39ls"); testdbGetArrFieldEqual("printf2.VAL$", DBF_CHAR, NELEMENTS(buf2)+2, NELEMENTS(buf2t), buf2t); } testIocShutdownOk(); testdbCleanup(); } static void testArrayInputs() { epicsInt32 oneToTwelve[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; testDiag("testArrayInputs"); startTestIoc("linkInitTest.db"); testdbGetFieldEqual("aai1.NORD", DBR_LONG, 10); testdbGetFieldEqual("aai1.UDF", DBR_UCHAR, 0); testdbGetFieldEqual("aai2.NORD", DBR_LONG, 10); testdbGetFieldEqual("aai2.UDF", DBR_UCHAR, 0); testdbGetFieldEqual("sa1.NORD", DBR_LONG, 10); testdbGetFieldEqual("sa1.UDF", DBR_UCHAR, 0); testdbGetFieldEqual("sa2.NORD", DBR_LONG, 0); testdbGetFieldEqual("sa2.UDF", DBR_UCHAR, 1); testdbGetFieldEqual("wf1.NORD", DBR_LONG, 10); testdbGetFieldEqual("wf1.UDF", DBR_UCHAR, 0); testdbGetFieldEqual("wf2.NORD", DBR_LONG, 10); testdbGetFieldEqual("wf2.UDF", DBR_UCHAR, 0); testdbGetArrFieldEqual("aai1.VAL", DBF_LONG, 12, 10, &oneToTwelve[0]); testdbGetArrFieldEqual("aai2.VAL", DBF_LONG, 12, 10, &oneToTwelve[0]); testdbGetArrFieldEqual("sa1.VAL", DBF_LONG, 12, 10, &oneToTwelve[2]); testdbGetArrFieldEqual("sa2.VAL", DBF_LONG, 10, 0, NULL); testdbGetArrFieldEqual("wf1.VAL", DBF_LONG, 12, 10, &oneToTwelve[0]); testdbGetArrFieldEqual("wf2.VAL", DBF_LONG, 12, 10, &oneToTwelve[0]); testdbPutFieldOk("sa1.INDX", DBF_LONG, 3); testdbGetArrFieldEqual("sa1.VAL", DBF_LONG, 12, 9, &oneToTwelve[3]); testdbPutFieldOk("sa1.NELM", DBF_LONG, 3); testdbGetArrFieldEqual("sa1.VAL", DBF_LONG, 12, 3, &oneToTwelve[3]); testdbPutFieldOk("sa2.VAL", DBF_LONG, 1); testdbGetArrFieldEqual("sa2.VAL", DBF_LONG, 10, 1, &oneToTwelve[0]); testDiag("testScalarInputs"); testdbGetFieldEqual("li1", DBR_LONG, 1); testdbGetFieldEqual("i64i1", DBR_INT64, 1LL); testdbGetFieldEqual("li2", DBR_LONG, 1); testdbGetFieldEqual("i64i2", DBR_INT64, 1LL); testIocShutdownOk(); testdbCleanup(); } static void testEventRecord() { testMonitor *countmon; testDiag("testEventRecord"); startTestIoc("linkInitTest.db"); countmon = testMonitorCreate("count1.VAL", DBR_LONG, 0); testdbGetFieldEqual("ev1.VAL", DBR_STRING, "soft event 1"); testdbGetFieldEqual("ev1.UDF", DBR_UCHAR, 0); testdbGetFieldEqual("ev2.VAL", DBR_STRING, ""); testdbGetFieldEqual("ev2.UDF", DBR_UCHAR, 1); testdbGetFieldEqual("count1.VAL", DBR_LONG, 0); testdbPutFieldOk("ev1.PROC", DBF_UCHAR, 1); testMonitorWait(countmon); testdbGetFieldEqual("count1.VAL", DBR_LONG, 1); testdbPutFieldOk("ev2.PROC", DBF_UCHAR, 1); testMonitorWait(countmon); testdbGetFieldEqual("ev2.UDF", DBR_UCHAR, 0); testdbGetFieldEqual("count1.VAL", DBR_LONG, 2); testdbPutFieldOk("count1.EVNT", DBF_STRING, "Tock"); testdbPutFieldOk("ev2.PROC", DBF_UCHAR, 1); testMonitorWait(countmon); testdbGetFieldEqual("count1.VAL", DBR_LONG, 3); testMonitorDestroy(countmon); testIocShutdownOk(); testdbCleanup(); } void testInt64Inputs(void) { testDiag("testInt64Inputs"); startTestIoc("linkInitTest.db"); testdbGetFieldEqual("i1.VAL", DBR_INT64, 1234567890123456789LL); testdbGetFieldEqual("i2.VAL", DBR_INT64, 1234567890123456789LL); testdbGetFieldEqual("i3.VAL", DBR_INT64, 1234567890123456789LL); testdbGetFieldEqual("i4.NORD", DBR_LONG, 1); testdbGetFieldEqual("i4.VAL", DBR_INT64, 1234567890123456789LL); testIocShutdownOk(); testdbCleanup(); } MAIN(linkInitTest) { testPlan(78); testLongStringInit(); testCalcInit(); testPrintfStrings(); testArrayInputs(); testEventRecord(); testInt64Inputs(); return testDone(); } base-7.0.3.1/modules/database/test/std/rec/linkInitTest.db0000664000577000060420000000440413557101274022103 0ustar anjaesctlrecord(lsi, "longstr1") { field(SIZV, "100") field(INP, ["!----------------------------------------------!"]) } record(lsi, "longstr2") { field(SIZV, "100") field(INP, {const: ["!----------------------------------------------!"]}) } record(lsi, "longstr3") { field(SIZV, "100") field(INP, {const: "!----------------------------------------------!"}) } record(lsi, "longstr4") { field(SIZV, "100") field(INP, ["One","Two","Three","Four"]) } record(ai, "emptylink" ) { field(INP, {calc: {expr:"0"}}) } record(ai, "emptylink1" ) { field(INP, {calc: {expr:"A", args:[1], time:"a"}}) field(TSEL, -2) } record(printf, "printf1") { field(SIZV, "100") field(INP0, ["Test string, exactly 40 characters long"]) } record(printf, "printf2") { field(SIZV, "100") field(INP0, ["Longer test string, more that 40 characters long"]) } record(aai, "aai1") { field(NELM, 10) field(FTVL, "LONG") field(INP, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) } record(aai, "aai2") { field(NELM, 10) field(FTVL, "LONG") field(INP, {const:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]}) } record(subArray, "sa1") { field(FTVL, "LONG") field(MALM, 12) field(INP, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) field(INDX, 2) field(NELM, 10) } record(subArray, "sa2") { field(FTVL, "LONG") field(MALM, 10) field(NELM, 1) } record(waveform, "wf1") { field(NELM, 10) field(FTVL, "LONG") field(INP, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) } record(waveform, "wf2") { field(NELM, 10) field(FTVL, "LONG") field(INP, {const:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]}) } record(longin, "li1") { field(INP, 1) } record(int64in, "i64i1") { field(INP, 1) } record(longin, "li2") { field(INP, {const:1}) } record(int64in, "i64i2") { field(INP, {const:1}) } record(longin, "count1" ) { field(INP, {calc: {expr:"VAL+1"}}) field(SCAN, "Event") field(EVNT, "soft event 1") } record(event, "ev1") { field(INP, ["soft event 1"]) } record(event, "ev2") { field(INP, "count1.EVNT") } record(int64in, "i1") { field(INP, [1234567890123456789,]) } record(int64in, "i2") { field(INP, {const:1234567890123456789,}) } record(int64in, "i3") { field(INP, 1234567890123456789) } record(waveform, "i4") { field(NELM, 1) field(FTVL, "INT64") field(INP, [1234567890123456789,]) } base-7.0.3.1/modules/database/test/std/rec/linkRetargetLink.db0000664000577000060420000000070413557101274022732 0ustar anjaesctlrecord(ai, "rec:ai") { field(INP, "0") } record(ai, "rec:src1") { field(VAL, "1") } record(ai, "rec:src2") { field(VAL, "2") } record(stringout, "rec:link1") { field(VAL, "rec:src1") field(OUT, "rec:ai.INP CA") } record(stringout, "rec:link2") { field(VAL, "rec:src2 CP") field(OUT, "rec:ai.INP CA") } record(ai, "rec:j1") { field(INP, {calc:{ expr:"A+5", args:5 }}) field(PINI, "YES") } base-7.0.3.1/modules/database/test/std/rec/linkRetargetLinkTest.c0000664000577000060420000000666713557101274023445 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Michael Davidsaver * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver * * Test using several stringout records to retarget the link of another record */ #define EPICS_DBCA_PRIVATE_API #include #include "dbAccess.h" #include "dbUnitTest.h" #include "errlog.h" #include "epicsThread.h" #include "testMain.h" void recTestIoc_registerRecordDeviceDriver(struct dbBase *); static void testRetarget(void) { testMonitor *lnkmon, *valmon; testDiag("In testRetarget"); lnkmon = testMonitorCreate("rec:ai.INP", DBE_VALUE, 0); valmon = testMonitorCreate("rec:ai", DBE_VALUE, 0); /* initially rec:ai.INP is CONSTANT */ testdbGetFieldEqual("rec:ai", DBR_DOUBLE, 0.0); testdbGetFieldEqual("rec:ai.INP", DBR_STRING, "0"); /* rec:ai.INP becomes DB_LINK, but no processing is triggered */ testdbPutFieldOk("rec:link1.PROC", DBF_LONG, 0); testMonitorWait(lnkmon); testdbGetFieldEqual("rec:ai", DBR_DOUBLE, 0.0); testdbGetFieldEqual("rec:ai.INP", DBR_STRING, "rec:src1 NPP NMS"); /* trigger a read from rec:ai.INP */ testdbPutFieldOk("rec:ai.PROC", DBF_LONG, 0); testMonitorWait(valmon); testdbGetFieldEqual("rec:ai", DBR_DOUBLE, 1.0); /* rec:ai.INP becomes CA_LINK w/ CP, processing is triggered */ testdbPutFieldOk("rec:link2.PROC", DBF_LONG, 0); testMonitorWait(lnkmon); testMonitorWait(valmon); testdbGetFieldEqual("rec:ai", DBR_DOUBLE, 2.0); testdbGetFieldEqual("rec:ai.INP", DBR_STRING, "rec:src2 CP NMS"); testMonitorDestroy(lnkmon); testMonitorDestroy(valmon); } #define testLongStrEq(PV, VAL) testdbGetArrFieldEqual(PV, DBF_CHAR, sizeof(VAL)+2, sizeof(VAL), VAL) #define testPutLongStr(PV, VAL) testdbPutArrFieldOk(PV, DBF_CHAR, sizeof(VAL), VAL); static void testRetargetJLink(void) { testDiag("In testRetargetJLink"); testdbGetFieldEqual("rec:j1", DBF_DOUBLE, 10.0); /* minimal args */ testLongStrEq("rec:j1.INP$", "{\"calc\":{\"expr\":\"A+5\",\"args\":5}}"); /* with [] */ testPutLongStr("rec:j1.INP$", "{\"calc\":{\"expr\":\"A+5\",\"args\":[7]}}"); testdbPutFieldOk("rec:j1.PROC", DBF_LONG, 1); /* with const */ testPutLongStr("rec:j1.INP$", "{\"calc\":{\"expr\":\"A+5\",\"args\":[{\"const\":7}]}}"); testdbPutFieldOk("rec:j1.PROC", DBF_LONG, 1); testdbGetFieldEqual("rec:j1", DBF_DOUBLE, 12.0); testLongStrEq("rec:j1.INP$", "{\"calc\":{\"expr\":\"A+5\",\"args\":[{\"const\":7}]}}"); } MAIN(linkRetargetLinkTest) { testPlan(18); testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); recTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("linkRetargetLink.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); /* wait for local CA links to be connected or dbPutField() will fail */ /* wait for initial CA_CONNECT actions to be processed. * Assume that local CA links deliver callbacks synchronously * eg. that ca_create_channel() will invoke the connection callback * before returning. */ dbCaSync(); testRetarget(); testRetargetJLink(); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/std/rec/mbbioDirectTest.c0000664000577000060420000000706013557101274022403 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2017 Dirk Zimoch * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Theory of Operation * * For each of the two soft device supports (soft/raw soft/soft), * there is a combination of mbboDirect -> val / sim -> mbbiDirect. * * The intermediate records are of type double (val) and long (sim) to * check conversion from/to double. * * For each device support, the following is done: * 1. The mbboDirect record is set to a specific value through the database. * 2. The initial value is checked on both ends. * 3. Two single bits (5 & 31) are toggled, values checked on both ends. * 4. Sim mode is activated, one bit (0) is toggled, values are checked, * data path (old value in val, new value in sim) is checked. */ #include #include "dbAccess.h" #include "errlog.h" #include "dbStaticLib.h" #include "dbTest.h" #include "dbUnitTest.h" #include "testMain.h" #include "epicsThread.h" #include "epicsExport.h" static void testmbbioFields(const char* rec, unsigned int value) { char field[40]; unsigned int i; testdbGetFieldEqual(rec, DBF_ULONG, value); for (i=0; i < 32; i++) { sprintf(field,"%s.B%X", rec, i); testdbGetFieldEqual(field, DBF_ULONG, (value>>i)&1); } } static void testmbbioRecords(unsigned int count, unsigned int value) { char rec[40]; unsigned int i; for (i = 1; i <= count; i++) { sprintf(rec, "do%d", i); testDiag(" ### %s ###", rec); testmbbioFields(rec, value); sprintf(rec, "di%d", i); testmbbioFields(rec, value); } } static void putN(const char* pattern, unsigned int count, unsigned int value) { char field[40]; unsigned int i; for (i = 1; i <= count; i++) { sprintf(field, pattern, i); testdbPutFieldOk(field, DBF_ULONG, value); } } static void testN(const char* pattern, unsigned int count, unsigned int value) { char field[40]; unsigned int i; for (i = 1; i <= count; i++) { sprintf(field, pattern, i); testdbGetFieldEqual(field, DBF_ULONG, value); } } void recTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(mbbioDirectTest) { unsigned int value = 0xdeadbeef; unsigned int simvalue = 0; char macros [40]; const unsigned int N = 2; testPlan(N*((32+1)*2*4+4+3)); testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); recTestIoc_registerRecordDeviceDriver(pdbbase); sprintf(macros, "INIT=%#x", value); testdbReadDatabase("mbbioDirectTest.db", NULL, macros); eltc(0); testIocInitOk(); eltc(1); testDiag("##### check initial value #####"); testmbbioRecords(N, value); testDiag("##### set bit 5 #####"); putN("do%u.B5", N, 1); value |= (1<<5); testN("val%d", N, value); testmbbioRecords(N, value); testDiag("##### clear bit 31 (0x1f) #####"); putN("do%u.B1F", N, 0); value &= ~(1<<31); testN("val%d", N, value); testmbbioRecords(N, value); testDiag("##### simulation mode #####"); dbpf("sim", "1"); simvalue = value & ~1; putN("do%u.B0", N, 0); /* old value in lo* */ testN("val%d", N, value); /* sim value in sim* */ testN("sim%d", N, simvalue); testmbbioRecords(N, simvalue); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/std/rec/mbbioDirectTest.db0000664000577000060420000000155313557101274022547 0ustar anjaesctlrecord(bo, "sim") { field(ZNAM, "off") field(ONAM, "simulation") } record(mbboDirect, "do1") { field(DOL, "$(INIT=0)") field(DTYP, "Soft Channel") field(OUT, "val1 PP") field(SIOL, "sim1 PP") field(SIML, "sim") field(PINI, "YES") } record(ao, "val1") { field(FLNK, "di1") } record(longout, "sim1") { field(FLNK, "di1") } record(mbbiDirect, "di1") { field(DTYP, "Soft Channel") field(INP, "val1") field(SIOL, "sim1 PP") field(SIML, "sim") } record(mbboDirect, "do2") { field(DOL, "$(INIT=0)") field(DTYP, "Raw Soft Channel") field(OUT, "val2 PP") field(SIOL, "sim2 PP") field(SIML, "sim") field(PINI, "YES") } record(ao, "val2") { field(FLNK, "di2") } record(longout, "sim2") { field(FLNK, "di2") } record(mbbiDirect, "di2") { field(DTYP, "Raw Soft Channel") field(INP, "val2") field(SIML, "sim") field(SIOL, "sim2") } base-7.0.3.1/modules/database/test/std/rec/netget.plt0000664000577000060420000000575013557101274021167 0ustar anjaesctl#!/usr/bin/env perl use strict; use warnings; use lib '@TOP@/lib/perl'; use Test::More tests => 3; use EPICS::IOC; $ENV{HARNESS_ACTIVE} = 1 if scalar @ARGV && shift eq '-tap'; # Keep traffic local and avoid duplicates over multiple interfaces $ENV{EPICS_CA_AUTO_ADDR_LIST} = 'NO'; $ENV{EPICS_CA_ADDR_LIST} = 'localhost'; $ENV{EPICS_CA_SERVER_PORT} = 55064; $ENV{EPICS_CAS_BEACON_PORT} = 55065; $ENV{EPICS_CAS_INTF_ADDR_LIST} = 'localhost'; $ENV{EPICS_PVA_AUTO_ADDR_LIST} = 'NO'; $ENV{EPICS_PVA_ADDR_LIST} = 'localhost'; $ENV{EPICS_PVAS_SERVER_PORT} = 55075; $ENV{EPICS_PVA_BROADCAST_PORT} = 55076; $ENV{EPICS_PVAS_INTF_ADDR_LIST} = 'localhost'; my $bin = "@TOP@/bin/@ARCH@"; my $exe = ($^O =~ m/^(MSWin32|cygwin)$/x) ? '.exe' : ''; my $prefix = "test-$$"; my $ioc = EPICS::IOC->new(); #$ioc->debug(1); $SIG{__DIE__} = $SIG{INT} = $SIG{QUIT} = sub { $ioc->kill; BAIL_OUT('Caught signal'); }; # Watchdog utilities sub kill_bail { my $doing = shift; return sub { $ioc->kill; BAIL_OUT("Timeout $doing"); } } sub watchdog (&$$) { my ($do, $timeout, $abort) = @_; $SIG{ALRM} = $abort; alarm $timeout; &$do; alarm 0; } # Start the IOC my $softIoc = "$bin/softIocPVA$exe"; $softIoc = "$bin/softIoc$exe" unless -x $softIoc; BAIL_OUT("Can't find a softIoc executable") unless -x $softIoc; watchdog { $ioc->start($softIoc, '-x', $prefix); $ioc->cmd; # Wait for command prompt } 10, kill_bail('starting softIoc'); # Get Base Version number from PV my $pv = "$prefix:BaseVersion"; watchdog { my @pvs = $ioc->dbl('stringin'); grep(m/^ $pv $/x, @pvs) or BAIL_OUT('No BaseVersion record found'); } 10, kill_bail('running dbl'); my $version; watchdog { $version = $ioc->dbgf("$pv"); } 10, kill_bail('getting BaseVersion'); like($version, qr/^ \d+ \. \d+ \. \d+ /x, "Got BaseVersion '$version' from iocsh"); # Channel Access SKIP: { my $caget = "$bin/caget$exe"; skip "caget not available", 1 unless -x $caget; # CA Server Diagnostics watchdog { note("CA server configuration:\n", map(" $_\n", $ioc->cmd('casr', 1))); } 10, kill_bail('running casr'); # CA Client test watchdog { my $caVersion = `$caget -w5 $pv`; like($caVersion, qr/^ $pv \s+ \Q$version\E $/x, 'Got same BaseVersion from caget'); } 10, kill_bail('doing caget'); } # PV Access SKIP: { my $pvget = "$bin/pvget$exe"; skip "softIocPVA not available", 1 if $softIoc eq "$bin/softIoc$exe"; # PVA Server Diagnostics watchdog { note("PVA server configuration:\n", map(" $_\n", $ioc->cmd('pvasr'))); } 10, kill_bail('running pvasr'); skip "pvget not available", 1 unless -x $pvget; # PVA Client test watchdog { my $pvaVersion = `$pvget -w5 $pv`; like($pvaVersion, qr/^ $pv \s .* \Q$version\E \s* $/x, 'Got same BaseVersion from pvget'); } 10, kill_bail('doing pvget'); } $ioc->kill; base-7.0.3.1/modules/database/test/std/rec/recMiscTest.c0000664000577000060420000000403213557101274021541 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2017 Michael Davidsaver * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include "dbAccess.h" #include "errlog.h" #include "dbStaticLib.h" #include "dbUnitTest.h" #include "testMain.h" static void testint64BeforeInit(void) { const char *S; DBENTRY dbent; /* check dbGet/PutString */ testDiag("In %s", EPICS_FUNCTION); dbInitEntryFromRecord(testdbRecordPtr("out64"), &dbent); if(dbFindField(&dbent, "VAL")) testAbort("Failed to find out64.VAL"); S = dbGetString(&dbent); testOk(S && strcmp(S, "0")==0, "initial value \"%s\"", S); testOk1(dbPutString(&dbent, "0x12345678abcdef00")==0); S = dbGetString(&dbent); testOk(S && strcmp(S, "1311768467750121216")==0, "1311768467750121216 \"%s\"", S); dbFinishEntry(&dbent); } static void testint64AfterInit(void) { testDiag("In %s", EPICS_FUNCTION); /* check dbGet/PutField and DB links */ testdbGetFieldEqual("in64", DBF_UINT64, 0ULL); testdbGetFieldEqual("out64", DBF_UINT64, 0x12345678abcdef00ULL); testdbPutFieldOk("out64.PROC", DBF_LONG, 1); testdbGetFieldEqual("in64", DBF_UINT64, 0x12345678abcdef00ULL); testdbPutFieldOk("out64.VAL", DBF_UINT64, 0x22345678abcdef00ULL); testdbPutFieldOk("in64.PROC", DBF_LONG, 1); testdbGetFieldEqual("in64", DBF_UINT64, 0x22345678abcdef00ULL); } void recTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(recMiscTest) { testPlan(10); testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); recTestIoc_registerRecordDeviceDriver(pdbbase); testdbReadDatabase("recMiscTest.db", NULL, NULL); testint64BeforeInit(); eltc(0); testIocInitOk(); eltc(1); testint64AfterInit(); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/std/rec/recMiscTest.db0000664000577000060420000000020613557101274021703 0ustar anjaesctl # check int64in/out record(int64in, "in64") { field(INP , "out64 NPP") } record(int64out, "out64") { field(OUT , "in64 NPP") } base-7.0.3.1/modules/database/test/std/rec/regressArray1.db0000664000577000060420000000023313557101274022210 0ustar anjaesctlrecord(waveform, "wf") { field(FTVL, "DOUBLE") field(NELM, "1") field(FLNK, "co") } record(calcout, "co") { field(CALC, "A") field(INPA, "wf") } base-7.0.3.1/modules/database/test/std/rec/regressHex.db0000664000577000060420000000100013557101274021566 0ustar anjaesctlrecord(ai, "ai1") { field(INP, 0x10) } record(longin, "li1") { field(INP, 0x10) } record(mbbiDirect, "mi1") { field(INP, 0x10) field(NOBT, 8) } record(aSub, "as1") { field(INPA, 0x10) field(FTA, "CHAR") field(INPB, 0x10) field(FTB, "UCHAR") field(INPC, 0x10) field(FTC, "SHORT") field(INPD, 0x10) field(FTD, "USHORT") field(INPE, 0x10) field(FTE, "LONG") field(INPF, 0x10) field(FTF, "ULONG") field(INPG, 0x10) field(FTG, "FLOAT") field(INPH, 0x10) field(FTH, "DOUBLE") } base-7.0.3.1/modules/database/test/std/rec/regressLinkMS.db0000664000577000060420000000036413557101274022213 0ustar anjaesctlrecord(ai, "alarm") { field(HIGH, 1) field(HSV, MINOR) field(HIHI, 2) field(HHSV, MAJOR) field(FLNK, "latch") } record(calc, "latch") { field(INPA, "alarm NPP MS") field(INPB, "latch NPP MS") field(CALC, "A") } base-7.0.3.1/modules/database/test/std/rec/regressTest.c0000664000577000060420000000763013557101274021635 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 Michael Davidsaver * EPICS BASE 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 /* * Tests for specific regressions */ void regressTest_registerRecordDeviceDriver(struct dbBase *); static void startRegressTestIoc(const char *dbfile) { testdbPrepare(); testdbReadDatabase("regressTest.dbd", NULL, NULL); regressTest_registerRecordDeviceDriver(pdbbase); testdbReadDatabase(dbfile, NULL, NULL); eltc(0); testIocInitOk(); eltc(1); } /* * https://bugs.launchpad.net/epics-base/+bug/1577108 */ static void testArrayLength1(void) { waveformRecord *precwf; calcoutRecord *precco; double *pbuf; startRegressTestIoc("regressArray1.db"); precwf = (waveformRecord*)testdbRecordPtr("wf"); precco = (calcoutRecord*)testdbRecordPtr("co"); dbScanLock((dbCommon*)precwf); pbuf = (double*)precwf->bptr; dbScanUnlock((dbCommon*)precwf); testdbPutFieldOk("wf", DBF_DOUBLE, 2.0); dbScanLock((dbCommon*)precwf); testOk(precwf->nord==1, "wf.NORD = %u == 1", (unsigned)precwf->nord); testOk(pbuf[0]==2.0, "wf.VAL[0] = %f == 2.0", pbuf[0]); dbScanUnlock((dbCommon*)precwf); dbScanLock((dbCommon*)precco); testOk(precco->a==2.0, "co.A = %f == 2.0", precco->a); dbScanUnlock((dbCommon*)precco); testdbGetFieldEqual("co", DBF_DOUBLE, 2.0); testIocShutdownOk(); testdbCleanup(); } /* * https://bugs.launchpad.net/epics-base/+bug/1699445 */ static void testHexConstantLinks(void) { startRegressTestIoc("regressHex.db"); testdbGetFieldEqual("ai1", DBR_LONG, 0x10); testdbGetFieldEqual("li1", DBR_LONG, 0x10); testdbGetFieldEqual("mi1", DBR_LONG, 0x10); testTodoBegin("Needs JSON5 for hex arrays"); testdbGetFieldEqual("as1.A", DBR_LONG, 0x10); testdbGetFieldEqual("as1.B", DBR_LONG, 0x10); testdbGetFieldEqual("as1.C", DBR_LONG, 0x10); testdbGetFieldEqual("as1.D", DBR_LONG, 0x10); testdbGetFieldEqual("as1.E", DBR_LONG, 0x10); testdbGetFieldEqual("as1.F", DBR_LONG, 0x10); testdbGetFieldEqual("as1.G", DBR_LONG, 0x10); testdbGetFieldEqual("as1.H", DBR_LONG, 0x10); testTodoEnd(); testIocShutdownOk(); testdbCleanup(); } static void testLinkMS(void) { startRegressTestIoc("regressLinkMS.db"); testdbPutFieldOk("alarm", DBF_DOUBLE, 0.5); testdbGetFieldEqual("latch", DBR_DOUBLE, 0.5); testdbGetFieldEqual("latch.SEVR", DBR_LONG, 0); testdbPutFieldOk("alarm", DBF_DOUBLE, 1.5); testdbGetFieldEqual("latch", DBR_DOUBLE, 1.5); testdbGetFieldEqual("latch.SEVR", DBR_LONG, 1); testdbPutFieldOk("alarm", DBF_DOUBLE, 0.5); testdbGetFieldEqual("latch", DBR_DOUBLE, 0.5); testdbGetFieldEqual("latch.SEVR", DBR_LONG, 0); testdbPutFieldOk("alarm", DBF_DOUBLE, 2.5); testdbGetFieldEqual("latch", DBR_DOUBLE, 2.5); testdbGetFieldEqual("latch.SEVR", DBR_LONG, 2); testdbPutFieldOk("alarm", DBF_DOUBLE, 0.5); testdbGetFieldEqual("latch", DBR_DOUBLE, 0.5); testdbGetFieldEqual("latch.SEVR", DBR_LONG, 0); testIocShutdownOk(); testdbCleanup(); } /* lp:1798855 disconnected CA link must alarm */ static void testCADisconn(void) { testDiag("In testCADisconn()"); startRegressTestIoc("badCaLink.db"); testdbPutFieldOk("ai:disconn.PROC", DBF_LONG, 1); testdbGetFieldEqual("ai:disconn.SEVR", DBF_LONG, INVALID_ALARM); testdbGetFieldEqual("ai:disconn.STAT", DBF_LONG, LINK_ALARM); } MAIN(regressTest) { testPlan(34); testArrayLength1(); testHexConstantLinks(); testLinkMS(); testCADisconn(); return testDone(); } base-7.0.3.1/modules/database/test/std/rec/rtemsTestHarness.c0000664000577000060420000000101213557101274022625 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ extern void epicsRunRecordTests(void); int main(int argc, char **argv) { epicsRunRecordTests(); /* calls epicsExit(0) */ return 0; } base-7.0.3.1/modules/database/test/std/rec/scanEventTest.c0000664000577000060420000001112713557101274022105 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2018 Paul Scherrer Institut * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Dirk Zimoch */ #include #include "dbStaticLib.h" #include "dbAccessDefs.h" #include "dbUnitTest.h" #include "testMain.h" #include "osiFileName.h" #include "epicsThread.h" #include "dbScan.h" void scanEventTest_registerRecordDeviceDriver(struct dbBase *); /* test name to event number: num = 0 is no event, num > 0 is numeric event num < 0 is string event (use same unique number for aliases) */ const struct {char* name; int num;} events[] = { /* No events */ {NULL, 0}, {"", 0}, {" ", 0}, {"0", 0}, {"0.000000", 0}, {"-0.00000", 0}, {"0.9", 0}, /* Numeric events */ {"2", 2}, {"2.000000", 2}, {"2.5", 2}, {" 2.5 ", 2}, {"+0x02", 2}, {"3", 3}, /* Named events */ {"info 1", -1}, {" info 1 ", -1}, {"-0.9", -2}, {"-2", -3}, {"-2.000000", -4}, {"-2.5", -5}, {" -2.5 ", -5}, {"nan", -6}, {"NaN", -7}, {"-NAN", -8}, {"-inf", -9}, {"inf", -10}, }; MAIN(scanEventTest) { int i, e; int aliases[512] ; int expected_count[512]; #define INDX(i) 256-events[i].num #define MAXEV 5 testPlan(NELEMENTS(events)*2+(MAXEV+1)*5); testdbPrepare(); memset(aliases, 0, sizeof(aliases)); memset(expected_count, 0, sizeof(expected_count)); testdbReadDatabase("scanEventTest.dbd", NULL, NULL); scanEventTest_registerRecordDeviceDriver(pdbbase); for (i = 0; i < NELEMENTS(events); i++) { char substitutions[256]; sprintf(substitutions, "N=%d,EVENT=%s", i, events[i].name); testdbReadDatabase("scanEventTest.db", NULL, substitutions); } testIocInitOk(); testDiag("Test if eventNameToHandle() strips spaces and handles numeric events"); for (i = 0; i < NELEMENTS(events); i++) { EVENTPVT pev = eventNameToHandle(events[i].name); /* test that some names are not events (num=0) */ if (events[i].num == 0) testOk(pev == NULL, "\"%s\" -> no event", events[i].name); else { expected_count[INDX(i)]++; /* +1 for postEvent */ if (events[i].num > 0) { testOk(pev != NULL, "\"%s\" -> numeric event %d", events[i].name, events[i].num); expected_count[INDX(i)]++; /* +1 for post_event */ } else { /* test that some strings resolve the same event (num!=0) */ if (!aliases[INDX(i)]) { aliases[INDX(i)] = i; testOk(pev != NULL, "\"%s\" -> new named event", events[i].name); } else { testOk(pev == eventNameToHandle(events[aliases[INDX(i)]].name), "\"%s\" alias for \"%s\"", events[i].name, events[aliases[INDX(i)]].name); } } } post_event(events[i].num); /* triggers numeric events only */ postEvent(pev); } testDiag("Check calculated numeric events (backward compatibility)"); for (e = 0; e <= MAXEV; e++) { testdbPutFieldOk("eventnum", DBR_LONG, e); testdbGetFieldEqual("e1", DBR_LONG, e); testdbGetFieldEqual("e2", DBR_LONG, e); testdbPutFieldOk("e3", DBR_LONG, e); testdbPutFieldOk("e3.PROC", DBR_LONG, 1); for (i = 0; i < NELEMENTS(events); i++) if (e > 0 && e < 256 && events[i].num == e) { /* numeric events */ expected_count[INDX(i)]+=3; /* +1 for eventnum->e1, +1 for e2<-eventnum, +1 for e3 */ break; } } /* Allow records to finish processing */ testSyncCallback(); testDiag("Check if events have been processed the expected number of times"); for (i = 0; i < NELEMENTS(events); i++) { char pvname[100]; sprintf(pvname, "c%d", i); testDiag("Event \"%s\" expected %d times", events[i].name, expected_count[INDX(i)]); testdbGetFieldEqual(pvname, DBR_LONG, expected_count[INDX(i)]); } testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/std/rec/scanEventTest.db0000664000577000060420000000041713557101274022250 0ustar anjaesctlrecord(calc, "c$(N)") { field(SCAN, "Event") field(EVNT, "$(EVENT)") field(CALC, "VAL+1") } record(dfanout, "eventnum") { field(OUTA, "e1 PP") field(FLNK, "e2") } record(event, "e1") { } record(event, "e2") { field(INP, "eventnum") } record(event, "e3") { } base-7.0.3.1/modules/database/test/std/rec/simmSetup.db0000664000577000060420000000035413557101274021450 0ustar anjaesctl# no info record(ai, "ai-0") { } # only scan record(ai, "ai-1") { field(SSCN,"2 second") } # only delay record(ai, "ai-2") { field(SDLY,".234") } # scan and delay record(ai, "ai-3") { field(SSCN,"5 second") field(SDLY,".345") } base-7.0.3.1/modules/database/test/std/rec/simmSimlFail.db0000664000577000060420000000017213557101274022046 0ustar anjaesctl# siml target doesn't exist record(ao, "ao-0") { field(SIML, "non-exist") field(HIGH, "1.5") field(HSV, "MINOR") } base-7.0.3.1/modules/database/test/std/rec/simmTest.c0000664000577000060420000004102113557101274021120 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2017 ITER Organization * EPICS BASE 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 "recSup.h" #include "aiRecord.h" #include "aoRecord.h" #include "aaiRecord.h" #include "aaoRecord.h" #include "biRecord.h" #include "boRecord.h" #include "mbbiRecord.h" #include "mbboRecord.h" #include "mbbiDirectRecord.h" #include "mbboDirectRecord.h" #include "longinRecord.h" #include "longoutRecord.h" #include "int64inRecord.h" #include "int64outRecord.h" #include "stringinRecord.h" #include "stringoutRecord.h" #include "lsiRecord.h" #include "lsoRecord.h" #include "eventRecord.h" #include "histogramRecord.h" #include "waveformRecord.h" /* * Tests for simulation mode */ void simmTest_registerRecordDeviceDriver(struct dbBase *); static void startSimmTestIoc(const char *dbfile) { testdbPrepare(); testdbReadDatabase("simmTest.dbd", NULL, NULL); simmTest_registerRecordDeviceDriver(pdbbase); testdbReadDatabase(dbfile, NULL, NULL); eltc(0); testIocInitOk(); eltc(1); } static char *rawSupp[] = { "ai", "bi", "mbbi", "mbbiDirect", }; static int hasRawSimmSupport(const char *rectype) { int i; for (i = 0; i < (sizeof(rawSupp)/sizeof(rawSupp[0])); i++) if (strcmp(rectype, rawSupp[i]) == 0) return 1; return 0; } #define PVNAMELENGTH 60 static char nameVAL[PVNAMELENGTH]; static char nameB0[PVNAMELENGTH]; static char nameRVAL[PVNAMELENGTH]; static char nameSGNL[PVNAMELENGTH]; static char nameSIMM[PVNAMELENGTH]; static char nameSIML[PVNAMELENGTH]; static char nameSVAL[PVNAMELENGTH]; static char nameSIOL[PVNAMELENGTH]; static char nameSCAN[PVNAMELENGTH]; static char namePROC[PVNAMELENGTH]; static char namePACT[PVNAMELENGTH]; static char nameSTAT[PVNAMELENGTH]; static char nameSEVR[PVNAMELENGTH]; static char nameSIMS[PVNAMELENGTH]; static char nameTSE[PVNAMELENGTH]; static char nameSimmode[PVNAMELENGTH]; static char nameSimval[PVNAMELENGTH]; static char nameSimvalNORD[PVNAMELENGTH]; static char nameSimvalLEN[PVNAMELENGTH]; #define SETNAME(field) strcpy(name ## field, name); strcat(name ## field, "." #field) static void setNames(const char *name) { SETNAME(VAL); SETNAME(B0); SETNAME(RVAL); SETNAME(SGNL); SETNAME(SVAL); SETNAME(SIMM); SETNAME(SIML); SETNAME(SIOL); SETNAME(SIMS); SETNAME(SCAN); SETNAME(PROC); SETNAME(PACT); SETNAME(STAT); SETNAME(SEVR); SETNAME(TSE); strcpy(nameSimmode, name); strcat(nameSimmode, ":simmode"); strcpy(nameSimval, name); strcat(nameSimval, ":simval"); strcpy(nameSimvalNORD, name); strcat(nameSimvalNORD, ":simval.NORD"); strcpy(nameSimvalLEN, name); strcat(nameSimvalLEN, ":simval.LEN"); } /* * Parsing of info items and xsimm structure setting */ static void testSimmSetup(void) { aiRecord *precai; testDiag("##### Simm initialization #####"); /* no config */ precai = (aiRecord*)testdbRecordPtr("ai-0"); testOk(precai->simpvt == NULL, "ai-0.SIMPVT = %p == NULL [no callback]", precai->simpvt); testOk(precai->sscn == USHRT_MAX, "ai-0.SSCN = %u == USHRT_MAX (not set)", precai->sscn); testOk(precai->sdly < 0., "ai-0.SDLY = %g < 0.0 (not set)", precai->sdly); /* with SCAN */ precai = (aiRecord*)testdbRecordPtr("ai-1"); testOk(precai->sscn == 5, "ai-1.SSCN = %u == 5 (2 second)", precai->sscn); testOk(precai->sdly < 0., "ai-1.SDLY = %g < 0.0 (not set)", precai->sdly); /* with DELAY */ precai = (aiRecord*)testdbRecordPtr("ai-2"); testOk(precai->sscn == USHRT_MAX, "ai-2.SSCN = %u == USHRT_MAX (not set)", precai->sscn); testOk(precai->sdly == 0.234, "ai-2.SDLY = %g == 0.234", precai->sdly); /* with SCAN and DELAY */ precai = (aiRecord*)testdbRecordPtr("ai-3"); testOk(precai->sscn == 4, "ai-3.SSCN = %u == 4 (5 second)", precai->sscn); testOk(precai->sdly == 0.345, "ai-3.SDLY = %g == 0.345", precai->sdly); } /* * Invalid SIML link sets LINK/NO_ALARM if in NO_ALARM */ static void testSimlFail(void) { aoRecord *precao; testDiag("##### Behavior for failing SIML #####"); precao = (aoRecord*)testdbRecordPtr("ao-0"); /* before anything: UDF INVALID */ testOk(precao->stat == UDF_ALARM, "ao-0.STAT = %u [%s] == %u [UDF]", precao->stat, epicsAlarmConditionStrings[precao->stat], UDF_ALARM); testOk(precao->sevr == INVALID_ALARM, "ao-0.SEVR = %u [%s] == %u [INVALID]", precao->sevr, epicsAlarmSeverityStrings[precao->sevr], INVALID_ALARM); /* legal value: LINK NO_ALARM */ testdbPutFieldOk("ao-0.VAL", DBR_DOUBLE, 1.0); testOk(precao->stat == LINK_ALARM, "ao-0.STAT = %u [%s] == %u [LINK]", precao->stat, epicsAlarmConditionStrings[precao->stat], LINK_ALARM); testOk(precao->sevr == NO_ALARM, "ao-0.SEVR = %u [%s] == %u [NO_ALARM]", precao->sevr, epicsAlarmSeverityStrings[precao->sevr], NO_ALARM); /* HIGH/MINOR overrides */ testdbPutFieldOk("ao-0.VAL", DBR_DOUBLE, 2.0); testOk(precao->stat == HIGH_ALARM, "ao-0.STAT = %u [%s] == %u [HIGH]", precao->stat, epicsAlarmConditionStrings[precao->stat], HIGH_ALARM); testOk(precao->sevr == MINOR_ALARM, "ao-0.SEVR = %u [%s] == %u [MINOR]", precao->sevr, epicsAlarmSeverityStrings[precao->sevr], MINOR_ALARM); } /* * SIMM triggered SCAN swapping, by writing to SIMM and through SIML */ static void testSimmToggle(const char *name, epicsEnum16 *psscn) { testDiag("## SIMM toggle and SCAN swapping ##"); /* SIMM mode by setting the field */ testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0); testOk(*psscn == 1, "SSCN = %u == 1 (Event)", *psscn); testDiag("set SIMM to YES"); testdbPutFieldOk(nameSIMM, DBR_STRING, "YES"); testdbGetFieldEqual(nameSCAN, DBR_USHORT, 1); testOk(*psscn == 0, "SSCN = %u == 0 (Passive)", *psscn); /* Change simm:SCAN when simmYES */ testdbPutFieldOk(nameSCAN, DBR_USHORT, 3); testDiag("set SIMM to NO"); testdbPutFieldOk(nameSIMM, DBR_STRING, "NO"); testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0); testOk(*psscn == 3, "SSCN = %u == 3 (10 second)", *psscn); *psscn = 1; if (hasRawSimmSupport(name)) { testDiag("set SIMM to RAW"); testdbPutFieldOk(nameSIMM, DBR_STRING, "RAW"); testdbGetFieldEqual(nameSCAN, DBR_USHORT, 1); testOk(*psscn == 0, "SSCN = %u == 0 (Passive)", *psscn); testDiag("set SIMM to NO"); testdbPutFieldOk(nameSIMM, DBR_STRING, "NO"); testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0); testOk(*psscn == 1, "SSCN = %u == 1 (Event)", *psscn); } else { testDiag("Record type %s has no support for simmRAW", name); } /* SIMM mode through SIML */ testdbPutFieldOk(nameSIML, DBR_STRING, nameSimmode); testDiag("set SIMM (via SIML -> simmode) to YES"); testdbPutFieldOk(nameSimmode, DBR_USHORT, 1); testdbPutFieldOk(namePROC, DBR_LONG, 0); testdbGetFieldEqual(nameSIMM, DBR_USHORT, 1); testdbGetFieldEqual(nameSCAN, DBR_USHORT, 1); testOk(*psscn == 0, "SSCN = %u == 0 (Passive)", *psscn); testDiag("set SIMM (via SIML -> simmode) to NO"); testdbPutFieldOk(nameSimmode, DBR_USHORT, 0); testdbPutFieldOk(namePROC, DBR_LONG, 0); testdbGetFieldEqual(nameSIMM, DBR_USHORT, 0); testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0); testOk(*psscn == 1, "SSCN = %u == 1 (Event)", *psscn); if (hasRawSimmSupport(name)) { testDiag("set SIMM (via SIML -> simmode) to RAW"); testdbPutFieldOk(nameSimmode, DBR_USHORT, 2); testdbPutFieldOk(namePROC, DBR_LONG, 0); testdbGetFieldEqual(nameSIMM, DBR_USHORT, 2); testdbGetFieldEqual(nameSCAN, DBR_USHORT, 1); testOk(*psscn == 0, "SSCN = %u == 0 (Passive)", *psscn); testDiag("set SIMM (via SIML -> simmode) to NO"); testdbPutFieldOk(nameSimmode, DBR_USHORT, 0); testdbPutFieldOk(namePROC, DBR_LONG, 0); testdbGetFieldEqual(nameSIMM, DBR_USHORT, 0); testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0); testOk(*psscn == 1, "SSCN = %u == 1 (Event)", *psscn); } else { testDiag("Record type %s has no support for simmRAW", name); } } /* * Reading from SVAL (direct write or through SIOL link) */ static void testSvalRead(const char *name, const epicsTimeStamp *mytime, const epicsTimeStamp *svtime) { epicsTimeStamp last; if (strcmp(name, "histogram") == 0) strcpy(nameVAL, nameSGNL); if (strcmp(name, "aai") != 0 && strcmp(name, "waveform") != 0 && strcmp(name, "lsi") != 0) { testDiag("## Reading from SVAL ##"); testDiag("in simmNO, SVAL must be ignored"); testdbPutFieldOk(nameSimmode, DBR_USHORT, 0); testdbPutFieldOk(nameVAL, DBR_LONG, 0); if (strcmp(name, "stringin") == 0) testdbPutFieldOk(nameSVAL, DBR_STRING, "1"); else testdbPutFieldOk(nameSVAL, DBR_USHORT, 1); testdbPutFieldOk(namePROC, DBR_LONG, 0); testdbGetFieldEqual(nameVAL, DBR_USHORT, 0); testDiag("in simmYES, SVAL is used for VAL"); testdbPutFieldOk(nameSIMS, DBR_USHORT, 0); testdbPutFieldOk(nameSimmode, DBR_USHORT, 1); testdbPutFieldOk(namePROC, DBR_LONG, 0); testdbGetFieldEqual(nameVAL, DBR_USHORT, 1); testDiag("No SIMS setting: STAT/SEVR == NO_ALARM"); testdbGetFieldEqual(nameSTAT, DBR_STRING, "NO_ALARM"); testdbGetFieldEqual(nameSEVR, DBR_USHORT, 0); if (hasRawSimmSupport(name)) { testDiag("in simmRAW, SVAL is used for RVAL"); testdbPutFieldOk(nameSimmode, DBR_USHORT, 2); testdbPutFieldOk(namePROC, DBR_LONG, 0); testdbGetFieldEqual(nameRVAL, DBR_USHORT, 1); } else { testDiag("Record type %s has no support for simmRAW", name); } } testDiag("## Reading from SIOL->SVAL ##"); /* Set SIOL link to simval */ testdbPutFieldOk(nameSIOL, DBR_STRING, nameSimval); testDiag("in simmNO, SIOL->SVAL must be ignored"); testdbPutFieldOk(nameSimmode, DBR_USHORT, 0); testdbPutFieldOk(nameVAL, DBR_LONG, 0); testdbPutFieldOk(nameSimval, DBR_LONG, 1); testdbPutFieldOk(namePROC, DBR_LONG, 0); testdbGetFieldEqual(nameVAL, DBR_USHORT, 0); testDiag("in simmYES, SIOL->SVAL is used for VAL"); testdbPutFieldOk(nameSIMS, DBR_USHORT, 3); testdbPutFieldOk(nameSimmode, DBR_USHORT, 1); testdbPutFieldOk(namePROC, DBR_LONG, 0); testdbGetFieldEqual(nameVAL, DBR_USHORT, 1); testDiag("SIMS is INVALID: STAT/SEVR == SIMM/INVALID"); testdbGetFieldEqual(nameSTAT, DBR_STRING, "SIMM"); testdbGetFieldEqual(nameSEVR, DBR_USHORT, 3); testdbPutFieldOk(nameSIMS, DBR_USHORT, 0); if (hasRawSimmSupport(name)) { testDiag("in simmRAW, SIOL->SVAL is used for RVAL"); testdbPutFieldOk(nameSimmode, DBR_USHORT, 2); testdbPutFieldOk(namePROC, DBR_LONG, 0); testdbGetFieldEqual(nameRVAL, DBR_USHORT, 1); } else { testDiag("Record type %s has no support for simmRAW", name); } /* My timestamp must be later than simval's */ testOk(epicsTimeLessThan(svtime, mytime), "simval time < my time [TSE = 0]"); testDiag("for TSE=-2 (from device) and simmYES, take time stamp from IOC or input link"); /* Set simmYES */ testdbPutFieldOk(nameSimmode, DBR_USHORT, 1); /* Set TSE to -2 (from device) and reprocess: timestamps is taken through SIOL from simval */ testdbPutFieldOk(nameTSE, DBR_SHORT, -2); testdbPutFieldOk(namePROC, DBR_LONG, 0); testOk(epicsTimeEqual(svtime, mytime), "simval time == my time [TSE = -2]"); last = *mytime; /* With TSE=-2 and no SIOL, timestamp is taken from IOC */ testdbPutFieldOk(nameSIOL, DBR_STRING, ""); testdbPutFieldOk(namePROC, DBR_LONG, 0); testOk(epicsTimeLessThan(&last, mytime), "new time stamp from IOC [TSE = -2, no SIOL]"); /* Reset TSE */ testdbPutFieldOk(nameTSE, DBR_SHORT, 0); } /* * Writing through SIOL link */ static void testSiolWrite(const char *name, const epicsTimeStamp *mytime) { epicsTimeStamp now; testDiag("## Writing through SIOL ##"); /* Set SIOL link to simval */ testdbPutFieldOk(nameSIOL, DBR_STRING, nameSimval); testDiag("in simmNO, SIOL must be ignored"); testdbPutFieldOk(nameSimmode, DBR_USHORT, 0); if (strcmp(name, "mbboDirect") == 0) testdbPutFieldOk(nameB0, DBR_LONG, 1); else testdbPutFieldOk(nameVAL, DBR_LONG, 1); if (strcmp(name, "aao") == 0) testdbGetFieldEqual(nameSimvalNORD, DBR_USHORT, 0); else if (strcmp(name, "lso") == 0) testdbGetFieldEqual(nameSimvalLEN, DBR_USHORT, 0); else testdbGetFieldEqual(nameSimval, DBR_USHORT, 0); testDiag("in simmYES, SIOL is used to write VAL"); testdbPutFieldOk(nameSimmode, DBR_USHORT, 1); if (strcmp(name, "mbboDirect") == 0) testdbPutFieldOk(nameB0, DBR_LONG, 1); else testdbPutFieldOk(nameVAL, DBR_LONG, 1); testdbGetFieldEqual(nameSimval, DBR_USHORT, 1); /* Set TSE to -2 (from device) and reprocess: timestamp is taken from IOC */ epicsTimeGetCurrent(&now); testdbPutFieldOk(nameTSE, DBR_SHORT, -2); testdbPutFieldOk(namePROC, DBR_LONG, 0); testOk(epicsTimeLessThan(&now, mytime), "new time stamp from IOC [TSE = -2]"); /* Reset TSE */ testdbPutFieldOk(nameTSE, DBR_SHORT, 0); } /* * Asynchronous processing using simm:DELAY */ static void testSimmDelay(const char *name, epicsFloat64 *psdly, const epicsTimeStamp *mytime) { epicsTimeStamp now; const double delay = 0.01; /* 10 ms */ testDiag("## Asynchronous processing with simm:DELAY ##"); /* Set delay to something just long enough */ *psdly = delay; /* Process in simmNO: synchronous */ testDiag("simm:DELAY and simmNO processes synchronously"); testdbPutFieldOk(nameSimmode, DBR_USHORT, 0); epicsTimeGetCurrent(&now); testdbPutFieldOk(namePROC, DBR_LONG, 0); testdbGetFieldEqual(namePACT, DBR_USHORT, 0); testOk(epicsTimeLessThan(&now, mytime), "time stamp is recent"); /* Process in simmYES: asynchronous */ testDiag("simm:DELAY and simmYES processes asynchronously"); testdbPutFieldOk(nameSimmode, DBR_USHORT, 1); testdbPutFieldOk(namePROC, DBR_LONG, 0); testdbGetFieldEqual(namePACT, DBR_USHORT, 1); epicsTimeGetCurrent(&now); epicsThreadSleep(1.75*delay); testdbGetFieldEqual(namePACT, DBR_USHORT, 0); testOk(epicsTimeLessThan(&now, mytime), "time stamp taken from second pass processing"); /* Reset delay */ *psdly = -1.; } #define RUNALLTESTSREAD(type) \ testDiag("################################################### Record Type " #type); \ setNames(#type); \ testSimmToggle(#type, &((type ## Record*)testdbRecordPtr(#type))->sscn); \ testSvalRead(#type, &((type ## Record*)testdbRecordPtr(#type))->time, \ &((type ## Record*)testdbRecordPtr(#type ":simval"))->time); \ testSimmDelay(#type, &((type ## Record*)testdbRecordPtr(#type))->sdly, \ &((type ## Record*)testdbRecordPtr(#type))->time) #define RUNALLTESTSWRITE(type) \ testDiag("################################################### Record Type " #type); \ setNames(#type); \ testSimmToggle(#type, &((type ## Record*)testdbRecordPtr(#type))->sscn); \ testSiolWrite(#type, &((type ## Record*)testdbRecordPtr(#type))->time); \ testSimmDelay(#type, &((type ## Record*)testdbRecordPtr(#type))->sdly, \ &((type ## Record*)testdbRecordPtr(#type))->time) static void testAllRecTypes(void) { RUNALLTESTSREAD(ai); RUNALLTESTSWRITE(ao); RUNALLTESTSREAD(aai); RUNALLTESTSWRITE(aao); RUNALLTESTSREAD(bi); RUNALLTESTSWRITE(bo); RUNALLTESTSREAD(mbbi); RUNALLTESTSWRITE(mbbo); RUNALLTESTSREAD(mbbiDirect); RUNALLTESTSWRITE(mbboDirect); RUNALLTESTSREAD(longin); RUNALLTESTSWRITE(longout); RUNALLTESTSREAD(int64in); RUNALLTESTSWRITE(int64out); RUNALLTESTSREAD(stringin); RUNALLTESTSWRITE(stringout); RUNALLTESTSREAD(lsi); RUNALLTESTSWRITE(lso); RUNALLTESTSREAD(event); RUNALLTESTSREAD(waveform); RUNALLTESTSREAD(histogram); } MAIN(simmTest) { testPlan(1176); startSimmTestIoc("simmTest.db"); testSimmSetup(); testSimlFail(); testAllRecTypes(); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/std/rec/simmTest.substitutions0000664000577000060420000000102113557101274023631 0ustar anjaesctlfile "simmTestSimple.template" { { TYPE="ai" } { TYPE="ao" } { TYPE="bi" } { TYPE="bo" } { TYPE="mbbi" } { TYPE="mbbo" } { TYPE="mbbiDirect" } { TYPE="mbboDirect" } { TYPE="longin" } { TYPE="longout" } { TYPE="int64in" } { TYPE="int64out" } { TYPE="stringin" } { TYPE="stringout" } { TYPE="lsi" } { TYPE="lso" } { TYPE="event" } } file "simmTestArray.template" { { TYPE="aai" } { TYPE="aao" } { TYPE="waveform" } } file "simmTestHistogram.template" { { TYPE="histogram" } } file "simmSetup.db" { {} file "simmSimlFail.db" { {} } base-7.0.3.1/modules/database/test/std/rec/simmTestArray.template0000664000577000060420000000046713557101274023521 0ustar anjaesctl# Array type records # Regular simulation mode and simm:SCAN tests record($(TYPE), "$(TYPE)") { field(SSCN,"Event") field(FTVL,"SHORT") field(NELM,"2") } record($(TYPE), "$(TYPE):simval") { field(FTVL,"SHORT") field(NELM,"2") } record(bo, "$(TYPE):simmode") { field(ZNAM,"off") field(ONAM,"on") } base-7.0.3.1/modules/database/test/std/rec/simmTestHistogram.template0000664000577000060420000000036413557101274024374 0ustar anjaesctl# Array type records # Regular simulation mode and simm:SCAN tests record($(TYPE), "$(TYPE)") { field(SSCN,"Event") field(NELM,"2") } record(ai, "$(TYPE):simval") { } record(bo, "$(TYPE):simmode") { field(ZNAM,"off") field(ONAM,"on") } base-7.0.3.1/modules/database/test/std/rec/simmTestSimple.template0000664000577000060420000000032213557101274023662 0ustar anjaesctl# Regular simulation mode and simm:SCAN tests record($(TYPE), "$(TYPE)") { field(SSCN,"Event") } record($(TYPE), "$(TYPE):simval") { } record(bo, "$(TYPE):simmode") { field(ZNAM,"off") field(ONAM,"on") } base-7.0.3.1/modules/database/test/std/rec/softTest.c0000664000577000060420000001464213557101274021137 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2017 UChicago Argonne LLC, as operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include "dbAccess.h" #include "dbStaticLib.h" #include "dbTest.h" #include "dbUnitTest.h" #include "epicsThread.h" #include "epicsEvent.h" #include "errlog.h" #include "registryFunction.h" #include "subRecord.h" #include "testMain.h" static void checkDtyp(const char *rec) { char dtyp[16]; strcpy(dtyp, rec); strcat(dtyp, ".DTYP"); testdbGetFieldEqual(dtyp, DBR_LONG, 0); testdbGetFieldEqual(dtyp, DBR_STRING, "Soft Channel"); } static void doProcess(const char *rec) { char proc[16]; strcpy(proc, rec); strcat(proc, ".PROC"); testdbPutFieldOk(proc, DBR_CHAR, 1); } /* Group 0 are soft-channel input records with INP being a DB or CA link * to the PV 'source'. Their VAL fields all start out with the default * value for the type, i.e. 0 or an empty string. Triggering record * processing reads the value from the 'source' PV. */ static void testGroup0(void) { const char ** rec; const char * records[] = { "ai0", "bi0", "di0", "ii0", "li0", "lsi0", "mi0", "si0", "ai0c", "bi0c", "di0c", "ii0c", "li0c", "lsi0c", "mi0c", "si0c", NULL }; testDiag("============ Starting %s ============", EPICS_FUNCTION); testdbPutFieldOk("source", DBR_LONG, 1); /* The above put sends CA monitors to all of the CA links, but * doesn't trigger record processing (the links are not CP/CPP). * How could we wait until all of those monitors have arrived, * instead of just waiting for an arbitrary time period? */ epicsThreadSleep(1.0); /* FIXME: Wait here? */ for (rec = records; *rec; rec++) { if (strncmp(*rec, "lsi0", 4) != 0) testdbGetFieldEqual(*rec, DBR_LONG, 0); checkDtyp(*rec); doProcess(*rec); testdbGetFieldEqual(*rec, DBR_LONG, 1); } testdbPutFieldOk("source", DBR_LONG, 0); epicsThreadSleep(1.0); /* FIXME: Wait here as above */ for (rec = records; *rec; rec++) { doProcess(*rec); testdbGetFieldEqual(*rec, DBR_LONG, 0); } } /* Group 1 are all soft-channel input records with INP being a non-zero * "const" JSON-link, 9 for most records, 1 for the binary. Their VAL * fields should all be initialized to that constant value. Triggering * record processing should succeed, but shouldn't change VAL. */ static void testGroup1(void) { const char ** rec; const char * records[] = { "bi1", "ai1", "di1", "ii1", "li1", "lsi1", "mi1", "si1", NULL }; int init = 1; /* bi1 initializes to 1 */ testDiag("============ Starting %s ============", EPICS_FUNCTION); for (rec = records; *rec; rec++) { testdbGetFieldEqual(*rec, DBR_LONG, init); doProcess(*rec); testdbGetFieldEqual(*rec, DBR_LONG, init); init = 9; /* other records initialize to 9 */ } } /* Group 2 are all soft-channel input records with INP being a CONSTANT * link with value 9 for most records, 1 for the binary. Their VAL * fields should all be initialized to that constant value. Triggering * record processing should succeed, but shouldn't change VAL. */ static void testGroup2(void) { const char ** rec; const char * records[] = { "bi2", "ai2", "di2", "ii2", "li2", "mi2", NULL }; int init = 1; /* bi1 initializes to 1 */ testDiag("============ Starting %s ============", EPICS_FUNCTION); for (rec = records; *rec; rec++) { testdbGetFieldEqual(*rec, DBR_LONG, init); doProcess(*rec); testdbGetFieldEqual(*rec, DBR_LONG, init); init = 9; /* other records initialize to 9 */ } } int dest; epicsEventId destEvent; static long destSubr(subRecord *prec) { dest = prec->val; prec->val = -1; epicsEventMustTrigger(destEvent); return 0; } static void checkOutput3(const char *rec, int value) { testDiag("Checking record '%s'", rec); testdbPutFieldOk(rec, DBR_LONG, value); epicsEventMustWait(destEvent); testOk(dest == value, "value %d output -> %d", value, dest); } /* Group 3 are all soft-channel output records with OUT being a DB or * local CA link to the subRecord 'dest'; DB links have the PP flag, * for CA links the VAL field is marked PP. Putting a value to the * output record writes that value to 'dest'. */ static void testGroup3(void) { const char ** rec; const char * records[] = { "ao3", "bo3", "io3", "lo3", "lso3", "mo3", "so3", "ao3c", "bo3c", "io3c", "lo3c", "lso3c", "mo3c", "so3c", NULL, }; destEvent = epicsEventMustCreate(epicsEventEmpty); testDiag("============ Starting %s ============", EPICS_FUNCTION); for (rec = records; *rec; rec++) { checkOutput3(*rec, 1); checkDtyp(*rec); } checkOutput3("do3.B0", 1); checkDtyp("do3"); checkOutput3("do3c.B0", 1); checkDtyp("do3c"); for (rec = records; *rec; rec++) { checkOutput3(*rec, 0); } checkOutput3("do3.B0", 0); checkOutput3("do3c.B0", 0); } static void checkOutput4(const char *rec, int value) { testDiag("Checking record '%s'", rec); testdbPutFieldOk(rec, DBR_LONG, value); } /* Group 4 are all soft-channel output records with OUT being empty * (i.e. a CONSTANT link). Putting a value to the record must succeed. */ static void testGroup4(void) { const char ** rec; const char * records[] = { "ao4", "bo4", "do4.B0", "io4", "lo4", "lso4", "mo4", "so4", NULL, }; testDiag("============ Starting %s ============", EPICS_FUNCTION); for (rec = records; *rec; rec++) { checkOutput4(*rec, 0); } } void recTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(softTest) { testPlan(258); testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); recTestIoc_registerRecordDeviceDriver(pdbbase); registryFunctionAdd("destSubr", (REGISTRYFUNCTION) destSubr); testdbReadDatabase("softTest.db", NULL, NULL); eltc(0); testIocInitOk(); eltc(1); testGroup0(); testGroup1(); testGroup2(); testGroup3(); testGroup4(); testIocShutdownOk(); testdbCleanup(); return testDone(); } base-7.0.3.1/modules/database/test/std/rec/softTest.db0000664000577000060420000002054213557101274021276 0ustar anjaesctl# Group 0 are all soft-channel input records with INP being a DB link # to the PV 'source'. Their VAL fields all start out with the default # value for the type, i.e. 0 or an empty string. Triggering record # processing should read the integer value from the 'source' PV. record(longin, "source") {} record(ai, "ai0") { field(DTYP, "Soft Channel") field(INP, "source") } record(bi, "bi0") { field(DTYP, "Soft Channel") field(INP, "source") field(ZNAM, "Zero") field(ONAM, "One") } record(int64in, "ii0") { field(DTYP, "Soft Channel") field(INP, "source") } record(longin, "li0") { field(DTYP, "Soft Channel") field(INP, "source") } record(mbbiDirect, "di0") { field(DTYP, "Soft Channel") field(NOBT, 4) field(INP, "source") } record(mbbi, "mi0") { field(DTYP, "Soft Channel") field(NOBT, 4) field(INP, "source") field(ZRST, "Zero") field(ONST, "One") field(TWST, "Two") field(THST, "Three") field(FRST, "Four") field(FVST, "Five") field(SXST, "Six") field(SVST, "Seven") field(EIST, "Eight") field(NIST, "Nine") field(TEST, "Ten") field(ELST, "Eleven") field(TWST, "Twelve") field(TTST, "Thirteen") field(FTST, "Fourteen") field(FFST, "Fifteen") } record(lsi, "lsi0") { field(DTYP, "Soft Channel") field(SIZV, 40) field(INP, "source") } record(stringin, "si0") { field(DTYP, "Soft Channel") field(INP, "source") } record(ai, "ai0c") { field(DTYP, "Soft Channel") field(INP, "source CA") } record(bi, "bi0c") { field(DTYP, "Soft Channel") field(INP, "source CA") field(ZNAM, "Zero") field(ONAM, "One") } record(int64in, "ii0c") { field(DTYP, "Soft Channel") field(INP, "source CA") } record(longin, "li0c") { field(DTYP, "Soft Channel") field(INP, "source CA") } record(mbbiDirect, "di0c") { field(DTYP, "Soft Channel") field(NOBT, 4) field(INP, "source CA") } record(mbbi, "mi0c") { field(DTYP, "Soft Channel") field(NOBT, 4) field(INP, "source CA") field(ZRST, "Zero") field(ONST, "One") field(TWST, "Two") field(THST, "Three") field(FRST, "Four") field(FVST, "Five") field(SXST, "Six") field(SVST, "Seven") field(EIST, "Eight") field(NIST, "Nine") field(TEST, "Ten") field(ELST, "Eleven") field(TWST, "Twelve") field(TTST, "Thirteen") field(FTST, "Fourteen") field(FFST, "Fifteen") } record(lsi, "lsi0c") { field(DTYP, "Soft Channel") field(SIZV, 40) field(INP, "source CA") } record(stringin, "si0c") { field(DTYP, "Soft Channel") field(INP, "source CA") } # Group 1 are all soft-channel input records with INP being a non-zero # "const" JSON-link, 9 for most records, 1 for the binary. Their VAL # fields should all be initialized to that constant value. Triggering # record processing should succeed, but shouldn't change VAL. record(ai, "ai1") { field(DTYP, "Soft Channel") field(INP, {const:9}) } record(bi, "bi1") { field(DTYP, "Soft Channel") field(INP, {const:1}) field(ZNAM, "Zero") field(ONAM, "One") } record(int64in, "ii1") { field(DTYP, "Soft Channel") field(INP, {const:9}) } record(longin, "li1") { field(DTYP, "Soft Channel") field(INP, {const:9}) } record(mbbiDirect, "di1") { field(DTYP, "Soft Channel") field(NOBT, 4) field(INP, {const:9}) } record(mbbi, "mi1") { field(DTYP, "Soft Channel") field(NOBT, 4) field(INP, {const:9}) field(ZRST, "Zero") field(ONST, "One") field(TWST, "Two") field(THST, "Three") field(FRST, "Four") field(FVST, "Five") field(SXST, "Six") field(SVST, "Seven") field(EIST, "Eight") field(NIST, "Nine") field(TEST, "Ten") field(ELST, "Eleven") field(TWST, "Twelve") field(TTST, "Thirteen") field(FTST, "Fourteen") field(FFST, "Fifteen") } record(lsi, "lsi1") { field(DTYP, "Soft Channel") field(SIZV, 40) field(INP, {const:"9"}) } record(stringin, "si1") { field(DTYP, "Soft Channel") field(INP, {const:"9"}) } # Group 2 are all soft-channel input records with INP being a CONSTANT # link with value 9 for most records, 1 for the binary. Their VAL # fields should all be initialized to that constant value. Triggering # record processing should succeed, but shouldn't change VAL. record(ai, "ai2") { field(DTYP, "Soft Channel") field(INP, 9) } record(bi, "bi2") { field(DTYP, "Soft Channel") field(INP, 1) field(ZNAM, "Zero") field(ONAM, "One") } record(int64in, "ii2") { field(DTYP, "Soft Channel") field(INP, 9) } record(longin, "li2") { field(DTYP, "Soft Channel") field(INP, 9) } record(mbbiDirect, "di2") { field(DTYP, "Soft Channel") field(NOBT, 4) field(INP, 9) } record(mbbi, "mi2") { field(DTYP, "Soft Channel") field(NOBT, 4) field(INP, 9) field(ZRST, "Zero") field(ONST, "One") field(TWST, "Two") field(THST, "Three") field(FRST, "Four") field(FVST, "Five") field(SXST, "Six") field(SVST, "Seven") field(EIST, "Eight") field(NIST, "Nine") field(TEST, "Ten") field(ELST, "Eleven") field(TWST, "Twelve") field(TTST, "Thirteen") field(FTST, "Fourteen") field(FFST, "Fifteen") } # Group 3 are all soft-channel output records with OUT being a DB or # CA link to the PV 'dest' with PP. Putting a value to the record # under test writes the value to 'dest' and processes it. record(sub, "dest") { field(SNAM, "destSubr") field(VAL, -1) } record(ao, "ao3") { field(DTYP, "Soft Channel") field(OUT, "dest PP") } record(bo, "bo3") { field(DTYP, "Soft Channel") field(OUT, "dest PP") field(ZNAM, "Zero") field(ONAM, "One") } record(int64out, "io3") { field(DTYP, "Soft Channel") field(OUT, "dest PP") } record(longout, "lo3") { field(DTYP, "Soft Channel") field(OUT, "dest PP") } record(mbboDirect, "do3") { field(DTYP, "Soft Channel") field(NOBT, 4) field(OUT, "dest PP") } record(mbbo, "mo3") { field(DTYP, "Soft Channel") field(NOBT, 4) field(OUT, "dest PP") field(ZRST, "Zero") field(ONST, "One") field(TWST, "Two") field(THST, "Three") field(FRST, "Four") field(FVST, "Five") field(SXST, "Six") field(SVST, "Seven") field(EIST, "Eight") field(NIST, "Nine") field(TEST, "Ten") field(ELST, "Eleven") field(TWST, "Twelve") field(TTST, "Thirteen") field(FTST, "Fourteen") field(FFST, "Fifteen") } record(lso, "lso3") { field(DTYP, "Soft Channel") field(OUT, "dest PP") field(SIZV, 40) } record(stringout, "so3") { field(DTYP, "Soft Channel") field(OUT, "dest PP") } record(ao, "ao3c") { field(DTYP, "Soft Channel") field(OUT, "dest CA") } record(bo, "bo3c") { field(DTYP, "Soft Channel") field(OUT, "dest CA") field(ZNAM, "Zero") field(ONAM, "One") } record(int64out, "io3c") { field(DTYP, "Soft Channel") field(OUT, "dest CA") } record(longout, "lo3c") { field(DTYP, "Soft Channel") field(OUT, "dest CA") } record(mbboDirect, "do3c") { field(DTYP, "Soft Channel") field(NOBT, 4) field(OUT, "dest CA") } record(mbbo, "mo3c") { field(DTYP, "Soft Channel") field(NOBT, 4) field(OUT, "dest CA") field(ZRST, "Zero") field(ONST, "One") field(TWST, "Two") field(THST, "Three") field(FRST, "Four") field(FVST, "Five") field(SXST, "Six") field(SVST, "Seven") field(EIST, "Eight") field(NIST, "Nine") field(TEST, "Ten") field(ELST, "Eleven") field(TWST, "Twelve") field(TTST, "Thirteen") field(FTST, "Fourteen") field(FFST, "Fifteen") } record(lso, "lso3c") { field(DTYP, "Soft Channel") field(OUT, "dest CA") field(SIZV, 40) } record(stringout, "so3c") { field(DTYP, "Soft Channel") field(OUT, "dest CA") } # Group 4 are all soft-channel output records with OUT being empty # (i.e. a CONSTANT link). Putting a value to the record must succeed. record(ao, "ao4") { field(DTYP, "Soft Channel") } record(bo, "bo4") { field(DTYP, "Soft Channel") field(ZNAM, "Zero") field(ONAM, "One") } record(int64out, "io4") { field(DTYP, "Soft Channel") } record(longout, "lo4") { field(DTYP, "Soft Channel") } record(mbboDirect, "do4") { field(DTYP, "Soft Channel") field(NOBT, 4) } record(mbbo, "mo4") { field(DTYP, "Soft Channel") field(NOBT, 4) field(ZRST, "Zero") field(ONST, "One") field(TWST, "Two") field(THST, "Three") field(FRST, "Four") field(FVST, "Five") field(SXST, "Six") field(SVST, "Seven") field(EIST, "Eight") field(NIST, "Nine") field(TEST, "Ten") field(ELST, "Eleven") field(TWST, "Twelve") field(TTST, "Thirteen") field(FTST, "Fourteen") field(FFST, "Fifteen") } record(lso, "lso4") { field(DTYP, "Soft Channel") field(SIZV, 40) } record(stringout, "so4") { field(DTYP, "Soft Channel") } base-7.0.3.1/modules/database/test/tools/Base.plt0000664000577000060420000000542413557101274020346 0ustar anjaesctl#!/usr/bin/perl use lib '@TOP@/lib/perl'; use Test::More tests => 127; use DBD::Base; use DBD::Registrar; note "*** Testing DBD::Base class ***"; my $base = DBD::Base->new('test', 'Base class'); isa_ok $base, 'DBD::Base'; is $base->what, 'Base class', 'DBD Object type'; is $base->name, 'test', 'Base class name'; my $base2 = DBD::Base->new('test2', 'Base class'); isa_ok $base, 'DBD::Base'; ok !$base->equals($base2), 'Different names'; my $reg = DBD::Registrar->new('test'); ok !$base->equals($reg), 'Different types'; eval { $base->add_comment('testing'); }; ok $@, 'add_comment died'; { local *STDERR; my $warning = ''; open STDERR, '>', \$warning; $base->add_pod('testing'); like $warning, qr/^Warning:/, 'add_pod warned'; # Also proves that warnContext works } note "*** Testing push/pop contexts ***"; pushContext "a"; pushContext "b"; eval { popContext "b"; }; ok !$@, "pop: Expected context didn't die"; eval { popContext "b"; }; ok $@, "pop: Incorrect context died"; # Also proves that dieContext dies properly note "*** Testing basic RXs ***"; # For help in debugging regex's, wrap tests below inside # use re 'debugcolor'; # ... # no re; like($_, qr/^ $RXident $/x, "Good RXident: $_") foreach qw(a A1 a111 z9 Z9 Z_999); unlike($_, qr/^ $RXident $/x, "Bad RXident: $_") foreach qw(. 1 _ : a. _: 9.0); like($_, qr/^ $RXname $/x, "Good RXname: $_") foreach qw(a A1 a1:x _ -; Z[9] Z<999> a{x}b); unlike($_, qr/^ $RXname $/x, "Bad RXname: $_") foreach qw({x} a{x} {x}b @A 9.0% $x); like($_, qr/^ $RXhex $/x, "Good RXhex: $_") foreach qw(0x0 0XA 0xAf 0x99 0xfedbca987654321 0XDEADBEEF); unlike($_, qr/^ $RXhex $/x, "Bad RXhex: $_") foreach qw(1 x1 0123 0b1010101 -0x12345); like($_, qr/^ $RXoct $/x, "Good RXoct: $_") foreach qw(0 01 07 077 0777 00001 010101 01234567); unlike($_, qr/^ $RXoct $/x, "Bad RXoct: $_") foreach qw(1 08 018 0f 0x777 00009 0b1010101); like($_, qr/^ $RXuint $/x, "Good RXuint: $_") foreach qw(0 01 1 9 999 00001 987654321); unlike($_, qr/^ $RXuint $/x, "Bad RXuint: $_") foreach qw(-1 0x1 -9 0xf 1.0 1e3 -0x9 0b1010101); like($_, qr/^ $RXint $/x, "Good RXint: $_") foreach qw(0 1 9 -09 999 -90909 00001 010101 123456789); unlike($_, qr/^ $RXint $/x, "Bad RXint: $_") foreach qw(0f 0-1 0x777 1.0 1e30 fedcba 0b1010101); like($_, qr/^ $RXnum $/x, "Good RXnum: $_") foreach qw(0 01 0.1 .9 -.9 9.0 -1e2 0.1e+1 .1e1 -.1e1 -1.1E-1 3.1415926535); unlike($_, qr/^ $RXnum $/x, "Bad RXnum: $_") foreach qw(0f 0-1 e1 1.e1 1.x -e2 1e3-0 +1 0b1010101); # All '\' chars must be doubled inside qr() like($_, qr/^ $RXdqs $/x, "Good RXdqs: $_") foreach qw("" "a" "\\"" "\\\\" "\\'" "\\x" "\\\\\\"" "\\"\\\\\\""); unlike($_, qr/^ $RXdqs $/x, "Bad RXdqs: $_") foreach qw(x 'x' "x\\" "x\\"x\\"); base-7.0.3.1/modules/database/test/tools/Breaktable.plt0000664000577000060420000000117013557101274021522 0ustar anjaesctl#!/usr/bin/env perl use lib '@TOP@/lib/perl'; use Test::More tests => 9; use DBD::Breaktable; my $bpt = DBD::Breaktable->new('test'); isa_ok $bpt, 'DBD::Breaktable'; is $bpt->name, 'test', 'Breakpoint table name'; is $bpt->points, 0, 'Points == zero'; $bpt->add_point(0, 0.5); is $bpt->points, 1, 'First point added'; is_deeply $bpt->point(0), [0, 0.5], 'First point correct'; $bpt->add_point(1, 1.5); is $bpt->points, 2, 'Second point added'; is_deeply $bpt->point(0), [0, 0.5], 'First point still correct'; is_deeply $bpt->point(1), [1, 1.5], 'Second point correct'; is_deeply $bpt->point(2), undef, 'Third point undefined'; base-7.0.3.1/modules/database/test/tools/DBD.plt0000664000577000060420000000334713557101274020067 0ustar anjaesctl#!/usr/bin/env perl use lib '@TOP@/lib/perl'; use Test::More tests => 18; use DBD; my $dbd = DBD->new; isa_ok $dbd, 'DBD'; is keys %{$dbd->breaktables}, 0, 'No breaktables yet'; my $brk = DBD::Breaktable->new('Brighton'); $dbd->add($brk); my %brks = %{$dbd->breaktables}; is_deeply \%brks, {Brighton => $brk}, 'Added breaktable'; is keys %{$dbd->drivers}, 0, 'No drivers yet'; my $drv = DBD::Driver->new('Danforth'); $dbd->add($drv); my %drvs = %{$dbd->drivers}; is_deeply \%drvs, {Danforth => $drv}, 'Added driver'; is keys %{$dbd->functions}, 0, 'No functions yet'; my $fnc = DBD::Function->new('Frank'); $dbd->add($fnc); my %fncs = %{$dbd->functions}; is_deeply \%fncs, {Frank => $fnc}, 'Added function'; is keys %{$dbd->menus}, 0, 'No menus yet'; my $menu = DBD::Menu->new('Mango'); $dbd->add($menu); my %menus = %{$dbd->menus}; is_deeply \%menus, {Mango => $menu}, 'Added menu'; is $dbd->menu('Mango'), $menu, 'Named menu'; is keys %{$dbd->recordtypes}, 0, 'No recordtypes yet'; my $rtyp = DBD::Recordtype->new('Rita'); $dbd->add($rtyp); my %rtypes = %{$dbd->recordtypes}; is_deeply \%rtypes, {Rita => $rtyp}, 'Added recordtype'; is $dbd->recordtype('Rita'), $rtyp, 'Named recordtype'; is keys %{$dbd->registrars}, 0, 'No registrars yet'; my $reg = DBD::Registrar->new('Reggie'); $dbd->add($reg); my %regs = %{$dbd->registrars}; is_deeply \%regs, {Reggie => $reg}, 'Added registrar'; is keys %{$dbd->variables}, 0, 'No variables yet'; my $ivar = DBD::Variable->new('IntVar'); my $dvar = DBD::Variable->new('DblVar', 'double'); $dbd->add($ivar); my %vars = %{$dbd->variables}; is_deeply \%vars, {IntVar => $ivar}, 'First variable'; $dbd->add($dvar); %vars = %{$dbd->variables}; is_deeply \%vars, {IntVar => $ivar, DblVar => $dvar}, 'Second variable'; base-7.0.3.1/modules/database/test/tools/Device.plt0000664000577000060420000000162513557101274020672 0ustar anjaesctl#!/usr/bin/env perl use lib '@TOP@/lib/perl'; use Test::More tests => 16; use DBD::Device; my $dev = DBD::Device->new('VME_IO', 'test', 'Device'); isa_ok $dev, 'DBD::Device'; is $dev->name, 'test', 'Device name'; is $dev->link_type, 'VME_IO', 'Link type'; is $dev->choice, 'Device', 'Choice string'; ok $dev->legal_addr('#C0xFEED S123 @xxx'), 'Address legal'; my %dev_addrs = ( CONSTANT => '12345', PV_LINK => 'Any:Record.NAME CPP.MS', VME_IO => '# C1 S2 @Anything', CAMAC_IO => '# B1 C2 N3 A4 F5 @Anything', RF_IO => '# R1 M2 D3 E4', AB_IO => '# L1 A2 C3 S4 @Anything', GPIB_IO => '# L1 A2 @Anything', BITBUS_IO => '# L1 N2 P3 S4 @Anything', BBGPIB_IO => '# L1 B2 G3 @Anything', VXI_IO => '# V1 C2 S3 @Anything', INST_IO => '@Anything' ); while (my ($link, $addr) = each(%dev_addrs)) { $dev->init($link, 'test', 'Device'); ok $dev->legal_addr($addr), "$link address"; } base-7.0.3.1/modules/database/test/tools/Driver.plt0000664000577000060420000000030513557101274020720 0ustar anjaesctl#!/usr/bin/env perl use lib '@TOP@/lib/perl'; use Test::More tests => 2; use DBD::Driver; my $drv = DBD::Driver->new('test'); isa_ok $drv, 'DBD::Driver'; is $drv->name, 'test', 'Driver name'; base-7.0.3.1/modules/database/test/tools/Function.plt0000664000577000060420000000032013557101274021247 0ustar anjaesctl#!/usr/bin/env perl use lib '@TOP@/lib/perl'; use Test::More tests => 2; use DBD::Function; my $func = DBD::Function->new('test'); isa_ok $func, 'DBD::Function'; is $func->name, 'test', 'Function name'; base-7.0.3.1/modules/database/test/tools/Makefile0000664000577000060420000000125713557101274020413 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* TOP = ../../../.. include $(TOP)/configure/CONFIG TESTS += Base TESTS += Breaktable TESTS += DBD TESTS += Device TESTS += Driver TESTS += Function TESTS += Menu TESTS += Recfield TESTS += Recordtype TESTS += Registrar TESTS += Variable TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES base-7.0.3.1/modules/database/test/tools/Menu.plt0000664000577000060420000000240113557101274020370 0ustar anjaesctl#!/usr/bin/env perl use lib '@TOP@/lib/perl'; use Test::More tests => 14; use DBD::Menu; my $menu = DBD::Menu->new('test'); isa_ok $menu, 'DBD::Menu'; is $menu->name, 'test', 'Menu name'; is $menu->choices, 0, 'Choices == zero'; $menu->add_choice('ch1', 'Choice 1'); is $menu->choices, 1, 'First choice added'; ok $menu->legal_choice('Choice 1'), 'First choice legal'; is_deeply $menu->choice(0), ['ch1', 'Choice 1'], 'First choice found'; $menu->add_choice('ch2', 'Choice 2'); is $menu->choices, 2, 'Second choice added'; ok $menu->legal_choice('Choice 1'), 'First choice still legal'; is_deeply $menu->choice(0), ['ch1', 'Choice 1'], 'First choice still found'; ok $menu->legal_choice('Choice 2'), 'Second choice legal'; is_deeply $menu->choice(1), ['ch2', 'Choice 2'], 'Second choice found'; ok !$menu->legal_choice('Choice 3'), 'Third choice not legal'; is_deeply $menu->choice(2), undef, 'Third choice undefined'; like $menu->toDeclaration, qr/ ^ \s* \# \s* ifndef \s+ test_NUM_CHOICES \s* \n \s* typedef \s+ enum \s+ \{ \s* \n \s* ch1 \s+ \/\* [^*]* \*\/, \s* \n \s* ch2 \s+ \/\* [^*]* \*\/ \s* \n \s* \} \s* test \s* ; \s* \n \s* \# \s* define \s+ test_NUM_CHOICES \s+ 2 \s* \n \s* \# \s* endif \s* \n \s* $ /x, 'C declaration'; base-7.0.3.1/modules/database/test/tools/Recfield.plt0000664000577000060420000001161013557101274021203 0ustar anjaesctl#!/usr/bin/env perl use lib '@TOP@/lib/perl'; use Test::More tests => 76; use DBD::Recfield; my $fld_string = DBD::Recfield->new('str', 'DBF_STRING'); isa_ok $fld_string, 'DBD::Recfield'; isa_ok $fld_string, 'DBD::Recfield::DBF_STRING'; $fld_string->set_number(0); is $fld_string->number, 0, 'Field number'; $fld_string->add_attribute("size", "41"); is keys %{$fld_string->attributes}, 1, "Size set"; ok $fld_string->legal_value("Hello, world!"), 'Legal value'; ok !$fld_string->legal_value("x"x41), 'Illegal string'; $fld_string->check_valid; like $fld_string->toDeclaration, qr/^\s*char\s+str\[41\];\s*$/, "C declaration"; my $fld_char = DBD::Recfield->new('chr', 'DBF_CHAR'); isa_ok $fld_char, 'DBD::Recfield'; isa_ok $fld_char, 'DBD::Recfield::DBF_CHAR'; is $fld_char->name, 'chr', 'Field name'; is $fld_char->dbf_type, 'DBF_CHAR', 'Field type'; ok !$fld_char->legal_value("-129"), 'Illegal - value'; ok $fld_char->legal_value("-128"), 'Legal - value'; ok $fld_char->legal_value("127"), 'Legal + value'; ok !$fld_char->legal_value("0x80"), 'Illegal + hex value'; $fld_char->check_valid; like $fld_char->toDeclaration, qr/^\s*epicsInt8\s+chr;\s*$/, "C declaration"; my $fld_uchar = DBD::Recfield->new('uchr', 'DBF_UCHAR'); isa_ok $fld_uchar, 'DBD::Recfield'; isa_ok $fld_uchar, 'DBD::Recfield::DBF_UCHAR'; is $fld_uchar->name, 'uchr', 'Field name'; is $fld_uchar->dbf_type, 'DBF_UCHAR', 'Field type'; ok !$fld_uchar->legal_value("-1"), 'Illegal - value'; ok $fld_uchar->legal_value("0"), 'Legal 0 value'; ok $fld_uchar->legal_value("0377"), 'Legal + value'; ok !$fld_uchar->legal_value("0400"), 'Illegal + octal value'; $fld_uchar->check_valid; like $fld_uchar->toDeclaration, qr/^\s*epicsUInt8\s+uchr;\s*$/, "C declaration"; my $fld_short = DBD::Recfield->new('shrt', 'DBF_SHORT'); isa_ok $fld_short, 'DBD::Recfield'; isa_ok $fld_short, 'DBD::Recfield::DBF_SHORT'; is $fld_short->name, 'shrt', 'Field name'; is $fld_short->dbf_type, 'DBF_SHORT', 'Field type'; ok !$fld_short->legal_value("-32769"), 'Illegal - value'; ok $fld_short->legal_value("-32768"), 'Legal - value'; ok $fld_short->legal_value("32767"), 'Legal + value'; ok !$fld_short->legal_value("0x8000"), 'Illegal + hex value'; $fld_short->check_valid; like $fld_short->toDeclaration, qr/^\s*epicsInt16\s+shrt;\s*$/, "C declaration"; my $fld_ushort = DBD::Recfield->new('ushrt', 'DBF_USHORT'); isa_ok $fld_ushort, 'DBD::Recfield'; isa_ok $fld_ushort, 'DBD::Recfield::DBF_USHORT'; is $fld_ushort->name, 'ushrt', 'Field name'; is $fld_ushort->dbf_type, 'DBF_USHORT', 'Field type'; ok !$fld_ushort->legal_value("-1"), 'Illegal - value'; ok $fld_ushort->legal_value("0"), 'Legal 0 value'; ok $fld_ushort->legal_value("65535"), 'Legal + value'; ok !$fld_ushort->legal_value("0x10000"), 'Illegal + hex value'; $fld_ushort->check_valid; like $fld_ushort->toDeclaration, qr/^\s*epicsUInt16\s+ushrt;\s*$/, "C declaration"; my $fld_long = DBD::Recfield->new('lng', 'DBF_LONG'); isa_ok $fld_long, 'DBD::Recfield'; isa_ok $fld_long, 'DBD::Recfield::DBF_LONG'; is $fld_long->name, 'lng', 'Field name'; is $fld_long->dbf_type, 'DBF_LONG', 'Field type'; ok $fld_long->legal_value("-12345678"), 'Legal - value'; ok $fld_long->legal_value("0x12345678"), 'Legal + value'; ok !$fld_long->legal_value("0xfigure"), 'Illegal value'; $fld_long->check_valid; like $fld_long->toDeclaration, qr/^\s*epicsInt32\s+lng;\s*$/, "C declaration"; my $fld_ulong = DBD::Recfield->new('ulng', 'DBF_ULONG'); isa_ok $fld_ulong, 'DBD::Recfield'; isa_ok $fld_ulong, 'DBD::Recfield::DBF_ULONG'; is $fld_ulong->name, 'ulng', 'Field name'; is $fld_ulong->dbf_type, 'DBF_ULONG', 'Field type'; ok !$fld_ulong->legal_value("-1"), 'Illegal - value'; ok $fld_ulong->legal_value("00"), 'Legal 0 value'; ok $fld_ulong->legal_value("0xffffffff"), 'Legal + value'; ok !$fld_ulong->legal_value("0xfacepaint"), 'Illegal value'; $fld_ulong->check_valid; like $fld_ulong->toDeclaration, qr/^\s*epicsUInt32\s+ulng;\s*$/, "C declaration"; my $fld_float = DBD::Recfield->new('flt', 'DBF_FLOAT'); isa_ok $fld_float, 'DBD::Recfield'; isa_ok $fld_float, 'DBD::Recfield::DBF_FLOAT'; is $fld_float->name, 'flt', 'Field name'; is $fld_float->dbf_type, 'DBF_FLOAT', 'Field type'; ok $fld_float->legal_value("-1.2345678e9"), 'Legal - value'; ok $fld_float->legal_value("0.12345678e9"), 'Legal + value'; ok !$fld_float->legal_value("0x1.5"), 'Illegal value'; $fld_float->check_valid; like $fld_float->toDeclaration, qr/^\s*epicsFloat32\s+flt;\s*$/, "C declaration"; my $fld_double = DBD::Recfield->new('dbl', 'DBF_DOUBLE'); isa_ok $fld_double, 'DBD::Recfield'; isa_ok $fld_double, 'DBD::Recfield::DBF_DOUBLE'; is $fld_double->name, 'dbl', 'Field name'; is $fld_double->dbf_type, 'DBF_DOUBLE', 'Field type'; ok $fld_double->legal_value("-12345e-67"), 'Legal - value'; ok $fld_double->legal_value("12345678e+9"), 'Legal + value'; ok !$fld_double->legal_value("e5"), 'Illegal value'; $fld_double->check_valid; like $fld_double->toDeclaration, qr/^\s*epicsFloat64\s+dbl;\s*$/, "C declaration"; base-7.0.3.1/modules/database/test/tools/Recordtype.plt0000664000577000060420000000365713557101274021622 0ustar anjaesctl#!/usr/bin/env perl use lib '@TOP@/lib/perl'; use Test::More tests => 23; use DBD::Recordtype; use DBD::Recfield; use DBD::Device; my $rtyp = DBD::Recordtype->new('test'); isa_ok $rtyp, 'DBD::Recordtype'; is $rtyp->name, 'test', 'Record name'; is $rtyp->fields, 0, 'No fields yet'; is $rtyp->equals($rtyp), 1, 'A declaration == itself'; my $rt2 = DBD::Recordtype->new('test'); is $rtyp->equals($rt2), 1, 'A declaration == a different declaration'; my $fld1 = DBD::Recfield->new('NAME', 'DBF_STRING'); $fld1->add_attribute("size", "41"); $fld1->check_valid; my $fld2 = DBD::Recfield->new('DTYP', 'DBF_DEVICE'); $fld2->check_valid; $rtyp->add_field($fld1); is $rtyp->fields, 1, 'First field added'; $rtyp->add_field($fld2); is $rtyp->fields, 2, 'Second field added'; is $rtyp->equals($rtyp), 1, 'A definition == itself'; is $rt2->equals($rtyp), 1, 'A declaration == a definition'; $rt2->add_field($fld1); my $fld3 = DBD::Recfield->new('DTYP', 'DBF_DEVICE'); $fld3->check_valid; $rt2->add_field($fld3); is $rt2->equals($rtyp), 1, 'Identical definitions are equal'; $fld3->add_attribute("pp", "TRUE"); is $rt2->equals($rtyp), 0, 'Different definitions are not equal'; my @fields = $rtyp->fields; is_deeply \@fields, [$fld1, $fld2], 'Field list'; my @names = $rtyp->field_names; is_deeply \@names, ['NAME', 'DTYP'], 'Field name list'; is $rtyp->field('NAME'), $fld1, 'Field name lookup'; is $fld1->number, 0, 'Field number 0'; is $fld2->number, 1, 'Field number 1'; is $rtyp->devices, 0, 'No devices yet'; my $dev1 = DBD::Device->new('INST_IO', 'testDset', 'test device'); $rtyp->add_device($dev1); is $rtyp->devices, 1, 'First device added'; my @devices = $rtyp->devices; is_deeply \@devices, [$dev1], 'Device list'; is $rtyp->device('test device'), $dev1, 'Device name lookup'; is $rtyp->cdefs, 0, 'No cdefs yet'; $rtyp->add_cdef("cdef"); is $rtyp->cdefs, 1, 'First cdef added'; my @cdefs = $rtyp->cdefs; is_deeply \@cdefs, ["cdef"], 'cdef list'; base-7.0.3.1/modules/database/test/tools/Registrar.plt0000664000577000060420000000032113557101274021425 0ustar anjaesctl#!/usr/bin/env perl use lib '@TOP@/lib/perl'; use Test::More tests => 2; use DBD::Registrar; my $reg = DBD::Registrar->new('test'); isa_ok $reg, 'DBD::Registrar'; is $reg->name, 'test', 'Registrar name'; base-7.0.3.1/modules/database/test/tools/Variable.plt0000664000577000060420000000055013557101274021214 0ustar anjaesctl#!/usr/bin/env perl use lib '@TOP@/lib/perl'; use Test::More tests => 4; use DBD::Variable; my $ivar = DBD::Variable->new('test'); isa_ok $ivar, 'DBD::Variable'; is $ivar->name, 'test', 'Variable name'; is $ivar->var_type, 'int', 'variable defaults to int'; my $dvar = DBD::Variable->new('test', 'double'); is $dvar->var_type, 'double', 'double variable'; base-7.0.3.1/modules/libcom/Makefile0000664000577000060420000000132413557101274015770 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* TOP = ../.. include $(TOP)/configure/CONFIG DIRS += src DIRS += RTEMS RTEMS_DEPEND_DIRS = src DIRS += vxWorks vxWorks_DEPEND_DIRS = src DIRS += test test_DEPEND_DIRS = RTEMS vxWorks include $(TOP)/configure/RULES_DIRS base-7.0.3.1/modules/libcom/RTEMS/Makefile0000664000577000060420000000242413557101274016664 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* TOP = ../../.. include $(TOP)/configure/CONFIG include $(TOP)/configure/CONFIG_LIBCOM_VERSION PERL_SCRIPTS += epicsMakeMemFs.pl INC += epicsRtemsInitHooks.h INC += epicsMemFs.h ifeq ($(RTEMS_QEMU_FIXUPS),YES) rtems_init_CPPFLAGS += -DQEMU_FIXUPS endif rtemsCom_SRCS += rtems_init.c rtemsCom_SRCS += rtems_config.c rtemsCom_SRCS += rtems_netconfig.c rtemsCom_SRCS += rtems_util.c rtemsCom_SRCS += setBootConfigFromNVRAM.c rtemsCom_SRCS += epicsRtemsInitHookPre.c rtemsCom_SRCS += epicsRtemsInitHookPost.c rtemsCom_SRCS += epicsMemFs.c ifeq ($(RTEMS_BSP),pc386) rtemsCom_SRCS += ne2kpci.c endif LIBRARY_RTEMS = rtemsCom # shared library ABI version. SHRLIB_VERSION = $(EPICS_LIBCOM_MAJOR_VERSION).$(EPICS_LIBCOM_MINOR_VERSION).$(EPICS_LIBCOM_MAINTENANCE_VERSION) include $(TOP)/configure/RULES base-7.0.3.1/modules/libcom/RTEMS/epicsMakeMemFs.pl0000664000577000060420000000274513557101274020420 0ustar anjaesctl#!/usr/bin/env perl # use File::Basename; use Text::Wrap; use strict; my $outfile = shift; my $varname = shift; open(my $DST, '>', $outfile) or die "Failed to open $outfile"; my $inputs = join "\n * ", @ARGV; print $DST < EOF my $N = 0; $Text::Wrap::break = ','; $Text::Wrap::columns = 78; $Text::Wrap::separator = ",\n"; for my $fname (@ARGV) { my $realfname = $fname; # strip leading "../" "./" or "/" $fname =~ s(^\.{0,2}/)()g; my $file = basename($fname); my @dirs = split('/', dirname($fname)); print $DST "/* $realfname */\n", "static const char * const file_${N}_dir[] = {", map("\"$_\", ", @dirs), "NULL};\n", "static const char file_${N}_data[] = {\n", " "; open(my $SRC, '<', $realfname) or die "Failed to open $realfname"; binmode $SRC; my ($buf, @bufs); while (read($SRC, $buf, 4096)) { @bufs[-1] .= ',' if @bufs; # Need ',' between buffers push @bufs, join(",", map(ord, split(//, $buf))); } print $DST wrap('', ' ', @bufs); close $SRC; print $DST < #include #include #include #include #include #include #include "epicsMemFs.h" #ifndef PATH_MAX # define PATH_MAX 100 #endif int epicsMemFsLoad(const epicsMemFS *fs) { char initdir[PATH_MAX]; const epicsMemFile * const *fileptr = fs->files; if(getcwd(initdir, sizeof(initdir)-1)==NULL) { perror("getcwd"); return errno; } initdir[sizeof(initdir)-1] = '\0'; for(;*fileptr; fileptr++) { const epicsMemFile *curfile = *fileptr; int fd; ssize_t ret; size_t sofar; const char * const *dir = curfile->directory; /* jump back to the root each time, * slow but simple. */ if(chdir(initdir)) { perror("chdir"); return errno; } printf("-> /"); /* traverse directory tree, creating as necessary */ for(;*dir; dir++) { int ret; if(**dir=='.') continue; /* ignore '.' and '..' */ printf("%s/", *dir); ret = chdir(*dir); if(ret==-1 && errno==ENOENT) { /* this directory doesn't exist */ if(mkdir(*dir,0744)==-1) { printf("\n"); perror("mkdir"); return errno; } if(chdir(*dir)==-1) { printf("\n"); perror("chdir2"); return errno; } } else if(ret==-1) { printf("\n"); perror("chdir1"); return errno; } } /* no file name creates an empty directory */ if(!curfile->name) { printf("\n"); continue; } printf("%s", curfile->name); /* create or overwrite */ fd = open(curfile->name, O_WRONLY|O_CREAT|O_TRUNC, 0644); if(fd==-1) { printf("\n"); perror("open"); return errno; } sofar = 0; while(sofarsize) { ret = write(fd, curfile->data+sofar, curfile->size-sofar); if(ret<=0) { printf("\n"); perror("write"); return errno; } sofar += ret; } close(fd); printf(" - ok\n"); } if(chdir(initdir)) perror("chdir"); return 0; } base-7.0.3.1/modules/libcom/RTEMS/epicsMemFs.h0000664000577000060420000000142013557101274017423 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef EPICSMEMFS_H #define EPICSMEMFS_H #include typedef struct { const char * const *directory; /* NULL terminated list of directories */ const char *name; /* file name */ const char *data; /* file contents */ size_t size; /* size of file contents in bytes */ } epicsMemFile; typedef struct { const epicsMemFile * const *files; } epicsMemFS; int epicsMemFsLoad(const epicsMemFS *fs); #endif // EPICSMEMFS_H base-7.0.3.1/modules/libcom/RTEMS/epicsRtemsInitHookPost.c0000664000577000060420000000116213557101274022017 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 The University of Chicago, as Operator of Argonne * National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Dummy version -- use if application does not provide its own version */ #include "epicsRtemsInitHooks.h" int epicsRtemsInitPostSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config) { return 0; } base-7.0.3.1/modules/libcom/RTEMS/epicsRtemsInitHookPre.c0000664000577000060420000000116113557101274021617 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 The University of Chicago, as Operator of Argonne * National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Dummy version -- use if application does not provide its own version */ #include "epicsRtemsInitHooks.h" int epicsRtemsInitPreSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config) { return 0; } base-7.0.3.1/modules/libcom/RTEMS/epicsRtemsInitHooks.h0000664000577000060420000000170113557101274021340 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 The University of Chicago, as Operator of Argonne * National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Hooks into RTEMS startup code */ #include #include extern char *env_nfsServer; extern char *env_nfsPath; extern char *env_nfsMountPoint; /* * Return 0 for success, non-zero for failure (will cause panic) */ int epicsRtemsInitPreSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config); int epicsRtemsInitPostSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config); /* Return 0 if local file system was setup, or non-zero (will fall back to network */ int epicsRtemsMountLocalFilesystem(char **argv); base-7.0.3.1/modules/libcom/RTEMS/ne2kpci.c0000664000577000060420000000341513557101274016724 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Wrapper around the ISA ne2000 driver to support detection of the PCI variant. * Can be used with the ne2k_pci device provided by QEMU. * * eg. "qemu-system-i386 ... -net nic,model=ne2k_pci" */ #include #include #include #include #include /* The plain ISA driver attach() * which doesn't (can't?) do any test to see if * the HW is really present */ extern int rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach); int rtems_ne2kpci_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach) { uint8_t irq; uint32_t bar0; int B, D, F, ret; printk("Probing for NE2000 on PCI (aka. Realtek 8029)\n"); if(pci_find_device(PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8029, 0, &B, &D, &F)) { printk("Not found\n"); return 0; } printk("Found %d:%d.%d\n", B, D, F); ret = pci_read_config_dword(B, D, F, PCI_BASE_ADDRESS_0, &bar0); ret|= pci_read_config_byte(B, D, F, PCI_INTERRUPT_LINE, &irq); if(ret || (bar0&PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_IO) { printk("Failed reading card config\n"); return 0; } config->irno = irq; config->port = bar0&PCI_BASE_ADDRESS_IO_MASK; printk("Using port=0x%x irq=%u\n", (unsigned)config->port, config->irno); return rtems_ne_driver_attach(config, attach); } base-7.0.3.1/modules/libcom/RTEMS/rtems_config.c0000664000577000060420000000526113557101274020051 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * RTEMS configuration for EPICS * Author: W. Eric Norum * norume@aps.anl.gov * (630) 252-4793 */ #include /* *********************************************************************** * RTEMS CONFIGURATION * *********************************************************************** */ #define CONFIGURE_RTEMS_INIT_TASKS_TABLE #if __RTEMS_MAJOR__>4 || (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>9) || (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==9 && __RTEMS_REVISION__==99) # define CONFIGURE_UNIFIED_WORK_AREAS #else # define CONFIGURE_EXECUTIVE_RAM_SIZE (2000*1024) #endif #define CONFIGURE_MAXIMUM_TASKS rtems_resource_unlimited(30) #define CONFIGURE_MAXIMUM_BARRIERS rtems_resource_unlimited(30) #define CONFIGURE_MAXIMUM_SEMAPHORES rtems_resource_unlimited(500) #define CONFIGURE_MAXIMUM_TIMERS rtems_resource_unlimited(20) #define CONFIGURE_MAXIMUM_MESSAGE_QUEUES rtems_resource_unlimited(5) #define CONFIGURE_MAXIMUM_USER_EXTENSIONS 1 #define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 150 #define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM #define CONFIGURE_MAXIMUM_DRIVERS 8 #define CONFIGURE_MICROSECONDS_PER_TICK 20000 #define CONFIGURE_INIT_TASK_PRIORITY 80 #define CONFIGURE_MALLOC_STATISTICS 1 #define CONFIGURE_INIT #define CONFIGURE_INIT_TASK_INITIAL_MODES (RTEMS_PREEMPT | \ RTEMS_NO_TIMESLICE | \ RTEMS_NO_ASR | \ RTEMS_INTERRUPT_LEVEL(0)) #define CONFIGURE_INIT_TASK_ATTRIBUTES (RTEMS_FLOATING_POINT | RTEMS_LOCAL) #define CONFIGURE_INIT_TASK_STACK_SIZE (16*1024) rtems_task Init (rtems_task_argument argument); #define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER #define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER #define CONFIGURE_FILESYSTEM_NFS #define CONFIGURE_FILESYSTEM_IMFS /* * This should be made BSP dependent, not CPU dependent but I know of no * appropriate conditionals to use. * The new general time support makes including the RTC driverr less important. */ #if !defined(mpc604) && !defined(__mc68040__) && !defined(__mcf5200__) && !defined(mpc7455) && !defined(__arm__) && !defined(__nios2__)/* don't have RTC code */ #define CONFIGURE_APPLICATION_NEEDS_RTC_DRIVER #endif #include #include base-7.0.3.1/modules/libcom/RTEMS/rtems_init.c0000664000577000060420000005177613557101274017563 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * RTEMS startup task for EPICS * Author: W. Eric Norum * eric.norum@usask.ca * (306) 966-5394 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "epicsVersion.h" #include "epicsThread.h" #include "epicsTime.h" #include "epicsExit.h" #include "envDefs.h" #include "errlog.h" #include "logClient.h" #include "osiUnistd.h" #include "iocsh.h" #include "osdTime.h" #include "epicsMemFs.h" #include "epicsRtemsInitHooks.h" #define RTEMS_VERSION_INT VERSION_INT(__RTEMS_MAJOR__, __RTEMS_MINOR__, 0, 0) /* * Prototypes for some functions not in header files */ void tzset(void); int fileno(FILE *); int main(int argc, char **argv); static void logReset (void) { void rtems_bsp_reset_cause(char *buf, size_t capacity) __attribute__((weak)); void (*fp)(char *buf, size_t capacity) = rtems_bsp_reset_cause; if (fp) { char buf[80]; fp(buf, sizeof buf); errlogPrintf ("Startup after %s.\n", buf); } else { errlogPrintf ("Startup.\n"); } } /* *********************************************************************** * FATAL ERROR REPORTING * *********************************************************************** */ /* * Delay for a while, then terminate */ static void delayedPanic (const char *msg) { extern rtems_interval rtemsTicksPerSecond; rtems_task_wake_after (rtemsTicksPerSecond); rtems_panic (msg); } /* * Log error and terminate */ void LogFatal (const char *msg, ...) { va_list ap; va_start (ap, msg); errlogVprintf (msg, ap); va_end (ap); delayedPanic (msg); } /* * Log RTEMS error and terminate */ void LogRtemsFatal (const char *msg, rtems_status_code sc) { errlogPrintf ("%s: %s\n", msg, rtems_status_text (sc)); delayedPanic (msg); } /* * Log network error and terminate */ void LogNetFatal (const char *msg, int err) { errlogPrintf ("%s: %d\n", msg, err); delayedPanic (msg); } void * mustMalloc(int size, const char *msg) { void *p; if ((p = malloc (size)) == NULL) LogFatal ("Can't allocate space for %s.\n", msg); return p; } /* *********************************************************************** * REMOTE FILE ACCESS * *********************************************************************** */ #ifdef OMIT_NFS_SUPPORT # include #endif const epicsMemFS *epicsRtemsFSImage __attribute__((weak)); const epicsMemFS *epicsRtemsFSImage = (void*)&epicsRtemsFSImage; /* hook to allow app specific FS setup */ int epicsRtemsMountLocalFilesystem(char **argv) __attribute__((weak)); int epicsRtemsMountLocalFilesystem(char **argv) { if(epicsRtemsFSImage==(void*)&epicsRtemsFSImage) return -1; /* no FS image provided. */ else if(epicsRtemsFSImage==NULL) return 0; /* no FS image provided, but none is needed. */ else { printf("***** Using compiled in file data *****\n"); if (epicsMemFsLoad(epicsRtemsFSImage) != 0) { printf("Can't unpack tar filesystem\n"); return -1; } else { argv[1] = "/"; return 0; } } } static int initialize_local_filesystem(char **argv) { extern char _DownloadLocation[] __attribute__((weak)); extern char _FlashBase[] __attribute__((weak)); extern char _FlashSize[] __attribute__((weak)); argv[0] = rtems_bsdnet_bootp_boot_file_name; if (epicsRtemsMountLocalFilesystem(argv)==0) { return 1; /* FS setup successful */ } else if (_FlashSize && (_DownloadLocation || _FlashBase)) { extern char _edata[]; size_t flashIndex = _edata - _DownloadLocation; char *header = _FlashBase + flashIndex; if (memcmp(header + 257, "ustar ", 8) == 0) { int fd; printf ("***** Unpack in-memory file system (IMFS) *****\n"); if (rtems_tarfs_load("/", (unsigned char *)header, (size_t)_FlashSize - flashIndex) != 0) { printf("Can't unpack tar filesystem\n"); return 0; } if ((fd = open(rtems_bsdnet_bootp_cmdline, 0)) >= 0) { close(fd); printf ("***** Found startup script (%s) in IMFS *****\n", rtems_bsdnet_bootp_cmdline); argv[1] = rtems_bsdnet_bootp_cmdline; return 1; } printf ("***** Startup script (%s) not in IMFS *****\n", rtems_bsdnet_bootp_cmdline); } } return 0; } #ifndef OMIT_NFS_SUPPORT #if __RTEMS_MAJOR__>4 || \ (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>9) || \ (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==9 && __RTEMS_REVISION__==99) int nfsMount(char *uidhost, char *path, char *mntpoint) { int devl = strlen(uidhost) + strlen(path) + 2; char *dev; int rval = -1; if ((dev = malloc(devl)) == NULL) { fprintf(stderr,"nfsMount: out of memory\n"); return -1; } sprintf(dev, "%s:%s", uidhost, path); printf("Mount %s on %s\n", dev, mntpoint); if (rtems_mkdir(mntpoint, S_IRWXU | S_IRWXG | S_IRWXO)) printf("Warning -- unable to make directory \"%s\"\n", mntpoint); if (mount(dev, mntpoint, RTEMS_FILESYSTEM_TYPE_NFS, RTEMS_FILESYSTEM_READ_WRITE, NULL)) { perror("mount failed"); } else { rval = 0; } free(dev); return rval; } #define NFS_INIT #else #define NFS_INIT rpcUdpInit(); nfsInit(0,0); #endif #endif static void initialize_remote_filesystem(char **argv, int hasLocalFilesystem) { #ifdef OMIT_NFS_SUPPORT printf ("***** Initializing TFTP *****\n"); #if __RTEMS_MAJOR__>4 || \ (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>9) || \ (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==9 && __RTEMS_REVISION__==99) mount_and_make_target_path(NULL, "/TFTP", RTEMS_FILESYSTEM_TYPE_TFTPFS, RTEMS_FILESYSTEM_READ_WRITE, NULL); #else rtems_bsdnet_initialize_tftp_filesystem (); #endif if (!hasLocalFilesystem) { char *path; int pathsize = 200; int l; path = mustMalloc(pathsize, "Command path name "); strcpy (path, "/TFTP/BOOTP_HOST/epics/"); l = strlen (path); if (gethostname (&path[l], pathsize - l - 10) || (path[l] == '\0')) { LogFatal ("Can't get host name"); } strcat (path, "/st.cmd"); argv[1] = path; } #else char *server_name; char *server_path; char *mount_point; char *cp; int l = 0; printf ("***** Initializing NFS *****\n"); NFS_INIT if (env_nfsServer && env_nfsPath && env_nfsMountPoint) { server_name = env_nfsServer; server_path = env_nfsPath; mount_point = env_nfsMountPoint; cp = mount_point; while ((cp = strchr(cp+1, '/')) != NULL) { *cp = '\0'; if ((mkdir (mount_point, 0755) != 0) && (errno != EEXIST)) LogFatal("Can't create directory \"%s\": %s.\n", mount_point, strerror(errno)); *cp = '/'; } argv[1] = rtems_bsdnet_bootp_cmdline; } else if (hasLocalFilesystem) { return; } else { /* * Use first component of nvram/bootp command line pathname * to set up initial NFS mount. A "/tftpboot/" is prepended * if the pathname does not begin with a '/'. This allows * NFS and TFTP to have a similar view of the remote system. */ if (rtems_bsdnet_bootp_cmdline[0] == '/') cp = rtems_bsdnet_bootp_cmdline + 1; else cp = rtems_bsdnet_bootp_cmdline; cp = strchr(cp, '/'); if ((cp == NULL) || ((l = cp - rtems_bsdnet_bootp_cmdline) == 0)) LogFatal("\"%s\" is not a valid command pathname.\n", rtems_bsdnet_bootp_cmdline); cp = mustMalloc(l + 20, "NFS mount paths"); server_path = cp; server_name = rtems_bsdnet_bootp_server_name; if (rtems_bsdnet_bootp_cmdline[0] == '/') { mount_point = server_path; strncpy(mount_point, rtems_bsdnet_bootp_cmdline, l); mount_point[l] = '\0'; argv[1] = rtems_bsdnet_bootp_cmdline; /* * Its probably common to embed the mount point in the server * name so, when this is occurring, dont clobber the mount point * by appending the first node from the command path. This allows * the mount point to be a different path then the server's mount * path. * * This allows for example a line similar to as follows the DHCP * configuration file. * * server-name "159.233@192.168.0.123:/vol/vol0/bootRTEMS"; */ if ( server_name ) { const size_t allocSize = strlen ( server_name ) + 2; char * const pServerName = mustMalloc( allocSize, "NFS mount paths"); char * const pServerPath = mustMalloc ( allocSize, "NFS mount paths"); const int scanfStatus = sscanf ( server_name, "%[^:] : / %s", pServerName, pServerPath + 1u ); if ( scanfStatus == 2 ) { pServerPath[0u]= '/'; server_name = pServerName; server_path = pServerPath; } else { free ( pServerName ); free ( pServerPath ); } } } else { char *abspath = mustMalloc(strlen(rtems_bsdnet_bootp_cmdline)+2,"Absolute command path"); strcpy(server_path, "/tftpboot/"); mount_point = server_path + strlen(server_path); strncpy(mount_point, rtems_bsdnet_bootp_cmdline, l); mount_point[l] = '\0'; mount_point--; strcpy(abspath, "/"); strcat(abspath, rtems_bsdnet_bootp_cmdline); argv[1] = abspath; } } errlogPrintf("nfsMount(\"%s\", \"%s\", \"%s\")\n", server_name, server_path, mount_point); nfsMount(server_name, server_path, mount_point); #endif } static char rtems_etc_hosts[] = "127.0.0.1 localhost\n"; /* If it doesn't already exist, create /etc/hosts with an entry for 'localhost' */ static void fixup_hosts(void) { FILE *fp; int ret; struct stat STAT; ret=stat("/etc/hosts", &STAT); if(ret==0) { return; /* already exists, assume file */ } else if(errno!=ENOENT) { perror("error: fixup_hosts stat /etc/hosts"); return; } ret = mkdir("/etc", 0775); if(ret!=0 && errno!=EEXIST) { perror("error: fixup_hosts create /etc"); return; } if((fp=fopen("/etc/hosts", "w"))==NULL) { perror("error: fixup_hosts create /etc/hosts"); } if(fwrite(rtems_etc_hosts, 1, sizeof(rtems_etc_hosts)-1, fp)!=sizeof(rtems_etc_hosts)-1) { perror("error: failed to write /etc/hosts"); } fclose(fp); } /* * Get to the startup script directory * The TFTP filesystem requires a trailing '/' on chdir arguments. */ static void set_directory (const char *commandline) { const char *cp; char *directoryPath; int l; cp = strrchr(commandline, '/'); if (cp == NULL) { l = 0; cp = "/"; } else { l = cp - commandline; cp = commandline; } directoryPath = mustMalloc(l + 2, "Command path directory "); strncpy(directoryPath, cp, l); directoryPath[l] = '/'; directoryPath[l+1] = '\0'; if (chdir (directoryPath) < 0) LogFatal ("Can't set initial directory(%s): %s\n", directoryPath, strerror(errno)); else errlogPrintf("chdir(\"%s\")\n", directoryPath); free(directoryPath); } /* *********************************************************************** * RTEMS/EPICS COMMANDS * *********************************************************************** */ /* * RTEMS status */ static void rtems_netstat (unsigned int level) { rtems_bsdnet_show_if_stats (); rtems_bsdnet_show_mbuf_stats (); if (level >= 1) { rtems_bsdnet_show_inet_routes (); } if (level >= 2) { rtems_bsdnet_show_ip_stats (); rtems_bsdnet_show_icmp_stats (); rtems_bsdnet_show_udp_stats (); rtems_bsdnet_show_tcp_stats (); } } static const iocshArg netStatArg0 = { "level",iocshArgInt}; static const iocshArg * const netStatArgs[1] = {&netStatArg0}; static const iocshFuncDef netStatFuncDef = {"netstat",1,netStatArgs}; static void netStatCallFunc(const iocshArgBuf *args) { rtems_netstat(args[0].ival); } static const iocshFuncDef heapSpaceFuncDef = {"heapSpace",0,NULL}; static void heapSpaceCallFunc(const iocshArgBuf *args) { rtems_malloc_statistics_t s; double x; malloc_get_statistics(&s); x = s.space_available - (unsigned long)(s.lifetime_allocated - s.lifetime_freed); if (x >= 1024*1024) printf("Heap space: %.1f MB\n", x / (1024 * 1024)); else printf("Heap space: %.1f kB\n", x / 1024); } #ifndef OMIT_NFS_SUPPORT static const iocshArg nfsMountArg0 = { "[uid.gid@]host",iocshArgString}; static const iocshArg nfsMountArg1 = { "server path",iocshArgString}; static const iocshArg nfsMountArg2 = { "mount point",iocshArgString}; static const iocshArg * const nfsMountArgs[3] = {&nfsMountArg0,&nfsMountArg1, &nfsMountArg2}; static const iocshFuncDef nfsMountFuncDef = {"nfsMount",3,nfsMountArgs}; static void nfsMountCallFunc(const iocshArgBuf *args) { char *cp = args[2].sval; while ((cp = strchr(cp+1, '/')) != NULL) { *cp = '\0'; if ((mkdir (args[2].sval, 0755) != 0) && (errno != EEXIST)) { printf("Can't create directory \"%s\": %s.\n", args[2].sval, strerror(errno)); return; } *cp = '/'; } nfsMount(args[0].sval, args[1].sval, args[2].sval); } #endif void zoneset(const char *zone) { if(zone) setenv("TZ", zone, 1); else unsetenv("TZ"); tzset(); } static const iocshArg zonesetArg0 = {"zone string", iocshArgString}; static const iocshArg * const zonesetArgs[1] = {&zonesetArg0}; static const iocshFuncDef zonesetFuncDef = {"zoneset",1,zonesetArgs}; static void zonesetCallFunc(const iocshArgBuf *args) { zoneset(args[0].sval); } /* * Register RTEMS-specific commands */ static void iocshRegisterRTEMS (void) { iocshRegister(&netStatFuncDef, netStatCallFunc); iocshRegister(&heapSpaceFuncDef, heapSpaceCallFunc); #ifndef OMIT_NFS_SUPPORT iocshRegister(&nfsMountFuncDef, nfsMountCallFunc); #endif iocshRegister(&zonesetFuncDef, &zonesetCallFunc); } /* * Set up the console serial line (no handshaking) */ static void initConsole (void) { struct termios t; if (tcgetattr (fileno (stdin), &t) < 0) { printf ("tcgetattr failed: %s\n", strerror (errno)); return; } t.c_iflag &= ~(IXOFF | IXON | IXANY); if (tcsetattr (fileno (stdin), TCSANOW, &t) < 0) { printf ("tcsetattr failed: %s\n", strerror (errno)); return; } } /* * Ensure that the configuration object files * get pulled in from the library */ extern rtems_configuration_table Configuration; extern struct rtems_bsdnet_config rtems_bsdnet_config; const void *rtemsConfigArray[] = { &Configuration, &rtems_bsdnet_config }; /* * Hook to ensure that BSP cleanup code gets run on exit */ static void exitHandler(void) { rtems_shutdown_executive(0); } /* * RTEMS Startup task */ rtems_task Init (rtems_task_argument ignored) { int result; char *argv[3] = { NULL, NULL, NULL }; char *cp; rtems_task_priority newpri; rtems_status_code sc; rtems_time_of_day now; /* * Explain why we're here */ logReset(); /* * Architecture-specific hooks */ if (epicsRtemsInitPreSetBootConfigFromNVRAM(&rtems_bsdnet_config) != 0) delayedPanic("epicsRtemsInitPreSetBootConfigFromNVRAM"); if (rtems_bsdnet_config.bootp == NULL) { extern void setBootConfigFromNVRAM(void); setBootConfigFromNVRAM(); } if (epicsRtemsInitPostSetBootConfigFromNVRAM(&rtems_bsdnet_config) != 0) delayedPanic("epicsRtemsInitPostSetBootConfigFromNVRAM"); /* * Override RTEMS configuration */ rtems_task_set_priority ( RTEMS_SELF, epicsThreadGetOssPriorityValue(epicsThreadPriorityIocsh), &newpri); /* * Create a reasonable environment */ initConsole (); putenv ("TERM=xterm"); putenv ("IOCSH_HISTSIZE=20"); /* * Display some OS information */ printf("\n***** RTEMS Version: %s *****\n", rtems_get_version_string()); /* * Start network */ if ((cp = getenv("EPICS_TS_NTP_INET")) != NULL) rtems_bsdnet_config.ntp_server[0] = cp; if (rtems_bsdnet_config.network_task_priority == 0) { unsigned int p; if (epicsThreadHighestPriorityLevelBelow(epicsThreadPriorityScanLow, &p) == epicsThreadBooleanStatusSuccess) { rtems_bsdnet_config.network_task_priority = epicsThreadGetOssPriorityValue(p); } } printf("\n***** Initializing network *****\n"); rtems_bsdnet_initialize_network(); printf("\n***** Setting up file system *****\n"); initialize_remote_filesystem(argv, initialize_local_filesystem(argv)); fixup_hosts(); /* * More environment: iocsh prompt and hostname */ { char hostname[1024]; gethostname(hostname, 1023); char *cp = mustMalloc(strlen(hostname)+3, "iocsh prompt"); sprintf(cp, "%s> ", hostname); epicsEnvSet ("IOCSH_PS1", cp); epicsEnvSet("IOC_NAME", hostname); } /* * Use BSP-supplied time of day if available otherwise supply default time. * It is very likely that other time synchronization facilities in EPICS * will soon override this value. */ if (rtems_clock_get(RTEMS_CLOCK_GET_TOD,&now) != RTEMS_SUCCESSFUL) { now.year = 2001; now.month = 1; now.day = 1; now.hour = 0; now.minute = 0; now.second = 0; now.ticks = 0; if ((sc = rtems_clock_set (&now)) != RTEMS_SUCCESSFUL) printf ("***** Can't set time: %s\n", rtems_status_text (sc)); } if (getenv("TZ") == NULL) { const char *tzp = envGetConfigParamPtr(&EPICS_TZ); if (!tzp || *tzp) printf("Warning: No timezone information, times will be displayed in UTC.\n"); else epicsEnvSet("TZ", tzp); } tzset(); osdTimeRegister(); /* * Run the EPICS startup script */ printf ("***** Preparing EPICS application *****\n"); iocshRegisterRTEMS (); set_directory (argv[1]); epicsEnvSet ("IOC_STARTUP_SCRIPT", argv[1]); atexit(exitHandler); errlogFlush(); printf ("***** Starting EPICS application *****\n"); result = main ((sizeof argv / sizeof argv[0]) - 1, argv); printf ("***** IOC application terminating *****\n"); epicsThreadSleep(1.0); epicsExit(result); } #if defined(QEMU_FIXUPS) /* Override some hooks (weak symbols) * if BSP defaults aren't configured for running tests. */ /* Ensure that stdio goes to serial (so it can be captured) */ #if defined(__i386__) && !USE_COM1_AS_CONSOLE #include extern int BSPPrintkPort; void bsp_predriver_hook(void) { BSPConsolePort = BSP_CONSOLE_PORT_COM1; BSPPrintkPort = BSP_CONSOLE_PORT_COM1; } #endif /* reboot immediately when done. */ #if defined(__i386__) && BSP_PRESS_KEY_FOR_RESET void bsp_cleanup(void) { #if RTEMS_VERSION_INT>=VERSION_INT(4,10,0,0) void bsp_reset(); bsp_reset(); #else rtemsReboot(); #endif } #endif #endif /* QEMU_FIXUPS */ int cexpdebug __attribute__((weak)); base-7.0.3.1/modules/libcom/RTEMS/rtems_netconfig.c0000664000577000060420000001077013557101274020561 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * RTEMS network configuration for EPICS * Author: W. Eric Norum * eric.norum@usask.ca * (306) 966-5394 * * This file can be copied to an application source dirctory * and modified to override the values shown below. */ #include #include #include extern void rtems_bsdnet_loopattach(); static struct rtems_bsdnet_ifconfig loopback_config = { "lo0", /* name */ (int (*)(struct rtems_bsdnet_ifconfig *, int))rtems_bsdnet_loopattach, /* attach function */ NULL, /* link to next interface */ "127.0.0.1", /* IP address */ "255.0.0.0", /* IP net mask */ }; /* * The following conditionals select the network interface card. * * On RTEMS-pc386 targets all network drivers which support run-time * probing are linked. * On other targets the network interface specified by the board-support * package is used. * To use a different NIC for a particular application, copy this file to the * application directory and make the appropriate changes. */ #if defined(__i386__) extern int rtems_ne2kpci_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach); static struct rtems_bsdnet_ifconfig ne2k_driver_config = { "ne2", /* name */ rtems_ne2kpci_driver_attach, /* attach function */ &loopback_config, /* link to next interface */ }; extern int rtems_fxp_attach (struct rtems_bsdnet_ifconfig *, int); static struct rtems_bsdnet_ifconfig fxp_driver_config = { "fxp1", /* name */ rtems_fxp_attach, /* attach function */ &ne2k_driver_config, /* link to next interface */ }; extern int rtems_3c509_driver_attach (struct rtems_bsdnet_ifconfig *, int); static struct rtems_bsdnet_ifconfig e3c509_driver_config = { "ep0", /* name */ rtems_3c509_driver_attach, /* attach function */ &fxp_driver_config, /* link to next interface */ }; #define FIRST_DRIVER_CONFIG &e3c509_driver_config #else # if defined(__PPC) /* * FIXME: This really belongs in the BSP */ # ifndef RTEMS_BSP_NETWORK_DRIVER_NAME # define RTEMS_BSP_NETWORK_DRIVER_NAME "dc1" # endif # ifndef RTEMS_BSP_NETWORK_DRIVER_ATTACH # define RTEMS_BSP_NETWORK_DRIVER_ATTACH rtems_dec21140_driver_attach extern int rtems_dec21140_driver_attach(); # endif # endif static struct rtems_bsdnet_ifconfig bsp_driver_config = { RTEMS_BSP_NETWORK_DRIVER_NAME, /* name */ RTEMS_BSP_NETWORK_DRIVER_ATTACH, /* attach function */ &loopback_config, /* link to next interface */ }; #define FIRST_DRIVER_CONFIG &bsp_driver_config #endif /* * Allow configure/os/CONFIG_SITE.Common.RTEMS to provide domain name */ #ifdef RTEMS_NETWORK_CONFIG_DNS_DOMAINNAME # define XSTR(x) STR(x) # define STR(x) #x # define MY_DOMAINNAME XSTR(RTEMS_NETWORK_CONFIG_DNS_DOMAINNAME) #else # define MY_DOMAINNAME NULL #endif /* * Allow non-BOOTP network configuration */ #ifndef MY_DO_BOOTP # define MY_DO_BOOTP rtems_bsdnet_do_bootp #endif /* * Allow site- and BSP-specific network buffer space configuration. * The macro values are specified in KBytes. */ #ifndef RTEMS_NETWORK_CONFIG_MBUF_SPACE # define RTEMS_NETWORK_CONFIG_MBUF_SPACE 180 #endif #ifndef RTEMS_NETWORK_CONFIG_CLUSTER_SPACE # define RTEMS_NETWORK_CONFIG_CLUSTER_SPACE 350 #endif /* * Network configuration */ struct rtems_bsdnet_config rtems_bsdnet_config = { FIRST_DRIVER_CONFIG, /* Link to next interface */ MY_DO_BOOTP, /* How to find network config */ 10, /* If 0 then the network daemons will run at a */ /* priority just less than the lowest-priority */ /* EPICS scan thread. */ /* If non-zero then the network daemons will run */ /* at this *RTEMS* priority */ RTEMS_NETWORK_CONFIG_MBUF_SPACE*1024, RTEMS_NETWORK_CONFIG_CLUSTER_SPACE*1024, NULL, /* Host name */ MY_DOMAINNAME, /* Domain name */ }; base-7.0.3.1/modules/libcom/RTEMS/rtems_util.c0000664000577000060420000000257413557101274017565 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * RTEMS utilitiy routines for EPICS * Author: W. Eric Norum * eric@cls.usask.ca * (306) 966-6055 * * Supplies routines that are present in vxWorks but missing in RTEMS. */ #include #include #include #include #include #include /* * Like connect(), but with an explicit timeout */ int connectWithTimeout (int sfd, struct sockaddr *addr, int addrlen, struct timeval *timeout) { struct timeval sv; socklen_t svlen = sizeof sv; int ret; if (!timeout) return connect (sfd, addr, addrlen); if (getsockopt (sfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&sv, &svlen) < 0) return -1; if (setsockopt (sfd, SOL_SOCKET, SO_RCVTIMEO, (char *)timeout, sizeof *timeout) < 0) return -1; ret = connect (sfd, addr, addrlen); setsockopt (sfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&sv, sizeof sv); return ret; } base-7.0.3.1/modules/libcom/RTEMS/setBootConfigFromNVRAM.c0000664000577000060420000002643513557101274021575 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include char *env_nfsServer; char *env_nfsPath; char *env_nfsMountPoint; /* * Split argument string of form nfs_server:nfs_export: * The nfs_export component will be used as: * - the path to the directory exported from the NFS server * - the local mount point * - a prefix of * For example, the argument string: * romeo:/export/users:smith/ioc/iocexample/st.cmd * would: * - mount /export/users from NFS server romeo on /export/users * - chdir to /export/users/smith/ioc/iocexample * - read commands from st.cmd */ static void splitRtemsBsdnetBootpCmdline(void) { char *cp1, *cp2, *cp3; if ((cp1 = rtems_bsdnet_bootp_cmdline) == NULL) return; if (((cp2 = strchr(cp1, ':')) != NULL) && (((cp3 = strchr(cp2+1, ' ')) != NULL) || ((cp3 = strchr(cp2+1, ':')) != NULL))) { int l1 = cp2 - cp1; int l2 = cp3 - cp2 - 1; int l3 = strlen(cp3) - 1; if (l1 && l2 && l3) { *cp2++ = '\0'; *cp3 = '\0'; env_nfsServer = cp1; env_nfsMountPoint = env_nfsPath = epicsStrDup(cp2); *cp3 = '/'; rtems_bsdnet_bootp_cmdline = cp2; } } } /* * Split NFS mount information of the form nfs_server:host_path:local_path */ static void splitNfsMountPath(char *nfsString) { char *cp2, *cp3; if (nfsString == NULL) return; if (((cp2 = strchr(nfsString, ':')) != NULL) && (((cp3 = strchr(cp2+1, ' ')) != NULL) || ((cp3 = strchr(cp2+1, ':')) != NULL))) { int l1 = cp2 - nfsString; int l2 = cp3 - cp2 - 1; int l3 = strlen(cp3) - 1; if (l1 && l2 && l3) { *cp2++ = '\0'; *cp3++ = '\0'; env_nfsServer = nfsString; env_nfsPath = cp2; env_nfsMountPoint = cp3; } } } #if defined(HAVE_MOTLOAD) /* * Motorola MOTLOAD NVRAM Access */ static char * gev(const char *parm, volatile char *nvp) { const char *val; const char *name; char *ret; char c; for (;;) { if (*nvp == '\0') return NULL; name = parm; while ((c = *nvp++) != '\0') { if ((c == '=') && (*name == '\0')) { val = (char *)nvp; while (*nvp++ != '\0') continue; ret = malloc(nvp - val); if (ret == NULL) return NULL; strcpy(ret, val); return ret; } if (c != *name++) { while (*nvp++ != '\0') continue; break; } } } } static char * motScriptParm(const char *mot_script_boot, char parm) { const char *cp; char *ret; int l; while (*mot_script_boot != '\0') { if (isspace(*(unsigned char *)mot_script_boot) && (*(mot_script_boot+1) == '-') && (*(mot_script_boot+2) == parm)) { mot_script_boot += 3; cp = mot_script_boot; while ((*mot_script_boot != '\0') && !isspace(*(unsigned char *)mot_script_boot)) mot_script_boot++; l = mot_script_boot - cp; ret = malloc(l+1); if (ret == NULL) return NULL; strncpy(ret, cp, l); *(ret+l) = '\0'; return ret; } mot_script_boot++; } return NULL; } void setBootConfigFromNVRAM(void) { char *cp; const char *mot_script_boot; char *nvp; # if defined(BSP_NVRAM_BASE_ADDR) nvp = (volatile unsigned char *)(BSP_NVRAM_BASE_ADDR+0x70f8); # elif defined(BSP_I2C_VPD_EEPROM_DEV_NAME) char gev_buf[3592]; int fd; if ((fd = open(BSP_I2C_VPD_EEPROM_DEV_NAME, 0)) < 0) { printf("Can't open %s: %s\n", BSP_I2C_VPD_EEPROM_DEV_NAME, strerror(errno)); return; } lseek(fd, 0x10f8, SEEK_SET); if (read(fd, gev_buf, sizeof gev_buf) != sizeof gev_buf) { printf("Can't read %s: %s\n", BSP_I2C_VPD_EEPROM_DEV_NAME, strerror(errno)); return; } close(fd); nvp = gev_buf; # else # error "No way to read GEV!" # endif if (rtems_bsdnet_config.bootp != NULL) return; mot_script_boot = gev("mot-script-boot", nvp); if ((rtems_bsdnet_bootp_server_name = gev("mot-/dev/enet0-sipa", nvp)) == NULL) rtems_bsdnet_bootp_server_name = motScriptParm(mot_script_boot, 's'); if ((rtems_bsdnet_config.gateway = gev("mot-/dev/enet0-gipa", nvp)) == NULL) rtems_bsdnet_config.gateway = motScriptParm(mot_script_boot, 'g'); if ((rtems_bsdnet_config.ifconfig->ip_netmask = gev("mot-/dev/enet0-snma", nvp)) == NULL) rtems_bsdnet_config.ifconfig->ip_netmask = motScriptParm(mot_script_boot, 'm'); rtems_bsdnet_config.name_server[0] = gev("rtems-dns-server", nvp); if (rtems_bsdnet_config.name_server[0] == NULL) rtems_bsdnet_config.name_server[0] = rtems_bsdnet_bootp_server_name; cp = gev("rtems-dns-domainname", nvp); if (cp) rtems_bsdnet_config.domainname = cp; if ((rtems_bsdnet_config.ifconfig->ip_address = gev("mot-/dev/enet0-cipa", nvp)) == NULL) rtems_bsdnet_config.ifconfig->ip_address = motScriptParm(mot_script_boot, 'c'); rtems_bsdnet_config.hostname = gev("rtems-client-name", nvp); if (rtems_bsdnet_config.hostname == NULL) rtems_bsdnet_config.hostname = rtems_bsdnet_config.ifconfig->ip_address; if ((rtems_bsdnet_bootp_boot_file_name = gev("mot-/dev/enet0-file", nvp)) == NULL) rtems_bsdnet_bootp_boot_file_name = motScriptParm(mot_script_boot, 'f'); rtems_bsdnet_bootp_cmdline = gev("epics-script", nvp); splitRtemsBsdnetBootpCmdline(); splitNfsMountPath(gev("epics-nfsmount", nvp)); rtems_bsdnet_config.ntp_server[0] = gev("epics-ntpserver", nvp); if (rtems_bsdnet_config.ntp_server[0] == NULL) rtems_bsdnet_config.ntp_server[0] = rtems_bsdnet_bootp_server_name; if ((cp = gev("epics-tz", nvp)) != NULL) epicsEnvSet("TZ", cp); } #elif defined(HAVE_PPCBUG) /* * Motorola PPCBUG NVRAM Access */ struct ppcbug_nvram { uint32_t PacketVersionIdentifier; uint32_t NodeControlMemoryAddress; uint32_t BootFileLoadAddress; uint32_t BootFileExecutionAddress; uint32_t BootFileExecutionDelay; uint32_t BootFileLength; uint32_t BootFileByteOffset; uint32_t TraceBufferAddress; uint32_t ClientIPAddress; uint32_t ServerIPAddress; uint32_t SubnetIPAddressMask; uint32_t BroadcastIPAddressMask; uint32_t GatewayIPAddress; uint8_t BootpRarpRetry; uint8_t TftpRarpRetry; uint8_t BootpRarpControl; uint8_t UpdateControl; char BootFilenameString[64]; char ArgumentFilenameString[64]; }; static char *addr(char *cbuf, uint32_t addr) { struct in_addr a; if ((a.s_addr = addr) == 0) return NULL; return (char *)inet_ntop(AF_INET, &a, cbuf, INET_ADDRSTRLEN); } void setBootConfigFromNVRAM(void) { static struct ppcbug_nvram nvram; static char ip_address[INET_ADDRSTRLEN]; static char ip_netmask[INET_ADDRSTRLEN]; static char server[INET_ADDRSTRLEN]; static char gateway[INET_ADDRSTRLEN]; if (rtems_bsdnet_config.bootp != NULL) return; /* * Get network configuation from PPCBUG. * The 'correct' way to do this would be to issue a .NETCFIG PPCBUG * system call. Unfortunately it is very difficult to issue such a * call once RTEMS is up and running so we just copy from the 'known' * location of the network configuration parameters. * Care must be taken to access the NVRAM a byte at a time. */ #if defined(NVRAM_INDIRECT) { volatile char *addrLo = (volatile char *)0x80000074; volatile char *addrHi = (volatile char *)0x80000075; volatile char *data = (volatile char *)0x80000077; int addr = 0x1000; char *d = (char *)&nvram; while (d < ((char *)&nvram + sizeof nvram)) { *addrLo = addr & 0xFF; *addrHi = (addr >> 8) & 0xFF; *d++ = *data; addr++; } } #else { volatile char *s = (volatile char *)0xFFE81000; char *d = (char *)&nvram; while (d < ((char *)&nvram + sizeof nvram)) *d++ = *s++; } #endif /* * Assume that the boot server is also the name, log and ntp server! */ rtems_bsdnet_config.name_server[0] = rtems_bsdnet_config.ntp_server[0] = rtems_bsdnet_bootp_server_name = addr(server, nvram.ServerIPAddress); rtems_bsdnet_bootp_server_address.s_addr = nvram.ServerIPAddress; /* * Nothing better to use as host name! */ rtems_bsdnet_config.ifconfig->ip_address = rtems_bsdnet_config.hostname = addr(ip_address, nvram.ClientIPAddress); rtems_bsdnet_config.gateway = addr(gateway, nvram.GatewayIPAddress); rtems_bsdnet_config.ifconfig->ip_netmask = addr(ip_netmask, nvram.SubnetIPAddressMask); rtems_bsdnet_bootp_boot_file_name = nvram.BootFilenameString; rtems_bsdnet_bootp_cmdline = nvram.ArgumentFilenameString; splitRtemsBsdnetBootpCmdline(); } #elif defined(__mcf528x__) static char * env(const char *parm, const char *defaultValue) { const char *cp = bsp_getbenv(parm); if (!cp) { if (!defaultValue) return NULL; cp = defaultValue; printf ("%s environment variable missing -- using %s.\n", parm, cp); } return epicsStrDup(cp); } void setBootConfigFromNVRAM(void) { const char *cp1; if (rtems_bsdnet_config.bootp != NULL) return; rtems_bsdnet_config.gateway = env("GATEWAY", NULL); rtems_bsdnet_config.ifconfig->ip_netmask = env("NETMASK", "255.255.252.0"); rtems_bsdnet_bootp_server_name = env("SERVER", "192.168.0.1"); rtems_bsdnet_config.name_server[0] = env("NAMESERVER", rtems_bsdnet_bootp_server_name); rtems_bsdnet_config.ntp_server[0] = env("NTPSERVER", rtems_bsdnet_bootp_server_name); cp1 = env("DOMAIN", NULL); if (cp1 != NULL) rtems_bsdnet_config.domainname = cp1; rtems_bsdnet_config.hostname = env("HOSTNAME", "iocNobody"); rtems_bsdnet_config.ifconfig->ip_address = env("IPADDR0", "192.168.0.2"); rtems_bsdnet_bootp_boot_file_name = env("BOOTFILE", "uC5282App.boot"); rtems_bsdnet_bootp_cmdline = env("CMDLINE", "epics/iocBoot/iocNobody/st.cmd"); splitNfsMountPath(env("NFSMOUNT", NULL)); if ((cp1 = env("TZ", NULL)) != NULL) epicsEnvSet("TZ", cp1); } #else /* * Placeholder for systems without NVRAM */ void setBootConfigFromNVRAM(void) { printf("SYSTEM HAS NO NON-VOLATILE RAM!\n"); printf("YOU MUST USE SOME OTHER METHOD TO OBTAIN NETWORK CONFIGURATION\n"); } #endif base-7.0.3.1/modules/libcom/src/Com.rc0000664000577000060420000000217013557101274016163 0ustar anjaesctl#include #include "epicsVersion.h" VS_VERSION_INFO VERSIONINFO FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_UNKNOWN FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "Comments","Common Library for EPICS\0" VALUE "CompanyName", "The EPICS collaboration\0" VALUE "FileDescription", "Common Library\0" VALUE "FileVersion", EPICS_VERSION_STRING "\0" VALUE "InternalName", "Com\0" VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" VALUE "OriginalFilename", "Com.dll\0" VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" VALUE "ProductVersion", EPICS_VERSION_STRING "\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END base-7.0.3.1/modules/libcom/src/Makefile0000664000577000060420000000514713557101274016566 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* LIBCOM := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) TOP = ../../.. include $(TOP)/configure/CONFIG # Uncomment this to remove the (benign) valgrind helper stubs #USR_CFLAGS += -DNVALGRIND INC += valgrind/valgrind.h INC += libComVersion.h INC += libComVersionNum.h include $(LIBCOM)/as/Makefile include $(LIBCOM)/bucketLib/Makefile include $(LIBCOM)/calc/Makefile include $(LIBCOM)/cvtFast/Makefile include $(LIBCOM)/cppStd/Makefile include $(LIBCOM)/cxxTemplates/Makefile include $(LIBCOM)/dbmf/Makefile include $(LIBCOM)/ellLib/Makefile include $(LIBCOM)/env/Makefile include $(LIBCOM)/error/Makefile include $(LIBCOM)/fdmgr/Makefile include $(LIBCOM)/flex/Makefile include $(LIBCOM)/freeList/Makefile include $(LIBCOM)/gpHash/Makefile include $(LIBCOM)/iocsh/Makefile include $(LIBCOM)/log/Makefile include $(LIBCOM)/macLib/Makefile include $(LIBCOM)/misc/Makefile include $(LIBCOM)/osi/Makefile include $(LIBCOM)/pool/Makefile include $(LIBCOM)/ring/Makefile include $(LIBCOM)/taskwd/Makefile include $(LIBCOM)/timer/Makefile include $(LIBCOM)/yacc/Makefile include $(LIBCOM)/yajl/Makefile # Library to build: LIBRARY=Com Com_SYS_LIBS_WIN32 = ws2_32 advapi32 user32 dbghelp Com_RCS = Com.rc ifeq ($(T_A),$(EPICS_HOST_ARCH)) # Antelope & flex are needed to finish libCom DELAY_INSTALL_LIBS = YES endif EXPANDVARS += EPICS_LIBCOM_MAJOR_VERSION EXPANDVARS += EPICS_LIBCOM_MINOR_VERSION EXPANDVARS += EPICS_LIBCOM_MAINTENANCE_VERSION EXPANDVARS += EPICS_LIBCOM_DEVELOPMENT_FLAG EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))") # shared library ABI version. SHRLIB_VERSION = $(EPICS_LIBCOM_MAJOR_VERSION).$(EPICS_LIBCOM_MINOR_VERSION).$(EPICS_LIBCOM_MAINTENANCE_VERSION) include $(TOP)/configure/RULES include $(LIBCOM)/as/RULES include $(LIBCOM)/env/RULES include $(LIBCOM)/error/RULES include $(LIBCOM)/flex/RULES include $(LIBCOM)/misc/RULES include $(LIBCOM)/osi/RULES include $(LIBCOM)/yajl/RULES # Can't use EXPAND as generated headers must appear # in O.Common, but EXPAND emits rules for O.$(T_A) ../O.Common/libComVersionNum.h: ../libComVersionNum.h@ $(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@ base-7.0.3.1/modules/libcom/src/as/Makefile0000664000577000060420000000142713557101274017166 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 Brookhaven Science Associates, as Operator of # Brookhaven National Lab. # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************ # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/as INC += asLib.h INC += asTrapWrite.h Com_SRCS += asLib.c Com_SRCS += asTrapWrite.c CLEANS += asLib.c asLib_lex.c base-7.0.3.1/modules/libcom/src/as/RULES0000664000577000060420000000170213557101274016337 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 Brookhaven Science Associates, as Operator of # Brookhaven National Lab. # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************ # This is a Makefile fragment, see src/libCom/Makefile. # Extra rule since asLib_lex.c is included by asLib.c asLib$(DEP): asLib_lex.c asLib.c: asLib_lex.c # Ensure that lexer and parser are built before they are needed asLib.c: $(INSTALL_HOST_BIN)/antelope$(HOSTEXE) asLib_lex.c: $(INSTALL_HOST_BIN)/e_flex$(HOSTEXE) asLib_lex.c: $(INSTALL_INCLUDE)/flex.skel.static base-7.0.3.1/modules/libcom/src/as/asLib.h0000664000577000060420000002141313557101274016726 0ustar anjaesctl/* asLib.h */ /*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Marty Kraimer Date: 09-27-93*/ #ifndef INCasLibh #define INCasLibh #include "shareLib.h" #include "ellLib.h" #include "errMdef.h" #include "errlog.h" #ifdef __cplusplus extern "C" { #endif /* 0 - Use (unverified) client provided host name string. * 1 - Use actual client IP address. HAG() are resolved to IPs at ACF load time. */ epicsShareExtern int asCheckClientIP; typedef struct asgMember *ASMEMBERPVT; typedef struct asgClient *ASCLIENTPVT; typedef int (*ASINPUTFUNCPTR)(char *buf,int max_size); typedef enum{ asClientCOAR /*Change of access rights*/ /*For now this is all*/ } asClientStatus; typedef void (*ASCLIENTCALLBACK) (ASCLIENTPVT,asClientStatus); /* The following routines are macros with the following syntax long asCheckGet(ASCLIENTPVT asClientPvt); long asCheckPut(ASCLIENTPVT asClientPvt); */ #define asCheckGet(asClientPvt) \ (!asActive || ((asClientPvt)->access >= asREAD)) #define asCheckPut(asClientPvt) \ (!asActive || ((asClientPvt)->access >= asWRITE)) /* More convenience macros void *asTrapWriteWithData(ASCLIENTPVT asClientPvt, const char *userid, const char *hostid, void *addr, int dbrType, int no_elements, void *data); void asTrapWriteAfter(ASCLIENTPVT asClientPvt); */ #define asTrapWriteWithData(asClientPvt, user, host, addr, type, count, data) \ ((asActive && (asClientPvt)->trapMask) \ ? asTrapWriteBeforeWithData((user), (host), (addr), (type), (count), (data)) \ : 0) #define asTrapWriteAfter(pvt) \ if (pvt) asTrapWriteAfterWrite(pvt) /* This macro is for backwards compatibility, upgrade any code calling it to use asTrapWriteWithData() instead ASAP: void *asTrapWriteBefore(ASCLIENTPVT asClientPvt, const char *userid, const char *hostid, void *addr); */ #define asTrapWriteBefore(asClientPvt, user, host, addr) \ asTrapWriteWithData(asClientPvt, user, host, addr, 0, 0, NULL) epicsShareFunc long epicsShareAPI asInitialize(ASINPUTFUNCPTR inputfunction); epicsShareFunc long epicsShareAPI asInitFile( const char *filename,const char *substitutions); epicsShareFunc long epicsShareAPI asInitFP(FILE *fp,const char *substitutions); epicsShareFunc long epicsShareAPI asInitMem(const char *acf, const char *substitutions); /*caller must provide permanent storage for asgName*/ epicsShareFunc long epicsShareAPI asAddMember( ASMEMBERPVT *asMemberPvt,const char *asgName); epicsShareFunc long epicsShareAPI asRemoveMember(ASMEMBERPVT *asMemberPvt); /*caller must provide permanent storage for newAsgName*/ epicsShareFunc long epicsShareAPI asChangeGroup( ASMEMBERPVT *asMemberPvt,const char *newAsgName); epicsShareFunc void * epicsShareAPI asGetMemberPvt(ASMEMBERPVT asMemberPvt); epicsShareFunc void epicsShareAPI asPutMemberPvt( ASMEMBERPVT asMemberPvt,void *userPvt); /*client must provide permanent storage for user and host*/ epicsShareFunc long epicsShareAPI asAddClient( ASCLIENTPVT *asClientPvt,ASMEMBERPVT asMemberPvt, int asl,const char *user,char *host); /*client must provide permanent storage for user and host*/ epicsShareFunc long epicsShareAPI asChangeClient( ASCLIENTPVT asClientPvt,int asl,const char *user,char *host); epicsShareFunc long epicsShareAPI asRemoveClient(ASCLIENTPVT *asClientPvt); epicsShareFunc void * epicsShareAPI asGetClientPvt(ASCLIENTPVT asClientPvt); epicsShareFunc void epicsShareAPI asPutClientPvt( ASCLIENTPVT asClientPvt,void *userPvt); epicsShareFunc long epicsShareAPI asRegisterClientCallback( ASCLIENTPVT asClientPvt, ASCLIENTCALLBACK pcallback); epicsShareFunc long epicsShareAPI asComputeAllAsg(void); /* following declared below after ASG is declared epicsShareFunc long epicsShareAPI asComputeAsg(ASG *pasg); */ epicsShareFunc long epicsShareAPI asCompute(ASCLIENTPVT asClientPvt); epicsShareFunc int epicsShareAPI asDump( void (*memcallback)(ASMEMBERPVT,FILE *), void (*clientcallback)(ASCLIENTPVT,FILE *),int verbose); epicsShareFunc int epicsShareAPI asDumpFP(FILE *fp, void (*memcallback)(ASMEMBERPVT,FILE *), void (*clientcallback)(ASCLIENTPVT,FILE *),int verbose); epicsShareFunc int epicsShareAPI asDumpUag(const char *uagname); epicsShareFunc int epicsShareAPI asDumpUagFP(FILE *fp,const char *uagname); epicsShareFunc int epicsShareAPI asDumpHag(const char *hagname); epicsShareFunc int epicsShareAPI asDumpHagFP(FILE *fp,const char *hagname); epicsShareFunc int epicsShareAPI asDumpRules(const char *asgname); epicsShareFunc int epicsShareAPI asDumpRulesFP(FILE *fp,const char *asgname); epicsShareFunc int epicsShareAPI asDumpMem(const char *asgname, void (*memcallback)(ASMEMBERPVT,FILE *),int clients); epicsShareFunc int epicsShareAPI asDumpMemFP(FILE *fp,const char *asgname, void (*memcallback)(ASMEMBERPVT,FILE *),int clients); epicsShareFunc int epicsShareAPI asDumpHash(void); epicsShareFunc int epicsShareAPI asDumpHashFP(FILE *fp); epicsShareFunc void * epicsShareAPI asTrapWriteBeforeWithData( const char *userid, const char *hostid, void *addr, int dbrType, int no_elements, void *data); epicsShareFunc void epicsShareAPI asTrapWriteAfterWrite(void *pvt); #define S_asLib_clientsExist (M_asLib| 1) /*Client Exists*/ #define S_asLib_noUag (M_asLib| 2) /*User Access Group does not exist*/ #define S_asLib_noHag (M_asLib| 3) /*Host Access Group does not exist*/ #define S_asLib_noAccess (M_asLib| 4) /*access security: no access allowed*/ #define S_asLib_noModify (M_asLib| 5) /*access security: no modification allowed*/ #define S_asLib_badConfig (M_asLib| 6) /*access security: bad configuration file*/ #define S_asLib_badCalc (M_asLib| 7) /*access security: bad calculation espression*/ #define S_asLib_dupAsg (M_asLib| 8) /*Duplicate Access Security Group */ #define S_asLib_InitFailed (M_asLib| 9) /*access security: Init failed*/ #define S_asLib_asNotActive (M_asLib|10) /*access security is not active*/ #define S_asLib_badMember (M_asLib|11) /*access security: bad ASMEMBERPVT*/ #define S_asLib_badClient (M_asLib|12) /*access security: bad ASCLIENTPVT*/ #define S_asLib_badAsg (M_asLib|13) /*access security: bad ASG*/ #define S_asLib_noMemory (M_asLib|14) /*access security: no Memory */ /*Private declarations */ epicsShareExtern int asActive; /* definition of access rights*/ typedef enum{asNOACCESS,asREAD,asWRITE} asAccessRights; struct gphPvt; /*Base pointers for access security*/ typedef struct asBase{ ELLLIST uagList; ELLLIST hagList; ELLLIST asgList; struct gphPvt *phash; } ASBASE; epicsShareExtern volatile ASBASE *pasbase; /*Defs for User Access Groups*/ typedef struct{ ELLNODE node; char *user; } UAGNAME; typedef struct uag{ ELLNODE node; char *name; ELLLIST list; /*list of UAGNAME*/ } UAG; /*Defs for Host Access Groups*/ typedef struct{ ELLNODE node; char host[1]; } HAGNAME; typedef struct hag{ ELLNODE node; char *name; ELLLIST list; /*list of HAGNAME*/ } HAG; /*Defs for Access SecurityGroups*/ typedef struct { ELLNODE node; UAG *puag; }ASGUAG; typedef struct { ELLNODE node; HAG *phag; }ASGHAG; #define AS_TRAP_WRITE 1 typedef struct{ ELLNODE node; asAccessRights access; int level; unsigned long inpUsed; /*bitmap of which inputs are used*/ int result; /*Result of calc converted to TRUE/FALSE*/ char *calc; void *rpcl; ELLLIST uagList; /*List of ASGUAG*/ ELLLIST hagList; /*List of ASGHAG*/ int trapMask; } ASGRULE; typedef struct{ ELLNODE node; char *inp; void *capvt; struct asg *pasg; int inpIndex; }ASGINP; typedef struct asg{ ELLNODE node; char *name; ELLLIST inpList; ELLLIST ruleList; ELLLIST memberList; double *pavalue; /*pointer to array of input values*/ unsigned long inpBad; /*bitmap of which inputs are bad*/ unsigned long inpChanged; /*bitmap of inputs that changed*/ } ASG; typedef struct asgMember { ELLNODE node; ASG *pasg; ELLLIST clientList; const char *asgName; void *userPvt; } ASGMEMBER; typedef struct asgClient { ELLNODE node; ASGMEMBER *pasgMember; const char *user; char *host; void *userPvt; ASCLIENTCALLBACK pcallback; int level; asAccessRights access; int trapMask; } ASGCLIENT; epicsShareFunc long epicsShareAPI asComputeAsg(ASG *pasg); /*following is "friend" function*/ epicsShareFunc void * epicsShareAPI asCalloc(size_t nobj,size_t size); epicsShareFunc char * epicsShareAPI asStrdup(unsigned char *str); epicsShareFunc void asFreeAll(ASBASE *pasbase); #ifdef __cplusplus } #endif #endif /*INCasLibh*/ base-7.0.3.1/modules/libcom/src/as/asLib.y0000664000577000060420000001064213557101274016751 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ %{ static int yyerror(char *); static int yy_start; #include "asLibRoutines.c" static int yyFailed = FALSE; static int line_num=1; static UAG *yyUag=NULL; static HAG *yyHag=NULL; static ASG *yyAsg=NULL; static ASGRULE *yyAsgRule=NULL; %} %start asconfig %token tokenUAG tokenHAG tokenASG tokenRULE tokenCALC %token tokenINP %token tokenINTEGER %token tokenSTRING %union { int Int; char *Str; } %% asconfig: asconfig asconfig_item | asconfig_item asconfig_item: tokenUAG uag_head uag_body | tokenUAG uag_head | tokenHAG hag_head hag_body | tokenHAG hag_head | tokenASG asg_head asg_body | tokenASG asg_head ; uag_head: '(' tokenSTRING ')' { yyUag = asUagAdd($2); if(!yyUag) yyerror(""); free((void *)$2); } ; uag_body: '{' uag_user_list '}' { ; } ; uag_user_list: uag_user_list ',' uag_user_list_name | uag_user_list_name ; uag_user_list_name: tokenSTRING { if (asUagAddUser(yyUag,$1)) yyerror(""); free((void *)$1); } ; hag_head: '(' tokenSTRING ')' { yyHag = asHagAdd($2); if(!yyHag) yyerror(""); free((void *)$2); } ; hag_body: '{' hag_host_list '}' ; hag_host_list: hag_host_list ',' hag_host_list_name | hag_host_list_name ; hag_host_list_name: tokenSTRING { if (asHagAddHost(yyHag,$1)) yyerror(""); free((void *)$1); } ; asg_head: '(' tokenSTRING ')' { yyAsg = asAsgAdd($2); if(!yyAsg) yyerror(""); free((void *)$2); } ; asg_body: '{' asg_body_list '}' { } asg_body_list: asg_body_list asg_body_item | asg_body_item asg_body_item: inp_config | rule_config ; inp_config: tokenINP '(' tokenSTRING ')' { if (asAsgAddInp(yyAsg,$3,$1)) yyerror(""); free((void *)$3); } ; rule_config: tokenRULE rule_head rule_body | tokenRULE rule_head rule_head: rule_head_manditory rule_head_options rule_head_manditory: '(' tokenINTEGER ',' tokenSTRING { asAccessRights rights; if((strcmp($4,"NONE")==0)) { rights=asNOACCESS; } else if((strcmp($4,"READ")==0)) { rights=asREAD; } else if((strcmp($4,"WRITE")==0)) { rights=asWRITE; } else { yyerror("Access rights must be NONE, READ or WRITE"); rights = asNOACCESS; } yyAsgRule = asAsgAddRule(yyAsg,rights,$2); free((void *)$4); } ; rule_head_options: ')' | rule_log_options rule_log_options: ',' tokenSTRING ')' { if((strcmp($2,"TRAPWRITE")==0)) { long status; status = asAsgAddRuleOptions(yyAsgRule,AS_TRAP_WRITE); if(status) yyerror(""); } else if((strcmp($2,"NOTRAPWRITE")!=0)) { yyerror("Log options must be TRAPWRITE or NOTRAPWRITE"); } free((void *)$2); } ; rule_body: '{' rule_list '}' ; rule_list: rule_list rule_list_item | rule_list_item ; rule_list_item: tokenUAG '(' rule_uag_list ')' | tokenHAG '(' rule_hag_list ')' | tokenCALC '(' tokenSTRING ')' { if (asAsgRuleCalc(yyAsgRule,$3)) yyerror(""); free((void *)$3); } ; rule_uag_list: rule_uag_list ',' rule_uag_list_name | rule_uag_list_name ; rule_uag_list_name: tokenSTRING { if (asAsgRuleUagAdd(yyAsgRule,$1)) yyerror(""); free((void *)$1); } ; rule_hag_list: rule_hag_list ',' rule_hag_list_name | rule_hag_list_name ; rule_hag_list_name: tokenSTRING { if (asAsgRuleHagAdd(yyAsgRule,$1)) yyerror(""); free((void *)$1); } ; %% #include "asLib_lex.c" static int yyerror(char *str) { if (strlen(str)) errlogPrintf("%s at line %d\n", str, line_num); else errlogPrintf("Error at line %d\n", line_num); yyFailed = TRUE; return 0; } static int myParse(ASINPUTFUNCPTR inputfunction) { static int FirstFlag = 1; int rtnval; my_yyinput = &inputfunction; if (!FirstFlag) { line_num=1; yyFailed = FALSE; yyreset(); yyrestart(NULL); } FirstFlag = 0; rtnval = yyparse(); if(rtnval!=0 || yyFailed) return(-1); else return(0); } base-7.0.3.1/modules/libcom/src/as/asLibRoutines.c0000664000577000060420000011337213557101274020460 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Marty Kraimer Date: 10-15-93 */ #include #include #include #include #include #define epicsExportSharedSymbols #include "osiSock.h" #include "epicsTypes.h" #include "epicsStdio.h" #include "dbDefs.h" #include "epicsThread.h" #include "cantProceed.h" #include "epicsMutex.h" #include "errlog.h" #include "gpHash.h" #include "freeList.h" #include "macLib.h" #include "postfix.h" #include "asLib.h" int asCheckClientIP; static epicsMutexId asLock; #define LOCK epicsMutexMustLock(asLock) #define UNLOCK epicsMutexUnlock(asLock) /*following must be global because asCa nneeds it*/ epicsShareDef ASBASE volatile *pasbase=NULL; static ASBASE *pasbasenew=NULL; epicsShareDef int asActive = FALSE; static void *freeListPvt = NULL; #define DEFAULT "DEFAULT" /* Defined in asLib.y */ static int myParse(ASINPUTFUNCPTR inputfunction); /*private routines */ static long asAddMemberPvt(ASMEMBERPVT *pasMemberPvt,const char *asgName); static long asComputeAllAsgPvt(void); static long asComputeAsgPvt(ASG *pasg); static long asComputePvt(ASCLIENTPVT asClientPvt); static UAG *asUagAdd(const char *uagName); static long asUagAddUser(UAG *puag,const char *user); static HAG *asHagAdd(const char *hagName); static long asHagAddHost(HAG *phag,const char *host); static ASG *asAsgAdd(const char *asgName); static long asAsgAddInp(ASG *pasg,const char *inp,int inpIndex); static ASGRULE *asAsgAddRule(ASG *pasg,asAccessRights access,int level); static long asAsgAddRuleOptions(ASGRULE *pasgrule,int trapMask); static long asAsgRuleUagAdd(ASGRULE *pasgrule,const char *name); static long asAsgRuleHagAdd(ASGRULE *pasgrule,const char *name); static long asAsgRuleCalc(ASGRULE *pasgrule,const char *calc); /* asInitialize can be called while access security is already active. This is accomplished by doing the following: The version pointed to by pasbase is kept as is but locked against changes A new version is created and pointed to by pasbasenew If anything goes wrong. The original version is kept. This results is some wasted space but at least things still work. If the new access security configuration is successfully read then: the old memberList is moved from old to new. the old structures are freed. */ static void asInitializeOnce(void *arg) { osiSockAttach(); asLock = epicsMutexMustCreate(); } long epicsShareAPI asInitialize(ASINPUTFUNCPTR inputfunction) { ASG *pasg; long status; ASBASE *pasbaseold; GPHENTRY *pgphentry; UAG *puag; UAGNAME *puagname; HAG *phag; HAGNAME *phagname; static epicsThreadOnceId asInitializeOnceFlag = EPICS_THREAD_ONCE_INIT; epicsThreadOnce(&asInitializeOnceFlag,asInitializeOnce,(void *)0); LOCK; pasbasenew = asCalloc(1,sizeof(ASBASE)); if(!freeListPvt) freeListInitPvt(&freeListPvt,sizeof(ASGCLIENT),20); ellInit(&pasbasenew->uagList); ellInit(&pasbasenew->hagList); ellInit(&pasbasenew->asgList); asAsgAdd(DEFAULT); status = myParse(inputfunction); if(status) { status = S_asLib_badConfig; /*Not safe to call asFreeAll */ UNLOCK; return(status); } pasg = (ASG *)ellFirst(&pasbasenew->asgList); while(pasg) { pasg->pavalue = asCalloc(CALCPERFORM_NARGS, sizeof(double)); pasg = (ASG *)ellNext(&pasg->node); } gphInitPvt(&pasbasenew->phash, 256); /*Hash each uagname and each hagname*/ puag = (UAG *)ellFirst(&pasbasenew->uagList); while(puag) { puagname = (UAGNAME *)ellFirst(&puag->list); while(puagname) { pgphentry = gphAdd(pasbasenew->phash,puagname->user,puag); if(!pgphentry) { errlogPrintf("Duplicated user '%s' in UAG '%s'\n", puagname->user, puag->name); } puagname = (UAGNAME *)ellNext(&puagname->node); } puag = (UAG *)ellNext(&puag->node); } phag = (HAG *)ellFirst(&pasbasenew->hagList); while(phag) { phagname = (HAGNAME *)ellFirst(&phag->list); while(phagname) { pgphentry = gphAdd(pasbasenew->phash,phagname->host,phag); if(!pgphentry) { errlogPrintf("Duplicated host '%s' in HAG '%s'\n", phagname->host, phag->name); } phagname = (HAGNAME *)ellNext(&phagname->node); } phag = (HAG *)ellNext(&phag->node); } pasbaseold = (ASBASE *)pasbase; pasbase = (ASBASE volatile *)pasbasenew; if(pasbaseold) { ASG *poldasg; ASGMEMBER *poldmem; ASGMEMBER *pnextoldmem; poldasg = (ASG *)ellFirst(&pasbaseold->asgList); while(poldasg) { poldmem = (ASGMEMBER *)ellFirst(&poldasg->memberList); while(poldmem) { pnextoldmem = (ASGMEMBER *)ellNext(&poldmem->node); ellDelete(&poldasg->memberList,&poldmem->node); status = asAddMemberPvt(&poldmem,poldmem->asgName); poldmem = pnextoldmem; } poldasg = (ASG *)ellNext(&poldasg->node); } asFreeAll(pasbaseold); } asActive = TRUE; UNLOCK; return(0); } long epicsShareAPI asInitFile(const char *filename,const char *substitutions) { FILE *fp; long status; fp = fopen(filename,"r"); if(!fp) { errlogPrintf("asInitFile: Can't open file '%s'\n", filename); return(S_asLib_badConfig); } status = asInitFP(fp,substitutions); if(fclose(fp)==EOF) { errMessage(0,"asInitFile: fclose failed!"); if(!status) status = S_asLib_badConfig; } return(status); } #define BUF_SIZE 200 static char *my_buffer; static char *my_buffer_ptr; static FILE *stream; static char *mac_input_buffer=NULL; static MAC_HANDLE *macHandle = NULL; static int myInputFunction(char *buf, int max_size) { int l,n; char *fgetsRtn; if(*my_buffer_ptr==0) { if(macHandle) { fgetsRtn = fgets(mac_input_buffer,BUF_SIZE,stream); if(fgetsRtn) { n = macExpandString(macHandle,mac_input_buffer, my_buffer,BUF_SIZE); if(n<0) { errlogPrintf("access security: macExpandString failed\n" "input line: %s\n",mac_input_buffer); return(0); } } } else { fgetsRtn = fgets(my_buffer,BUF_SIZE,stream); } if(fgetsRtn==NULL) return(0); my_buffer_ptr = my_buffer; } l = strlen(my_buffer_ptr); n = (l<=max_size ? l : max_size); memcpy(buf,my_buffer_ptr,n); my_buffer_ptr += n; return(n); } long epicsShareAPI asInitFP(FILE *fp,const char *substitutions) { char buffer[BUF_SIZE]; char mac_buffer[BUF_SIZE]; long status; char **macPairs; buffer[0] = 0; my_buffer = buffer; my_buffer_ptr = my_buffer; stream = fp; if(substitutions) { if((status = macCreateHandle(&macHandle,NULL))) { errMessage(status,"asInitFP: macCreateHandle error"); return(status); } macParseDefns(macHandle,substitutions,&macPairs); if(macPairs ==NULL) { macDeleteHandle(macHandle); macHandle = NULL; } else { macInstallMacros(macHandle,macPairs); free(macPairs); mac_input_buffer = mac_buffer; } } status = asInitialize(myInputFunction); if(macHandle) { macDeleteHandle(macHandle); macHandle = NULL; } return(status); } static const char* membuf; static int memInputFunction(char *buf, int max_size) { int ret = 0; if(!membuf) return ret; while(max_size && *membuf) { *buf++ = *membuf++; max_size--; ret++; } return ret; } long epicsShareAPI asInitMem(const char *acf, const char *substitutions) { long ret = S_asLib_InitFailed; if(!acf) return ret; membuf = acf; ret = asInitialize(&memInputFunction); membuf = NULL; return ret; } long epicsShareAPI asAddMember(ASMEMBERPVT *pasMemberPvt,const char *asgName) { long status; if(!asActive) return(S_asLib_asNotActive); LOCK; status = asAddMemberPvt(pasMemberPvt,asgName); UNLOCK; return(status); } long epicsShareAPI asRemoveMember(ASMEMBERPVT *asMemberPvt) { ASGMEMBER *pasgmember; if(!asActive) return(S_asLib_asNotActive); pasgmember = *asMemberPvt; if(!pasgmember) return(S_asLib_badMember); LOCK; if (ellCount(&pasgmember->clientList) > 0) { UNLOCK; return(S_asLib_clientsExist); } if(pasgmember->pasg) { ellDelete(&pasgmember->pasg->memberList,&pasgmember->node); } else { errMessage(-1,"Logic error in asRemoveMember"); UNLOCK; return(-1); } free(pasgmember); *asMemberPvt = NULL; UNLOCK; return(0); } long epicsShareAPI asChangeGroup(ASMEMBERPVT *asMemberPvt,const char *newAsgName) { ASGMEMBER *pasgmember; long status; if(!asActive) return(S_asLib_asNotActive); pasgmember = *asMemberPvt; if(!pasgmember) return(S_asLib_badMember); LOCK; if(pasgmember->pasg) { ellDelete(&pasgmember->pasg->memberList,&pasgmember->node); } else { errMessage(-1,"Logic error in asChangeGroup"); UNLOCK; return(-1); } status = asAddMemberPvt(asMemberPvt,newAsgName); UNLOCK; return(status); } void * epicsShareAPI asGetMemberPvt(ASMEMBERPVT asMemberPvt) { ASGMEMBER *pasgmember = asMemberPvt; if(!asActive) return(NULL); if(!pasgmember) return(NULL); return(pasgmember->userPvt); } void epicsShareAPI asPutMemberPvt(ASMEMBERPVT asMemberPvt,void *userPvt) { ASGMEMBER *pasgmember = asMemberPvt; if(!asActive) return; if(!pasgmember) return; pasgmember->userPvt = userPvt; return; } long epicsShareAPI asAddClient(ASCLIENTPVT *pasClientPvt,ASMEMBERPVT asMemberPvt, int asl,const char *user,char *host) { ASGMEMBER *pasgmember = asMemberPvt; ASGCLIENT *pasgclient; int len, i; long status; if(!asActive) return(S_asLib_asNotActive); if(!pasgmember) return(S_asLib_badMember); pasgclient = freeListCalloc(freeListPvt); if(!pasgclient) return(S_asLib_noMemory); len = strlen(host); for (i = 0; i < len; i++) { host[i] = (char)tolower((int)host[i]); } *pasClientPvt = pasgclient; pasgclient->pasgMember = asMemberPvt; pasgclient->level = asl; pasgclient->user = user; pasgclient->host = host; LOCK; ellAdd(&pasgmember->clientList,&pasgclient->node); status = asComputePvt(pasgclient); UNLOCK; return(status); } long epicsShareAPI asChangeClient( ASCLIENTPVT asClientPvt,int asl,const char *user,char *host) { ASGCLIENT *pasgclient = asClientPvt; long status; int len, i; if(!asActive) return(S_asLib_asNotActive); if(!pasgclient) return(S_asLib_badClient); len = strlen(host); for (i = 0; i < len; i++) { host[i] = (char)tolower((int)host[i]); } LOCK; pasgclient->level = asl; pasgclient->user = user; pasgclient->host = host; status = asComputePvt(pasgclient); UNLOCK; return(status); } long epicsShareAPI asRemoveClient(ASCLIENTPVT *asClientPvt) { ASGCLIENT *pasgclient = *asClientPvt; ASGMEMBER *pasgMember; if(!asActive) return(S_asLib_asNotActive); if(!pasgclient) return(S_asLib_badClient); LOCK; pasgMember = pasgclient->pasgMember; if(!pasgMember) { errMessage(-1,"asRemoveClient: No ASGMEMBER"); UNLOCK; return(-1); } ellDelete(&pasgMember->clientList,&pasgclient->node); UNLOCK; freeListFree(freeListPvt,pasgclient); *asClientPvt = NULL; return(0); } long epicsShareAPI asRegisterClientCallback(ASCLIENTPVT asClientPvt, ASCLIENTCALLBACK pcallback) { ASGCLIENT *pasgclient = asClientPvt; if(!asActive) return(S_asLib_asNotActive); if(!pasgclient) return(S_asLib_badClient); LOCK; pasgclient->pcallback = pcallback; (*pasgclient->pcallback)(pasgclient,asClientCOAR); UNLOCK; return(0); } void * epicsShareAPI asGetClientPvt(ASCLIENTPVT asClientPvt) { ASGCLIENT *pasgclient = asClientPvt; if(!asActive) return(NULL); if(!pasgclient) return(NULL); return(pasgclient->userPvt); } void epicsShareAPI asPutClientPvt(ASCLIENTPVT asClientPvt,void *userPvt) { ASGCLIENT *pasgclient = asClientPvt; if(!asActive) return; if(!pasgclient) return; LOCK; pasgclient->userPvt = userPvt; UNLOCK; } long epicsShareAPI asComputeAllAsg(void) { long status; if(!asActive) return(S_asLib_asNotActive); LOCK; status = asComputeAllAsgPvt(); UNLOCK; return(status); } long epicsShareAPI asComputeAsg(ASG *pasg) { long status; if(!asActive) return(S_asLib_asNotActive); LOCK; status = asComputeAsgPvt(pasg); UNLOCK; return(status); } long epicsShareAPI asCompute(ASCLIENTPVT asClientPvt) { long status; if(!asActive) return(S_asLib_asNotActive); LOCK; status = asComputePvt(asClientPvt); UNLOCK; return(status); } /*The dump routines do not lock. Thus they may get inconsistant data.*/ /*HOWEVER if they did lock and a user interrupts one of then then BAD BAD*/ static const char *asAccessName[] = {"NONE","READ","WRITE"}; static const char *asTrapOption[] = {"NOTRAPWRITE","TRAPWRITE"}; static const char *asLevelName[] = {"ASL0","ASL1"}; int epicsShareAPI asDump( void (*memcallback)(struct asgMember *,FILE *), void (*clientcallback)(struct asgClient *,FILE *), int verbose) { return asDumpFP(stdout,memcallback,clientcallback,verbose); } int epicsShareAPI asDumpFP( FILE *fp, void (*memcallback)(struct asgMember *,FILE *), void (*clientcallback)(struct asgClient *,FILE *), int verbose) { UAG *puag; UAGNAME *puagname; HAG *phag; HAGNAME *phagname; ASG *pasg; ASGINP *pasginp; ASGRULE *pasgrule; ASGHAG *pasghag; ASGUAG *pasguag; ASGMEMBER *pasgmember; ASGCLIENT *pasgclient; if(!asActive) return(0); puag = (UAG *)ellFirst(&pasbase->uagList); if(!puag) fprintf(fp,"No UAGs\n"); while(puag) { fprintf(fp,"UAG(%s)",puag->name); puagname = (UAGNAME *)ellFirst(&puag->list); if(puagname) fprintf(fp," {"); else fprintf(fp,"\n"); while(puagname) { fprintf(fp,"%s",puagname->user); puagname = (UAGNAME *)ellNext(&puagname->node); if(puagname) fprintf(fp,","); else fprintf(fp,"}\n"); } puag = (UAG *)ellNext(&puag->node); } phag = (HAG *)ellFirst(&pasbase->hagList); if(!phag) fprintf(fp,"No HAGs\n"); while(phag) { fprintf(fp,"HAG(%s)",phag->name); phagname = (HAGNAME *)ellFirst(&phag->list); if(phagname) fprintf(fp," {"); else fprintf(fp,"\n"); while(phagname) { fprintf(fp,"%s",phagname->host); phagname = (HAGNAME *)ellNext(&phagname->node); if(phagname) fprintf(fp,","); else fprintf(fp,"}\n"); } phag = (HAG *)ellNext(&phag->node); } pasg = (ASG *)ellFirst(&pasbase->asgList); if(!pasg) fprintf(fp,"No ASGs\n"); while(pasg) { int print_end_brace; fprintf(fp,"ASG(%s)",pasg->name); pasginp = (ASGINP *)ellFirst(&pasg->inpList); pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); if(pasginp || pasgrule) { fprintf(fp," {\n"); print_end_brace = TRUE; } else { fprintf(fp,"\n"); print_end_brace = FALSE; } while(pasginp) { fprintf(fp,"\tINP%c(%s)",(pasginp->inpIndex + 'A'),pasginp->inp); if(verbose) { if((pasg->inpBad & (1ul << pasginp->inpIndex))) fprintf(fp," INVALID"); else fprintf(fp," VALID"); fprintf(fp," value=%f",pasg->pavalue[pasginp->inpIndex]); } fprintf(fp,"\n"); pasginp = (ASGINP *)ellNext(&pasginp->node); } while(pasgrule) { int print_end_brace; fprintf(fp,"\tRULE(%d,%s,%s)", pasgrule->level,asAccessName[pasgrule->access], asTrapOption[pasgrule->trapMask]); pasguag = (ASGUAG *)ellFirst(&pasgrule->uagList); pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); if(pasguag || pasghag || pasgrule->calc) { fprintf(fp," {\n"); print_end_brace = TRUE; } else { fprintf(fp,"\n"); print_end_brace = FALSE; } if(pasguag) fprintf(fp,"\t\tUAG("); while(pasguag) { fprintf(fp,"%s",pasguag->puag->name); pasguag = (ASGUAG *)ellNext(&pasguag->node); if(pasguag) fprintf(fp,","); else fprintf(fp,")\n"); } pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); if(pasghag) fprintf(fp,"\t\tHAG("); while(pasghag) { fprintf(fp,"%s",pasghag->phag->name); pasghag = (ASGHAG *)ellNext(&pasghag->node); if(pasghag) fprintf(fp,","); else fprintf(fp,")\n"); } if(pasgrule->calc) { fprintf(fp,"\t\tCALC(\"%s\")",pasgrule->calc); if(verbose) fprintf(fp," result=%s",(pasgrule->result==1 ? "TRUE" : "FALSE")); fprintf(fp,"\n"); } if(print_end_brace) fprintf(fp,"\t}\n"); pasgrule = (ASGRULE *)ellNext(&pasgrule->node); } pasgmember = (ASGMEMBER *)ellFirst(&pasg->memberList); if(!verbose) pasgmember = NULL; if(pasgmember) fprintf(fp,"\tMEMBERLIST\n"); while(pasgmember) { if(strlen(pasgmember->asgName)==0) fprintf(fp,"\t\t"); else fprintf(fp,"\t\t%s",pasgmember->asgName); if(memcallback) memcallback(pasgmember,fp); fprintf(fp,"\n"); pasgclient = (ASGCLIENT *)ellFirst(&pasgmember->clientList); while(pasgclient) { fprintf(fp,"\t\t\t %s %s",pasgclient->user,pasgclient->host); if(pasgclient->level>=0 && pasgclient->level<=1) fprintf(fp," %s",asLevelName[pasgclient->level]); else fprintf(fp," Illegal Level %d",pasgclient->level); if(pasgclient->access<=2) fprintf(fp," %s %s", asAccessName[pasgclient->access], asTrapOption[pasgclient->trapMask]); else fprintf(fp," Illegal Access %d",pasgclient->access); if(clientcallback) clientcallback(pasgclient,fp); fprintf(fp,"\n"); pasgclient = (ASGCLIENT *)ellNext(&pasgclient->node); } pasgmember = (ASGMEMBER *)ellNext(&pasgmember->node); } if(print_end_brace) fprintf(fp,"}\n"); pasg = (ASG *)ellNext(&pasg->node); } return(0); } int epicsShareAPI asDumpUag(const char *uagname) { return asDumpUagFP(stdout,uagname); } int epicsShareAPI asDumpUagFP(FILE *fp,const char *uagname) { UAG *puag; UAGNAME *puagname; if(!asActive) return(0); puag = (UAG *)ellFirst(&pasbase->uagList); if(!puag) fprintf(fp,"No UAGs\n"); while(puag) { if(uagname && strcmp(uagname,puag->name)!=0) { puag = (UAG *)ellNext(&puag->node); continue; } fprintf(fp,"UAG(%s)",puag->name); puagname = (UAGNAME *)ellFirst(&puag->list); if(puagname) fprintf(fp," {"); else fprintf(fp,"\n"); while(puagname) { fprintf(fp,"%s",puagname->user); puagname = (UAGNAME *)ellNext(&puagname->node); if(puagname) fprintf(fp,","); else fprintf(fp,"}\n"); } puag = (UAG *)ellNext(&puag->node); } return(0); } int epicsShareAPI asDumpHag(const char *hagname) { return asDumpHagFP(stdout,hagname); } int epicsShareAPI asDumpHagFP(FILE *fp,const char *hagname) { HAG *phag; HAGNAME *phagname; if(!asActive) return(0); phag = (HAG *)ellFirst(&pasbase->hagList); if(!phag) fprintf(fp,"No HAGs\n"); while(phag) { if(hagname && strcmp(hagname,phag->name)!=0) { phag = (HAG *)ellNext(&phag->node); continue; } fprintf(fp,"HAG(%s)",phag->name); phagname = (HAGNAME *)ellFirst(&phag->list); if(phagname) fprintf(fp," {"); else fprintf(fp,"\n"); while(phagname) { fprintf(fp,"%s",phagname->host); phagname = (HAGNAME *)ellNext(&phagname->node); if(phagname) fprintf(fp,","); else fprintf(fp,"}\n"); } phag = (HAG *)ellNext(&phag->node); } return(0); } int epicsShareAPI asDumpRules(const char *asgname) { return asDumpRulesFP(stdout,asgname); } int epicsShareAPI asDumpRulesFP(FILE *fp,const char *asgname) { ASG *pasg; ASGINP *pasginp; ASGRULE *pasgrule; ASGHAG *pasghag; ASGUAG *pasguag; if(!asActive) return(0); pasg = (ASG *)ellFirst(&pasbase->asgList); if(!pasg) fprintf(fp,"No ASGs\n"); while(pasg) { int print_end_brace; if(asgname && strcmp(asgname,pasg->name)!=0) { pasg = (ASG *)ellNext(&pasg->node); continue; } fprintf(fp,"ASG(%s)",pasg->name); pasginp = (ASGINP *)ellFirst(&pasg->inpList); pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); if(pasginp || pasgrule) { fprintf(fp," {\n"); print_end_brace = TRUE; } else { fprintf(fp,"\n"); print_end_brace = FALSE; } while(pasginp) { fprintf(fp,"\tINP%c(%s)",(pasginp->inpIndex + 'A'),pasginp->inp); if ((pasg->inpBad & (1ul << pasginp->inpIndex))) fprintf(fp," INVALID"); fprintf(fp," value=%f",pasg->pavalue[pasginp->inpIndex]); fprintf(fp,"\n"); pasginp = (ASGINP *)ellNext(&pasginp->node); } while(pasgrule) { int print_end_brace; fprintf(fp,"\tRULE(%d,%s,%s)", pasgrule->level,asAccessName[pasgrule->access], asTrapOption[pasgrule->trapMask]); pasguag = (ASGUAG *)ellFirst(&pasgrule->uagList); pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); if(pasguag || pasghag || pasgrule->calc) { fprintf(fp," {\n"); print_end_brace = TRUE; } else { fprintf(fp,"\n"); print_end_brace = FALSE; } if(pasguag) fprintf(fp,"\t\tUAG("); while(pasguag) { fprintf(fp,"%s",pasguag->puag->name); pasguag = (ASGUAG *)ellNext(&pasguag->node); if(pasguag) fprintf(fp,","); else fprintf(fp,")\n"); } pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); if(pasghag) fprintf(fp,"\t\tHAG("); while(pasghag) { fprintf(fp,"%s",pasghag->phag->name); pasghag = (ASGHAG *)ellNext(&pasghag->node); if(pasghag) fprintf(fp,","); else fprintf(fp,")\n"); } if(pasgrule->calc) { fprintf(fp,"\t\tCALC(\"%s\")",pasgrule->calc); fprintf(fp," result=%s",(pasgrule->result==1 ? "TRUE" : "FALSE")); fprintf(fp,"\n"); } if(print_end_brace) fprintf(fp,"\t}\n"); pasgrule = (ASGRULE *)ellNext(&pasgrule->node); } if(print_end_brace) fprintf(fp,"}\n"); pasg = (ASG *)ellNext(&pasg->node); } return(0); } int epicsShareAPI asDumpMem(const char *asgname,void (*memcallback)(ASMEMBERPVT,FILE *), int clients) { return asDumpMemFP(stdout,asgname,memcallback,clients); } int epicsShareAPI asDumpMemFP(FILE *fp,const char *asgname, void (*memcallback)(ASMEMBERPVT,FILE *),int clients) { ASG *pasg; ASGMEMBER *pasgmember; ASGCLIENT *pasgclient; if(!asActive) return(0); pasg = (ASG *)ellFirst(&pasbase->asgList); if(!pasg) fprintf(fp,"No ASGs\n"); while(pasg) { if(asgname && strcmp(asgname,pasg->name)!=0) { pasg = (ASG *)ellNext(&pasg->node); continue; } fprintf(fp,"ASG(%s)\n",pasg->name); pasgmember = (ASGMEMBER *)ellFirst(&pasg->memberList); if(pasgmember) fprintf(fp,"\tMEMBERLIST\n"); while(pasgmember) { if(strlen(pasgmember->asgName)==0) fprintf(fp,"\t\t"); else fprintf(fp,"\t\t%s",pasgmember->asgName); if(memcallback) memcallback(pasgmember,fp); fprintf(fp,"\n"); pasgclient = (ASGCLIENT *)ellFirst(&pasgmember->clientList); if(!clients) pasgclient = NULL; while(pasgclient) { fprintf(fp,"\t\t\t %s %s", pasgclient->user,pasgclient->host); if(pasgclient->level>=0 && pasgclient->level<=1) fprintf(fp," %s",asLevelName[pasgclient->level]); else fprintf(fp," Illegal Level %d",pasgclient->level); if(pasgclient->access<=2) fprintf(fp," %s %s", asAccessName[pasgclient->access], asTrapOption[pasgclient->trapMask]); else fprintf(fp," Illegal Access %d",pasgclient->access); fprintf(fp,"\n"); pasgclient = (ASGCLIENT *)ellNext(&pasgclient->node); } pasgmember = (ASGMEMBER *)ellNext(&pasgmember->node); } pasg = (ASG *)ellNext(&pasg->node); } return(0); } epicsShareFunc int epicsShareAPI asDumpHash(void) { return asDumpHashFP(stdout); } epicsShareFunc int epicsShareAPI asDumpHashFP(FILE *fp) { if(!asActive) return(0); gphDumpFP(fp,pasbase->phash); return(0); } /*Start of private routines*/ /* asCalloc is "friend" function */ epicsShareFunc void * epicsShareAPI asCalloc(size_t nobj,size_t size) { void *p; p=callocMustSucceed(nobj,size,"asCalloc"); return(p); } epicsShareFunc char * epicsShareAPI asStrdup(unsigned char *str) { size_t len = strlen((char *) str); char *buf = asCalloc(1, len + 1); strcpy(buf, (char *) str); return buf; } static long asAddMemberPvt(ASMEMBERPVT *pasMemberPvt,const char *asgName) { ASGMEMBER *pasgmember; ASG *pgroup; ASGCLIENT *pasgclient; if(*pasMemberPvt) { pasgmember = *pasMemberPvt; } else { pasgmember = asCalloc(1,sizeof(ASGMEMBER)); ellInit(&pasgmember->clientList); *pasMemberPvt = pasgmember; } pasgmember->asgName = asgName; pgroup = (ASG *)ellFirst(&pasbase->asgList); while(pgroup) { if(strcmp(pgroup->name,pasgmember->asgName)==0) goto got_it; pgroup = (ASG *)ellNext(&pgroup->node); } /* Put it in DEFAULT*/ pgroup = (ASG *)ellFirst(&pasbase->asgList); while(pgroup) { if(strcmp(pgroup->name,DEFAULT)==0) goto got_it; pgroup = (ASG *)ellNext(&pgroup->node); } errMessage(-1,"Logic Error in asAddMember"); return(-1); got_it: pasgmember->pasg = pgroup; ellAdd(&pgroup->memberList,&pasgmember->node); pasgclient = (ASGCLIENT *)ellFirst(&pasgmember->clientList); while(pasgclient) { asComputePvt((ASCLIENTPVT)pasgclient); pasgclient = (ASGCLIENT *)ellNext(&pasgclient->node); } return(0); } static long asComputeAllAsgPvt(void) { ASG *pasg; if(!asActive) return(S_asLib_asNotActive); pasg = (ASG *)ellFirst(&pasbase->asgList); while(pasg) { asComputeAsgPvt(pasg); pasg = (ASG *)ellNext(&pasg->node); } return(0); } static long asComputeAsgPvt(ASG *pasg) { ASGRULE *pasgrule; ASGMEMBER *pasgmember; ASGCLIENT *pasgclient; if(!asActive) return(S_asLib_asNotActive); pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); while(pasgrule) { double result = pasgrule->result; /* set for VAL */ long status; if(pasgrule->calc && (pasg->inpChanged & pasgrule->inpUsed)) { status = calcPerform(pasg->pavalue,&result,pasgrule->rpcl); if(status) { pasgrule->result = 0; errMessage(status,"asComputeAsg"); } else { pasgrule->result = ((result>.99) && (result<1.01)) ? 1 : 0; } } pasgrule = (ASGRULE *)ellNext(&pasgrule->node); } pasg->inpChanged = FALSE; pasgmember = (ASGMEMBER *)ellFirst(&pasg->memberList); while(pasgmember) { pasgclient = (ASGCLIENT *)ellFirst(&pasgmember->clientList); while(pasgclient) { asComputePvt((ASCLIENTPVT)pasgclient); pasgclient = (ASGCLIENT *)ellNext(&pasgclient->node); } pasgmember = (ASGMEMBER *)ellNext(&pasgmember->node); } return(0); } static long asComputePvt(ASCLIENTPVT asClientPvt) { asAccessRights access=asNOACCESS; int trapMask=0; ASGCLIENT *pasgclient = asClientPvt; ASGMEMBER *pasgMember; ASG *pasg; ASGRULE *pasgrule; asAccessRights oldaccess; GPHENTRY *pgphentry; if(!asActive) return(S_asLib_asNotActive); if(!pasgclient) return(S_asLib_badClient); pasgMember = pasgclient->pasgMember; if(!pasgMember) return(S_asLib_badMember); pasg = pasgMember->pasg; if(!pasg) return(S_asLib_badAsg); oldaccess=pasgclient->access; pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); while(pasgrule) { if(access == asWRITE) break; if(access>=pasgrule->access) goto next_rule; if(pasgclient->level > pasgrule->level) goto next_rule; /*if uagList is empty then no need to check uag*/ if(ellCount(&pasgrule->uagList)>0){ ASGUAG *pasguag; UAG *puag; pasguag = (ASGUAG *)ellFirst(&pasgrule->uagList); while(pasguag) { if((puag = pasguag->puag)) { pgphentry = gphFind(pasbase->phash,pasgclient->user,puag); if(pgphentry) goto check_hag; } pasguag = (ASGUAG *)ellNext(&pasguag->node); } goto next_rule; } check_hag: /*if hagList is empty then no need to check hag*/ if(ellCount(&pasgrule->hagList)>0) { ASGHAG *pasghag; HAG *phag; pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); while(pasghag) { if((phag = pasghag->phag)) { pgphentry=gphFind(pasbase->phash,pasgclient->host,phag); if(pgphentry) goto check_calc; } pasghag = (ASGHAG *)ellNext(&pasghag->node); } goto next_rule; } check_calc: if(!pasgrule->calc || (!(pasg->inpBad & pasgrule->inpUsed) && (pasgrule->result==1))) { access = pasgrule->access; trapMask = pasgrule->trapMask; } next_rule: pasgrule = (ASGRULE *)ellNext(&pasgrule->node); } pasgclient->access = access; pasgclient->trapMask = trapMask; if(pasgclient->pcallback && oldaccess!=access) { (*pasgclient->pcallback)(pasgclient,asClientCOAR); } return(0); } void asFreeAll(ASBASE *pasbase) { UAG *puag; UAGNAME *puagname; HAG *phag; HAGNAME *phagname; ASG *pasg; ASGINP *pasginp; ASGRULE *pasgrule; ASGHAG *pasghag; ASGUAG *pasguag; void *pnext; puag = (UAG *)ellFirst(&pasbase->uagList); while(puag) { puagname = (UAGNAME *)ellFirst(&puag->list); while(puagname) { pnext = ellNext(&puagname->node); ellDelete(&puag->list,&puagname->node); free(puagname); puagname = pnext; } pnext = ellNext(&puag->node); ellDelete(&pasbase->uagList,&puag->node); free(puag); puag = pnext; } phag = (HAG *)ellFirst(&pasbase->hagList); while(phag) { phagname = (HAGNAME *)ellFirst(&phag->list); while(phagname) { pnext = ellNext(&phagname->node); ellDelete(&phag->list,&phagname->node); free(phagname); phagname = pnext; } pnext = ellNext(&phag->node); ellDelete(&pasbase->hagList,&phag->node); free(phag); phag = pnext; } pasg = (ASG *)ellFirst(&pasbase->asgList); while(pasg) { free(pasg->pavalue); pasginp = (ASGINP *)ellFirst(&pasg->inpList); while(pasginp) { pnext = ellNext(&pasginp->node); ellDelete(&pasg->inpList,&pasginp->node); free(pasginp); pasginp = pnext; } pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); while(pasgrule) { free(pasgrule->calc); free(pasgrule->rpcl); pasguag = (ASGUAG *)ellFirst(&pasgrule->uagList); while(pasguag) { pnext = ellNext(&pasguag->node); ellDelete(&pasgrule->uagList,&pasguag->node); free(pasguag); pasguag = pnext; } pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); while(pasghag) { pnext = ellNext(&pasghag->node); ellDelete(&pasgrule->hagList,&pasghag->node); free(pasghag); pasghag = pnext; } pnext = ellNext(&pasgrule->node); ellDelete(&pasg->ruleList,&pasgrule->node); free(pasgrule); pasgrule = pnext; } pnext = ellNext(&pasg->node); ellDelete(&pasbase->asgList,&pasg->node); free(pasg); pasg = pnext; } gphFreeMem(pasbase->phash); free(pasbase); } /*Beginning of routines called by lex code*/ static UAG *asUagAdd(const char *uagName) { UAG *pprev; UAG *pnext; UAG *puag; int cmpvalue; ASBASE *pasbase = (ASBASE *)pasbasenew; /*Insert in alphabetic order*/ pnext = (UAG *)ellFirst(&pasbase->uagList); while(pnext) { cmpvalue = strcmp(uagName,pnext->name); if(cmpvalue < 0) break; if(cmpvalue==0) { errlogPrintf("Duplicate User Access Group named '%s'\n", uagName); return(NULL); } pnext = (UAG *)ellNext(&pnext->node); } puag = asCalloc(1,sizeof(UAG)+strlen(uagName)+1); ellInit(&puag->list); puag->name = (char *)(puag+1); strcpy(puag->name,uagName); if(pnext==NULL) { /*Add to end of list*/ ellAdd(&pasbase->uagList,&puag->node); } else { pprev = (UAG *)ellPrevious(&pnext->node); ellInsert(&pasbase->uagList,&pprev->node,&puag->node); } return(puag); } static long asUagAddUser(UAG *puag,const char *user) { UAGNAME *puagname; if(!puag) return(0); puagname = asCalloc(1,sizeof(UAGNAME)+strlen(user)+1); puagname->user = (char *)(puagname+1); strcpy(puagname->user,user); ellAdd(&puag->list,&puagname->node); return(0); } static HAG *asHagAdd(const char *hagName) { HAG *pprev; HAG *pnext; HAG *phag; int cmpvalue; ASBASE *pasbase = (ASBASE *)pasbasenew; /*Insert in alphabetic order*/ pnext = (HAG *)ellFirst(&pasbase->hagList); while(pnext) { cmpvalue = strcmp(hagName,pnext->name); if(cmpvalue < 0) break; if(cmpvalue==0) { errlogPrintf("Duplicate Host Access Group named '%s'\n", hagName); return(NULL); } pnext = (HAG *)ellNext(&pnext->node); } phag = asCalloc(1,sizeof(HAG)+strlen(hagName)+1); ellInit(&phag->list); phag->name = (char *)(phag+1); strcpy(phag->name,hagName); if(pnext==NULL) { /*Add to end of list*/ ellAdd(&pasbase->hagList,&phag->node); } else { pprev = (HAG *)ellPrevious(&pnext->node); ellInsert(&pasbase->hagList,&pprev->node,&phag->node); } return(phag); } static long asHagAddHost(HAG *phag,const char *host) { HAGNAME *phagname; if (!phag) return 0; if(!asCheckClientIP) { size_t i, len = strlen(host); phagname = asCalloc(1, sizeof(HAGNAME) + len); for (i = 0; i < len; i++) { phagname->host[i] = (char)tolower((int)host[i]); } } else { struct sockaddr_in addr; epicsUInt32 ip; if(aToIPAddr(host, 0, &addr)) { static const char unresolved[] = "unresolved:"; errlogPrintf("ACF: Unable to resolve host '%s'\n", host); phagname = asCalloc(1, sizeof(HAGNAME) + sizeof(unresolved)-1+strlen(host)); strcpy(phagname->host, unresolved); strcat(phagname->host, host); } else { ip = ntohl(addr.sin_addr.s_addr); phagname = asCalloc(1, sizeof(HAGNAME) + 24); epicsSnprintf(phagname->host, 24, "%u.%u.%u.%u", (ip>>24)&0xff, (ip>>16)&0xff, (ip>>8)&0xff, (ip>>0)&0xff); } } ellAdd(&phag->list, &phagname->node); return 0; } static ASG *asAsgAdd(const char *asgName) { ASG *pprev; ASG *pnext; ASG *pasg; int cmpvalue; ASBASE *pasbase = (ASBASE *)pasbasenew; /*Insert in alphabetic order*/ pnext = (ASG *)ellFirst(&pasbase->asgList); while(pnext) { cmpvalue = strcmp(asgName,pnext->name); if(cmpvalue < 0) break; if(cmpvalue==0) { if(strcmp(DEFAULT,pnext->name)==0) { if(ellCount(&pnext->inpList)==0 && ellCount(&pnext->ruleList)==0) return(pnext); } errlogPrintf("Duplicate Access Security Group named '%s'\n", asgName); return(NULL); } pnext = (ASG *)ellNext(&pnext->node); } pasg = asCalloc(1,sizeof(ASG)+strlen(asgName)+1); ellInit(&pasg->inpList); ellInit(&pasg->ruleList); ellInit(&pasg->memberList); pasg->name = (char *)(pasg+1); strcpy(pasg->name,asgName); if(pnext==NULL) { /*Add to end of list*/ ellAdd(&pasbase->asgList,&pasg->node); } else { pprev = (ASG *)ellPrevious(&pnext->node); ellInsert(&pasbase->asgList,&pprev->node,&pasg->node); } return(pasg); } static long asAsgAddInp(ASG *pasg,const char *inp,int inpIndex) { ASGINP *pasginp; if(!pasg) return(0); pasginp = asCalloc(1,sizeof(ASGINP)+strlen(inp)+1); pasginp->inp = (char *)(pasginp+1); strcpy(pasginp->inp,inp); pasginp->pasg = pasg; pasginp->inpIndex = inpIndex; ellAdd(&pasg->inpList,&pasginp->node); return(0); } static ASGRULE *asAsgAddRule(ASG *pasg,asAccessRights access,int level) { ASGRULE *pasgrule; if(!pasg) return(0); pasgrule = asCalloc(1,sizeof(ASGRULE)); pasgrule->access = access; pasgrule->trapMask = 0; pasgrule->level = level; ellInit(&pasgrule->uagList); ellInit(&pasgrule->hagList); ellAdd(&pasg->ruleList,&pasgrule->node); return(pasgrule); } static long asAsgAddRuleOptions(ASGRULE *pasgrule,int trapMask) { if(!pasgrule) return(0); pasgrule->trapMask = trapMask; return(0); } static long asAsgRuleUagAdd(ASGRULE *pasgrule, const char *name) { ASGUAG *pasguag; UAG *puag; ASBASE *pasbase = (ASBASE *)pasbasenew; if (!pasgrule) return 0; puag = (UAG *)ellFirst(&pasbase->uagList); while (puag) { if (strcmp(puag->name, name)==0) break; puag = (UAG *)ellNext(&puag->node); } if (!puag){ errlogPrintf("No User Access Group named '%s' defined\n", name); return S_asLib_noUag; } pasguag = asCalloc(1, sizeof(ASGUAG)); pasguag->puag = puag; ellAdd(&pasgrule->uagList, &pasguag->node); return 0; } static long asAsgRuleHagAdd(ASGRULE *pasgrule, const char *name) { ASGHAG *pasghag; HAG *phag; ASBASE *pasbase = (ASBASE *)pasbasenew; if (!pasgrule) return 0; phag = (HAG *)ellFirst(&pasbase->hagList); while (phag) { if (strcmp(phag->name, name)==0) break; phag = (HAG *)ellNext(&phag->node); } if (!phag){ errlogPrintf("No Host Access Group named '%s' defined\n", name); return S_asLib_noHag; } pasghag = asCalloc(1, sizeof(ASGHAG)); pasghag->phag = phag; ellAdd(&pasgrule->hagList, &pasghag->node); return 0; } static long asAsgRuleCalc(ASGRULE *pasgrule,const char *calc) { short err; long status; size_t insize; unsigned long stores; if (!pasgrule) return 0; insize = strlen(calc) + 1; pasgrule->calc = asCalloc(1, insize); strcpy(pasgrule->calc, calc); pasgrule->rpcl = asCalloc(1, INFIX_TO_POSTFIX_SIZE(insize)); status = postfix(pasgrule->calc, pasgrule->rpcl, &err); if(status) { free(pasgrule->calc); free(pasgrule->rpcl); pasgrule->calc = NULL; pasgrule->rpcl = NULL; status = S_asLib_badCalc; errlogPrintf("%s in CALC expression '%s'\n", calcErrorStr(err), calc); return status; } calcArgUsage(pasgrule->rpcl, &pasgrule->inpUsed, &stores); /* Until someone proves stores are not dangerous, don't allow them */ if (stores) { free(pasgrule->calc); free(pasgrule->rpcl); pasgrule->calc = NULL; pasgrule->rpcl = NULL; status = S_asLib_badCalc; errlogPrintf("Assignment operator used in CALC expression '%s'\n", calc); } return(status); } base-7.0.3.1/modules/libcom/src/as/asLib_lex.l0000664000577000060420000000406713557101274017610 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ newline "\n" backslash "\\" doublequote "\"" comment "#" whitespace [ \t\r] escape {backslash}. stringchar [^"\n\\] name [a-zA-Z0-9_\-+:.\[\]<>;] digit [0-9] punctuation [(){},] link [A-L] %{ static ASINPUTFUNCPTR *my_yyinput; #undef YY_INPUT #define YY_INPUT(b,r,ms) (r=(*my_yyinput)((char *)b,ms)) static int yyreset(void) { line_num=1; BEGIN INITIAL; return(0); } %} %% UAG { return(tokenUAG); } HAG { return(tokenHAG); } ASG { return(tokenASG); } RULE { return(tokenRULE); } CALC { return(tokenCALC); } INP{link} { yylval.Int = (unsigned char)yytext[3]; yylval.Int -= 'A'; return(tokenINP); } {digit}+ { /*integer*/ yylval.Int = atoi((char *)yytext); return(tokenINTEGER); } {name}+ { /*unquoted string*/ yylval.Str=asStrdup(yytext); return(tokenSTRING); } {doublequote}({stringchar}|{escape})*{doublequote} { /* quoted string */ yylval.Str=asStrdup(yytext+1); yylval.Str[strlen(yylval.Str)-1] = '\0'; return(tokenSTRING); } {doublequote}({stringchar}|{escape})*{newline} { /* bad string */ yyerror("Newline in quoted string, closing quote missing"); } {punctuation} { return(yytext[0]); } {newline} { line_num++; } {comment}.* ; {whitespace} ; . { char message[40]; YY_BUFFER_STATE *dummy=0; if (isprint((int) yytext[0])) { sprintf(message, "Invalid character '%c'", yytext[0]); } else { sprintf(message, "Invalid character 0x%2.2x", yytext[0]); } yyerror(message); /*The following suppress compiler warning messages*/ if (0) yyunput('c',(unsigned char *) message); if (0) yy_switch_to_buffer(*dummy); } %% base-7.0.3.1/modules/libcom/src/as/asTrapWrite.c0000664000577000060420000001363313557101274020141 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /*asTrapWrite.c */ /* Author: Marty Kraimer Date: 07NOV2000 */ /* Matthias Clausen and Vladis Korobov at DESY * implemented the first logging of Channel Access Puts * This implementation uses many ideas from their implementation */ #include #include #include #include #define epicsExportSharedSymbols #include "ellLib.h" #include "freeList.h" #include "epicsStdio.h" #include "cantProceed.h" #include "epicsMutex.h" #include "ellLib.h" #include "asLib.h" #include "asTrapWrite.h" typedef struct listenerPvt { ELLNODE node; struct listener *plistener; void *userPvt; }listenerPvt; typedef struct listener{ ELLNODE node; asTrapWriteListener func; }listener; typedef struct writeMessage { ELLNODE node; asTrapWriteMessage message; ELLLIST listenerPvtList; }writeMessage; typedef struct asTrapWritePvt { ELLLIST listenerList; ELLLIST writeMessageList; void *freeListWriteMessage; void *freeListListenerPvt; epicsMutexId lock; }asTrapWritePvt; static asTrapWritePvt *pasTrapWritePvt = 0; static void asTrapWriteInit(void) { pasTrapWritePvt = callocMustSucceed(1,sizeof(asTrapWritePvt),"asTrapWriteInit"); ellInit(&pasTrapWritePvt->listenerList); ellInit(&pasTrapWritePvt->writeMessageList); freeListInitPvt( &pasTrapWritePvt->freeListWriteMessage,sizeof(writeMessage),20); freeListInitPvt( &pasTrapWritePvt->freeListListenerPvt,sizeof(listenerPvt),20); pasTrapWritePvt->lock = epicsMutexMustCreate(); } asTrapWriteId epicsShareAPI asTrapWriteRegisterListener( asTrapWriteListener func) { listener *plistener; if(pasTrapWritePvt==0) asTrapWriteInit(); plistener = callocMustSucceed(1,sizeof(listener),"asTrapWriteRegisterListener"); plistener->func = func; epicsMutexMustLock(pasTrapWritePvt->lock); ellAdd(&pasTrapWritePvt->listenerList,&plistener->node); epicsMutexUnlock(pasTrapWritePvt->lock); return((asTrapWriteId)plistener); } void epicsShareAPI asTrapWriteUnregisterListener(asTrapWriteId id) { listener *plistener = (listener *)id; writeMessage *pwriteMessage; if(pasTrapWritePvt==0) return; epicsMutexMustLock(pasTrapWritePvt->lock); pwriteMessage = (writeMessage *)ellFirst(&pasTrapWritePvt->writeMessageList); while(pwriteMessage) { listenerPvt *plistenerPvt = (listenerPvt *)ellFirst(&pwriteMessage->listenerPvtList); while(plistenerPvt) { listenerPvt *pnext = (listenerPvt *)ellNext(&plistenerPvt->node); if(plistenerPvt->plistener == plistener) { ellDelete(&pwriteMessage->listenerPvtList,&plistenerPvt->node); freeListFree(pasTrapWritePvt->freeListListenerPvt, plistenerPvt); } plistenerPvt = pnext; } pwriteMessage = (writeMessage *)ellNext(&pwriteMessage->node); } ellDelete(&pasTrapWritePvt->listenerList,&plistener->node); free(plistener); epicsMutexUnlock(pasTrapWritePvt->lock); } void * epicsShareAPI asTrapWriteBeforeWithData( const char *userid, const char *hostid, void *addr, int dbrType, int no_elements, void *data) { writeMessage *pwriteMessage; listener *plistener; if (pasTrapWritePvt == 0 || ellCount(&pasTrapWritePvt->listenerList) <= 0) return 0; pwriteMessage = (writeMessage *)freeListCalloc( pasTrapWritePvt->freeListWriteMessage); pwriteMessage->message.userid = userid; pwriteMessage->message.hostid = hostid; pwriteMessage->message.serverSpecific = addr; pwriteMessage->message.dbrType = dbrType; pwriteMessage->message.no_elements = no_elements; pwriteMessage->message.data = data; ellInit(&pwriteMessage->listenerPvtList); epicsMutexMustLock(pasTrapWritePvt->lock); ellAdd(&pasTrapWritePvt->writeMessageList, &pwriteMessage->node); plistener = (listener *)ellFirst(&pasTrapWritePvt->listenerList); while (plistener) { listenerPvt *plistenerPvt = (listenerPvt *)freeListCalloc( pasTrapWritePvt->freeListListenerPvt); plistenerPvt->plistener = plistener; pwriteMessage->message.userPvt = 0; plistener->func(&pwriteMessage->message, 0); plistenerPvt->userPvt = pwriteMessage->message.userPvt; ellAdd(&pwriteMessage->listenerPvtList, &plistenerPvt->node); plistener = (listener *)ellNext(&plistener->node); } epicsMutexUnlock(pasTrapWritePvt->lock); return pwriteMessage; } void epicsShareAPI asTrapWriteAfterWrite(void *pvt) { writeMessage *pwriteMessage = (writeMessage *)pvt; listenerPvt *plistenerPvt; if (pwriteMessage == 0 || pasTrapWritePvt == 0) return; epicsMutexMustLock(pasTrapWritePvt->lock); plistenerPvt = (listenerPvt *)ellFirst(&pwriteMessage->listenerPvtList); while (plistenerPvt) { listenerPvt *pnext = (listenerPvt *)ellNext(&plistenerPvt->node); listener *plistener = plistenerPvt->plistener; pwriteMessage->message.userPvt = plistenerPvt->userPvt; plistener->func(&pwriteMessage->message, 1); ellDelete(&pwriteMessage->listenerPvtList, &plistenerPvt->node); freeListFree(pasTrapWritePvt->freeListListenerPvt, plistenerPvt); plistenerPvt = pnext; } ellDelete(&pasTrapWritePvt->writeMessageList, &pwriteMessage->node); freeListFree(pasTrapWritePvt->freeListWriteMessage, pwriteMessage); epicsMutexUnlock(pasTrapWritePvt->lock); } base-7.0.3.1/modules/libcom/src/as/asTrapWrite.h0000664000577000060420000000334613557101274020146 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /*asTrapWrite.h*/ /* Author: Marty Kraimer Date: 07NOV2000 */ #ifndef INCasTrapWriteh #define INCasTrapWriteh #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif typedef struct asTrapWriteMessage { const char *userid; const char *hostid; void *serverSpecific; void *userPvt; int dbrType; /* Data type from ca/db_access.h, NOT dbFldTypes.h */ int no_elements; void *data; /* Might be NULL if no data is available */ } asTrapWriteMessage; typedef void *asTrapWriteId; typedef void(*asTrapWriteListener)(asTrapWriteMessage *pmessage,int after); epicsShareFunc asTrapWriteId epicsShareAPI asTrapWriteRegisterListener( asTrapWriteListener func); epicsShareFunc void epicsShareAPI asTrapWriteUnregisterListener( asTrapWriteId id); /* * asTrapWriteListener is called before and after the write is performed. * The listener can set userPvt on the before call and retrieve it after * after = (0,1) (before,after) the put. * * Each asTrapWriteMessage can change or may be deleted after * the user's asTrapWriteListener returns * * asTrapWriteListener delays the associated server thread so it must not * do anything that causes to to block. */ #ifdef __cplusplus } #endif #endif /*INCasTrapWriteh*/ base-7.0.3.1/modules/libcom/src/bucketLib/Makefile0000664000577000060420000000077613557101274020475 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/bucketLib INC += bucketLib.h Com_SRCS += bucketLib.c base-7.0.3.1/modules/libcom/src/bucketLib/bucketLib.c0000664000577000060420000002506413557101274021102 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeffrey O. Hill * hill@atdiv.lanl.gov * (505) 665 1831 * Date: 9-93 * * NOTES: * .01 Storage for identifier must persist until an item is deleted */ #include #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsAssert.h" #include "freeList.h" /* bucketLib uses freeListLib inside the DLL */ #include "bucketLib.h" /* * these data type dependent routines are * provided in the bucketLib.c */ typedef BUCKETID bucketHash(BUCKET *pb, const void *pId); typedef ITEM **bucketCompare(ITEM **ppi, const void *pId); static bucketCompare bucketUnsignedCompare; static bucketCompare bucketPointerCompare; static bucketCompare bucketStringCompare; static bucketHash bucketUnsignedHash; static bucketHash bucketPointerHash; static bucketHash bucketStringHash; typedef struct { bucketHash *pHash; bucketCompare *pCompare; buckTypeOfId type; }bucketSET; static bucketSET BSET[] = { {bucketUnsignedHash, bucketUnsignedCompare, bidtUnsigned}, {bucketPointerHash, bucketPointerCompare, bidtPointer}, {bucketStringHash, bucketStringCompare, bidtString} }; static int bucketAddItem(BUCKET *prb, bucketSET *pBSET, const void *pId, const void *pApp); static void *bucketLookupItem(BUCKET *pb, bucketSET *pBSET, const void *pId); /* * bucket id bit width */ #define BUCKETID_BIT_WIDTH (sizeof(BUCKETID)*CHAR_BIT) /* * Maximum bucket size */ #define BUCKET_MAX_WIDTH 12 /* * bucketUnsignedCompare() */ static ITEM **bucketUnsignedCompare (ITEM **ppi, const void *pId) { unsigned id; unsigned *pItemId; ITEM *pi; id = * (unsigned *) pId; while ( (pi = *ppi) ) { if (bidtUnsigned == pi->type) { pItemId = (unsigned *) pi->pId; if (id == *pItemId) { return ppi; } } ppi = &pi->pItem; } return NULL; } /* * bucketPointerCompare() */ static ITEM **bucketPointerCompare (ITEM **ppi, const void *pId) { void *ptr; void **pItemId; ITEM *pi; ptr = * (void **) pId; while ( (pi = *ppi) ) { if (bidtPointer == pi->type ) { pItemId = (void **) pi->pId; if (ptr == *pItemId) { return ppi; } } ppi = &pi->pItem; } return NULL; } /* * bucketStringCompare () */ static ITEM **bucketStringCompare (ITEM **ppi, const void *pId) { const char *pStr = pId; ITEM *pi; int status; while ( (pi = *ppi) ) { if (bidtString == pi->type) { status = strcmp (pStr, (char *)pi->pId); if (status == '\0') { return ppi; } } ppi = &pi->pItem; } return NULL; } /* * bucketUnsignedHash () */ static BUCKETID bucketUnsignedHash (BUCKET *pb, const void *pId) { const unsigned *pUId = (const unsigned *) pId; unsigned src; BUCKETID hashid; src = *pUId; hashid = src; src = src >> pb->hashIdNBits; while (src) { hashid = hashid ^ src; src = src >> pb->hashIdNBits; } hashid = hashid & pb->hashIdMask; return hashid; } /* * bucketPointerHash () */ static BUCKETID bucketPointerHash (BUCKET *pb, const void *pId) { void * const *ppId = (void * const *) pId; size_t src; BUCKETID hashid; /* * This makes the assumption that size_t * can be used to hold a pointer value * (this assumption may not port to all * CPU architectures) */ src = (size_t) *ppId; hashid = src; src = src >> pb->hashIdNBits; while(src){ hashid = hashid ^ src; src = src >> pb->hashIdNBits; } hashid = hashid & pb->hashIdMask; return hashid; } /* * bucketStringHash () */ static BUCKETID bucketStringHash (BUCKET *pb, const void *pId) { const char *pStr = (const char *) pId; BUCKETID hashid; unsigned i; hashid = 0; i = 1; while(*pStr){ hashid += *pStr * i; pStr++; i++; } hashid = hashid % (pb->hashIdMask+1); return hashid; } /* * bucketCreate() */ epicsShareFunc BUCKET * epicsShareAPI bucketCreate (unsigned nHashTableEntries) { BUCKETID mask; unsigned nbits; BUCKET *pb; /* * no absurd sized buckets */ if (nHashTableEntries<=1) { fprintf (stderr, "Tiny bucket create failed\n"); return NULL; } /* * count the number of bits in the bucket id */ if ( BUCKETID_BIT_WIDTH > 0 ) { for (nbits=0; nbits=BUCKETID_BIT_WIDTH) { fprintf ( stderr, "%s at %d: Requested index width=%d to large. max=%ld\n", __FILE__, __LINE__, nbits, (long)(BUCKETID_BIT_WIDTH-1)); return NULL; } pb = (BUCKET *) calloc(1, sizeof(*pb)); if (!pb) { return pb; } pb->hashIdMask = mask; pb->hashIdNBits = nbits; freeListInitPvt(&pb->freeListPVT, sizeof(ITEM), 1024); pb->pTable = (ITEM **) calloc (mask+1, sizeof(*pb->pTable)); if (!pb->pTable) { freeListCleanup(pb->freeListPVT); free (pb); return NULL; } return pb; } /* * bucketFree() */ epicsShareFunc int epicsShareAPI bucketFree (BUCKET *prb) { /* * deleting a bucket with entries in use * will cause memory leaks and is not allowed */ assert (prb->nInUse==0); /* * free the free list */ freeListCleanup(prb->freeListPVT); free (prb->pTable); free (prb); return S_bucket_success; } /* * bucketAddItem() */ epicsShareFunc int epicsShareAPI bucketAddItemUnsignedId(BUCKET *prb, const unsigned *pId, const void *pApp) { return bucketAddItem(prb, &BSET[bidtUnsigned], pId, pApp); } epicsShareFunc int epicsShareAPI bucketAddItemPointerId(BUCKET *prb, void * const *pId, const void *pApp) { return bucketAddItem(prb, &BSET[bidtPointer], pId, pApp); } epicsShareFunc int epicsShareAPI bucketAddItemStringId(BUCKET *prb, const char *pId, const void *pApp) { return bucketAddItem(prb, &BSET[bidtString], pId, pApp); } static int bucketAddItem(BUCKET *prb, bucketSET *pBSET, const void *pId, const void *pApp) { BUCKETID hashid; ITEM **ppi; ITEM **ppiExists; ITEM *pi; /* * try to get it off the free list first. If * that fails then malloc() */ pi = (ITEM *) freeListMalloc(prb->freeListPVT); if (!pi) { return S_bucket_noMemory; } /* * create the hash index */ hashid = (*pBSET->pHash) (prb, pId); pi->pApp = pApp; pi->pId = pId; pi->type = pBSET->type; assert ((hashid & ~prb->hashIdMask) == 0); ppi = &prb->pTable[hashid]; /* * Dont reuse a resource id ! */ ppiExists = (*pBSET->pCompare) (ppi, pId); if (ppiExists) { freeListFree(prb->freeListPVT,pi); return S_bucket_idInUse; } pi->pItem = *ppi; prb->pTable[hashid] = pi; prb->nInUse++; return S_bucket_success; } /* * bucketLookupAndRemoveItem () */ static void *bucketLookupAndRemoveItem (BUCKET *prb, bucketSET *pBSET, const void *pId) { BUCKETID hashid; ITEM **ppi; ITEM *pi; void *pApp; /* * create the hash index */ hashid = (*pBSET->pHash) (prb, pId); assert((hashid & ~prb->hashIdMask) == 0); ppi = &prb->pTable[hashid]; ppi = (*pBSET->pCompare) (ppi, pId); if(!ppi){ return NULL; } prb->nInUse--; pi = *ppi; *ppi = pi->pItem; pApp = (void *) pi->pApp; /* * stuff it on the free list */ freeListFree(prb->freeListPVT, pi); return pApp; } epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemUnsignedId (BUCKET *prb, const unsigned *pId) { return bucketLookupAndRemoveItem(prb, &BSET[bidtUnsigned], pId); } epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemPointerId (BUCKET *prb, void * const *pId) { return bucketLookupAndRemoveItem(prb, &BSET[bidtPointer], pId); } epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemStringId (BUCKET *prb, const char *pId) { return bucketLookupAndRemoveItem(prb, &BSET[bidtString], pId); } /* * bucketRemoveItem() */ epicsShareFunc int epicsShareAPI bucketRemoveItemUnsignedId (BUCKET *prb, const unsigned *pId) { return bucketLookupAndRemoveItem(prb, &BSET[bidtUnsigned], pId)?S_bucket_success:S_bucket_uknId; } epicsShareFunc int epicsShareAPI bucketRemoveItemPointerId (BUCKET *prb, void * const *pId) { return bucketLookupAndRemoveItem(prb, &BSET[bidtPointer], pId)?S_bucket_success:S_bucket_uknId; } epicsShareFunc int epicsShareAPI bucketRemoveItemStringId (BUCKET *prb, const char *pId) { return bucketLookupAndRemoveItem(prb, &BSET[bidtString], pId)?S_bucket_success:S_bucket_uknId; } /* * bucketLookupItem() */ epicsShareFunc void * epicsShareAPI bucketLookupItemUnsignedId (BUCKET *prb, const unsigned *pId) { return bucketLookupItem(prb, &BSET[bidtUnsigned], pId); } epicsShareFunc void * epicsShareAPI bucketLookupItemPointerId (BUCKET *prb, void * const *pId) { return bucketLookupItem(prb, &BSET[bidtPointer], pId); } epicsShareFunc void * epicsShareAPI bucketLookupItemStringId (BUCKET *prb, const char *pId) { return bucketLookupItem(prb, &BSET[bidtString], pId); } static void *bucketLookupItem (BUCKET *pb, bucketSET *pBSET, const void *pId) { BUCKETID hashid; ITEM **ppi; /* * create the hash index */ hashid = (*pBSET->pHash) (pb, pId); assert((hashid & ~pb->hashIdMask) == 0); /* * at the bottom level just * linear search for it. */ ppi = (*pBSET->pCompare) (&pb->pTable[hashid], pId); if(ppi){ return (void *) (*ppi)->pApp; } return NULL; } /* * bucketShow() */ epicsShareFunc int epicsShareAPI bucketShow(BUCKET *pb) { ITEM **ppi; ITEM *pi; unsigned nElem; double X; double XX; double mean; double stdDev; unsigned count; unsigned maxEntries; printf( " Bucket entries in use = %d bytes in use = %ld\n", pb->nInUse, (long) (sizeof(*pb)+(pb->hashIdMask+1)* sizeof(ITEM *)+pb->nInUse*sizeof(ITEM))); ppi = pb->pTable; nElem = pb->hashIdMask+1; X = 0.0; XX = 0.0; maxEntries = 0; while (ppi < &pb->pTable[nElem]) { pi = *ppi; count = 0; while (pi) { count++; pi = pi->pItem; } X += count; XX += count*count; if (count > maxEntries) maxEntries = count; ppi++; } mean = X/nElem; stdDev = sqrt(XX/nElem - mean*mean); printf( " Bucket entries/hash id - mean = %f std dev = %f max = %d\n", mean, stdDev, maxEntries); return S_bucket_success; } base-7.0.3.1/modules/libcom/src/bucketLib/bucketLib.h0000664000577000060420000000611113557101274021077 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeffrey O. Hill * hill@luke.lanl.gov * (505) 665 1831 * Date: 9-93 * * NOTES: * .01 Storage for identifier must persist until an item is deleted */ #ifndef INCbucketLibh #define INCbucketLibh #ifdef __cplusplus extern "C" { #endif #include "errMdef.h" #include "epicsTypes.h" #include "shareLib.h" typedef unsigned BUCKETID; typedef enum {bidtUnsigned, bidtPointer, bidtString} buckTypeOfId; typedef struct item{ struct item *pItem; const void *pId; const void *pApp; buckTypeOfId type; }ITEM; typedef struct bucket{ ITEM **pTable; void *freeListPVT; unsigned hashIdMask; unsigned hashIdNBits; unsigned nInUse; }BUCKET; epicsShareFunc BUCKET * epicsShareAPI bucketCreate (unsigned nHashTableEntries); epicsShareFunc int epicsShareAPI bucketFree (BUCKET *prb); epicsShareFunc int epicsShareAPI bucketShow (BUCKET *pb); /* * !! Identifier must exist (and remain constant) at the specified address until * the item is deleted from the bucket !! */ epicsShareFunc int epicsShareAPI bucketAddItemUnsignedId (BUCKET *prb, const unsigned *pId, const void *pApp); epicsShareFunc int epicsShareAPI bucketAddItemPointerId (BUCKET *prb, void * const *pId, const void *pApp); epicsShareFunc int epicsShareAPI bucketAddItemStringId (BUCKET *prb, const char *pId, const void *pApp); epicsShareFunc int epicsShareAPI bucketRemoveItemUnsignedId (BUCKET *prb, const unsigned *pId); epicsShareFunc int epicsShareAPI bucketRemoveItemPointerId (BUCKET *prb, void * const *pId); epicsShareFunc int epicsShareAPI bucketRemoveItemStringId (BUCKET *prb, const char *pId); epicsShareFunc void * epicsShareAPI bucketLookupItemUnsignedId (BUCKET *prb, const unsigned *pId); epicsShareFunc void * epicsShareAPI bucketLookupItemPointerId (BUCKET *prb, void * const *pId); epicsShareFunc void * epicsShareAPI bucketLookupItemStringId (BUCKET *prb, const char *pId); epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemUnsignedId (BUCKET *prb, const unsigned *pId); epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemPointerId (BUCKET *prb, void * const *pId); epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemStringId (BUCKET *prb, const char *pId); /* * Status returned by bucketLib functions */ #define BUCKET_SUCCESS S_bucket_success #define S_bucket_success 0 #define S_bucket_noMemory (M_bucket | 1) /*Memory allocation failed*/ #define S_bucket_idInUse (M_bucket | 2) /*Identifier already in use*/ #define S_bucket_uknId (M_bucket | 3) /*Unknown identifier*/ #ifdef __cplusplus } #endif #endif /*INCbucketLibh*/ base-7.0.3.1/modules/libcom/src/calc/Makefile0000664000577000060420000000102013557101274017452 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/calc INC += postfix.h Com_SRCS += postfix.c Com_SRCS += calcPerform.c base-7.0.3.1/modules/libcom/src/calc/calcPerform.c0000664000577000060420000002275613557101274020436 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Julie Sander and Bob Dalesio * Date: 07-27-87 */ #include #include #include #include #define epicsExportSharedSymbols #include "osiUnistd.h" #include "dbDefs.h" #include "epicsMath.h" #include "epicsTypes.h" #include "errlog.h" #include "postfix.h" #include "postfixPvt.h" static double calcRandom(void); static int cond_search(const char **ppinst, int match); #ifndef PI #define PI 3.14159265358979323 #endif /* Turn off global optimization for 64-bit MSVC builds */ #if defined(_WIN32) && defined(_M_X64) && !defined(_MINGW) # pragma optimize("g", off) #endif /* calcPerform * * Evalutate the postfix expression */ epicsShareFunc long calcPerform(double *parg, double *presult, const char *pinst) { double stack[CALCPERFORM_STACK+1]; /* zero'th entry not used */ double *ptop; /* stack pointer */ double top; /* value from top of stack */ epicsInt32 itop; /* integer from top of stack */ epicsUInt32 utop; /* unsigned integer from top of stack */ int op; int nargs; /* initialize */ ptop = stack; /* RPN evaluation loop */ while ((op = *pinst++) != END_EXPRESSION){ switch (op){ case LITERAL_DOUBLE: memcpy(++ptop, pinst, sizeof(double)); pinst += sizeof(double); break; case LITERAL_INT: memcpy(&itop, pinst, sizeof(epicsInt32)); *++ptop = itop; pinst += sizeof(epicsInt32); break; case FETCH_VAL: *++ptop = *presult; break; case FETCH_A: case FETCH_B: case FETCH_C: case FETCH_D: case FETCH_E: case FETCH_F: case FETCH_G: case FETCH_H: case FETCH_I: case FETCH_J: case FETCH_K: case FETCH_L: *++ptop = parg[op - FETCH_A]; break; case STORE_A: case STORE_B: case STORE_C: case STORE_D: case STORE_E: case STORE_F: case STORE_G: case STORE_H: case STORE_I: case STORE_J: case STORE_K: case STORE_L: parg[op - STORE_A] = *ptop--; break; case CONST_PI: *++ptop = PI; break; case CONST_D2R: *++ptop = PI/180.; break; case CONST_R2D: *++ptop = 180./PI; break; case UNARY_NEG: *ptop = - *ptop; break; case ADD: top = *ptop--; *ptop += top; break; case SUB: top = *ptop--; *ptop -= top; break; case MULT: top = *ptop--; *ptop *= top; break; case DIV: top = *ptop--; *ptop /= top; break; case MODULO: itop = (epicsInt32) *ptop--; if (itop) *ptop = (epicsInt32) *ptop % itop; else *ptop = epicsNAN; break; case POWER: top = *ptop--; *ptop = pow(*ptop, top); break; case ABS_VAL: *ptop = fabs(*ptop); break; case EXP: *ptop = exp(*ptop); break; case LOG_10: *ptop = log10(*ptop); break; case LOG_E: *ptop = log(*ptop); break; case MAX: nargs = *pinst++; while (--nargs) { top = *ptop--; if (*ptop < top || isnan(top)) *ptop = top; } break; case MIN: nargs = *pinst++; while (--nargs) { top = *ptop--; if (*ptop > top || isnan(top)) *ptop = top; } break; case SQU_RT: *ptop = sqrt(*ptop); break; case ACOS: *ptop = acos(*ptop); break; case ASIN: *ptop = asin(*ptop); break; case ATAN: *ptop = atan(*ptop); break; case ATAN2: top = *ptop--; *ptop = atan2(top, *ptop); /* Ouch!: Args backwards! */ break; case COS: *ptop = cos(*ptop); break; case SIN: *ptop = sin(*ptop); break; case TAN: *ptop = tan(*ptop); break; case COSH: *ptop = cosh(*ptop); break; case SINH: *ptop = sinh(*ptop); break; case TANH: *ptop = tanh(*ptop); break; case CEIL: *ptop = ceil(*ptop); break; case FLOOR: *ptop = floor(*ptop); break; case FINITE: nargs = *pinst++; top = finite(*ptop); while (--nargs) { --ptop; top = top && finite(*ptop); } *ptop = top; break; case ISINF: *ptop = isinf(*ptop); break; case ISNAN: nargs = *pinst++; top = isnan(*ptop); while (--nargs) { --ptop; top = top || isnan(*ptop); } *ptop = top; break; case NINT: top = *ptop; *ptop = (epicsInt32) (top >= 0 ? top + 0.5 : top - 0.5); break; case RANDOM: *++ptop = calcRandom(); break; case REL_OR: top = *ptop--; *ptop = *ptop || top; break; case REL_AND: top = *ptop--; *ptop = *ptop && top; break; case REL_NOT: *ptop = ! *ptop; break; /* For bitwise operations on values with bit 31 set, double values * must first be cast to unsigned to correctly set that bit; the * double value must be negative in that case. The result must be * cast to a signed integer before converting to the double result. */ case BIT_OR: utop = *ptop--; *ptop = (epicsInt32) ((epicsUInt32) *ptop | utop); break; case BIT_AND: utop = *ptop--; *ptop = (epicsInt32) ((epicsUInt32) *ptop & utop); break; case BIT_EXCL_OR: utop = *ptop--; *ptop = (epicsInt32) ((epicsUInt32) *ptop ^ utop); break; case BIT_NOT: utop = *ptop; *ptop = (epicsInt32) ~utop; break; /* The shift operators use signed integers, so a right-shift will * extend the sign bit into the left-hand end of the value. The * double-casting through unsigned here is important, see above. */ case RIGHT_SHIFT: utop = *ptop--; *ptop = ((epicsInt32) (epicsUInt32) *ptop) >> (utop & 31); break; case LEFT_SHIFT: utop = *ptop--; *ptop = ((epicsInt32) (epicsUInt32) *ptop) << (utop & 31); break; case NOT_EQ: top = *ptop--; *ptop = *ptop != top; break; case LESS_THAN: top = *ptop--; *ptop = *ptop < top; break; case LESS_OR_EQ: top = *ptop--; *ptop = *ptop <= top; break; case EQUAL: top = *ptop--; *ptop = *ptop == top; break; case GR_OR_EQ: top = *ptop--; *ptop = *ptop >= top; break; case GR_THAN: top = *ptop--; *ptop = *ptop > top; break; case COND_IF: if (*ptop-- == 0.0 && cond_search(&pinst, COND_ELSE)) return -1; break; case COND_ELSE: if (cond_search(&pinst, COND_END)) return -1; break; case COND_END: break; default: errlogPrintf("calcPerform: Bad Opcode %d at %p\n", op, pinst-1); return -1; } } /* The stack should now have one item on it, the expression value */ if (ptop != stack + 1) return -1; *presult = *ptop; return 0; } #if defined(_WIN32) && defined(_M_X64) && !defined(_MINGW) # pragma optimize("", on) #endif epicsShareFunc long calcArgUsage(const char *pinst, unsigned long *pinputs, unsigned long *pstores) { unsigned long inputs = 0; unsigned long stores = 0; char op; while ((op = *pinst++) != END_EXPRESSION) { switch (op) { case LITERAL_DOUBLE: pinst += sizeof(double); break; case LITERAL_INT: pinst += sizeof(epicsInt32); break; case MIN: case MAX: case FINITE: case ISNAN: pinst++; break; case FETCH_A: case FETCH_B: case FETCH_C: case FETCH_D: case FETCH_E: case FETCH_F: case FETCH_G: case FETCH_H: case FETCH_I: case FETCH_J: case FETCH_K: case FETCH_L: /* Don't claim to use an arg we already stored to */ inputs |= (1 << (op - FETCH_A)) & ~stores; break; case STORE_A: case STORE_B: case STORE_C: case STORE_D: case STORE_E: case STORE_F: case STORE_G: case STORE_H: case STORE_I: case STORE_J: case STORE_K: case STORE_L: stores |= (1 << (op - STORE_A)); break; default: break; } } if (pinputs) *pinputs = inputs; if (pstores) *pstores = stores; return 0; } /* Generate a random number between 0 and 1 using the algorithm * seed = (multy * seed) + addy Random Number Generator by Knuth * SemiNumerical Algorithms * Chapter 1 * randy = seed / 65535.0 To normalize the number between 0 - 1 */ static unsigned short seed = 0xa3bf; static unsigned short multy = 191 * 8 + 5; /* 191 % 8 == 5 */ static unsigned short addy = 0x3141; static double calcRandom(void) { seed = (seed * multy) + addy; /* between 0 - 1 */ return (double) seed / 65535.0; } /* Search the instruction stream for a matching operator, skipping any * other conditional instructions found, and leave *ppinst pointing to * the next instruction to be executed. */ static int cond_search(const char **ppinst, int match) { const char *pinst = *ppinst; int count = 1; int op; while ((op = *pinst++) != END_EXPRESSION) { if (op == match && --count == 0) { *ppinst = pinst; return 0; } switch (op) { case LITERAL_DOUBLE: pinst += sizeof(double); break; case LITERAL_INT: pinst += sizeof(epicsInt32); break; case MIN: case MAX: case FINITE: case ISNAN: pinst++; break; case COND_IF: count++; break; } } return 1; } base-7.0.3.1/modules/libcom/src/calc/postfix.c0000664000577000060420000004045413557101274017670 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Subroutines used to convert an infix expression to a postfix expression * * Original Author: Bob Dalesio * Date: 12-12-86 */ #include #include #include #define epicsExportSharedSymbols #include "dbDefs.h" #include "epicsAssert.h" #include "epicsStdlib.h" #include "epicsString.h" #include "epicsTypes.h" #include "postfix.h" #include "postfixPvt.h" #include "shareLib.h" /* declarations for postfix */ /* element types */ typedef enum { OPERAND, LITERAL_OPERAND, STORE_OPERATOR, UNARY_OPERATOR, VARARG_OPERATOR, BINARY_OPERATOR, SEPERATOR, CLOSE_PAREN, CONDITIONAL, EXPR_TERMINATOR, } element_type; /* element table * * structure of an element */ typedef struct expression_element{ char *name; /* character representation of an element */ char in_stack_pri; /* priority on translation stack */ char in_coming_pri; /* priority in input string */ signed char runtime_effect; /* stack change, positive means push */ element_type type; /* element type */ rpn_opcode code; /* postfix opcode */ } ELEMENT; /* * NOTE: Keep these lists sorted. Elements are searched in reverse order, * and where two names start with the same substring we must pick out the * longest name first (hence the sort requirement). * NOTE: All VARARG_OPERATORs have to be made known to the calcExprDump() * routine at the end of this file. */ static const ELEMENT operands[] = { /* name prio's stack element type opcode */ {"!", 7, 8, 0, UNARY_OPERATOR, REL_NOT}, {"(", 0, 8, 0, UNARY_OPERATOR, NOT_GENERATED}, {"-", 7, 8, 0, UNARY_OPERATOR, UNARY_NEG}, {".", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"0", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"0X", 0, 0, 1, LITERAL_OPERAND,LITERAL_INT}, {"1", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"2", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"3", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"4", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"5", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"6", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"7", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"8", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"9", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"A", 0, 0, 1, OPERAND, FETCH_A}, {"ABS", 7, 8, 0, UNARY_OPERATOR, ABS_VAL}, {"ACOS", 7, 8, 0, UNARY_OPERATOR, ACOS}, {"ASIN", 7, 8, 0, UNARY_OPERATOR, ASIN}, {"ATAN", 7, 8, 0, UNARY_OPERATOR, ATAN}, {"ATAN2", 7, 8, -1, UNARY_OPERATOR, ATAN2}, {"B", 0, 0, 1, OPERAND, FETCH_B}, {"C", 0, 0, 1, OPERAND, FETCH_C}, {"CEIL", 7, 8, 0, UNARY_OPERATOR, CEIL}, {"COS", 7, 8, 0, UNARY_OPERATOR, COS}, {"COSH", 7, 8, 0, UNARY_OPERATOR, COSH}, {"D", 0, 0, 1, OPERAND, FETCH_D}, {"D2R", 0, 0, 1, OPERAND, CONST_D2R}, {"E", 0, 0, 1, OPERAND, FETCH_E}, {"EXP", 7, 8, 0, UNARY_OPERATOR, EXP}, {"F", 0, 0, 1, OPERAND, FETCH_F}, {"FINITE", 7, 8, 0, VARARG_OPERATOR,FINITE}, {"FLOOR", 7, 8, 0, UNARY_OPERATOR, FLOOR}, {"G", 0, 0, 1, OPERAND, FETCH_G}, {"H", 0, 0, 1, OPERAND, FETCH_H}, {"I", 0, 0, 1, OPERAND, FETCH_I}, {"INF", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"ISINF", 7, 8, 0, UNARY_OPERATOR, ISINF}, {"ISNAN", 7, 8, 0, VARARG_OPERATOR,ISNAN}, {"J", 0, 0, 1, OPERAND, FETCH_J}, {"K", 0, 0, 1, OPERAND, FETCH_K}, {"L", 0, 0, 1, OPERAND, FETCH_L}, {"LN", 7, 8, 0, UNARY_OPERATOR, LOG_E}, {"LOG", 7, 8, 0, UNARY_OPERATOR, LOG_10}, {"LOGE", 7, 8, 0, UNARY_OPERATOR, LOG_E}, {"MAX", 7, 8, 0, VARARG_OPERATOR,MAX}, {"MIN", 7, 8, 0, VARARG_OPERATOR,MIN}, {"NINT", 7, 8, 0, UNARY_OPERATOR, NINT}, {"NAN", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, {"NOT", 7, 8, 0, UNARY_OPERATOR, BIT_NOT}, {"PI", 0, 0, 1, OPERAND, CONST_PI}, {"R2D", 0, 0, 1, OPERAND, CONST_R2D}, {"RNDM", 0, 0, 1, OPERAND, RANDOM}, {"SIN", 7, 8, 0, UNARY_OPERATOR, SIN}, {"SINH", 7, 8, 0, UNARY_OPERATOR, SINH}, {"SQR", 7, 8, 0, UNARY_OPERATOR, SQU_RT}, {"SQRT", 7, 8, 0, UNARY_OPERATOR, SQU_RT}, {"TAN", 7, 8, 0, UNARY_OPERATOR, TAN}, {"TANH", 7, 8, 0, UNARY_OPERATOR, TANH}, {"VAL", 0, 0, 1, OPERAND, FETCH_VAL}, {"~", 7, 8, 0, UNARY_OPERATOR, BIT_NOT}, }; static const ELEMENT operators[] = { /* name prio's stack element type opcode */ {"!=", 3, 3, -1, BINARY_OPERATOR,NOT_EQ}, {"#", 3, 3, -1, BINARY_OPERATOR,NOT_EQ}, {"%", 5, 5, -1, BINARY_OPERATOR,MODULO}, {"&", 2, 2, -1, BINARY_OPERATOR,BIT_AND}, {"&&", 2, 2, -1, BINARY_OPERATOR,REL_AND}, {")", 0, 0, 0, CLOSE_PAREN, NOT_GENERATED}, {"*", 5, 5, -1, BINARY_OPERATOR,MULT}, {"**", 6, 6, -1, BINARY_OPERATOR,POWER}, {"+", 4, 4, -1, BINARY_OPERATOR,ADD}, {",", 0, 0, 0, SEPERATOR, NOT_GENERATED}, {"-", 4, 4, -1, BINARY_OPERATOR,SUB}, {"/", 5, 5, -1, BINARY_OPERATOR,DIV}, {":", 0, 0, -1, CONDITIONAL, COND_ELSE}, {":=", 0, 0, -1, STORE_OPERATOR, STORE_A}, {";", 0, 0, 0, EXPR_TERMINATOR,NOT_GENERATED}, {"<", 3, 3, -1, BINARY_OPERATOR,LESS_THAN}, {"<<", 2, 2, -1, BINARY_OPERATOR,LEFT_SHIFT}, {"<=", 3, 3, -1, BINARY_OPERATOR,LESS_OR_EQ}, {"=", 3, 3, -1, BINARY_OPERATOR,EQUAL}, {"==", 3, 3, -1, BINARY_OPERATOR,EQUAL}, {">", 3, 3, -1, BINARY_OPERATOR,GR_THAN}, {">=", 3, 3, -1, BINARY_OPERATOR,GR_OR_EQ}, {">>", 2, 2, -1, BINARY_OPERATOR,RIGHT_SHIFT}, {"?", 0, 0, -1, CONDITIONAL, COND_IF}, {"AND", 2, 2, -1, BINARY_OPERATOR,BIT_AND}, {"OR", 1, 1, -1, BINARY_OPERATOR,BIT_OR}, {"XOR", 1, 1, -1, BINARY_OPERATOR,BIT_EXCL_OR}, {"^", 6, 6, -1, BINARY_OPERATOR,POWER}, {"|", 1, 1, -1, BINARY_OPERATOR,BIT_OR}, {"||", 1, 1, -1, BINARY_OPERATOR,REL_OR}, }; /* get_element * * find the next expression element in the infix expression */ static int get_element(int opnd, const char **ppsrc, const ELEMENT **ppel) { const ELEMENT *ptable, *pel; *ppel = NULL; while (isspace((int) (unsigned char) **ppsrc)) ++*ppsrc; if (**ppsrc == '\0') return FALSE; if (opnd) { ptable = operands; pel = ptable + NELEMENTS(operands) - 1; } else { ptable = operators; pel = ptable + NELEMENTS(operators) - 1; } while (pel >= ptable) { size_t len = strlen(pel->name); if (epicsStrnCaseCmp(*ppsrc, pel->name, len) == 0) { *ppel = pel; *ppsrc += len; return TRUE; } --pel; } return FALSE; } /* postfix * * convert an infix expression to a postfix expression */ epicsShareFunc long postfix(const char *psrc, char *pout, short *perror) { ELEMENT stack[80]; ELEMENT *pstacktop = stack; const ELEMENT *pel; int operand_needed = TRUE; int runtime_depth = 0; int cond_count = 0; char * const pdest = pout; char *pnext; if (psrc == NULL || *psrc == '\0' || pout == NULL || perror == NULL) { if (perror) *perror = CALC_ERR_NULL_ARG; if (pout) *pout = END_EXPRESSION; return -1; } /* place the expression elements into postfix */ *pout = END_EXPRESSION; *perror = CALC_ERR_NONE; while (get_element(operand_needed, &psrc, &pel)) { switch (pel->type) { case OPERAND: *pout++ = pel->code; runtime_depth += pel->runtime_effect; operand_needed = FALSE; break; case LITERAL_OPERAND: runtime_depth += pel->runtime_effect; psrc -= strlen(pel->name); if (pel->code == LITERAL_DOUBLE) { double lit_d; epicsInt32 lit_i; if (epicsParseDouble(psrc, &lit_d, &pnext)) { *perror = CALC_ERR_BAD_LITERAL; goto bad; } psrc = pnext; lit_i = (epicsInt32) lit_d; if (lit_d != (double) lit_i) { *pout++ = pel->code; memcpy(pout, &lit_d, sizeof(double)); pout += sizeof(double); } else { *pout++ = LITERAL_INT; memcpy(pout, &lit_i, sizeof(epicsInt32)); pout += sizeof(epicsInt32); } } else { epicsUInt32 lit_ui; assert(pel->code == LITERAL_INT); if (epicsParseUInt32(psrc, &lit_ui, 0, &pnext)) { *perror = CALC_ERR_BAD_LITERAL; goto bad; } psrc = pnext; *pout++ = LITERAL_INT; memcpy(pout, &lit_ui, sizeof(epicsUInt32)); pout += sizeof(epicsUInt32); } operand_needed = FALSE; break; case STORE_OPERATOR: if (pout == pdest || pstacktop > stack || *--pout < FETCH_A || *pout > FETCH_L) { *perror = CALC_ERR_BAD_ASSIGNMENT; goto bad; } /* Convert fetch into a store on the stack */ *++pstacktop = *pel; pstacktop->code = STORE_A + *pout - FETCH_A; runtime_depth -= 1; operand_needed = TRUE; break; case UNARY_OPERATOR: case VARARG_OPERATOR: /* Move operators of >= priority to the output */ while ((pstacktop > stack) && (pstacktop->in_stack_pri >= pel->in_coming_pri)) { *pout++ = pstacktop->code; if (pstacktop->type == VARARG_OPERATOR) { *pout++ = 1 - pstacktop->runtime_effect; } runtime_depth += pstacktop->runtime_effect; pstacktop--; } /* Push new operator onto stack */ pstacktop++; *pstacktop = *pel; break; case BINARY_OPERATOR: /* Move operators of >= priority to the output */ while ((pstacktop > stack) && (pstacktop->in_stack_pri >= pel->in_coming_pri)) { *pout++ = pstacktop->code; if (pstacktop->type == VARARG_OPERATOR) { *pout++ = 1 - pstacktop->runtime_effect; } runtime_depth += pstacktop->runtime_effect; pstacktop--; } /* Push new operator onto stack */ pstacktop++; *pstacktop = *pel; operand_needed = TRUE; break; case SEPERATOR: /* Move operators to the output until open paren */ while (pstacktop->name[0] != '(') { if (pstacktop <= stack+1) { *perror = CALC_ERR_BAD_SEPERATOR; goto bad; } *pout++ = pstacktop->code; if (pstacktop->type == VARARG_OPERATOR) { *pout++ = 1 - pstacktop->runtime_effect; } runtime_depth += pstacktop->runtime_effect; pstacktop--; } operand_needed = TRUE; pstacktop->runtime_effect -= 1; break; case CLOSE_PAREN: /* Move operators to the output until matching paren */ while (pstacktop->name[0] != '(') { if (pstacktop <= stack+1) { *perror = CALC_ERR_PAREN_NOT_OPEN; goto bad; } *pout++ = pstacktop->code; if (pstacktop->type == VARARG_OPERATOR) { *pout++ = 1 - pstacktop->runtime_effect; } runtime_depth += pstacktop->runtime_effect; pstacktop--; } pstacktop--; /* remove ( from stack */ /* if there is a vararg operator before the opening paren, it inherits the (opening) paren's stack effect */ if ((pstacktop > stack) && pstacktop->type == VARARG_OPERATOR) { pstacktop->runtime_effect = (pstacktop+1)->runtime_effect; /* check for no arguments */ if (pstacktop->runtime_effect > 0) { *perror = CALC_ERR_INCOMPLETE; goto bad; } } break; case CONDITIONAL: /* Move operators of > priority to the output */ while ((pstacktop > stack) && (pstacktop->in_stack_pri > pel->in_coming_pri)) { *pout++ = pstacktop->code; if (pstacktop->type == VARARG_OPERATOR) { *pout++ = 1 - pstacktop->runtime_effect; } runtime_depth += pstacktop->runtime_effect; pstacktop--; } /* Add new element to the output */ *pout++ = pel->code; runtime_depth += pel->runtime_effect; /* For : operator, also push COND_END code to stack */ if (pel->name[0] == ':') { if (--cond_count < 0) { *perror = CALC_ERR_CONDITIONAL; goto bad; } pstacktop++; *pstacktop = *pel; pstacktop->code = COND_END; pstacktop->runtime_effect = 0; } else { cond_count++; } operand_needed = TRUE; break; case EXPR_TERMINATOR: /* Move everything from stack to the output */ while (pstacktop > stack) { if (pstacktop->name[0] == '(') { *perror = CALC_ERR_PAREN_OPEN; goto bad; } *pout++ = pstacktop->code; if (pstacktop->type == VARARG_OPERATOR) { *pout++ = 1 - pstacktop->runtime_effect; } runtime_depth += pstacktop->runtime_effect; pstacktop--; } if (cond_count != 0) { *perror = CALC_ERR_CONDITIONAL; goto bad; } if (runtime_depth > 1) { *perror = CALC_ERR_TOOMANY; goto bad; } operand_needed = TRUE; break; default: *perror = CALC_ERR_INTERNAL; goto bad; } if (runtime_depth < 0) { *perror = CALC_ERR_UNDERFLOW; goto bad; } if (runtime_depth >= CALCPERFORM_STACK) { *perror = CALC_ERR_OVERFLOW; goto bad; } } if (*psrc != '\0') { *perror = CALC_ERR_SYNTAX; goto bad; } /* Move everything from stack to the output */ while (pstacktop > stack) { if (pstacktop->name[0] == '(') { *perror = CALC_ERR_PAREN_OPEN; goto bad; } *pout++ = pstacktop->code; if (pstacktop->type == VARARG_OPERATOR) { *pout++ = 1 - pstacktop->runtime_effect; } runtime_depth += pstacktop->runtime_effect; pstacktop--; } *pout = END_EXPRESSION; if (cond_count != 0) { *perror = CALC_ERR_CONDITIONAL; goto bad; } if (operand_needed || runtime_depth != 1) { *perror = CALC_ERR_INCOMPLETE; goto bad; } return 0; bad: *pdest = END_EXPRESSION; return -1; } /* calcErrorStr * * Return a message string appropriate for the given error code */ epicsShareFunc const char * calcErrorStr(short error) { static const char *errStrs[] = { "No error", "Too many results returned", "Badly formed numeric literal", "Bad assignment target", "Comma without enclosing parentheses", "Close parenthesis found without open", "Parenthesis still open at end of expression", "Unbalanced conditional ?: operators", "Incomplete expression, operand missing", "Not enough operands provided", "Runtime stack overflow", "Syntax error, unknown operator/operand", "NULL or empty input argument to postfix()", "Internal error, unknown element type", }; if (error < CALC_ERR_NONE || error > CALC_ERR_INTERNAL) return NULL; return errStrs[error]; } /* calcExprDump * * Disassemble the given postfix instructions to stdout */ epicsShareFunc void calcExprDump(const char *pinst) { static const char *opcodes[] = { "End Expression", /* Operands */ "LITERAL_DOUBLE", "LITERAL_INT", "VAL", "FETCH_A", "FETCH_B", "FETCH_C", "FETCH_D", "FETCH_E", "FETCH_F", "FETCH_G", "FETCH_H", "FETCH_I", "FETCH_J", "FETCH_K", "FETCH_L", /* Assignment */ "STORE_A", "STORE_B", "STORE_C", "STORE_D", "STORE_E", "STORE_F", "STORE_G", "STORE_H", "STORE_I", "STORE_J", "STORE_K", "STORE_L", /* Trigonometry Constants */ "CONST_PI", "CONST_D2R", "CONST_R2D", /* Arithmetic */ "UNARY_NEG", "ADD", "SUB", "MULT", "DIV", "MODULO", "POWER", /* Algebraic */ "ABS_VAL", "EXP", "LOG_10", "LOG_E", "MAX", "MIN", "SQU_RT", /* Trigonometric */ "ACOS", "ASIN", "ATAN", "ATAN2", "COS", "COSH", "SIN", "SINH", "TAN", "TANH", /* Numeric */ "CEIL", "FLOOR", "FINITE", "ISINF", "ISNAN", "NINT", "RANDOM", /* Boolean */ "REL_OR", "REL_AND", "REL_NOT", /* Bitwise */ "BIT_OR", "BIT_AND", "BIT_EXCL_OR", "BIT_NOT", "RIGHT_SHIFT", "LEFT_SHIFT", /* Relationals */ "NOT_EQ", "LESS_THAN", "LESS_OR_EQ", "EQUAL", "GR_OR_EQ", "GR_THAN", /* Conditional */ "COND_IF", "COND_ELSE", "COND_END", /* Misc */ "NOT_GENERATED" }; char op; double lit_d; epicsInt32 lit_i; while ((op = *pinst) != END_EXPRESSION) { switch (op) { case LITERAL_DOUBLE: memcpy(&lit_d, ++pinst, sizeof(double)); printf("\tDouble %g\n", lit_d); pinst += sizeof(double); break; case LITERAL_INT: memcpy(&lit_i, ++pinst, sizeof(epicsInt32)); printf("\tInteger %d (0x%x)\n", lit_i, lit_i); pinst += sizeof(epicsInt32); break; case MIN: case MAX: case FINITE: case ISNAN: printf("\t%s, %d arg(s)\n", opcodes[(int) op], *++pinst); pinst++; break; default: printf("\t%s\n", opcodes[(int) op]); pinst++; } } } base-7.0.3.1/modules/libcom/src/calc/postfix.h0000664000577000060420000000626713557101274017701 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* postfix.h * Original Author: Bob Dalesio * Date: 9-21-88 */ #ifndef INCpostfixh #define INCpostfixh #include "shareLib.h" #define CALCPERFORM_NARGS 12 #define CALCPERFORM_STACK 80 #define INFIX_TO_POSTFIX_SIZE(n) ((n)*21/6) /* The above expression is an estimate of the maximum postfix buffer * size needed for a given infix expression buffer (the argument must count * the trailing nil byte in the input expression string). The actual size * needed is never larger than this value, although it is actually a * few bytes smaller for some sizes. * * The maximum expansion from infix to postfix is for the sub-expression * .1?.1: * which is 6 characters long and results in 21 bytes of postfix: * .1 => LITERAL_DOUBLE + 8 byte value * ? => COND_IF * .1 => LITERAL_DOUBLE + 8 byte value * : => COND_ELSE * ... * => COND_END * For other short expressions the factor 21/6 always gives a big enough * postfix buffer (proven by hand, look at '1+' and '.1+' as well). */ /* These are not hard limits, just default sizes for the database */ #define MAX_INFIX_SIZE 100 #define MAX_POSTFIX_SIZE INFIX_TO_POSTFIX_SIZE(MAX_INFIX_SIZE) /* Error numbers from postfix */ #define CALC_ERR_NONE 0 /* No error */ #define CALC_ERR_TOOMANY 1 /* Too many results returned */ #define CALC_ERR_BAD_LITERAL 2 /* Bad numeric literal */ #define CALC_ERR_BAD_ASSIGNMENT 3 /* Bad assignment target */ #define CALC_ERR_BAD_SEPERATOR 4 /* Comma without parentheses */ #define CALC_ERR_PAREN_NOT_OPEN 5 /* Close parenthesis without open */ #define CALC_ERR_PAREN_OPEN 6 /* Open parenthesis at end of expression */ #define CALC_ERR_CONDITIONAL 7 /* Unbalanced conditional ?: operators */ #define CALC_ERR_INCOMPLETE 8 /* Incomplete expression, operand missing */ #define CALC_ERR_UNDERFLOW 9 /* Runtime stack would underflow */ #define CALC_ERR_OVERFLOW 10 /* Runtime stack would overflow */ #define CALC_ERR_SYNTAX 11 /* Syntax error */ #define CALC_ERR_NULL_ARG 12 /* NULL or empty input argument */ #define CALC_ERR_INTERNAL 13 /* Internal error, bad element type */ /* Changes in the above errors must also be made in calcErrorStr() */ #ifdef __cplusplus extern "C" { #endif epicsShareFunc long postfix(const char *pinfix, char *ppostfix, short *perror); epicsShareFunc long calcPerform(double *parg, double *presult, const char *ppostfix); epicsShareFunc long calcArgUsage(const char *ppostfix, unsigned long *pinputs, unsigned long *pstores); epicsShareFunc const char * calcErrorStr(short error); epicsShareFunc void calcExprDump(const char *pinst); #ifdef __cplusplus } #endif #endif /* INCpostfixh */ base-7.0.3.1/modules/libcom/src/calc/postfixPvt.h0000664000577000060420000000422313557101274020361 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* postfixPvt.h * Original Author: Bob Dalesio * Date: 9-21-88 */ /* Notes: * 1. The FETCH_A through FETCH_L and STORE_A through STORE_L opcodes must * be contiguous. * 2. The LITERAL opcodes are followed by a binary representation of their * values, but these are not aligned properly. * 3. The var-arg functions MIN, MAX, FINITE and ISNAN are followed by * a byte giving the number of arguments to process. * 4. You can't use strlen() on an RPN buffer since the literal values * can contain zero bytes. */ #ifndef INCpostfixPvth #define INCpostfixPvth /* RPN opcodes */ typedef enum { END_EXPRESSION = 0, /* Operands */ LITERAL_DOUBLE, LITERAL_INT, FETCH_VAL, FETCH_A, FETCH_B, FETCH_C, FETCH_D, FETCH_E, FETCH_F, FETCH_G, FETCH_H, FETCH_I, FETCH_J, FETCH_K, FETCH_L, /* Assignment */ STORE_A, STORE_B, STORE_C, STORE_D, STORE_E, STORE_F, STORE_G, STORE_H, STORE_I, STORE_J, STORE_K, STORE_L, /* Trigonometry Constants */ CONST_PI, CONST_D2R, CONST_R2D, /* Arithmetic */ UNARY_NEG, ADD, SUB, MULT, DIV, MODULO, POWER, /* Algebraic */ ABS_VAL, EXP, LOG_10, LOG_E, MAX, MIN, SQU_RT, /* Trigonometric */ ACOS, ASIN, ATAN, ATAN2, COS, COSH, SIN, SINH, TAN, TANH, /* Numeric */ CEIL, FLOOR, FINITE, ISINF, ISNAN, NINT, RANDOM, /* Boolean */ REL_OR, REL_AND, REL_NOT, /* Bitwise */ BIT_OR, BIT_AND, BIT_EXCL_OR, BIT_NOT, RIGHT_SHIFT, LEFT_SHIFT, /* Relationals */ NOT_EQ, LESS_THAN, LESS_OR_EQ, EQUAL, GR_OR_EQ, GR_THAN, /* Conditional */ COND_IF, COND_ELSE, COND_END, /* Misc */ NOT_GENERATED } rpn_opcode; #endif /* INCpostfixPvth */ base-7.0.3.1/modules/libcom/src/cppStd/Makefile0000664000577000060420000000077613557101274020026 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/cppStd INC += epicsAlgorithm.h INC += epicsExcept.h base-7.0.3.1/modules/libcom/src/cppStd/epicsAlgorithm.h0000664000577000060420000000361213557101274021501 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ // epicsAlgorithm.h // Authors: Jeff Hill & Andrew Johnson #ifndef __EPICS_ALGORITHM_H__ #define __EPICS_ALGORITHM_H__ #include "epicsMath.h" // The C++ standard only requires types to be less-than comparable, so // the epicsMin and epicsMax templates only use operator < // epicsMin template inline const T& epicsMin (const T& a, const T& b) { return (b < a) ? b : a; } // If b is a NaN the above template returns a, but should return NaN. // These specializations ensure that epicsMin(x,NaN) == NaN template <> inline const float& epicsMin (const float& a, const float& b) { return (b < a) || isnan(b) ? b : a; } template <> inline const double& epicsMin (const double& a, const double& b) { return (b < a) || isnan(b) ? b : a; } // epicsMax template inline const T& epicsMax (const T& a, const T& b) { return (a < b) ? b : a; } // If b is a NaN the above template returns a, but should return NaN. // These specializations ensure that epicsMax(x,NaN) == NaN template <> inline const float& epicsMax (const float& a, const float& b) { return (a < b) || isnan(b) ? b : a; } template <> inline const double& epicsMax (const double& a, const double& b) { return (a < b) || isnan(b) ? b : a; } // epicsSwap template inline void epicsSwap(T& a, T& b) { T temp = a; a = b; b = temp; } #endif // __EPICS_ALGORITHM_H__ base-7.0.3.1/modules/libcom/src/cppStd/epicsExcept.h0000664000577000060420000000402513557101274021002 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ // Author: Andrew Johnson & Jeff Hill // Date: December 2000 #ifndef __EPICS_EXCEPT_H__ #define __EPICS_EXCEPT_H__ #define epicsThrowHere(exc) \ throw locationException(exc, __FILE__, __LINE__) class sourceLocation { public: // Functions sourceLocation(const char *fileName, int lineNumber); // sourceLocation(const sourceLocation&); Copy constructable // sourceLocation& operator=(const sourceLocation&); Assignable const char *fileName() const; int lineNumber() const; private: // Hide compiler-generated member functions sourceLocation(); // default constructor private: // Data const char *file; int line; }; template class locationException : public T, public sourceLocation { public: locationException(const T& exc, const char *fileName, int lineNumber); }; /* Example: * if (status) epicsThrowHere(std::logic_error("operation failed!")); * try { ... } catch(sourceLocation& where) { ... } */ // END OF DECLARATIONS // INLINE FUNCTIONS // sourceFileLocation inline sourceLocation::sourceLocation (const char *fileName, int lineNumber) : file(fileName), line(lineNumber) {} inline const char* sourceLocation::fileName () const { return this->file; } inline int sourceLocation::lineNumber () const { return this->line; } // locationException template inline locationException::locationException (const char *fileName, int lineNumber, const E& exc) : T(exc), sourceLocation(fileName, lineNumber) {} #endif // __EPICS_EXCEPT_H__ base-7.0.3.1/modules/libcom/src/cvtFast/Makefile0000664000577000060420000000077113557101274020176 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/cvtFast INC += cvtFast.h Com_SRCS += cvtFast.c base-7.0.3.1/modules/libcom/src/cvtFast/cvtFast.c0000664000577000060420000002710613557101274020315 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Fast numeric to string conversions * * Original Authors: * Bob Dalesio, Mark Anderson and Marty Kraimer * Date: 12 January 1993 */ #include #include #define epicsExportSharedSymbols #include "cvtFast.h" #include "epicsMath.h" #include "epicsStdio.h" /* * These routines convert numbers up to +/- 10,000,000. * They defer to sprintf() for numbers requiring more than * 8 places of precision. */ static epicsInt32 frac_multiplier[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; int cvtFloatToString(float flt_value, char *pdest, epicsUInt16 precision) { int got_one, i; epicsInt32 whole, iplace, number, fraction, fplace; float ftemp; char *startAddr; /* can this routine handle this conversion */ if (isnan(flt_value) || precision > 8 || flt_value > 10000000.0 || flt_value < -10000000.0) { if (precision > 8 || flt_value >= 1e8 || flt_value <= -1e8) { if (precision > 12) precision = 12; /* FIXME */ sprintf(pdest, "%*.*e", precision+6, precision, (double) flt_value); } else { if (precision > 3) precision = 3; /* FIXME */ sprintf(pdest, "%.*f", precision, (double) flt_value); } return((int)strlen(pdest)); } startAddr = pdest; /* determine the sign */ if (flt_value < 0){ *pdest++ = '-'; flt_value = -flt_value; }; /* remove the whole number portion */ whole = (epicsInt32)flt_value; ftemp = flt_value - whole; /* multiplier to convert fractional portion to integer */ fplace = frac_multiplier[precision]; fraction = (epicsInt32)(ftemp * fplace * 10); fraction = (fraction + 5) / 10; /* round up */ /* determine rounding into the whole number portion */ if ((fraction / fplace) >= 1){ whole++; fraction -= fplace; } /* whole numbers */ got_one = 0; for (iplace = 10000000; iplace >= 1; iplace /= 10){ if (whole >= iplace){ got_one = 1; number = whole / iplace; whole = whole - (number * iplace); *pdest = number + '0'; pdest++; }else if (got_one){ *pdest = '0'; pdest++; } } if (!got_one){ *pdest = '0'; pdest++; } /* fraction */ if (precision > 0){ /* convert fractional portional to ASCII */ *pdest = '.'; pdest++; for (fplace /= 10, i = precision; i > 0; fplace /= 10,i--){ number = fraction / fplace; fraction -= number * fplace; *pdest = number + '0'; pdest++; } } *pdest = 0; return((int)(pdest - startAddr)); } int cvtDoubleToString( double flt_value, char *pdest, epicsUInt16 precision) { epicsUInt16 got_one,i; epicsInt32 whole,iplace,number,fraction,fplace; double ftemp; char *startAddr; /* can this routine handle this conversion */ if (isnan(flt_value) || precision > 8 || flt_value > 10000000.0 || flt_value < -10000000.0) { if (precision > 8 || flt_value > 1e16 || flt_value < -1e16) { if(precision>17) precision=17; sprintf(pdest,"%*.*e",precision+7,precision, flt_value); } else { if(precision>3) precision=3; sprintf(pdest,"%.*f",precision,flt_value); } return((int)strlen(pdest)); } startAddr = pdest; /* determine the sign */ if (flt_value < 0){ *pdest++ = '-'; flt_value = -flt_value; }; /* remove the whole number portion */ whole = (epicsInt32)flt_value; ftemp = flt_value - whole; /* multiplier to convert fractional portion to integer */ fplace = frac_multiplier[precision]; fraction = (epicsInt32)(ftemp * fplace * 10); fraction = (fraction + 5) / 10; /* round up */ /* determine rounding into the whole number portion */ if ((fraction / fplace) >= 1){ whole++; fraction -= fplace; } /* whole numbers */ got_one = 0; for (iplace = 10000000; iplace >= 1; iplace /= 10){ if (whole >= iplace){ got_one = 1; number = whole / iplace; whole = whole - (number * iplace); *pdest = number + '0'; pdest++; }else if (got_one){ *pdest = '0'; pdest++; } } if (!got_one){ *pdest = '0'; pdest++; } /* fraction */ if (precision > 0){ /* convert fractional portional to ASCII */ *pdest = '.'; pdest++; for (fplace /= 10, i = precision; i > 0; fplace /= 10,i--){ number = fraction / fplace; fraction -= number * fplace; *pdest = number + '0'; pdest++; } } *pdest = 0; return((int)(pdest - startAddr)); } /* * These routines are provided for backwards compatibility, * extensions such as MEDM, edm and histtool use them. */ /* * cvtFloatToExpString * * Converts a float to a %e formatted string */ int cvtFloatToExpString(float val, char *pdest, epicsUInt16 precision) { return epicsSnprintf(pdest, MAX_STRING_SIZE, "%.*e", precision, val); } /* * cvtFloatToCompactString * * Converts a float to a %g formatted string. * The result uses %f notation for 10e-4 < |value| < 10e+4, * otherwise %e notation. */ int cvtFloatToCompactString(float val, char *pdest, epicsUInt16 precision) { if ((val < 1.e4 && val > 1.e-4) || (val > -1.e4 && val < -1.e-4) || val == 0.0) return cvtFloatToString(val, pdest, precision); return cvtFloatToExpString(val, pdest, precision); } /* * cvtDoubleToExpString * * Converts a double to a %e formatted string */ int cvtDoubleToExpString(double val, char *pdest, epicsUInt16 precision) { return epicsSnprintf(pdest, MAX_STRING_SIZE, "%.*e", precision, val); } /* * cvtDoubleToCompactString * * Converts a double to %g formatted string. * The result uses %f notation for 10e-4 < |value| < 10e+4, * otherwise %e notation. */ int cvtDoubleToCompactString(double val, char *pdest, epicsUInt16 precision) { if ((val < 1.e4 && val > 1.e-4) || (val > -1.e4 && val < -1.e-4) || val == 0.0) return cvtDoubleToString(val, pdest, precision); return cvtDoubleToExpString(val, pdest, precision); } /* Integer conversion primitives */ static size_t UInt32ToDec(epicsUInt32 val, char *pdest) { int i; char digit[10]; size_t len; for (i = 0; val; i++) { epicsUInt32 tenth = val / 10; digit[i] = val - tenth * 10 + '0'; val = tenth; } len = i; while (i > 0) *pdest++ = digit[--i]; *pdest = 0; return len; } static size_t UInt32ToBase(epicsUInt32 val, char *pdest, int base) { int i; char digit, digits[32]; size_t len; for (i = 0; val; i++) { epicsUInt32 tenth = val / base; digit = val - tenth * base; digits[i] = digit < 10 ? digit + '0' : digit - 10 + 'a'; val = tenth; } len = i; while (i > 0) *pdest++ = digits[--i]; *pdest = 0; return len; } static size_t UInt64ToDec(epicsUInt64 val, char *pdest) { int i; char digit[20]; size_t len; for (i = 0; val; i++) { epicsUInt64 tenth = val / 10; digit[i] = val - tenth * 10 + '0'; val = tenth; } len = i; while (i > 0) *pdest++ = digit[--i]; *pdest = 0; return len; } static size_t UInt64ToBase(epicsUInt64 val, char *pdest, int base) { int i; char digit, digits[64]; size_t len; for (i = 0; val; i++) { epicsUInt64 tenth = val / base; digit = val - tenth * base; digits[i] = digit < 10 ? digit + '0' : digit - 10 + 'a'; val = tenth; } len = i; while (i > 0) *pdest++ = digits[--i]; *pdest = 0; return len; } /* Integer conversion routines */ size_t cvtUInt32ToString(epicsUInt32 val, char *pdest) { if (val == 0) { *pdest++ = '0'; *pdest = 0; return 1; } return UInt32ToDec(val, pdest); } size_t cvtInt32ToString(epicsInt32 val, char *pdest) { if (val == 0) { *pdest++ = '0'; *pdest = 0; return 1; } if (val > 0) return UInt32ToDec(val, pdest); if (val == -0x80000000) { strcpy(pdest, "-2147483648"); return strlen(pdest); } *pdest++ = '-'; return 1 + UInt32ToDec(-val, pdest); } size_t cvtUInt64ToString(epicsUInt64 val, char *pdest) { if (val == 0) { *pdest++ = '0'; *pdest = 0; return 1; } return UInt64ToDec(val, pdest); } size_t cvtInt64ToString(epicsInt64 val, char *pdest) { if (val == 0) { *pdest++ = '0'; *pdest = 0; return 1; } if (val > 0) return UInt64ToDec(val, pdest); if (val == -0x8000000000000000LL) { strcpy(pdest, "-9223372036854775808"); return strlen(pdest); } *pdest++ = '-'; return 1 + UInt64ToDec(-val, pdest); } size_t cvtInt32ToHexString(epicsInt32 val, char *pdest) { if (val < 0) *pdest++ = '-'; *pdest++ = '0'; *pdest++ = 'x'; if (val == 0) { *pdest++ = '0'; *pdest = 0; return 3; } if (val > 0) return 2 + UInt32ToBase(val, pdest, 16); if (val == -0x80000000) { strcpy(pdest, "80000000"); return 11; } return 3 + UInt32ToBase(-val, pdest, 16); } size_t cvtUInt32ToHexString(epicsUInt32 val, char *pdest) { *pdest++ = '0'; *pdest++ = 'x'; if (val == 0) { *pdest++ = '0'; *pdest = 0; return 3; } return 2 + UInt32ToBase(val, pdest, 16); } size_t cvtInt32ToOctalString(epicsInt32 val, char *pdest) { if (val == 0) { *pdest++ = '0'; *pdest = 0; return 1; } if (val > 0) { *pdest++ = '0'; return 1 + UInt32ToBase(val, pdest, 8); } if (val == -0x80000000) { strcpy(pdest, "-020000000000"); return strlen(pdest); } *pdest++ = '-'; *pdest++ = '0'; return 2 + UInt32ToBase(-val, pdest, 8); } size_t cvtInt64ToHexString(epicsInt64 val, char *pdest) { if (val < 0) *pdest++ = '-'; *pdest++ = '0'; *pdest++ = 'x'; if (val == 0) { *pdest++ = '0'; *pdest = 0; return 3; } if (val > 0) return 2 + UInt64ToBase(val, pdest, 16); if (val == -0x8000000000000000LL) { strcpy(pdest, "8000000000000000"); return 19; } return 3 + UInt64ToBase(-val, pdest, 16); } size_t cvtUInt64ToHexString(epicsUInt64 val, char *pdest) { *pdest++ = '0'; *pdest++ = 'x'; if (val == 0) { *pdest++ = '0'; *pdest = 0; return 3; } return 2 + UInt64ToBase(val, pdest, 16); } base-7.0.3.1/modules/libcom/src/cvtFast/cvtFast.h0000664000577000060420000000530313557101274020315 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Fast numeric to string conversions * * Original Authors: * Bob Dalesio, Mark Anderson and Marty Kraimer * Date: 12 January 1993 */ #ifndef INCcvtFasth #define INCcvtFasth #include #include "epicsTypes.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /* * All functions return the number of characters in the output */ epicsShareFunc int cvtFloatToString(float val, char *pdest, epicsUInt16 prec); epicsShareFunc int cvtDoubleToString(double val, char *pdest, epicsUInt16 prec); epicsShareFunc int cvtFloatToExpString(float val, char *pdest, epicsUInt16 prec); epicsShareFunc int cvtDoubleToExpString(double val, char *pdest, epicsUInt16 prec); epicsShareFunc int cvtFloatToCompactString(float val, char *pdest, epicsUInt16 prec); epicsShareFunc int cvtDoubleToCompactString(double val, char *pdest, epicsUInt16 prec); epicsShareFunc size_t cvtInt32ToString(epicsInt32 val, char *pdest); epicsShareFunc size_t cvtUInt32ToString(epicsUInt32 val, char *pdest); epicsShareFunc size_t cvtInt64ToString(epicsInt64 val, char *pdest); epicsShareFunc size_t cvtUInt64ToString(epicsUInt64 val, char *pdest); epicsShareFunc size_t cvtInt32ToHexString(epicsInt32 val, char *pdest); epicsShareFunc size_t cvtUInt32ToHexString(epicsUInt32 val, char *pdest); epicsShareFunc size_t cvtInt32ToOctalString(epicsInt32 val, char *pdest); epicsShareFunc size_t cvtInt64ToHexString(epicsInt64 val, char *pdest); epicsShareFunc size_t cvtUInt64ToHexString(epicsUInt64 val, char *pdest); /* Support the original names */ #define cvtCharToString(val, str) cvtInt32ToString(val, str) #define cvtUcharToString(val, str) cvtUInt32ToString(val, str) #define cvtShortToString(val, str) cvtInt32ToString(val, str) #define cvtUshortToString(val, str) cvtUInt32ToString(val, str) #define cvtLongToString(val, str) cvtInt32ToString(val, str) #define cvtUlongToString(val, str) cvtUInt32ToString(val, str) #define cvtLongToHexString(val, str) cvtInt32ToHexString(val, str) #define cvtULongToHexString(val, str) cvtUInt32ToHexString(val, str) #define cvtLongToOctalString(val, str) cvtInt32ToOctalString(val, str) #ifdef __cplusplus } #endif #endif /*INCcvtFasth*/ base-7.0.3.1/modules/libcom/src/cxxTemplates/Makefile0000664000577000060420000000124213557101274021237 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/cxxTemplates INC += resourceLib.h INC += tsDLList.h INC += tsSLList.h INC += tsMinMax.h INC += tsFreeList.h INC += epicsSingleton.h INC += epicsGuard.h Com_SRCS += resourceLib.cpp Com_SRCS += epicsSingletonMutex.cpp base-7.0.3.1/modules/libcom/src/cxxTemplates/README0000664000577000060420000000244513557101274020465 0ustar anjaesctl C++ templates: tsSLList.h - type safe single linked list template tsDLList.h - type safe double linked list template resourceLib.h - hash table template tsFreeeList.h - free list allocator / deallocator the test subdir contains examples Since I am using templates the linked lists are type safe (no casting of pointers ala ellList and dllList). Also, the node class in embedded in the item on the list (more efficient use of pool). The file resourceLib.h provides a core hashing library "resTable " where "itemClass" objects are stored in the hash table and "idClass" is the data type of the key for the hash table. The identifier class provides the hash alg. I have provided simple string "stringId" and unsigned integer "uintId" key types in resourceLib.h. It is easy to implement a new key class. There are examples under cxxTemplate/test. The list/hashing templates all depend on a particular inheritance hierarchy. If the inheritance hierarchy is wrong nothing will compile. For instance, in tsDLList.h the template data type "T" must derive from tsDLNode. Likewise, in tsSLList.h "T" must derive from tsSLNode. Likewise, in resourceLib.h class "T" (the type stored in the hash table) must derive from class "ID" (the hash table key type) and also derive from tsSLNode. base-7.0.3.1/modules/libcom/src/cxxTemplates/epicsGuard.h0000664000577000060420000000620213557101274022037 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #ifndef epicsGuardh #define epicsGuardh #ifndef assert // allow use of epicsAssert.h # include #endif /* * Author: Jeffrey O. Hill */ template < class T > class epicsGuardRelease; // Automatically applies and releases the mutex. // This class is also useful in situations where // C++ exceptions are possible. template < class T > class epicsGuard { public: typedef epicsGuardRelease release_t; epicsGuard ( T & ); void assertIdenticalMutex ( const T & ) const; ~epicsGuard (); private: T * _pTargetMutex; epicsGuard ( const epicsGuard & ); epicsGuard & operator = ( const epicsGuard & ); friend class epicsGuardRelease < T >; }; // Automatically releases and reapplies the mutex. // This class is also useful in situations where // C++ exceptions are possible. template < class T > class epicsGuardRelease { public: typedef epicsGuard guard_t; epicsGuardRelease ( epicsGuard < T > & ); ~epicsGuardRelease (); private: epicsGuard < T > & _guard; T * _pTargetMutex; epicsGuardRelease ( const epicsGuardRelease & ); epicsGuardRelease & operator = ( const epicsGuardRelease & ); }; // same interface as epicsMutex class epicsMutexNOOP { public: void lock (); bool tryLock (); void unlock (); void show ( unsigned level ) const; }; template < class T > inline epicsGuard < T > :: epicsGuard ( T & mutexIn ) : _pTargetMutex ( & mutexIn ) { _pTargetMutex->lock (); } template < class T > inline epicsGuard < T > :: ~epicsGuard () { _pTargetMutex->unlock (); } template < class T > inline void epicsGuard < T > :: assertIdenticalMutex ( const T & mutexToVerify ) const { assert ( _pTargetMutex == & mutexToVerify ); } template < class T > inline epicsGuardRelease < T > :: epicsGuardRelease ( epicsGuard & guardIn ) : _guard ( guardIn ), _pTargetMutex ( guardIn._pTargetMutex ) { // Setting the guard's _pTargetMutex to nill will // allow assertIdenticalMutex to catch situations // where a guard is being used and it has been // released, and also situations where ~epicsGuard () // runs and an epicsGuardRelease is still referencing // the guard will be detected. _guard._pTargetMutex = 0; _pTargetMutex->unlock (); } template < class T > inline epicsGuardRelease < T > :: ~epicsGuardRelease () { _pTargetMutex->lock (); _guard._pTargetMutex = _pTargetMutex; } inline void epicsMutexNOOP::lock () {} inline bool epicsMutexNOOP::tryLock () { return true; } inline void epicsMutexNOOP::unlock () {} inline void epicsMutexNOOP::show ( unsigned ) const {} #endif // epicsGuardh base-7.0.3.1/modules/libcom/src/cxxTemplates/epicsSingleton.h0000664000577000060420000001357013557101274022745 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeffrey O. Hill */ #ifndef epicsSingleton_h #define epicsSingleton_h #include #include #include "shareLib.h" #include "epicsAssert.h" class epicsShareClass SingletonUntyped { public: SingletonUntyped (); ~SingletonUntyped (); typedef void * ( * PBuild ) (); void incrRefCount ( PBuild ); typedef void ( * PDestroy ) ( void * ); void decrRefCount ( PDestroy ); void * pInstance () const; private: void * _pInstance; std :: size_t _refCount; SingletonUntyped ( const SingletonUntyped & ); SingletonUntyped & operator = ( const SingletonUntyped & ); }; // This class exists for the purpose of avoiding file scope // object chicken and egg problems. It implements thread safe // lazy initialization. To avoid locking overhead retain a // copy of the epicsSingleton :: reference for future use. template < class TYPE > class epicsSingleton { public: class reference { public: reference ( epicsSingleton & ); reference ( const reference & ); ~reference (); // this somewhat convoluted reference of the return // type ref through the epicsSingleton template is // required for the archaic Tornado gnu compiler typename epicsSingleton < TYPE > :: reference & operator = ( const reference & ); TYPE * operator -> (); const TYPE * operator -> () const; TYPE & operator * (); const TYPE & operator * () const; private: epicsSingleton * _pSingleton; }; friend class reference; epicsSingleton () {} // mutex lock/unlock pair overhead incured // when either of these are called reference getReference (); const reference getReference () const; private: SingletonUntyped _singletonUntyped; static void * _build (); static void _destroy ( void * ); epicsSingleton ( const epicsSingleton & ); epicsSingleton & operator = ( const epicsSingleton & ); }; template < class TYPE > inline epicsSingleton < TYPE > :: reference :: reference ( epicsSingleton & es ): _pSingleton ( & es ) { es._singletonUntyped. incrRefCount ( & epicsSingleton < TYPE > :: _build ); } template < class TYPE > inline epicsSingleton < TYPE > :: reference :: reference ( const reference & ref ) : _pSingleton ( ref._pSingleton ) { assert ( _pSingleton ); _pSingleton->_singletonUntyped. incrRefCount ( & epicsSingleton < TYPE > :: _build ); } template < class TYPE > inline epicsSingleton < TYPE > :: reference :: ~reference () { assert ( _pSingleton ); _pSingleton->_singletonUntyped. decrRefCount ( & epicsSingleton < TYPE > :: _destroy ); } template < class TYPE > typename epicsSingleton < TYPE > :: reference & epicsSingleton < TYPE > :: reference :: operator = ( const reference & ref ) { if ( _pSingleton != ref._pSingleton ) { assert ( _pSingleton ); _pSingleton->_singletonUntyped. decrRefCount ( epicsSingleton < TYPE > :: _destroy ); _pSingleton = ref._pSingleton; assert ( _pSingleton ); _pSingleton->_singletonUntyped. incrRefCount ( & epicsSingleton < TYPE > :: _build ); } return *this; } template < class TYPE > inline TYPE * epicsSingleton < TYPE > :: reference :: operator -> () { assert ( _pSingleton ); return reinterpret_cast < TYPE * > ( _pSingleton->_singletonUntyped.pInstance () ); } template < class TYPE > inline const TYPE * epicsSingleton < TYPE > :: reference :: operator -> () const { assert ( _pSingleton ); return reinterpret_cast < const TYPE * > ( _pSingleton->_singletonUntyped.pInstance () ); } template < class TYPE > inline TYPE & epicsSingleton < TYPE > :: reference :: operator * () { return * this->operator -> (); } template < class TYPE > inline const TYPE & epicsSingleton < TYPE > :: reference :: operator * () const { return * this->operator -> (); } inline SingletonUntyped :: SingletonUntyped () : _pInstance ( 0 ), _refCount ( 0 ) { } inline void * SingletonUntyped :: pInstance () const { return _pInstance; } inline SingletonUntyped :: ~SingletonUntyped () { // we dont assert fail on non-zero _refCount // and or non nill _pInstance here because this // is designed to tolarate situations where // file scope epicsSingleton objects (which // theoretically dont have storage lifespan // issues) are deleted in a non-determanistic // order # if 0 assert ( _refCount == 0 ); assert ( _pInstance == 0 ); # endif } template < class TYPE > void * epicsSingleton < TYPE > :: _build () { return new TYPE (); } template < class TYPE > void epicsSingleton < TYPE > :: _destroy ( void * pDestroyTypeless ) { TYPE * pDestroy = reinterpret_cast < TYPE * > ( pDestroyTypeless ); delete pDestroy; } template < class TYPE > inline typename epicsSingleton < TYPE > :: reference epicsSingleton < TYPE > :: getReference () { return reference ( * this ); } template < class TYPE > inline const typename epicsSingleton < TYPE > :: reference epicsSingleton < TYPE > :: getReference () const { epicsSingleton < TYPE > * pConstCastAway = const_cast < epicsSingleton < TYPE > * > ( this ); return pConstCastAway->getReference (); } #endif // epicsSingleton_h base-7.0.3.1/modules/libcom/src/cxxTemplates/epicsSingletonMutex.cpp0000664000577000060420000000335413557101274024322 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeff O. Hill */ #include #define epicsExportSharedSymbols #include "epicsMutex.h" #include "epicsGuard.h" #include "epicsThread.h" #include "epicsSingleton.h" #ifndef SIZE_MAX # define SIZE_MAX UINT_MAX #endif static epicsThreadOnceId epicsSigletonOnceFlag ( EPICS_THREAD_ONCE_INIT ); static epicsMutex * pEPICSSigletonMutex = 0; extern "C" void SingletonMutexOnce ( void * /* pParm */ ) { // This class exists for the purpose of avoiding file scope // object chicken and egg problems. Therefore, pEPICSSigletonMutex // is never destroyed. pEPICSSigletonMutex = newEpicsMutex; } void SingletonUntyped :: incrRefCount ( PBuild pBuild ) { epicsThreadOnce ( & epicsSigletonOnceFlag, SingletonMutexOnce, 0 ); epicsGuard < epicsMutex > guard ( *pEPICSSigletonMutex ); assert ( _refCount < SIZE_MAX ); if ( _refCount == 0 ) { _pInstance = ( * pBuild ) (); } _refCount++; } void SingletonUntyped :: decrRefCount ( PDestroy pDestroy ) { epicsGuard < epicsMutex > guard ( *pEPICSSigletonMutex ); assert ( _refCount > 0 ); _refCount--; if ( _refCount == 0 ) { ( *pDestroy ) ( _pInstance ); _pInstance = 0; } } base-7.0.3.1/modules/libcom/src/cxxTemplates/resourceLib.cpp0000664000577000060420000000137713557101274022572 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeff Hill */ #include "resourceLib.h" #ifdef _MSC_VER # pragma warning ( push ) # pragma warning ( disable:4660 ) #endif template class intId < unsigned, 8u, sizeof(unsigned)*CHAR_BIT >; #ifdef _MSC_VER # pragma warning ( pop ) #endif base-7.0.3.1/modules/libcom/src/cxxTemplates/resourceLib.h0000664000577000060420000007671113557101274022243 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * General hash table templates for fast indexing of resources * of any base resource type and any resource identifier type. Fast * indexing is implemented with a hash lookup. The identifier type * implements the hash algorithm (or derives from one of the supplied * identifier types which provide a hashing routine). The table expands * dynamically depending on load, and without introducing non-determanistic * latency. * * Unsigned integer and string identifier classes are supplied here. * * Authors Jeffrey O. Hill * Marty Kraimer (string hash algorithm) * influenced by papers by Peter K. Pearson and Per-Ake Larson * * johill@lanl.gov * 505 665 1831 */ #ifndef INCresourceLibh #define INCresourceLibh #include #include #include #include #include #include #ifndef assert // allow use of epicsAssert.h #include #endif #include "tsSLList.h" #include "epicsString.h" #include "shareLib.h" typedef size_t resTableIndex; template < class T, class ID > class resTableIter; template < class T, class ID > class resTableIterConst; // // class resTable // // This class stores resource entries of type T which can be efficiently // located with a hash key of type ID. // // NOTES: // 1) class T must derive from class ID and also from class tsSLNode // // 2) If the "resTable::show (unsigned level)" member function is called then // class T must also implement a "show (unsigned level)" member function which // dumps increasing diagnostics information with increasing "level" to // standard out. // // 3) Classes of type ID must implement the following member functions: // // // equivalence test // bool operator == (const ID &); // // // ID to hash index convert (see examples below) // resTableIndex hash (unsigned nBitsHashIndex) const; // // 4) Storage for identifier of type ID must persist until the item of type // T is deleted from the resTable // template class resTable { public: resTable (); virtual ~resTable(); // Call " void T::show (unsigned level)" for each entry void show ( unsigned level ) const; void verify () const; int add ( T & res ); // returns -1 (id exists in table), 0 (success) T * remove ( const ID &idIn ); // remove entry void removeAll ( tsSLList & destination ); // remove all entries T * lookup ( const ID &idIn ) const; // locate entry // Call (pT->*pCB) () for each entry but expect poor performance // with sparcely populated tables void traverse ( void (T::*pCB)() ); void traverseConst ( void (T::*pCB)() const ) const; unsigned numEntriesInstalled () const; void setTableSize ( const unsigned newTableSize ); // iterate through all entries but expect poor performance // with sparcely populated tables typedef resTableIter < T, ID > iterator; typedef resTableIterConst < T, ID > iteratorConst; iterator firstIter (); iteratorConst firstIter () const; private: tsSLList < T > * pTable; unsigned nextSplitIndex; unsigned hashIxMask; unsigned hashIxSplitMask; unsigned nBitsHashIxSplitMask; unsigned logBaseTwoTableSize; unsigned nInUse; resTableIndex hash ( const ID & idIn ) const; T * find ( tsSLList & list, const ID & idIn ) const; void splitBucket (); unsigned tableSize () const; bool setTableSizePrivate ( unsigned logBaseTwoTableSize ); resTable ( const resTable & ); resTable & operator = ( const resTable & ); static unsigned resTableBitMask ( const unsigned nBits ); friend class resTableIter < T, ID >; friend class resTableIterConst < T, ID >; }; // // class resTableIter // // an iterator for the resource table class // template < class T, class ID > class resTableIter { public: resTableIter (); bool valid () const; bool operator == ( const resTableIter < T,ID > & rhs ) const; bool operator != ( const resTableIter < T,ID > & rhs ) const; resTableIter < T, ID > & operator = ( const resTableIter < T, ID > & ); T & operator * () const; T * operator -> () const; resTableIter < T, ID > & operator ++ (); resTableIter < T, ID > operator ++ ( int ); T * pointer (); private: tsSLIter < T > iter; unsigned index; resTable < T,ID > * pResTable; resTableIter ( resTable < T,ID > & tableIn ); void findNextEntry (); friend class resTable < T, ID >; }; // // class resTableIterConst // // an iterator for a const resource table class // template < class T, class ID > class resTableIterConst { public: resTableIterConst (); bool valid () const; bool operator == ( const resTableIterConst < T,ID > & rhs ) const; bool operator != ( const resTableIterConst < T,ID > & rhs ) const; resTableIterConst < T, ID > & operator = ( const resTableIterConst < T, ID > & ); const T & operator * () const; const T * operator -> () const; resTableIterConst < T, ID > & operator ++ (); resTableIterConst < T, ID > operator ++ ( int ); const T * pointer () const; private: tsSLIterConst < T > iter; unsigned index; const resTable < T,ID > * pResTable; resTableIterConst ( const resTable < T,ID > & tableIn ); void findNextEntry (); friend class resTable < T, ID >; }; // // Some ID classes that work with the above template // // // class intId // // signed or unsigned integer identifier (class T must be // a signed or unsigned integer type) // // this class works as type ID in resTable // // 1<. // Set this parameter to zero if unsure of the correct minimum // hash table size. // // MAX_ID_WIDTH specifies the maximum number of ls bits in an // integer identifier which might be set at any time. // // MIN_INDEX_WIDTH and MAX_ID_WIDTH are specified here at // compile time so that the hash index can be produced // efficiently. Hash indexes are produced more efficiently // when (MAX_ID_WIDTH - MIN_INDEX_WIDTH) is minimized. // template class intId { public: intId (const T &idIn); bool operator == (const intId &idIn) const; resTableIndex hash () const; const T getId() const; protected: T id; }; // // class chronIntIdResTable // // a specialized resTable which uses unsigned integer keys which are // allocated in chronological sequence // // NOTE: ITEM must public inherit from chronIntIdRes // class chronIntId : public intId { public: chronIntId ( const unsigned &idIn ); }; template class chronIntIdResTable : public resTable { public: chronIntIdResTable (); virtual ~chronIntIdResTable (); void idAssignAdd ( ITEM & item ); private: unsigned allocId; chronIntIdResTable ( const chronIntIdResTable & ); chronIntIdResTable & operator = ( const chronIntIdResTable & ); }; // // class chronIntIdRes // // resource with unsigned chronological identifier // template class chronIntIdRes : public chronIntId, public tsSLNode { public: chronIntIdRes (); private: void setId (unsigned newId); chronIntIdRes (const chronIntIdRes & ); friend class chronIntIdResTable; }; // // class stringId // // character string identifier // class epicsShareClass stringId { public: enum allocationType {copyString, refString}; stringId (const char * idIn, allocationType typeIn=copyString); virtual ~stringId(); resTableIndex hash () const; bool operator == (const stringId &idIn) const; const char * resourceName() const; // return the pointer to the string void show (unsigned level) const; private: stringId & operator = ( const stringId & ); stringId ( const stringId &); const char * pStr; const allocationType allocType; }; ///////////////////////////////////////////////// // resTable member functions ///////////////////////////////////////////////// // // resTable::resTable () // template inline resTable::resTable () : pTable ( 0 ), nextSplitIndex ( 0 ), hashIxMask ( 0 ), hashIxSplitMask ( 0 ), nBitsHashIxSplitMask ( 0 ), logBaseTwoTableSize ( 0 ), nInUse ( 0 ) {} template inline unsigned resTable::resTableBitMask ( const unsigned nBits ) { return ( 1 << nBits ) - 1; } // // resTable::remove () // // remove a res from the resTable // template T * resTable::remove ( const ID & idIn ) { if ( this->pTable ) { // search list for idIn and remove the first match tsSLList & list = this->pTable [ this->hash(idIn) ]; tsSLIter pItem = list.firstIter (); T *pPrev = 0; while ( pItem.valid () ) { const ID & idOfItem = *pItem; if ( idOfItem == idIn ) { if ( pPrev ) { list.remove ( *pPrev ); } else { list.get (); } this->nInUse--; break; } pPrev = pItem.pointer (); pItem++; } return pItem.pointer (); } else { return 0; } } template void resTable::removeAll ( tsSLList & destination ) { const unsigned N = this->tableSize (); for ( unsigned i = 0u; i < N; i++ ) { while ( T * pItem = this->pTable[i].get() ) { destination.add ( *pItem ); } } this->nInUse = 0; } // // resTable::lookup () // template inline T * resTable::lookup ( const ID & idIn ) const { if ( this->pTable ) { tsSLList & list = this->pTable [ this->hash ( idIn ) ]; return this->find ( list, idIn ); } else { return 0; } } // // resTable::hash () // template inline resTableIndex resTable::hash ( const ID & idIn ) const { resTableIndex h = idIn.hash (); resTableIndex h0 = h & this->hashIxMask; if ( h0 >= this->nextSplitIndex ) { return h0; } return h & this->hashIxSplitMask; } // // resTable::show // template void resTable::show ( unsigned level ) const { const unsigned N = this->tableSize (); printf ( "Hash table with %u buckets and %u items of type %s installed\n", N, this->nInUse, typeid(T).name() ); if ( level >= 1u && N ) { if ( level >= 2u ) { tsSLList * pList = this->pTable; while ( pList < & this->pTable[N] ) { tsSLIter pItem = pList->firstIter (); while ( pItem.valid () ) { tsSLIter pNext = pItem; pNext++; pItem.pointer()->show ( level - 2u ); pItem = pNext; } pList++; } } double X = 0.0; double XX = 0.0; unsigned maxEntries = 0u; unsigned empty = 0; for ( unsigned i = 0u; i < N; i++ ) { tsSLIter pItem = this->pTable[i].firstIter (); unsigned count = 0; while ( pItem.valid () ) { if ( level >= 3u ) { pItem->show ( level ); } count++; pItem++; } if ( count > 0u ) { X += count; XX += count * count; if ( count > maxEntries ) { maxEntries = count; } } else empty++; } double mean = X / N; double stdDev = sqrt( XX / N - mean * mean ); printf ( "entries per bucket: mean = %f std dev = %f max = %u\n", mean, stdDev, maxEntries ); printf("%u empty buckets\n", empty); if ( X != this->nInUse ) { printf ("this->nInUse didnt match items counted which was %f????\n", X ); } } } // self test template void resTable::verify () const { const unsigned N = this->tableSize (); if ( this->pTable ) { assert ( this->nextSplitIndex <= this->hashIxMask + 1 ); assert ( this->hashIxMask ); assert ( this->hashIxMask == ( this->hashIxSplitMask >> 1 ) ); assert ( this->hashIxSplitMask ); assert ( this->nBitsHashIxSplitMask ); assert ( resTableBitMask ( this->nBitsHashIxSplitMask ) == this->hashIxSplitMask ); assert ( this->logBaseTwoTableSize ); assert ( this->nBitsHashIxSplitMask <= this->logBaseTwoTableSize ); } else { assert ( this->nextSplitIndex == 0 ); assert ( this->hashIxMask == 0 ); assert ( this->hashIxSplitMask == 0 ); assert ( this->nBitsHashIxSplitMask == 0 ); assert ( this->logBaseTwoTableSize == 0 ); } unsigned total = 0u; for ( unsigned i = 0u; i < N; i++ ) { tsSLIter pItem = this->pTable[i].firstIter (); unsigned count = 0; while ( pItem.valid () ) { resTableIndex index = this->hash ( *pItem ); assert ( index == i ); count++; pItem++; } total += count; } assert ( total == this->nInUse ); } // // resTable::traverse // template void resTable::traverse ( void (T::*pCB)() ) { const unsigned N = this->tableSize (); for ( unsigned i = 0u; i < N; i++ ) { tsSLIter pItem = this->pTable[i].firstIter (); while ( pItem.valid () ) { tsSLIter pNext = pItem; pNext++; ( pItem.pointer ()->*pCB ) (); pItem = pNext; } } } // // resTable::traverseConst // template void resTable::traverseConst ( void (T::*pCB)() const ) const { const unsigned N = this->tableSize (); for ( unsigned i = 0u; i < N; i++ ) { const tsSLList < T > & table = this->pTable[i]; tsSLIterConst pItem = table.firstIter (); while ( pItem.valid () ) { tsSLIterConst pNext = pItem; pNext++; ( pItem.pointer ()->*pCB ) (); pItem = pNext; } } } template inline unsigned resTable::numEntriesInstalled () const { return this->nInUse; } template inline unsigned resTable::tableSize () const { if ( this->pTable ) { return ( this->hashIxMask + 1 ) + this->nextSplitIndex; } else { return 0; } } // it will be more efficent to call this once prior to installing // the first entry template void resTable::setTableSize ( const unsigned newTableSize ) { if ( newTableSize == 0u ) { return; } // // count the number of bits in newTableSize and round up // to the next power of two // unsigned newMask = newTableSize - 1; unsigned nbits; for ( nbits = 0; nbits < sizeof (newTableSize) * CHAR_BIT; nbits++ ) { unsigned nBitsMask = resTableBitMask ( nbits ); if ( ( newMask & ~nBitsMask ) == 0){ break; } } setTableSizePrivate ( nbits ); } template bool resTable::setTableSizePrivate ( unsigned logBaseTwoTableSizeIn ) { // dont shrink if ( this->logBaseTwoTableSize >= logBaseTwoTableSizeIn ) { return true; } // dont allow ridiculously small tables if ( logBaseTwoTableSizeIn < 4 ) { logBaseTwoTableSizeIn = 4; } const unsigned newTableSize = 1 << logBaseTwoTableSizeIn; # if ! defined (__GNUC__) || __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 92 ) const unsigned oldTableSize = this->pTable ? 1 << this->logBaseTwoTableSize : 0; # endif const unsigned oldTableOccupiedSize = this->tableSize (); tsSLList * pNewTable; try { pNewTable = ( tsSLList * ) ::operator new ( newTableSize * sizeof ( tsSLList ) ); } catch ( ... ){ if ( ! this->pTable ) { throw; } return false; } // run the constructors using placement new unsigned i; for ( i = 0u; i < oldTableOccupiedSize; i++ ) { new ( &pNewTable[i] ) tsSLList ( this->pTable[i] ); } for ( i = oldTableOccupiedSize; i < newTableSize; i++ ) { new ( &pNewTable[i] ) tsSLList; } // Run the destructors explicitly. Currently this destructor is a noop. // The Tornado II compiler and RedHat 6.2 will not compile ~tsSLList() but // since its a NOOP we can find an ugly workaround # if ! defined (__GNUC__) || __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 92 ) for ( i = 0; i < oldTableSize; i++ ) { this->pTable[i].~tsSLList(); } # endif if ( ! this->pTable ) { this->hashIxSplitMask = resTableBitMask ( logBaseTwoTableSizeIn ); this->nBitsHashIxSplitMask = logBaseTwoTableSizeIn; this->hashIxMask = this->hashIxSplitMask >> 1; this->nextSplitIndex = 0; } operator delete ( this->pTable ); this->pTable = pNewTable; this->logBaseTwoTableSize = logBaseTwoTableSizeIn; return true; } template void resTable::splitBucket () { // double the hash table when necessary // (this results in only a memcpy overhead, but // no hashing or entry redistribution) if ( this->nextSplitIndex > this->hashIxMask ) { bool success = this->setTableSizePrivate ( this->nBitsHashIxSplitMask + 1 ); if ( ! success ) { return; } this->nBitsHashIxSplitMask += 1; this->hashIxSplitMask = resTableBitMask ( this->nBitsHashIxSplitMask ); this->hashIxMask = this->hashIxSplitMask >> 1; this->nextSplitIndex = 0; } // rehash only the items in the split bucket tsSLList tmp ( this->pTable[ this->nextSplitIndex ] ); this->nextSplitIndex++; T *pItem = tmp.get(); while ( pItem ) { resTableIndex index = this->hash ( *pItem ); this->pTable[index].add ( *pItem ); pItem = tmp.get(); } } // // add a res to the resTable // template int resTable::add ( T &res ) { if ( ! this->pTable ) { this->setTableSizePrivate ( 10 ); } else if ( this->nInUse >= this->tableSize() ) { this->splitBucket (); tsSLList &list = this->pTable[this->hash(res)]; if ( this->find ( list, res ) != 0 ) { return -1; } } tsSLList &list = this->pTable[this->hash(res)]; if ( this->find ( list, res ) != 0 ) { return -1; } list.add ( res ); this->nInUse++; return 0; } // // find // searches from where the iterator points to the // end of the list for idIn // // iterator points to the item found upon return // (or NULL if nothing matching was found) // template T * resTable::find ( tsSLList &list, const ID &idIn ) const { tsSLIter pItem = list.firstIter (); while ( pItem.valid () ) { const ID & idOfItem = *pItem; if ( idOfItem == idIn ) { break; } pItem++; } return pItem.pointer (); } // // ~resTable::resTable() // template resTable::~resTable() { operator delete ( this->pTable ); } // // resTable::resTable ( const resTable & ) // private - not to be used - implemented to eliminate warnings // template inline resTable::resTable ( const resTable & ) { } // // resTable::resTable & operator = ( const resTable & ) // private - not to be used - implemented to eliminate warnings // template inline resTable & resTable::operator = ( const resTable & ) { return *this; } template inline resTableIterConst < T, ID > resTable::firstIter () const { return resTableIterConst < T, ID > ( *this ); } template inline resTableIter < T, ID > resTable::firstIter () { return resTableIter < T, ID > ( *this ); } ////////////////////////////////////////////// // resTableIter member functions ////////////////////////////////////////////// template < class T, class ID > inline resTableIter::resTableIter ( resTable < T,ID > & tableIn ) : index ( 0 ), pResTable ( & tableIn ) { this->findNextEntry (); } template < class T, class ID > inline resTableIter::resTableIter () : iter ( tsSLList::invalidIter() ), index ( 0 ), pResTable ( 0 ) { } template < class T, class ID > inline void resTableIter::findNextEntry () { if ( this->pResTable ) { while ( this->index < this->pResTable->tableSize() ) { this->iter = this->pResTable->pTable[this->index++].firstIter (); if ( this->iter.valid () ) { break; } } } } template < class T, class ID > inline bool resTableIter::valid () const { return this->iter.valid (); } template < class T, class ID > inline bool resTableIter::operator == ( const resTableIter < T,ID > & rhs ) const { return ( this->pResTable == rhs.pResTable && this->index == rhs.index && this->iter == rhs.iter ); } template < class T, class ID > inline bool resTableIter::operator != ( const resTableIter < T,ID > & rhs ) const { return ! this->operator == ( rhs ); } template < class T, class ID > inline resTableIter < T, ID > & resTableIter::operator = ( const resTableIter < T, ID > & rhs ) { this->pResTable = rhs.pResTable; this->index = rhs.index; this->iter = rhs.iter; return *this; } template < class T, class ID > inline T & resTableIter::operator * () const { return this->iter.operator * (); } template < class T, class ID > inline T * resTableIter::operator -> () const { return this->iter.operator -> (); } template < class T, class ID > inline resTableIter & resTableIter::operator ++ () { this->iter++; if ( ! this->iter.valid() ) { this->findNextEntry (); } return *this; } template < class T, class ID > inline resTableIter resTableIter::operator ++ ( int ) { resTableIter tmp = *this; this->operator ++ (); return tmp; } template < class T, class ID > inline T * resTableIter::pointer () { return this->iter.pointer (); } ////////////////////////////////////////////// // resTableIterConst member functions ////////////////////////////////////////////// template < class T, class ID > inline resTableIterConst::resTableIterConst ( const resTable < T,ID > & tableIn ) : index ( 0 ), pResTable ( & tableIn ) { this->findNextEntry (); } template < class T, class ID > inline resTableIterConst::resTableIterConst () : iter ( tsSLList::invalidIter() ), index ( 0 ), pResTable ( 0 ) { } template < class T, class ID > inline void resTableIterConst::findNextEntry () { if ( this->pResTable ) { while ( this->index < this->pResTable->tableSize() ) { const tsSLList * pList = & this->pResTable->pTable[this->index++]; this->iter = pList->firstIter (); if ( this->iter.valid () ) { break; } } } } template < class T, class ID > inline bool resTableIterConst::valid () const { return this->iter.valid (); } template < class T, class ID > inline bool resTableIterConst::operator == ( const resTableIterConst < T,ID > & rhs ) const { return ( this->pResTable == rhs.pResTable && this->index == rhs.index && this->iter == rhs.iter ); } template < class T, class ID > inline bool resTableIterConst::operator != ( const resTableIterConst < T,ID > & rhs ) const { return ! this->operator == ( rhs ); } template < class T, class ID > inline resTableIterConst < T, ID > & resTableIterConst::operator = ( const resTableIterConst < T, ID > & rhs ) { this->pResTable = rhs.pResTable; this->index = rhs.index; this->iter = rhs.iter; return *this; } template < class T, class ID > inline const T & resTableIterConst::operator * () const { return this->iter.operator * (); } template < class T, class ID > inline const T * resTableIterConst::operator -> () const { return this->iter.operator -> (); } template < class T, class ID > inline resTableIterConst & resTableIterConst::operator ++ () { this->iter++; if ( ! this->iter.valid() ) { this->findNextEntry (); } return *this; } template < class T, class ID > inline resTableIterConst resTableIterConst::operator ++ ( int ) { resTableIterConst tmp = *this; this->operator ++ (); return tmp; } template < class T, class ID > inline const T * resTableIterConst::pointer () const { return this->iter.pointer (); } ////////////////////////////////////////////// // chronIntIdResTable member functions ////////////////////////////////////////////// inline chronIntId::chronIntId ( const unsigned &idIn ) : intId ( idIn ) {} // // chronIntIdResTable::chronIntIdResTable() // template inline chronIntIdResTable::chronIntIdResTable () : resTable (), allocId(1u) {} template inline chronIntIdResTable::chronIntIdResTable ( const chronIntIdResTable & ) : resTable (), allocId(1u) {} template inline chronIntIdResTable & chronIntIdResTable:: operator = ( const chronIntIdResTable & ) { return *this; } // // chronIntIdResTable::~chronIntIdResTable() // (not inline because it is virtual) // template chronIntIdResTable::~chronIntIdResTable() {} // // chronIntIdResTable::add() // // NOTE: This detects (and avoids) the case where // the PV id wraps around and we attempt to have two // resources with the same id. // template inline void chronIntIdResTable::idAssignAdd (ITEM &item) { int status; do { item.chronIntIdRes::setId (allocId++); status = this->resTable::add (item); } while (status); } ///////////////////////////////////////////////// // chronIntIdRes member functions ///////////////////////////////////////////////// // // chronIntIdRes::chronIntIdRes // template inline chronIntIdRes::chronIntIdRes () : chronIntId (UINT_MAX) {} // // id::setId () // // workaround for bug in DEC compiler // template inline void chronIntIdRes::setId (unsigned newId) { this->id = newId; } ///////////////////////////////////////////////// // intId member functions ///////////////////////////////////////////////// // // intId::intId // // (if this is inline SUN PRO botches the template instantiation) template intId::intId (const T &idIn) : id (idIn) {} // // intId::operator == () // template inline bool intId::operator == (const intId &idIn) const { return this->id == idIn.id; } // // intId::getId () // template inline const T intId::getId () const { return this->id; } // // integerHash() // // converts any integer into a hash table index // template < class T > inline resTableIndex integerHash ( unsigned MIN_INDEX_WIDTH, unsigned MAX_ID_WIDTH, const T &id ) { resTableIndex hashid = static_cast ( id ); // // the intent here is to gurantee that all components of the // integer contribute even if the resTableIndex returned might // index a small table. // // On most compilers the optimizer will unroll this loop so this // is actually a very small inline function // // Experiments using the microsoft compiler show that this isnt // slower than switching on the architecture size and unrolling the // loop explicitly (that solution has resulted in portability // problems in the past). // unsigned width = MAX_ID_WIDTH; do { width >>= 1u; hashid ^= hashid>>width; } while (width>MIN_INDEX_WIDTH); // // the result here is always masked to the // proper size after it is returned to the "resTable" class // return hashid; } // // intId::hash() // template inline resTableIndex intId::hash () const { return integerHash ( MIN_INDEX_WIDTH, MAX_ID_WIDTH, this->id ); } //////////////////////////////////////////////////// // stringId member functions //////////////////////////////////////////////////// // // stringId::operator == () // inline bool stringId::operator == (const stringId &idIn) const { if (this->pStr!=NULL && idIn.pStr!=NULL) { return strcmp(this->pStr,idIn.pStr)==0; } return false; // not equal } // // stringId::resourceName () // inline const char * stringId::resourceName () const { return this->pStr; } #ifdef instantiateRecourceLib // // stringId::stringId() // stringId::stringId (const char * idIn, allocationType typeIn) : allocType (typeIn) { if (typeIn==copyString) { unsigned nChars = strlen (idIn) + 1u; this->pStr = new char [nChars]; memcpy ( (void *) this->pStr, idIn, nChars ); } else { this->pStr = idIn; } } // // stringId::show () // void stringId::show (unsigned level) const { if (level>2u) { printf ("resource id = %s\n", this->pStr); } } // // stringId::~stringId() // // // this needs to be instantiated only once (normally in libCom) // stringId::~stringId() { if (this->allocType==copyString) { if (this->pStr!=NULL) { // // the microsoft and solaris compilers will // not allow a pointer to "const char" // to be deleted // // the HP-UX compiler gives us a warning on // each cast away of const, but in this case // it cant be avoided. // // The DEC compiler complains that const isnt // really significant in a cast if it is present. // // I hope that deleting a pointer to "char" // is the same as deleting a pointer to // "const char" on all compilers // delete [] const_cast(this->pStr); } } } // // stringId::hash() // resTableIndex stringId::hash() const { if (!this->pStr) { return 0u; } return epicsStrHash(this->pStr, 0); } #endif // if instantiateRecourceLib is defined #endif // INCresourceLibh base-7.0.3.1/modules/libcom/src/cxxTemplates/test/Makefile0000664000577000060420000000177213557101274022226 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* TOP=../../../.. include $(TOP)/configure/CONFIG resourceLibTest_SRCS += resourceLibTest.cc TESTPROD_HOST += resourceLibTest tsDLListBench_SRCS += tsDLListBench.cc TESTPROD_HOST += tsDLListBench tsDLListTest_SRCS += tsDLListTest.cc TESTPROD_HOST += tsDLListTest tsSLListBench_SRCS += tsSLListBench.cc TESTPROD_HOST += tsSLListBench tsSLListTest_SRCS += tsSLListTest.cc TESTPROD_HOST += tsSLListTest minmaxTest_SRCS += minmaxTest.cc TESTPROD_HOST += minmaxTest PROD_LIBS = Com include $(TOP)/configure/RULES base-7.0.3.1/modules/libcom/src/cxxTemplates/test/minmaxTest.cc0000664000577000060420000000151613557101274023222 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include #include "tsMinMax.h" int main () { float f1 = 3.3f; float f2 = 3.4f; float f3; f3 = tsMin(f1,f2); assert(f3==f1); f3 = tsMax(f1,f2); assert(f3==f2); int i1 = 3; int i2 = 4; int i3; i3 = tsMin(i1,i2); assert(i3==i1); i3 = tsMax(i1,i2); assert(i3==i2); return 0; } base-7.0.3.1/modules/libcom/src/cxxTemplates/test/resourceLibTest.cc0000664000577000060420000001630413557101274024210 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include #include #include #include #include #define epicsExportSharedSymbols #define instantiateRecourceLib #include "resourceLib.h" #if defined(__GNUC__) && ( __GNUC__<2 || (__GNUC__==2 && __GNUC__<8) ) typedef intId testIntId; #else typedef intId testIntId; #endif #define verify(exp) ((exp) ? (void)0 : \ epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) static void empty() { } class albert : public testIntId, public tsSLNode { public: albert (resTable< albert, testIntId > &atIn, unsigned idIn) : testIntId(idIn), at(atIn) { verify (at.add (*this)==0); } void show (unsigned /* level */) { } void destroy() { at.remove(*this); delete this; } private: resTable< albert, testIntId > &at; }; class fred : public testIntId, public tsSLNode { public: fred (const char *pNameIn, unsigned idIn) : testIntId(idIn), pName(pNameIn) {} void show (unsigned) { printf("fred %s\n", pName); } void destroy() { // always on stack so noop } private: const char * const pName; }; class jane : public stringId, public tsSLNode { public: jane (const char *pNameIn) : stringId (pNameIn) {} void testTraverse(); void destroy() { // always on stack so noop } }; // // jane::testTraverse() // void jane::testTraverse() { printf("Traverse Test\n"); this->show(10); } int main() { unsigned i; clock_t start, finish; double duration; const unsigned LOOPS = 500000; resTable < fred, testIntId > intTbl; resTable < jane, stringId> strTbl; fred fred0("fred0",0); fred fred1("fred1",0x1000a432); fred fred2("fred2",0x0000a432); fred fred3("fred3",1); fred fred4("fred4",2); fred fred5("fred5",3); fred fred6("fred6",4); fred fred7("fred7",5); fred fred8("fred8",6); fred fred9("fred9",7); jane jane1("rrrrrrrrrrrrrrrrrrrrrrrrrr1"); jane jane2("rrrrrrrrrrrrrrrrrrrrrrrrrr2"); fred *pFred; jane *pJane; testIntId intId0 (0); testIntId intId1 (0x1000a432); testIntId intId2 (0x0000a432); testIntId intId3 (1); testIntId intId4 (2); testIntId intId5 (3); testIntId intId6 (4); testIntId intId7 (5); testIntId intId8 (6); testIntId intId9 (7); stringId strId1("rrrrrrrrrrrrrrrrrrrrrrrrrr1"); strTbl.verify (); stringId strId2("rrrrrrrrrrrrrrrrrrrrrrrrrr2"); strTbl.verify (); intTbl.setTableSize ( 100000 ); verify (intTbl.add(fred0)==0); intTbl.verify (); verify (intTbl.add(fred1)==0); intTbl.verify (); verify (intTbl.add(fred2)==0); intTbl.verify (); verify (intTbl.add(fred3)==0); intTbl.verify (); verify (intTbl.add(fred4)==0); intTbl.verify (); intTbl.setTableSize ( 200000 ); verify (intTbl.add(fred5)==0); intTbl.verify (); verify (intTbl.add(fred6)==0); intTbl.verify (); verify (intTbl.add(fred7)==0); intTbl.verify (); verify (intTbl.add(fred8)==0); intTbl.verify (); verify (intTbl.add(fred9)==0); intTbl.verify (); start = clock(); for (i=0; i alTbl; for (i=0; i alTblIter ( alTbl.firstIter() ); albert *pa; i=0; while ( ( pa = alTblIter.pointer() ) ) { i++; alTblIter++; } verify ( i == elementCount ); alTbl.verify (); for ( i = 0; i < elementCount; i++ ) { verify ( pAlbert[i] == alTbl.lookup( pAlbert[i]->getId() ) ); } alTbl.verify (); for ( i = 0; i < elementCount; i += 2 ) { verify ( pAlbert[i] == alTbl.remove ( pAlbert[i]->getId() ) ); } alTbl.verify (); for ( i = 0; i < elementCount; i += 2 ) { verify ( 0 == alTbl.lookup ( pAlbert[i]->getId() ) ); } alTbl.verify (); for ( i = 1; i < elementCount; i += 2 ) { verify ( pAlbert[i] == alTbl.lookup ( pAlbert[i]->getId() ) ); } alTbl.verify (); return 0; } base-7.0.3.1/modules/libcom/src/cxxTemplates/test/tsDLListBench.cc0000664000577000060420000000276513557101274023542 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include "tsDLList.h" #include #include #include class fred : public tsDLNode { public: fred() : count(0) {} void inc () {count++;} private: unsigned count; }; class jane : public fred, public tsDLNode { public: jane() {} private: }; #define LOOPCOUNT 100000 int main () { tsDLList list; tsDLIter iter = list.firstIter(); fred *pFred; unsigned i; clock_t clk; clock_t diff; double delay; for (i=0; iinc(); iter++; } diff = clock() - clk; delay = diff; delay = delay/CLOCKS_PER_SEC; delay = delay/LOOPCOUNT; printf("delay = %15.10f\n", delay); pFred = new fred(); clk = clock(); for (i=0; iinc(); } diff = clock() - clk; delay = diff; delay = delay/CLOCKS_PER_SEC; delay = delay/LOOPCOUNT; printf("delay = %15.10f\n", delay); return 0; } base-7.0.3.1/modules/libcom/src/cxxTemplates/test/tsDLListTest.cc0000664000577000060420000000652213557101274023435 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include "tsDLList.h" #include #include #define verify(exp) ((exp) ? (void)0 : \ epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) class fred : public tsDLNode { public: fred(const char * const pNameIn) : pName(pNameIn){} void show () {printf("%s\n", pName);} private: const char * const pName; }; class jane : public fred, public tsDLNode { public: jane(const char * const pNameIn) : fred(pNameIn){} private: }; int main () { unsigned i; tsDLList list; fred *pFred; fred *pFredII; fred *pFredBack; tsDLList janeList; tsDLIter janeFwdIter = janeList.firstIter(); tsDLIter janeBwdIter = janeList.lastIter(); jane *pJane; pFred = new fred ("A"); pFredII = new fred ("B"); list.add (*pFred); list.add (*pFredII); tsDLIter iter = list.firstIter(); verify (iter.pointer() == pFred); iter++; verify (iter.pointer() == pFredII); list.remove(*pFred); list.add(*pFred); pFredBack = list.get(); verify (pFredBack == pFredII); pFredBack = list.get(); verify (pFredBack == pFred); verify (list.count() == 0u); list.add(*pFred); list.add(*pFredII); list.add(* new fred("C")); list.add(* new fred("D")); iter = list.firstIter(); while ( iter.valid() ) { iter->show(); iter++; } pJane = new jane("JA"); janeList.add(*pJane); pJane = new jane("JB"); verify ( janeList.find ( *pJane ) == -1 ); janeList.add(*pJane); verify ( janeList.find ( *pJane ) == 1 ); while ( janeFwdIter.valid() ) { janeFwdIter->show(); janeFwdIter++; } while ( janeBwdIter.valid() ) { janeBwdIter->show(); janeBwdIter--; } iter = list.firstIter(); while ( iter.valid() ) { iter->show(); iter++; } tsDLIter < jane > bdIter = janeList.firstIter (); i = 0; while ( bdIter.valid () ) { i++; bdIter++; } verify ( i == janeList.count () ); iter = list.firstIter(); while ( iter.pointer() ) { list.remove( * iter.pointer() ); iter++; } verify (list.count()==0); janeFwdIter = janeList.firstIter(); while ( janeFwdIter.valid() ) { janeList.remove( * janeFwdIter.pointer() ); janeFwdIter++; } verify (janeList.count()==0); pJane = new jane("JA"); janeList.add(*pJane); pJane = new jane("JB"); janeList.add(*pJane); janeBwdIter = janeList.lastIter(); while ( janeBwdIter.valid() ) { janeList.remove( * janeBwdIter.pointer() ); janeBwdIter--; } verify (janeList.count()==0); return 0; } base-7.0.3.1/modules/libcom/src/cxxTemplates/test/tsSLListBench.cc0000664000577000060420000000330613557101274023551 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include "tsSLList.h" #include #include #include /* * gnuc does not provide this under sunos4 */ #if !defined(CLOCKS_PER_SEC) && defined(SUNOS4) # define CLOCKS_PER_SEC 1000000 #endif class fred : public tsSLNode { public: fred() : count(0) {} void inc () {count++;} private: unsigned count; }; class jane : public fred, public tsSLNode { public: jane() {} private: }; #define LOOPCOUNT 100000 int main () { tsSLList list; fred *pFred; unsigned i; clock_t clk; clock_t diff; double delay; for (i=0; i iter ( list.firstIter () ); while ( iter.valid () ) { iter->inc (); iter++; } } diff = clock() - clk; delay = diff; delay = delay/CLOCKS_PER_SEC; delay = delay/LOOPCOUNT; printf("delay = %15.10f\n", delay); pFred = new fred(); clk = clock(); { tsSLIter iter ( list.firstIter () ); for ( i=0; iinc(); } } diff = clock() - clk; delay = diff; delay = delay/CLOCKS_PER_SEC; delay = delay/LOOPCOUNT; printf("delay = %15.10f\n", delay); return 0; } base-7.0.3.1/modules/libcom/src/cxxTemplates/test/tsSLListTest.cc0000664000577000060420000000437413557101274023457 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include "tsSLList.h" #include #include class fred : public tsSLNode { public: fred(const char * const pNameIn) : pName(pNameIn){} void show () {printf("%s\n", pName);} private: const char * const pName; }; class jane : public fred, public tsSLNode { public: jane(const char * const pNameIn) : fred(pNameIn){} private: }; int main () { tsSLList list; fred *pFred; fred *pFredII; fred *pFredBack; tsSLList janeList; jane *pJane; pFred = new fred("A"); pFredII = new fred("B"); list.add(*pFred); list.add(*pFredII); { tsSLIter iter1 = list.firstIter (); tsSLIter iter2 = iter1; tsSLIter iter3 = iter1; assert ( iter1 == iter3++ ); assert ( iter3 == ++iter2 ); list.remove ( *pFredII ); // removes pFred } list.add ( *pFred ); pFredBack = list.get(); assert (pFredBack == pFred); pFredBack = list.get(); assert (pFredBack == pFredII); list.add(*pFredII); list.add(*pFred); while ( list.get () ); pFredBack = list.get(); assert (pFredBack == 0); list.add(*pFred); list.add(*pFredII); list.add(* new fred("C")); list.add(* new fred("D")); { tsSLIter < fred > iter = list.firstIter(); while ( iter.valid () ) { iter->show(); ++iter; } } pJane = new jane("JA"); janeList.add(*pJane); pJane = new jane("JB"); janeList.add(*pJane); { tsSLIter < jane > iter = janeList.firstIter (); while ( iter.valid () ) { iter->show(); ++iter; } } { tsSLIter < fred > iter = list.firstIter (); while ( iter.valid () ) { iter->show (); iter++; } } while ( list.get () ); { tsSLIter < fred > iter = list.firstIter (); assert ( ! iter.valid () ); } return 0; } base-7.0.3.1/modules/libcom/src/cxxTemplates/tsDLList.h0000664000577000060420000003610713557101274021462 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * type safe doubly linked list templates * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef tsDLListH_include #define tsDLListH_include template class tsDLList; template class tsDLIterConst; template class tsDLIter; // // class tsDLNode // // a node in a doubly linked list containing entries of type T // ( class T must publicly derive from class tsDLNode ) // template < class T > class tsDLNode { public: tsDLNode (); tsDLNode ( const tsDLNode & ); const tsDLNode & operator = ( const tsDLNode & ); private: T * pNext; T * pPrev; friend class tsDLList; friend class tsDLIter; friend class tsDLIterConst; }; // // class tsDLList // // a doubly linked list containing entries of type T // ( class T must publicly derive from class tsDLNode ) // template < class T > class tsDLList { public: tsDLList (); // create empty list unsigned count () const; // number of items on list void add ( T & item ); // add item to end of list void add ( tsDLList & addList ); // add to end of list - addList left empty void push ( T & item ); // add item to beginning of list void push ( tsDLList & pushList ); // add to beg of list - pushList left empty void remove ( T & item ); // remove item from list void removeAll ( tsDLList & destination ); T * get (); // removes first item on list T * pop (); // same as get () void insertAfter ( T & item, T & itemBefore ); // insert item immediately after itemBefore void insertBefore ( T & item, T & itemAfter ); // insert item immediately before itemAfter int find ( const T & item ) const; // returns -1 if not present, node number if present T * first ( void ) const; // ptr to first item on list T * last ( void ) const; // ptr to last item on list tsDLIterConst firstIter () const; tsDLIter firstIter (); tsDLIterConst lastIter () const; tsDLIter lastIter (); static tsDLIterConst invalidConstIter (); static tsDLIter invalidIter (); private: T * pFirst; T * pLast; unsigned itemCount; void clear (); tsDLList ( const tsDLList & ); // not allowed const tsDLList & operator = ( const tsDLList & ); // not allowed }; // // class tsDLIterConst // // bi-directional iterator for a const doubly linked list // template class tsDLIterConst { public: tsDLIterConst (); bool valid () const; bool operator == ( const tsDLIterConst & rhs ) const; bool operator != ( const tsDLIterConst & rhs ) const; tsDLIterConst & operator = ( const tsDLIterConst & ); const T & operator * () const; const T * operator -> () const; tsDLIterConst & operator ++ (); tsDLIterConst operator ++ (int); tsDLIterConst & operator -- (); tsDLIterConst operator -- (int); const T * pointer () const; private: const T * pEntry; tsDLIterConst ( const T * pInitialEntry ); friend class tsDLList ; }; // // class tsDLIter // // bi-directional iterator for a doubly linked list // template class tsDLIter { public: tsDLIter (); bool valid () const; bool operator == ( const tsDLIter & rhs ) const; bool operator != ( const tsDLIter & rhs ) const; tsDLIter & operator = ( const tsDLIter & ); T & operator * () const; T * operator -> () const; tsDLIter & operator ++ (); tsDLIter operator ++ ( int ); tsDLIter & operator -- (); tsDLIter operator -- ( int ); T * pointer () const; private: T * pEntry; tsDLIter ( T *pInitialEntry ); friend class tsDLList ; }; /////////////////////////////////// // tsDLNode member functions /////////////////////////////////// // // tsDLNode::tsDLNode () // template inline tsDLNode::tsDLNode() : pNext(0), pPrev(0) {} template inline tsDLNode::tsDLNode ( const tsDLNode & ) : pNext (0), pPrev(0) {} // // tsDLNode::operator = () // // when someone tries to copy another node into a node // do _not_ change the node pointers // template inline const tsDLNode & tsDLNode::operator = ( const tsDLNode & ) { return * this; } ////////////////////////////////////// // tsDLList member functions ////////////////////////////////////// // // tsDLList::tsDLList () // template inline tsDLList::tsDLList () { this->clear (); } // // tsDLList::count () // // (returns the number of items on the list) // template inline unsigned tsDLList::count () const { return this->itemCount; } // // tsDLList::first () // template inline T * tsDLList::first (void) const { return this->pFirst; } // // tsDLList::last () // template inline T *tsDLList::last (void) const { return this->pLast; } // // tsDLList::clear () // template inline void tsDLList::clear () { this->pFirst = 0; this->pLast = 0; this->itemCount = 0u; } // // tsDLList::remove () // template inline void tsDLList::remove ( T &item ) { tsDLNode &theNode = item; if ( this->pLast == &item ) { this->pLast = theNode.pPrev; } else { tsDLNode &nextNode = *theNode.pNext; nextNode.pPrev = theNode.pPrev; } if ( this->pFirst == &item ) { this->pFirst = theNode.pNext; } else { tsDLNode &prevNode = *theNode.pPrev; prevNode.pNext = theNode.pNext; } this->itemCount--; } // // tsDLList::removeAll () // template inline void tsDLList::removeAll ( tsDLList & destination ) { destination.pFirst = this->pFirst; destination.pLast = this->pLast; destination.itemCount = this->itemCount; this->pFirst = 0; this->pLast = 0; this->itemCount = 0; } // // tsDLList::get () // template inline T * tsDLList::get() { T *pItem = this->pFirst; if ( pItem ) { this->remove ( *pItem ); } return pItem; } // // tsDLList::pop () // // (returns the first item on the list) template inline T * tsDLList::pop () { return this->get (); } // // tsDLList::add () // // adds addList to the end of the list // (and removes all items from addList) // template inline void tsDLList::add ( tsDLList &addList ) { if ( addList.itemCount != 0u ) { if ( this->itemCount == 0u ) { this->pFirst = addList.pFirst; } else { tsDLNode *pLastNode = this->pLast; tsDLNode *pAddListFirstNode = addList.pFirst; pLastNode->pNext = addList.pFirst; pAddListFirstNode->pPrev = addList.pLast; } this->pLast = addList.pLast; this->itemCount += addList.itemCount; addList.clear(); } } // // tsDLList::add () // // add an item to the end of the list // template inline void tsDLList::add (T &item) { tsDLNode &theNode = item; theNode.pNext = 0; theNode.pPrev = this->pLast; if ( this->itemCount ) { tsDLNode &lastNode = *this->pLast; lastNode.pNext = &item; } else { this->pFirst = &item; } this->pLast = &item; this->itemCount++; } // // tsDLList::insertAfter () // // place item in the list immediately after itemBefore // template inline void tsDLList::insertAfter (T &item, T &itemBefore) { tsDLNode &nodeItem = item; tsDLNode &nodeBefore = itemBefore; nodeItem.pPrev = &itemBefore; nodeItem.pNext = nodeBefore.pNext; nodeBefore.pNext = &item; if (nodeItem.pNext) { tsDLNode *pNextNode = nodeItem.pNext; pNextNode->pPrev = &item; } else { this->pLast = &item; } this->itemCount++; } // // tsDLList::insertBefore () // // place item in the list immediately before itemAfter // template inline void tsDLList::insertBefore (T &item, T &itemAfter) { tsDLNode &node = item; tsDLNode &nodeAfter = itemAfter; node.pNext = &itemAfter; node.pPrev = nodeAfter.pPrev; nodeAfter.pPrev = &item; if (node.pPrev) { tsDLNode &prevNode = *node.pPrev; prevNode.pNext = &item; } else { this->pFirst = &item; } this->itemCount++; } // // tsDLList::push () // // adds pushList to the beginning of the list // (and removes all items from pushList) // template inline void tsDLList::push ( tsDLList & pushList ) { if ( pushList.itemCount != 0u ) { if ( this->itemCount ) { tsDLNode * pFirstNode = this->pFirst; tsDLNode * pAddListLastNode = pushList.pLast; pFirstNode->pPrev = pushList.pLast; pAddListLastNode->pNext = this->pFirst; } else { this->pLast = pushList.pLast; } this->pFirst = pushList.pFirst; this->itemCount += pushList.itemCount; pushList.clear(); } } // // tsDLList::push () // // add an item at the beginning of the list // template inline void tsDLList::push (T &item) { tsDLNode &theNode = item; theNode.pPrev = 0; theNode.pNext = this->pFirst; if ( this->itemCount ) { tsDLNode *pFirstNode = this->pFirst; pFirstNode->pPrev = & item; } else { this->pLast = & item; } this->pFirst = &item; this->itemCount++; } // // tsDLList::find () // returns -1 if the item isnt on the list // and the node number (beginning with zero if // it is) // template < class T > int tsDLList < T > :: find ( const T &item ) const { tsDLIterConst < T > thisItem ( &item ); tsDLIterConst < T > iter ( this->first () ); int itemNo = 0; while ( iter.valid () ) { if ( iter == thisItem ) { return itemNo; } itemNo++; iter++; } return -1; } template < class T > inline tsDLIterConst tsDLList < T > :: firstIter () const { return tsDLIterConst < T > ( this->pFirst ); } template < class T > inline tsDLIter tsDLList < T > :: firstIter () { return tsDLIter < T > ( this->pFirst ); } template < class T > inline tsDLIterConst tsDLList < T > :: lastIter () const { return tsDLIterConst < T > ( this->pLast ); } template < class T > inline tsDLIter tsDLList < T > :: lastIter () { return tsDLIter < T > ( this->pLast ); } template < class T > inline tsDLIterConst tsDLList < T > :: invalidConstIter () { return tsDLIterConst < T > ( 0 ); } template < class T > inline tsDLIter tsDLList < T > :: invalidIter () { return tsDLIter < T > ( 0 ); } ////////////////////////////////////////// // tsDLIterConst member functions ////////////////////////////////////////// template inline tsDLIterConst::tsDLIterConst ( const T *pInitialEntry ) : pEntry ( pInitialEntry ) {} template inline tsDLIterConst::tsDLIterConst () : pEntry ( 0 ) {} template inline bool tsDLIterConst::valid () const { return this->pEntry != 0; } template inline bool tsDLIterConst::operator == (const tsDLIterConst &rhs) const { return this->pEntry == rhs.pEntry; } template inline bool tsDLIterConst::operator != (const tsDLIterConst &rhs) const { return this->pEntry != rhs.pEntry; } template inline tsDLIterConst & tsDLIterConst::operator = ( const tsDLIterConst & rhs ) { this->pEntry = rhs.pEntry; return *this; } template inline const T & tsDLIterConst::operator * () const { return *this->pEntry; } template inline const T * tsDLIterConst::operator -> () const { return this->pEntry; } // // prefix ++ // template inline tsDLIterConst & tsDLIterConst::operator ++ () { const tsDLNode &node = *this->pEntry; this->pEntry = node.pNext; return *this; } // // postfix ++ // template inline tsDLIterConst tsDLIterConst::operator ++ (int) { const tsDLIterConst tmp = *this; const tsDLNode &node = *this->pEntry; this->pEntry = node.pNext; return tmp; } // // prefix -- // template inline tsDLIterConst & tsDLIterConst::operator -- () { const tsDLNode &entryNode = *this->pEntry; this->pEntry = entryNode.pPrev; return *this; } // // postfix -- // template inline tsDLIterConst tsDLIterConst::operator -- (int) { const tsDLIterConst tmp = *this; const tsDLNode &entryNode = *this->pEntry; this->pEntry = entryNode.pPrev; return tmp; } template inline const T * tsDLIterConst::pointer () const { return this->pEntry; } ////////////////////////////////////////// // tsDLIter member functions ////////////////////////////////////////// template inline tsDLIter::tsDLIter ( T * pInitialEntry ) : pEntry ( pInitialEntry ) {} template inline tsDLIter::tsDLIter () : pEntry ( 0 ) {} template inline bool tsDLIter::valid () const { return this->pEntry != 0; } template inline bool tsDLIter::operator == (const tsDLIter &rhs) const { return this->pEntry == rhs.pEntry; } template inline bool tsDLIter::operator != (const tsDLIter &rhs) const { return this->pEntry != rhs.pEntry; } template inline tsDLIter & tsDLIter::operator = ( const tsDLIter & rhs ) { this->pEntry = rhs.pEntry; return *this; } template inline T & tsDLIter::operator * () const { return *this->pEntry; } template inline T * tsDLIter::operator -> () const { return this->pEntry; } template inline tsDLIter & tsDLIter::operator ++ () // prefix ++ { const tsDLNode &entryNode = *this->pEntry; this->pEntry = entryNode.pNext; return *this; } template inline tsDLIter tsDLIter::operator ++ (int) // postfix ++ { const tsDLIter tmp = *this; const tsDLNode &entryNode = *this->pEntry; this->pEntry = entryNode.pNext; return tmp; } template inline tsDLIter & tsDLIter::operator -- () // prefix -- { const tsDLNode &entryNode = *this->pEntry; this->pEntry = entryNode.pPrev; return *this; } template inline tsDLIter tsDLIter::operator -- (int) // postfix -- { const tsDLIter tmp = *this; const tsDLNode &entryNode = *this->pEntry; this->pEntry = entryNode.pPrev; return tmp; } template inline T * tsDLIter::pointer () const { return this->pEntry; } #endif // tsDLListH_include base-7.0.3.1/modules/libcom/src/cxxTemplates/tsFreeList.h0000664000577000060420000001233013557101274022034 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #ifndef tsFreeList_h #define tsFreeList_h /* * Author: Jeff Hill */ // // TODO: this should allow free list chaining so that a free // list (in a different lock domain) is used to provide the // tsFreeListChunk. // // // To allow your class to be allocated off of a free list // using the new operator: // // 1) add the following static, private free list data members // to your class // // static tsFreeList < class classXYZ > freeList; // // 2) add the following member functions to your class // // inline void * classXYZ::operator new ( size_t size ) // { // return freeList.allocate ( size ); // } // // inline void classXYZ::operator delete ( void *pCadaver, size_t size ) // { // freeList.release ( pCadaver, size ); // } // // NOTES: // // 1) A NOOP mutex class may be specified if mutual exclusion isnt required // // 2) If you wish to force use of the new operator, then declare your class's // destructor as a protected member function. // // 3) Setting N to zero causes the free list to be bypassed // #ifdef EPICS_FREELIST_DEBUG # define tsFreeListDebugBypass 1 # define tsFreeListMemSetNew(P,SIZE) memset ( (P), 0xaa, (SIZE) ) # define tsFreeListMemSetDelete(P,SIZE) memset ( (P), 0xdd, (SIZE) ) #else # define tsFreeListDebugBypass 0 # define tsFreeListMemSetNew(P,SIZE) # define tsFreeListMemSetDelete(P,SIZE) #endif #include #include "string.h" #include "compilerDependencies.h" #include "epicsMutex.h" #include "epicsGuard.h" // ms visual studio 6.0 and before incorrectly // warn about a missing delete operator if only the // newly preferred delete operator with a size argument // is present #if defined ( _MSC_VER ) && _MSC_VER <= 1200 # pragma warning ( disable : 4291 ) #endif template < class T > union tsFreeListItem; template < class T, unsigned N> struct tsFreeListChunk; template < class T, unsigned N = 0x400, class MUTEX = epicsMutex > class tsFreeList { public: tsFreeList (); ~tsFreeList (); void * allocate ( size_t size ); void release ( void * p ); void release ( void * p, size_t size ); private: MUTEX mutex; tsFreeListItem < T > * pFreeList; tsFreeListChunk < T, N > * pChunkList; void * allocateFromNewChunk (); }; template < class T > union tsFreeListItem { public: char pad [ sizeof ( T ) ]; tsFreeListItem < T > * pNext; }; template < class T, unsigned N = 0x400 > struct tsFreeListChunk { tsFreeListItem < T > items [N]; tsFreeListChunk < T, N > * pNext; }; template < class T, unsigned N, class MUTEX > inline tsFreeList < T, N, MUTEX > :: tsFreeList () : pFreeList ( 0 ), pChunkList ( 0 ) {} template < class T, unsigned N, class MUTEX > tsFreeList < T, N, MUTEX > :: ~tsFreeList () { while ( tsFreeListChunk < T, N > *pChunk = this->pChunkList ) { this->pChunkList = this->pChunkList->pNext; delete pChunk; } } template < class T, unsigned N, class MUTEX > void * tsFreeList < T, N, MUTEX >::allocate ( size_t size ) { if ( size != sizeof ( T ) || N == 0u || tsFreeListDebugBypass ) { void * p = ::operator new ( size ); tsFreeListMemSetNew ( p, size ); return p; } epicsGuard < MUTEX > guard ( this->mutex ); tsFreeListItem < T > * p = this->pFreeList; if ( p ) { this->pFreeList = p->pNext; return static_cast < void * > ( p ); } return this->allocateFromNewChunk (); } template < class T, unsigned N, class MUTEX > void * tsFreeList < T, N, MUTEX >::allocateFromNewChunk () { tsFreeListChunk < T, N > * pChunk = new tsFreeListChunk < T, N >; for ( unsigned i=1u; i < N-1; i++ ) { pChunk->items[i].pNext = &pChunk->items[i+1]; } pChunk->items[N-1].pNext = 0; if ( N > 1 ) { this->pFreeList = &pChunk->items[1u]; } pChunk->pNext = this->pChunkList; this->pChunkList = pChunk; return static_cast ( &pChunk->items[0] ); } template < class T, unsigned N, class MUTEX > void tsFreeList < T, N, MUTEX >::release ( void * pCadaver, size_t size ) { if ( size != sizeof ( T ) ) { tsFreeListMemSetDelete ( pCadaver, size ); ::operator delete ( pCadaver ); } else { this->release ( pCadaver ); } } template < class T, unsigned N, class MUTEX > void tsFreeList < T, N, MUTEX >::release ( void * pCadaver ) { if ( N == 0u || tsFreeListDebugBypass ) { tsFreeListMemSetDelete ( pCadaver, sizeof ( T ) ); ::operator delete ( pCadaver ); } else if ( pCadaver ) { epicsGuard < MUTEX > guard ( this->mutex ); tsFreeListItem < T > * p = static_cast < tsFreeListItem < T > * > ( pCadaver ); p->pNext = this->pFreeList; this->pFreeList = p; } } #endif // tsFreeList_h base-7.0.3.1/modules/libcom/src/cxxTemplates/tsMinMax.h0000664000577000060420000000160513557101274021513 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ // // simple type safe inline template functions to replace // the min() and max() macros // #ifndef tsMinMaxh #define tsMinMaxh template inline const T & tsMax ( const T & a, const T & b ) { return ( a > b ) ? a : b; } template inline const T & tsMin ( const T & a, const T & b ) { return ( a < b ) ? a : b; } #endif // tsMinMaxh base-7.0.3.1/modules/libcom/src/cxxTemplates/tsSLList.h0000664000577000060420000002316713557101274021503 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * type safe singly linked list templates * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef tsSLListh #define tsSLListh #ifndef assert // allow use of epicsAssert.h #include #endif // // the hp compiler complains about parameterized friend // class that has not been declared without this? // template < class T > class tsSLList; template < class T > class tsSLIter; template < class T > class tsSLIterConst; // // tsSLNode // NOTE: class T must derive from tsSLNode // template class tsSLNode { public: tsSLNode (); tsSLNode < T > & operator = ( const tsSLNode < T > & ); private: void removeNextItem (); // removes the item after this node T *pNext; tsSLNode ( const tsSLNode < T > & ); friend class tsSLList < T >; friend class tsSLIter < T >; friend class tsSLIterConst < T >; }; // // tsSLList // NOTE: class T must derive from tsSLNode // template < class T > class tsSLList : public tsSLNode < T > { public: tsSLList (); // creates an empty list tsSLList ( tsSLList & ); void insert ( T &item, tsSLNode < T > &itemBefore ); // insert after item before void add ( T &item ); // add to the beginning of the list T * get (); // remove from the beginning of the list T * pop (); // same as get void push ( T &item ); // same as add T * first () const; void remove ( T &itemBefore ); tsSLIterConst firstIter () const; tsSLIter firstIter (); static tsSLIterConst invalidConstIter (); static tsSLIter invalidIter (); private: const tsSLList < T > & operator = ( const tsSLList < T > & ); }; // // tsSLIterConst // template < class T > class tsSLIterConst { public: tsSLIterConst (); bool valid () const; bool operator == (const tsSLIterConst &rhs) const; bool operator != (const tsSLIterConst &rhs) const; tsSLIterConst & operator = (const tsSLIterConst &); const T & operator * () const; const T * operator -> () const; tsSLIterConst & operator ++ (); tsSLIterConst operator ++ (int); const T * pointer () const; protected: const T * pEntry; tsSLIterConst ( const T *pInitialEntry ); friend class tsSLList < T >; }; // // tsSLIter // template < class T > class tsSLIter { public: tsSLIter (); bool valid () const; bool operator == (const tsSLIter &rhs) const; bool operator != (const tsSLIter &rhs) const; tsSLIter & operator = (const tsSLIter &); T & operator * () const; T * operator -> () const; tsSLIter & operator ++ (); tsSLIter operator ++ (int); T * pointer () const; private: T *pEntry; tsSLIter ( T *pInitialEntry ); friend class tsSLList < T >; }; ////////////////////////////////////////// // // tsSLNode inline member functions // ////////////////////////////////////////// // // tsSLNode::tsSLNode () // template < class T > inline tsSLNode < T > :: tsSLNode () : pNext ( 0 ) {} // // tsSLNode::tsSLNode ( const tsSLNode < T > & ) // private - not to be used - implemented to eliminate warnings // template < class T > inline tsSLNode < T > :: tsSLNode ( const tsSLNode < T > & ) { } // // tsSLNode::operator = // // when someone copies into a class deriving from this // do _not_ change the node pointers // template < class T > inline tsSLNode < T > & tsSLNode < T >::operator = ( const tsSLNode < T > & ) { return *this; } // // removeNextItem () // // removes the item after this node // template inline void tsSLNode::removeNextItem () { T *pItem = this->pNext; if ( pItem ) { tsSLNode < T > *pNode = pItem; this->pNext = pNode->pNext; } } ////////////////////////////////////////// // // tsSLList inline member functions // ////////////////////////////////////////// // // tsSLList::tsSLList() // create an empty list // template < class T > inline tsSLList < T > :: tsSLList () { } // // tsSLList::tsSLList( tsSLList & ) // template < class T > inline tsSLList < T > :: tsSLList ( tsSLList &listIn ) { this->pNext = listIn.pNext; listIn.pNext = 0; } // // tsSLList::insert() // (itemBefore might be the list header object and therefore // will not always be of type T) // template < class T > inline void tsSLList < T > :: insert ( T &item, tsSLNode < T > &itemBefore ) { tsSLNode < T > &node = item; node.pNext = itemBefore.pNext; itemBefore.pNext = &item; } // // tsSLList::add () // template < class T > inline void tsSLList < T > :: add ( T &item ) { this->insert ( item, *this ); } // // tsSLList::get () // template < class T > inline T * tsSLList < T > :: get () { tsSLNode < T > *pThisNode = this; T *pItem = pThisNode->pNext; pThisNode->removeNextItem (); return pItem; } // // tsSLList::pop () // template < class T > inline T * tsSLList < T > :: pop () { return this->get (); } // // tsSLList::push () // template inline void tsSLList < T > :: push ( T &item ) { this->add (item); } template inline T * tsSLList < T > :: first () const { const tsSLNode < T > *pThisNode = this; return pThisNode->pNext; } template inline void tsSLList < T > :: remove ( T &itemBefore ) { tsSLNode < T > *pBeforeNode = &itemBefore; tsSLNode < T > *pAfterNode = pBeforeNode->pNext; pBeforeNode->pNext = pAfterNode->pNext; } template inline tsSLIterConst tsSLList < T > :: firstIter () const { const tsSLNode < T > *pThisNode = this; return tsSLIterConst ( pThisNode->pNext ); } template inline tsSLIter tsSLList < T > :: firstIter () { tsSLNode < T > *pThisNode = this; return tsSLIter ( pThisNode->pNext ); } template inline tsSLIterConst tsSLList < T > :: invalidConstIter () { return tsSLIterConst ( 0 ); } template inline tsSLIter tsSLList < T > :: invalidIter () { return tsSLIter ( 0 ); } ////////////////////////////////////////// // // tsSLIterConst inline member functions // ////////////////////////////////////////// template < class T > inline tsSLIterConst::tsSLIterConst ( const T *pInitialEntry ) : pEntry ( pInitialEntry ) { } template < class T > inline tsSLIterConst::tsSLIterConst () : pEntry ( 0 ) { } template < class T > inline bool tsSLIterConst::valid () const { return this->pEntry != 0; } template < class T > inline bool tsSLIterConst::operator == ( const tsSLIterConst &rhs ) const { return this->pEntry == rhs.pConstEntry; } template < class T > inline bool tsSLIterConst::operator != (const tsSLIterConst &rhs) const { return this->pEntry != rhs.pConstEntry; } template < class T > inline tsSLIterConst & tsSLIterConst::operator = ( const tsSLIterConst & rhs ) { this->pEntry = rhs.pEntry; return *this; } template < class T > inline const T & tsSLIterConst::operator * () const { return *this->pEntry; } template < class T > inline const T * tsSLIterConst::operator -> () const { return this->pEntry; } template < class T > inline tsSLIterConst & tsSLIterConst::operator ++ () // prefix ++ { const tsSLNode < T > *pCurNode = this->pEntry; this->pEntry = pCurNode->pNext; return *this; } template < class T > inline tsSLIterConst tsSLIterConst::operator ++ ( int ) // postfix ++ { const tsSLIterConst tmp = *this; const tsSLNode < T > *pCurNode = this->pEntry; this->pEntry = pCurNode->pNext; return tmp; } template inline const T * tsSLIterConst < T > :: pointer () const { return this->pEntry; } ////////////////////////////////////////// // // tsSLIter inline member functions // ////////////////////////////////////////// template < class T > inline tsSLIter::tsSLIter ( T *pInitialEntry ) : pEntry ( pInitialEntry ) { } template < class T > inline tsSLIter::tsSLIter () : pEntry ( 0 ) { } template < class T > inline bool tsSLIter::valid () const { return this->pEntry != 0; } template < class T > inline bool tsSLIter::operator == ( const tsSLIter &rhs ) const { return this->pEntry == rhs.pEntry; } template < class T > inline bool tsSLIter::operator != ( const tsSLIter &rhs ) const { return this->pEntry != rhs.pEntry; } template < class T > inline tsSLIter & tsSLIter::operator = ( const tsSLIter & rhs ) { this->pEntry = rhs.pEntry; return *this; } template < class T > inline T & tsSLIter::operator * () const { return *this->pEntry; } template < class T > inline T * tsSLIter::operator -> () const { return this->pEntry; } template < class T > inline tsSLIter & tsSLIter::operator ++ () // prefix ++ { const tsSLNode < T > *pCurNode = this->pEntry; this->pEntry = pCurNode->pNext; return *this; } template < class T > inline tsSLIter tsSLIter::operator ++ ( int ) // postfix ++ { const tsSLIter tmp = *this; const tsSLNode < T > *pCurNode = this->pEntry; this->pEntry = pCurNode->pNext; return tmp; } template inline T * tsSLIter < T > :: pointer () const { return this->pEntry; } #endif // tsSLListh base-7.0.3.1/modules/libcom/src/dbmf/Makefile0000664000577000060420000000076013557101274017472 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/dbmf INC += dbmf.h Com_SRCS += dbmf.c base-7.0.3.1/modules/libcom/src/dbmf/dbmf.c0000664000577000060420000002024113557101274017102 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jim Kowalkowski and Marty Kraimer * Date: 4/97 * * Intended for applications that create and free requently * */ #include #include #include #include #include "valgrind/valgrind.h" #ifndef NVALGRIND /* buffer around allocations to detect out of bounds access */ #define REDZONE sizeof(double) #else #define REDZONE 0 #endif #define epicsExportSharedSymbols #include "cantProceed.h" #include "epicsMutex.h" #include "ellLib.h" #include "dbmf.h" /* #define DBMF_FREELIST_DEBUG 1 */ #ifndef DBMF_FREELIST_DEBUG /*Default values for dblfInit */ #define DBMF_SIZE 64 #define DBMF_INITIAL_ITEMS 10 typedef struct chunkNode {/*control block for each set of chunkItems*/ ELLNODE node; void *pchunk; int nNotFree; }chunkNode; typedef struct itemHeader{ void *pnextFree; chunkNode *pchunkNode; }itemHeader; typedef struct dbmfPrivate { ELLLIST chunkList; epicsMutexId lock; size_t size; size_t allocSize; int chunkItems; size_t chunkSize; int nAlloc; int nFree; int nGtSize; void *freeList; } dbmfPrivate; dbmfPrivate dbmfPvt; static dbmfPrivate *pdbmfPvt = NULL; int dbmfDebug=0; int dbmfInit(size_t size, int chunkItems) { if(pdbmfPvt) { printf("dbmfInit: Already initialized\n"); return(-1); } pdbmfPvt = &dbmfPvt; ellInit(&pdbmfPvt->chunkList); pdbmfPvt->lock = epicsMutexMustCreate(); /*allign to at least a double*/ pdbmfPvt->size = size + size%sizeof(double); /* layout is * | itemHeader | REDZONE | size | REDZONE | */ pdbmfPvt->allocSize = pdbmfPvt->size + sizeof(itemHeader) + 2*REDZONE; pdbmfPvt->chunkItems = chunkItems; pdbmfPvt->chunkSize = pdbmfPvt->allocSize * pdbmfPvt->chunkItems; pdbmfPvt->nAlloc = 0; pdbmfPvt->nFree = 0; pdbmfPvt->nGtSize = 0; pdbmfPvt->freeList = NULL; VALGRIND_CREATE_MEMPOOL(pdbmfPvt, REDZONE, 0); return(0); } void* dbmfMalloc(size_t size) { void **pnextFree; void **pfreeList; char *pmem = NULL; chunkNode *pchunkNode; itemHeader *pitemHeader; if(!pdbmfPvt) dbmfInit(DBMF_SIZE,DBMF_INITIAL_ITEMS); epicsMutexMustLock(pdbmfPvt->lock); pfreeList = &pdbmfPvt->freeList; if(*pfreeList == NULL) { int i; size_t nbytesTotal; if(dbmfDebug) printf("dbmfMalloc allocating new storage\n"); nbytesTotal = pdbmfPvt->chunkSize + sizeof(chunkNode); pmem = (char *)malloc(nbytesTotal); if(!pmem) { epicsMutexUnlock(pdbmfPvt->lock); cantProceed("dbmfMalloc malloc failed\n"); return(NULL); } pchunkNode = (chunkNode *)(pmem + pdbmfPvt->chunkSize); pchunkNode->pchunk = pmem; pchunkNode->nNotFree=0; ellAdd(&pdbmfPvt->chunkList,&pchunkNode->node); for(i=0; ichunkItems; i++) { pitemHeader = (itemHeader *)pmem; pitemHeader->pchunkNode = pchunkNode; pnextFree = &pitemHeader->pnextFree; *pnextFree = *pfreeList; *pfreeList = (void *)pmem; pdbmfPvt->nFree++; pmem += pdbmfPvt->allocSize; } } if(size<=pdbmfPvt->size) { pnextFree = *pfreeList; *pfreeList = *pnextFree; pmem = (void *)pnextFree; pdbmfPvt->nAlloc++; pdbmfPvt->nFree--; pitemHeader = (itemHeader *)pnextFree; pitemHeader->pchunkNode->nNotFree += 1; } else { pmem = malloc(sizeof(itemHeader) + 2*REDZONE + size); if(!pmem) { epicsMutexUnlock(pdbmfPvt->lock); cantProceed("dbmfMalloc malloc failed\n"); return(NULL); } pdbmfPvt->nAlloc++; pdbmfPvt->nGtSize++; pitemHeader = (itemHeader *)pmem; pitemHeader->pchunkNode = NULL; /* not part of free list */ if(dbmfDebug) printf("dbmfMalloc: size %lu mem %p\n", (unsigned long)size,pmem); } epicsMutexUnlock(pdbmfPvt->lock); pmem += sizeof(itemHeader) + REDZONE; VALGRIND_MEMPOOL_ALLOC(pdbmfPvt, pmem, size); return((void *)pmem); } char * dbmfStrdup(const char *str) { size_t len = strlen(str); char *buf = dbmfMalloc(len + 1); /* FIXME Can return NULL */ return strcpy(buf, str); } char * dbmfStrndup(const char *str, size_t len) { char *buf = dbmfMalloc(len + 1); /* FIXME Can return NULL */ buf[len] = '\0'; return strncpy(buf, str, len); } void dbmfFree(void* mem) { char *pmem = (char *)mem; chunkNode *pchunkNode; itemHeader *pitemHeader; if(!mem) return; if(!pdbmfPvt) { printf("dbmfFree called but dbmfInit never called\n"); return; } VALGRIND_MEMPOOL_FREE(pdbmfPvt, mem); pmem -= sizeof(itemHeader) + REDZONE; epicsMutexMustLock(pdbmfPvt->lock); pitemHeader = (itemHeader *)pmem; if(!pitemHeader->pchunkNode) { if(dbmfDebug) printf("dbmfGree: mem %p\n",pmem); free((void *)pmem); pdbmfPvt->nAlloc--; }else { void **pfreeList = &pdbmfPvt->freeList; void **pnextFree = &pitemHeader->pnextFree; pchunkNode = pitemHeader->pchunkNode; pchunkNode->nNotFree--; *pnextFree = *pfreeList; *pfreeList = pnextFree; pdbmfPvt->nAlloc--; pdbmfPvt->nFree++; } epicsMutexUnlock(pdbmfPvt->lock); } int dbmfShow(int level) { if(pdbmfPvt==NULL) { printf("Never initialized\n"); return(0); } printf("size %lu allocSize %lu chunkItems %d ", (unsigned long)pdbmfPvt->size, (unsigned long)pdbmfPvt->allocSize,pdbmfPvt->chunkItems); printf("nAlloc %d nFree %d nChunks %d nGtSize %d\n", pdbmfPvt->nAlloc,pdbmfPvt->nFree, ellCount(&pdbmfPvt->chunkList),pdbmfPvt->nGtSize); if(level>0) { chunkNode *pchunkNode; pchunkNode = (chunkNode *)ellFirst(&pdbmfPvt->chunkList); while(pchunkNode) { printf("pchunkNode %p nNotFree %d\n", (void*)pchunkNode,pchunkNode->nNotFree); pchunkNode = (chunkNode *)ellNext(&pchunkNode->node); } } if(level>1) { void **pnextFree;; epicsMutexMustLock(pdbmfPvt->lock); pnextFree = (void**)pdbmfPvt->freeList; while(pnextFree) { printf("%p\n",*pnextFree); pnextFree = (void**)*pnextFree; } epicsMutexUnlock(pdbmfPvt->lock); } return(0); } void dbmfFreeChunks(void) { chunkNode *pchunkNode; chunkNode *pnext;; if(!pdbmfPvt) { printf("dbmfFreeChunks called but dbmfInit never called\n"); return; } epicsMutexMustLock(pdbmfPvt->lock); if(pdbmfPvt->nFree != (pdbmfPvt->chunkItems * ellCount(&pdbmfPvt->chunkList))) { printf("dbmfFinish: not all free\n"); epicsMutexUnlock(pdbmfPvt->lock); return; } pchunkNode = (chunkNode *)ellFirst(&pdbmfPvt->chunkList); while(pchunkNode) { pnext = (chunkNode *)ellNext(&pchunkNode->node); ellDelete(&pdbmfPvt->chunkList,&pchunkNode->node); free(pchunkNode->pchunk); pchunkNode = pnext; } pdbmfPvt->nFree = 0; pdbmfPvt->freeList = NULL; epicsMutexUnlock(pdbmfPvt->lock); } #else /* DBMF_FREELIST_DEBUG */ int dbmfInit(size_t size, int chunkItems) { return 0; } void* dbmfMalloc(size_t size) { return malloc(size); } char * dbmfStrdup(const char *str) { return strdup((char*)str); } void dbmfFree(void* mem) { free(mem); } int dbmfShow(int level) { return 0; } void dbmfFreeChunks(void) {} #endif /* DBMF_FREELIST_DEBUG */ char * dbmfStrcat3(const char *lhs, const char *mid, const char *rhs) { size_t len = strlen(lhs) + strlen(mid) + strlen(rhs) + 1; char *buf = dbmfMalloc(len); strcpy(buf, lhs); strcat(buf, mid); strcat(buf, rhs); return buf; } base-7.0.3.1/modules/libcom/src/dbmf/dbmf.h0000664000577000060420000000313113557101274017106 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jim Kowalkowski and Marty Kraimer * Date: 4/97 * * A library to manage storage that is allocated and then freed. */ #ifndef DBMF_H #define DBMF_H #include #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc int dbmfInit(size_t size, int chunkItems); epicsShareFunc void * dbmfMalloc(size_t bytes); epicsShareFunc char * dbmfStrdup(const char *str); epicsShareFunc char * dbmfStrndup(const char *str, size_t len); epicsShareFunc char * dbmfStrcat3(const char *lhs, const char *mid, const char *rhs); epicsShareFunc void dbmfFree(void *bytes); epicsShareFunc void dbmfFreeChunks(void); epicsShareFunc int dbmfShow(int level); /* Rules: * 1) Size is always made a multiple of 8. * 2) if dbmfInit is not called before one of the other routines then it * is automatically called with size=64 and chunkItems=10 * 3) These routines should only be used to allocate storage that will * shortly thereafter be freed. * 4) dbmfFreeChunks can only free chunks that contain only free items */ #ifdef __cplusplus } #endif #endif base-7.0.3.1/modules/libcom/src/ellLib/Makefile0000664000577000060420000000101313557101274017755 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/ellLib INC += ellLib.h Com_SRCS += ellLib.c Com_SRCS += ellSort.c base-7.0.3.1/modules/libcom/src/ellLib/ellLib.c0000664000577000060420000002325313557101274017676 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: John Winans (ANL) * Date: 07-02-92 */ #include #define epicsExportSharedSymbols #include "epicsAssert.h" #include "ellLib.h" /**************************************************************************** * * This function adds a node to the end of a linked list. * *****************************************************************************/ void ellAdd (ELLLIST *pList, ELLNODE *pNode) { pNode->next = NULL; pNode->previous = pList->node.previous; if (pList->count) pList->node.previous->next = pNode; else pList->node.next = pNode; pList->node.previous = pNode; pList->count++; return; } /**************************************************************************** * * This function concatinates the second linked list to the end of the first * list. The second list is left empty. Either list (or both) lists may * be empty at the begining of the operation. * *****************************************************************************/ void ellConcat (ELLLIST *pDstList, ELLLIST *pAddList) { if (pAddList->count == 0) return; /* Add list is empty, nothing to add. */ if (pDstList->count == 0) { /* Destination list is empty... just transfer the add list over. */ pDstList->node.next = pAddList->node.next; pDstList->node.previous = pAddList->node.previous; pDstList->count = pAddList->count; } else { /* Destination list not empty... append the add list. */ pDstList->node.previous->next = pAddList->node.next; pAddList->node.next->previous = pDstList->node.previous; pDstList->node.previous = pAddList->node.previous; pDstList->count += pAddList->count; } pAddList->count = 0; pAddList->node.next = NULL; pAddList->node.previous = NULL; return; } /**************************************************************************** * * This function deletes a specific node from a specified list; * *****************************************************************************/ void ellDelete (ELLLIST *pList, ELLNODE *pNode) { if (pList->node.previous == pNode) pList->node.previous = pNode->previous; else pNode->next->previous = pNode->previous; if (pList->node.next == pNode) pList->node.next = pNode->next; else pNode->previous->next = pNode->next; pList->count--; return; } /**************************************************************************** * * This function extracts a sublist that starts with pStartNode and ends with * pEndNode from pSrcList and places it in pDstList. * * WRS is unclear as to what happens when pDstList is non-empty at the start * of the operation. We will place the extracted list at the END of pDstList * when it is non-empty. * *****************************************************************************/ void ellExtract (ELLLIST *pSrcList, ELLNODE *pStartNode, ELLNODE *pEndNode, ELLLIST *pDstList) { ELLNODE *pnode; int count; /* Cut the list out of the source list (update count later) */ if (pStartNode->previous != NULL) pStartNode->previous->next = pEndNode->next; else pSrcList->node.next = pEndNode->next; if (pEndNode->next != NULL) { pEndNode->next->previous = pStartNode->previous; pEndNode->next = NULL; } else pSrcList->node.previous = pStartNode->previous; /* Place the sublist into the destination list */ pStartNode->previous = pDstList->node.previous; if (pDstList->count) pDstList->node.previous->next = pStartNode; else pDstList->node.next = pStartNode; pDstList->node.previous = pEndNode; /* Adjust the counts */ pnode = pStartNode; count = 1; while(pnode != pEndNode) { pnode = pnode->next; count++; } pSrcList->count -= count; pDstList->count += count; return; } /**************************************************************************** * * This function returns the first node in the specified list. The node is * removed from the list. If the list is empty, NULL will be returned. * *****************************************************************************/ ELLNODE * ellGet (ELLLIST *pList) { ELLNODE *pnode = pList->node.next; if (pnode != NULL) ellDelete(pList, pnode); return pnode; } /**************************************************************************** * * This function returns the last node in the specified list. The node is * removed from the list. If the list is empty, NULL will be returned. * *****************************************************************************/ ELLNODE * ellPop (ELLLIST *pList) { ELLNODE *pnode = pList->node.previous; if (pnode != NULL) ellDelete(pList, pnode); return pnode; } /**************************************************************************** * * This function inserts the specified node pNode after pPrev in the list * plist. If pPrev is NULL, then pNode will be inserted at the head of the * list. * *****************************************************************************/ void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode) { if (pPrev != NULL) { pNode->previous = pPrev; pNode->next = pPrev->next; pPrev->next = pNode; } else { pNode->previous = NULL; pNode->next = plist->node.next; plist->node.next = pNode; } if (pNode->next == NULL) plist->node.previous = pNode; else pNode->next->previous = pNode; plist->count++; return; } /**************************************************************************** * * This function returns the nodeNum'th element in pList. If there is no * nodeNum'th node in the list, NULL will be returned. * *****************************************************************************/ ELLNODE * ellNth (ELLLIST *pList, int nodeNum) { ELLNODE *pnode; if ((nodeNum < 1) || (pList->count == 0)) return NULL; if (nodeNum > pList->count/2) { if (nodeNum > pList->count) return NULL; pnode = pList->node.previous; nodeNum = pList->count - nodeNum; while(nodeNum) { pnode = pnode->previous; nodeNum--; } return pnode; } pnode = pList->node.next; while (--nodeNum > 0) pnode = pnode->next; return pnode; } /**************************************************************************** * * This function returns the node, nStep nodes forward from pNode. If there is * no node that many steps from pNode, NULL will be returned. * *****************************************************************************/ ELLNODE * ellNStep (ELLNODE *pNode, int nStep) { if (nStep > 0) { while ((pNode != NULL) && nStep) { pNode = pNode->next; nStep--; } } else { while ((pNode != NULL) && nStep) { pNode = pNode->previous; nStep++; } } return pNode; } /**************************************************************************** * * This function returns the node number of pNode within pList. If the node is * not in pList, -1 is returned. Note that the first node is 1. * *****************************************************************************/ int ellFind (ELLLIST *pList, ELLNODE *pNode) { ELLNODE *got = pList->node.next; int count = 1; while ((got != pNode) && (got != NULL)) { got = got->next; count++; } if (got == NULL) return -1; return count; } /**************************************************************************** * * This function frees the nodes in a list. It makes the list into an empty * list, and calls freeFunc() for all the nodes that are (were) in that list. * * NOTE: the nodes in the list are free()'d on the assumption that the node * structures were malloc()'d one-at-a-time and that the ELLNODE structure is * the first member of the parent structure. * *****************************************************************************/ void ellFree2 (ELLLIST *pList, FREEFUNC freeFunc) { ELLNODE *nnode = pList->node.next; ELLNODE *pnode; while (nnode != NULL) { pnode = nnode; nnode = nnode->next; freeFunc(pnode); } pList->node.next = NULL; pList->node.previous = NULL; pList->count = 0; } /**************************************************************************** * * This function verifies that the list is consistent. * joh 12-12-97 * *****************************************************************************/ void ellVerify (ELLLIST *pList) { ELLNODE *pNode; ELLNODE *pNext; int count = 0; assert (pList); pNode = ellFirst(pList); if (pNode) { assert (ellPrevious(pNode) == NULL); while (1) { count++; pNext = ellNext(pNode); if (pNext) { assert (ellPrevious(pNext) == pNode); } else { break; } pNode = pNext; } assert (ellNext(pNode) == NULL); } assert (pNode == ellLast(pList)); assert (count == ellCount(pList)); } base-7.0.3.1/modules/libcom/src/ellLib/ellLib.h0000664000577000060420000000443213557101274017701 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: John Winans (ANL) * Andrew Johnson */ #ifndef INC_ellLib_H #define INC_ellLib_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif typedef struct ELLNODE { struct ELLNODE *next; struct ELLNODE *previous; } ELLNODE; #define ELLNODE_INIT {NULL, NULL} typedef struct ELLLIST { ELLNODE node; int count; } ELLLIST; #define ELLLIST_INIT {ELLNODE_INIT, 0} typedef void (*FREEFUNC)(void *); #define ellInit(PLIST) {\ (PLIST)->node.next = (PLIST)->node.previous = NULL;\ (PLIST)->count = 0;\ } #define ellCount(PLIST) ((PLIST)->count) #define ellFirst(PLIST) ((PLIST)->node.next) #define ellLast(PLIST) ((PLIST)->node.previous) #define ellNext(PNODE) ((PNODE)->next) #define ellPrevious(PNODE) ((PNODE)->previous) #define ellFree(PLIST) ellFree2(PLIST, free) epicsShareFunc void ellAdd (ELLLIST *pList, ELLNODE *pNode); epicsShareFunc void ellConcat (ELLLIST *pDstList, ELLLIST *pAddList); epicsShareFunc void ellDelete (ELLLIST *pList, ELLNODE *pNode); epicsShareFunc void ellExtract (ELLLIST *pSrcList, ELLNODE *pStartNode, ELLNODE *pEndNode, ELLLIST *pDstList); epicsShareFunc ELLNODE * ellGet (ELLLIST *pList); epicsShareFunc ELLNODE * ellPop (ELLLIST *pList); epicsShareFunc void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode); epicsShareFunc ELLNODE * ellNth (ELLLIST *pList, int nodeNum); epicsShareFunc ELLNODE * ellNStep (ELLNODE *pNode, int nStep); epicsShareFunc int ellFind (ELLLIST *pList, ELLNODE *pNode); typedef int (*pListCmp)(const ELLNODE* A, const ELLNODE* B); epicsShareFunc void ellSortStable(ELLLIST *pList, pListCmp); epicsShareFunc void ellFree2 (ELLLIST *pList, FREEFUNC freeFunc); epicsShareFunc void ellVerify (ELLLIST *pList); #ifdef __cplusplus } #endif #endif /* INC_ellLib_H */ base-7.0.3.1/modules/libcom/src/ellLib/ellSort.c0000664000577000060420000000430513557101274020114 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Assoc., as Operator of Argonne * National Laboratory. * Copyright (c) 2016 Michael Davidsaver * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Use of mergesort algorithm based on analysis by * http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html */ #include #define epicsExportSharedSymbols #include "epicsAssert.h" #include "ellLib.h" static void ellMoveN(ELLLIST* pTo, ELLLIST* pFrom, int count ) { for(;count && ellCount(pFrom); count--) { ELLNODE *node = ellGet(pFrom); ellAdd(pTo, node); } } /* Stable (MergeSort) to given list. * The comparison function cmp(A,B) is expected * to return -1 for AB. */ void ellSortStable(ELLLIST *pList, pListCmp cmp) { ELLLIST INP, P, Q; size_t insize = 1; /* initial sub-list size */ if(ellCount(pList)<=1) return; ellInit(&INP); ellInit(&P); ellInit(&Q); /* Process is to iteratively sort * a sequence of sub-lists of size 'insize' */ while(insize < ellCount(pList)) { assert(ellCount(&INP)==0); /* shift previous results to inputs */ ellConcat(&INP, pList); while(ellCount(&INP)) { ELLNODE *p, *q; /* Pull out the next pair of sub-lists */ ellMoveN(&Q, &INP, insize); ellMoveN(&P, &INP, insize); /* merge these sub-lists */ while((p=ellFirst(&P)) && (q=ellFirst(&Q))) { if((*cmp)(p,q) < 0) { ellAdd(pList, ellGet(&P)); } else { ellAdd(pList, ellGet(&Q)); } } /* concatenate any remaining to result */ if(ellFirst(&P)) ellConcat(pList, &P); else if(ellFirst(&Q)) ellConcat(pList, &Q); assert(!ellFirst(&P) && !ellFirst(&Q)); } insize *= 2; } } base-7.0.3.1/modules/libcom/src/env/Makefile0000664000577000060420000000114413557101274017347 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/env vpath %.pl $(USR_VPATH) $(SRC_DIRS) PERL_SCRIPTS += bldEnvData.pl INC += envDefs.h Com_SRCS += envSubr.c Com_SRCS += envData.c CLEANS += envData.c base-7.0.3.1/modules/libcom/src/env/RULES0000664000577000060420000000133213557101274016523 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. envData.c: $(LIBCOM)/env/envDefs.h \ $(INSTALL_HOST_BIN)/bldEnvData.pl \ $(CONFIG)/CONFIG_ENV $(CONFIG)/CONFIG_SITE_ENV \ $(wildcard $(CONFIG)/os/CONFIG_SITE_ENV.$(T_A)) $(PERL) $(INSTALL_HOST_BIN)/bldEnvData.pl $(QUIET_FLAG) -t $(T_A) \ -c $(CMPLR_CLASS) -s $(OS_CLASS) $(CONFIG) base-7.0.3.1/modules/libcom/src/env/bldEnvData.pl0000664000577000060420000000664413557101274020262 0ustar anjaesctl#!/usr/bin/env perl #************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # # Author: Kay-Uwe Kasemir # Date: 1-30-97 use strict; use FindBin qw($Bin); use lib ("$Bin/../../lib/perl"); use Getopt::Std; use File::Basename; use EPICS::Path; use EPICS::Release; use Text::Wrap; my $tool = basename($0); our ($opt_h, $opt_q, $opt_t, $opt_s, $opt_c); our $opt_o = 'envData.c'; $Getopt::Std::OUTPUT_HELP_VERSION = 1; $Text::Wrap::columns = 75; HELP_MESSAGE() unless getopts('ho:qt:s:c:') && @ARGV == 1; HELP_MESSAGE() if $opt_h; my $config = AbsPath(shift); my $env_defs = AbsPath('../env/envDefs.h'); # Parse the ENV_PARAM declarations in envDefs.h # to get the param names we are interested in # open SRC, '<', $env_defs or die "$tool: Cannot open $env_defs: $!\n"; my @vars; while () { if (m/epicsShareExtern\s+const\s+ENV_PARAM\s+([A-Za-z_]\w*)\s*;/) { push @vars, $1; } } close SRC; # A list of configure/CONFIG_* files to read # my @configs = ("$config/CONFIG_ENV", "$config/CONFIG_SITE_ENV"); if ($opt_t) { my $config_arch_env = "$config/os/CONFIG_SITE_ENV.$opt_t"; push @configs, $config_arch_env if -f $config_arch_env; } my @sources = ($env_defs, @configs); # Get values from the config files # my (%values, @dummy); readRelease($_, \%values, \@dummy) foreach @configs; expandRelease(\%values); # Get values from the command-line # $values{EPICS_BUILD_COMPILER_CLASS} = $opt_c if $opt_c; $values{EPICS_BUILD_OS_CLASS} = $opt_s if $opt_s; $values{EPICS_BUILD_TARGET_ARCH} = $opt_t if $opt_t; # Warn about vars with no configured value # my @undefs = grep {!exists $values{$_}} @vars; warn "$tool: No value given for $_\n" foreach @undefs; print "Generating $opt_o\n" unless $opt_q; # Start creating the output # open OUT, '>', $opt_o or die "$tool: Cannot create $opt_o: $!\n"; my $sources = join "\n", map {" * $_"} @sources; print OUT << "END"; /* Generated file $opt_o * * Created from $sources */ #include #define epicsExportSharedSymbols #include "envDefs.h" END # Define a default value for each named parameter # foreach my $var (@vars) { my $default = $values{$var}; if (defined $default) { $default =~ s/^"//; $default =~ s/"$//; } else { $default = ''; } print OUT "epicsShareDef const ENV_PARAM $var =\n", " {\"$var\", \"$default\"};\n"; } # Also provide a list of all defined parameters # print OUT "\n", "epicsShareDef const ENV_PARAM* env_param_list[] = {\n", wrap(' ', ' ', join(', ', map("&$_", @vars), 'NULL')), "\n};\n"; close OUT; sub HELP_MESSAGE { print STDERR "Usage: $tool [options] configure\n", " -h Help: Print this message\n", " -q Quiet: Only print errors\n", " -o file Output filename, default is $opt_o\n", " -t arch Target architecture \$(T_A) name\n", " -s os Operating system \$(OS_CLASS)\n", " -c comp Compiler class \$(CMPLR_CLASS)\n", "\n"; exit 1; } base-7.0.3.1/modules/libcom/src/env/envDefs.h0000664000577000060420000001071613557101274017457 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Roger A. Cole * Date: 07-20-91 * */ /**************************************************************************** * TITLE envDefs.h - definitions for environment get/set routines * * DESCRIPTION * This file defines the environment parameters for EPICS. These * ENV_PARAM's are created in envData.c by bldEnvData for * use by EPICS programs running under UNIX and VxWorks. * * User programs can define their own environment parameters for their * own use--the only caveat is that such parameters aren't automatically * setup by EPICS. * *****************************************************************************/ #ifndef envDefsH #define envDefsH #ifdef __cplusplus extern "C" { #endif #include "shareLib.h" typedef struct envParam { char *name; /* text name of the parameter */ char *pdflt; } ENV_PARAM; /* * bldEnvData.pl looks for "epicsShareExtern const ENV_PARAM ;" */ epicsShareExtern const ENV_PARAM EPICS_CA_ADDR_LIST; epicsShareExtern const ENV_PARAM EPICS_CA_CONN_TMO; epicsShareExtern const ENV_PARAM EPICS_CA_AUTO_ADDR_LIST; epicsShareExtern const ENV_PARAM EPICS_CA_REPEATER_PORT; epicsShareExtern const ENV_PARAM EPICS_CA_SERVER_PORT; epicsShareExtern const ENV_PARAM EPICS_CA_MAX_ARRAY_BYTES; epicsShareExtern const ENV_PARAM EPICS_CA_AUTO_ARRAY_BYTES; epicsShareExtern const ENV_PARAM EPICS_CA_MAX_SEARCH_PERIOD; epicsShareExtern const ENV_PARAM EPICS_CA_NAME_SERVERS; epicsShareExtern const ENV_PARAM EPICS_CA_MCAST_TTL; epicsShareExtern const ENV_PARAM EPICS_CAS_INTF_ADDR_LIST; epicsShareExtern const ENV_PARAM EPICS_CAS_IGNORE_ADDR_LIST; epicsShareExtern const ENV_PARAM EPICS_CAS_AUTO_BEACON_ADDR_LIST; epicsShareExtern const ENV_PARAM EPICS_CAS_BEACON_ADDR_LIST; epicsShareExtern const ENV_PARAM EPICS_CAS_SERVER_PORT; epicsShareExtern const ENV_PARAM EPICS_CA_BEACON_PERIOD; /* deprecated */ epicsShareExtern const ENV_PARAM EPICS_CAS_BEACON_PERIOD; epicsShareExtern const ENV_PARAM EPICS_CAS_BEACON_PORT; epicsShareExtern const ENV_PARAM EPICS_BUILD_COMPILER_CLASS; epicsShareExtern const ENV_PARAM EPICS_BUILD_OS_CLASS; epicsShareExtern const ENV_PARAM EPICS_BUILD_TARGET_ARCH; epicsShareExtern const ENV_PARAM EPICS_TZ; epicsShareExtern const ENV_PARAM EPICS_TS_NTP_INET; epicsShareExtern const ENV_PARAM EPICS_IOC_IGNORE_SERVERS; epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_PORT; epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_INET; epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_FILE_LIMIT; epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_FILE_NAME; epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_FILE_COMMAND; epicsShareExtern const ENV_PARAM EPICS_CMD_PROTO_PORT; epicsShareExtern const ENV_PARAM EPICS_AR_PORT; epicsShareExtern const ENV_PARAM IOCSH_PS1; epicsShareExtern const ENV_PARAM IOCSH_HISTSIZE; epicsShareExtern const ENV_PARAM IOCSH_HISTEDIT_DISABLE; epicsShareExtern const ENV_PARAM *env_param_list[]; struct in_addr; epicsShareFunc char * epicsShareAPI envGetConfigParam(const ENV_PARAM *pParam, int bufDim, char *pBuf); epicsShareFunc const char * epicsShareAPI envGetConfigParamPtr(const ENV_PARAM *pParam); epicsShareFunc long epicsShareAPI envPrtConfigParam(const ENV_PARAM *pParam); epicsShareFunc long epicsShareAPI envGetInetAddrConfigParam(const ENV_PARAM *pParam, struct in_addr *pAddr); epicsShareFunc long epicsShareAPI envGetDoubleConfigParam(const ENV_PARAM *pParam, double *pDouble); epicsShareFunc long epicsShareAPI envGetLongConfigParam(const ENV_PARAM *pParam, long *pLong); epicsShareFunc unsigned short epicsShareAPI envGetInetPortConfigParam (const ENV_PARAM *pEnv, unsigned short defaultPort); epicsShareFunc long epicsShareAPI envGetBoolConfigParam(const ENV_PARAM *pParam, int *pBool); epicsShareFunc long epicsShareAPI epicsPrtEnvParams(void); epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value); epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name); epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name); #ifdef __cplusplus } #endif #endif /*envDefsH*/ base-7.0.3.1/modules/libcom/src/env/envSubr.c0000664000577000060420000002666513557101274017516 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Roger A. Cole * Date: 07-20-91 */ /*+/mod*********************************************************************** * TITLE envSubr.c - routines to get and set EPICS environment parameters * * DESCRIPTION * These routines are oriented for use with EPICS environment * parameters under UNIX and VxWorks. They may be used for other * purposes, as well. * * Many EPICS environment parameters are predefined in envDefs.h. * * QUICK REFERENCE * #include "envDefs.h" * ENV_PARAM param; * char *envGetConfigParamPtr( pParam ) * char *envGetConfigParam( pParam, bufDim, pBuf ) * long envGetLongConfigParam( pParam, pLong ) * long envGetDoubleConfigParam( pParam, pDouble ) * long envGetInetAddrConfigParam( pParam, pAddr ) * long envPrtConfigParam( pParam ) * * SEE ALSO * $epics/share/bin/envSetupParams, envDefs.h * *-***************************************************************************/ #include #include #include #define epicsExportSharedSymbols #include "epicsStdlib.h" #include "epicsStdio.h" #include "epicsString.h" #include "errMdef.h" #include "errlog.h" #include "envDefs.h" #include "epicsAssert.h" #include "osiSock.h" /*+/subr********************************************************************** * NAME envGetConfigParamPtr - returns a pointer to the configuration * parameter value string * * DESCRIPTION * Returns a pointer to a configuration parameter value. * If the configuration parameter isn't found in the environment, * then a pointer to the default value for the parameter is copied. * If no parameter is found and there is no default, then * NULL is returned. * * RETURNS * pointer to the environment variable value string, or * NULL if no parameter value and no default value was found * * EXAMPLES * 1. Get the value for the EPICS-defined environment parameter * EPICS_TS_NTP_INET. * * #include "envDefs.h" * const char *pStr; * * pStr = envGetConfigParamPtr(&EPICS_TS_NTP_INET); * if (pStr) { * printf("NTP time server is: %s\n", pStr); * } * *-*/ const char * epicsShareAPI envGetConfigParamPtr( const ENV_PARAM *pParam /* I pointer to config param structure */ ) { const char *pEnv; /* pointer to environment string */ pEnv = getenv(pParam->name); if (pEnv == NULL) { pEnv = pParam->pdflt; } if (pEnv) { if (pEnv[0u] == '\0') { pEnv = NULL; } } return pEnv; } /*+/subr********************************************************************** * NAME envGetConfigParam - get value of a configuration parameter * * DESCRIPTION * Gets the value of a configuration parameter and copies it * into the caller's buffer. If the configuration parameter * isn't found in the environment, then the default value for * the parameter is copied. If no parameter is found and there * is no default, then '\0' is copied and NULL is returned. * * RETURNS * pointer to callers buffer, or * NULL if no parameter value or default value was found * * EXAMPLES * 1. Get the value for the EPICS-defined environment parameter * EPICS_TS_NTP_INET. * * #include "envDefs.h" * char temp[80]; * * printf("NTP time server is: %s\n", * envGetConfigParam(&EPICS_TS_NTP_INET, sizeof(temp), temp)); * * 2. Get the value for the DISPLAY environment parameter under UNIX. * * #include "envDefs.h" * char temp[80]; * ENV_PARAM display={"DISPLAY",""} * * if (envGetConfigParam(&display, sizeof(temp), temp) == NULL) * printf("DISPLAY isn't defined\n"); * else * printf("DISPLAY is %s\n", temp); * *-*/ char * epicsShareAPI envGetConfigParam( const ENV_PARAM *pParam,/* I pointer to config param structure */ int bufDim, /* I dimension of parameter buffer */ char *pBuf /* I pointer to parameter buffer */ ) { const char *pEnv; /* pointer to environment string */ pEnv = envGetConfigParamPtr(pParam); if (!pEnv) { return NULL; } strncpy(pBuf, pEnv, bufDim-1); pBuf[bufDim-1] = '\0'; return pBuf; } /*+/subr********************************************************************** * NAME envGetDoubleConfigParam - get value of a double configuration parameter * * DESCRIPTION * Gets the value of a configuration parameter and copies it into the * caller's real (double) buffer. If the configuration parameter isn't * found in the environment, then the default value for the parameter * is copied. * * If no parameter is found and there is no default, then -1 is * returned and the caller's buffer is unmodified. * * RETURNS * 0, or * -1 if an error is encountered * * EXAMPLE * 1. Get the value for the real environment parameter EPICS_THRESHOLD. * * #include "envDefs.h" * double threshold; * long status; * * status = envGetDoubleConfigParam(&EPICS_THRESHOLD, &threshold); * if (status == 0) { * printf("the threshold is: %lf\n", threshold); * } * else { * printf("%s could not be found or was not a real number\n", * EPICS_THRESHOLD.name); * } * *-*/ long epicsShareAPI envGetDoubleConfigParam( const ENV_PARAM *pParam,/* I pointer to config param structure */ double *pDouble /* O pointer to place to store value */ ) { char text[128]; char *ptext; int count; ptext = envGetConfigParam(pParam, sizeof text, text); if (ptext != NULL) { count = epicsScanDouble(text, pDouble); if (count == 1) { return 0; } (void)fprintf(stderr,"Unable to find a real number in %s=%s\n", pParam->name, text); } return -1; } /*+/subr********************************************************************** * NAME envGetInetAddrConfigParam - get value of an inet addr config parameter * * DESCRIPTION * Gets the value of a configuration parameter and copies it into * the caller's (struct in_addr) buffer. If the configuration parameter * isn't found in the environment, then the default value for * the parameter is copied. * * If no parameter is found and there is no default, then -1 is * returned and the callers buffer is unmodified. * * RETURNS * 0, or * -1 if an error is encountered * * EXAMPLE * 1. Get the value for the inet address environment parameter EPICS_INET. * * #include "envDefs.h" * struct in_addr addr; * long status; * * status = envGetInetAddrConfigParam(&EPICS_INET, &addr); * if (status == 0) { * printf("the s_addr is: %x\n", addr.s_addr); * } * else { * printf("%s could not be found or was not an inet address\n", * EPICS_INET.name); * } * *-*/ long epicsShareAPI envGetInetAddrConfigParam( const ENV_PARAM *pParam,/* I pointer to config param structure */ struct in_addr *pAddr /* O pointer to struct to receive inet addr */ ) { char text[128]; char *ptext; long status; struct sockaddr_in sin; ptext = envGetConfigParam(pParam, sizeof text, text); if (ptext) { status = aToIPAddr (text, 0u, &sin); if (status == 0) { *pAddr = sin.sin_addr; return 0; } (void)fprintf(stderr,"Unable to find an IP address or valid host name in %s=%s\n", pParam->name, text); } return -1; } /*+/subr********************************************************************** * NAME envGetLongConfigParam - get value of an integer config parameter * * DESCRIPTION * Gets the value of a configuration parameter and copies it * into the caller's integer (long) buffer. If the configuration * parameter isn't found in the environment, then the default value for * the parameter is copied. * * If no parameter is found and there is no default, then -1 is * returned and the callers buffer is unmodified. * * RETURNS * 0, or * -1 if an error is encountered * * EXAMPLE * 1. Get the value as a long for the integer environment parameter * EPICS_NUMBER_OF_ITEMS. * * #include "envDefs.h" * long count; * long status; * * status = envGetLongConfigParam(&EPICS_NUMBER_OF_ITEMS, &count); * if (status == 0) { * printf("and the count is: %d\n", count); * } * else { * printf("%s could not be found or was not an integer\n", * EPICS_NUMBER_OF_ITEMS.name); * } * *-*/ long epicsShareAPI envGetLongConfigParam( const ENV_PARAM *pParam,/* I pointer to config param structure */ long *pLong /* O pointer to place to store value */ ) { char text[128]; char *ptext; int count; ptext = envGetConfigParam(pParam, sizeof text, text); if (ptext) { count = sscanf(text, "%ld", pLong); if (count == 1) return 0; (void)fprintf(stderr,"Unable to find an integer in %s=%s\n", pParam->name, text); } return -1; } long epicsShareAPI envGetBoolConfigParam(const ENV_PARAM *pParam, int *pBool) { char text[20]; if(!envGetConfigParam(pParam, sizeof(text), text)) return -1; *pBool = epicsStrCaseCmp(text, "yes")==0; return 0; } /*+/subr********************************************************************** * NAME envPrtConfigParam - print value of a configuration parameter * * DESCRIPTION * Prints the value of a configuration parameter. * * RETURNS * 0 * * EXAMPLE * 1. Print the value for the EPICS-defined environment parameter * EPICS_TS_NTP_INET. * * #include "envDefs.h" * * envPrtConfigParam(&EPICS_TS_NTP_INET); * *-*/ long epicsShareAPI envPrtConfigParam( const ENV_PARAM *pParam) /* pointer to config param structure */ { const char *pVal; pVal = envGetConfigParamPtr(pParam); if (pVal == NULL) fprintf(stdout, "%s is undefined\n", pParam->name); else fprintf(stdout,"%s: %s\n", pParam->name, pVal); return 0; } /*+/subr********************************************************************** * NAME epicsPrtEnvParams - print value of all configuration parameters * * DESCRIPTION * Prints all configuration parameters and their current value. * * RETURNS * 0 * * EXAMPLE * 1. Print the value for all EPICS-defined environment parameters. * * #include "envDefs.h" * * epicsPrtEnvParams(); * *-*/ long epicsShareAPI epicsPrtEnvParams(void) { const ENV_PARAM **ppParam = env_param_list; while (*ppParam != NULL) envPrtConfigParam(*(ppParam++)); return 0; } /* * envGetInetPortConfigParam () */ epicsShareFunc unsigned short epicsShareAPI envGetInetPortConfigParam (const ENV_PARAM *pEnv, unsigned short defaultPort) { long longStatus; long epicsParam; longStatus = envGetLongConfigParam (pEnv, &epicsParam); if (longStatus!=0) { epicsParam = (long) defaultPort; errlogPrintf ("EPICS Environment \"%s\" integer fetch failed\n", pEnv->name); errlogPrintf ("setting \"%s\" = %ld\n", pEnv->name, epicsParam); } if (epicsParam<=IPPORT_USERRESERVED || epicsParam>USHRT_MAX) { errlogPrintf ("EPICS Environment \"%s\" out of range\n", pEnv->name); /* * Quit if the port is wrong due coding error */ assert (epicsParam != (long) defaultPort); epicsParam = (long) defaultPort; errlogPrintf ("Setting \"%s\" = %ld\n", pEnv->name, epicsParam); } /* * ok to clip to unsigned short here because we checked the range */ return (unsigned short) epicsParam; } base-7.0.3.1/modules/libcom/src/error/Makefile0000664000577000060420000000160213557101274017707 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/error INC += epicsPrint.h INC += errMdef.h INC += errSymTbl.h INC += errlog.h Com_SRCS += errlog.c Com_SRCS += errSymLib.c Com_SRCS += errSymTbl.c # For bldErrSymTbl # ERR_S_FILES += $(LIBCOM)/osi/devLib.h ERR_S_FILES += $(LIBCOM)/osi/epicsTime.h ERR_S_FILES += $(LIBCOM)/as/asLib.h ERR_S_FILES += $(LIBCOM)/misc/epicsStdlib.h ERR_S_FILES += $(LIBCOM)/pool/epicsThreadPool.h ERR_S_FILES += $(LIBCOM)/error/errMdef.h CLEANS += errSymTbl.c base-7.0.3.1/modules/libcom/src/error/RULES0000664000577000060420000000104613557101274017066 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. errSymTbl.c: $(ERR_S_FILES) $(LIBCOM)/error/makeStatTbl.pl $(PERL) $(LIBCOM)/error/makeStatTbl.pl $(ERR_S_FILES) base-7.0.3.1/modules/libcom/src/error/epicsPrint.h0000664000577000060420000000126613557101274020546 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /*epicsPrint.h */ /*This is now obsolete. Replaced by errlog.h */ #ifndef INCepicsPrintH #define INCepicsPrintH #include "errlog.h" #endif /*INCepicsPrintH*/ base-7.0.3.1/modules/libcom/src/error/errMdef.h0000664000577000060420000000351313557101274020007 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Error Handling definitions */ /* * Author: Marty Kraimer * Date: 6-1-90 */ #ifndef INC_errMdef_H #define INC_errMdef_H #define RTN_SUCCESS(STATUS) ((STATUS)==0) /* Module numbers start above 500 for compatibility with vxWorks errnoLib */ /* FIXME: M_xxx values could be declared as integer variables and set * at runtime from registration routines; the S_xxx definitions would * still work with that change, with careful initialization. */ /* libCom */ #define M_asLib (501 << 16) /* Access Security */ #define M_bucket (502 << 16) /* Bucket Hash */ #define M_devLib (503 << 16) /* Hardware RegisterAccess */ #define M_stdlib (504 << 16) /* EPICS Standard library */ #define M_pool (505 << 16) /* Thread pool */ #define M_time (506 << 16) /* epicsTime */ /* ioc */ #define M_dbAccess (511 << 16) /* Database Access Routines */ #define M_dbLib (512 << 16) /* Static Database Access */ #define M_drvSup (513 << 16) /* Driver Support */ #define M_devSup (514 << 16) /* Device Support */ #define M_recSup (515 << 16) /* Record Support */ /* cas */ #define M_cas (521 << 16) /* CA server */ #define M_gddFuncTbl (522 << 16) /* gdd jump table */ #define M_casApp (523 << 16) /* CA server application */ #endif /*INC_errMdef_H*/ base-7.0.3.1/modules/libcom/src/error/errSymLib.c0000664000577000060420000002007713557101274020332 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * errSymLib.c * Author: Marty Kraimer * Date: 6-1-90 */ #include #include #include #include #include #include #define epicsExportSharedSymbols #include "cantProceed.h" #include "epicsAssert.h" #include "epicsStdio.h" #include "errMdef.h" #include "errSymTbl.h" #include "ellLib.h" #include "errlog.h" #define NHASH 256 static epicsUInt16 errhash(long errNum); typedef struct errnumnode { ELLNODE node; long errNum; struct errnumnode *hashnode; const char *message; long pad; } ERRNUMNODE; static ELLLIST errnumlist = ELLLIST_INIT; static ERRNUMNODE **hashtable; static int initialized = 0; extern ERRSYMTAB_ID errSymTbl; /**************************************************************** * ERRSYMBLD * * Create the normal ell LIST of sorted error messages nodes * Followed by linked hash lists - that link together those * ell nodes that have a common hash number. * ***************************************************************/ int errSymBld(void) { ERRSYMBOL *errArray = errSymTbl->symbols; ERRNUMNODE *perrNumNode = NULL; ERRNUMNODE *pNextNode = NULL; ERRNUMNODE **phashnode = NULL; int i; int modnum; if (initialized) return(0); hashtable = (ERRNUMNODE**)callocMustSucceed (NHASH, sizeof(ERRNUMNODE*),"errSymBld"); for (i = 0; i < errSymTbl->nsymbols; i++, errArray++) { modnum = errArray->errNum >> 16; if (modnum < 501) { fprintf(stderr, "errSymBld: ERROR - Module number in errSymTbl < 501 was Module=%lx Name=%s\n", errArray->errNum, errArray->name); continue; } if ((errSymbolAdd(errArray->errNum, errArray->name)) < 0) { fprintf(stderr, "errSymBld: ERROR - errSymbolAdd() failed \n"); continue; } } perrNumNode = (ERRNUMNODE *) ellFirst(&errnumlist); while (perrNumNode) { /* hash each perrNumNode->errNum */ epicsUInt16 hashInd = errhash(perrNumNode->errNum); phashnode = (ERRNUMNODE**)&hashtable[hashInd]; pNextNode = (ERRNUMNODE*) *phashnode; /* search for last node (NULL) of hashnode linked list */ while (pNextNode) { phashnode = &pNextNode->hashnode; pNextNode = *phashnode; } *phashnode = perrNumNode; perrNumNode = (ERRNUMNODE *) ellNext((ELLNODE *) perrNumNode); } initialized = 1; return(0); } /**************************************************************** * HASH * returns the hash index of errNum ****************************************************************/ static epicsUInt16 errhash(long errNum) { epicsUInt16 modnum; epicsUInt16 errnum; modnum = (unsigned short) (errNum >> 16); errnum = (unsigned short) (errNum & 0xffff); return (((modnum - 500) * 20) + errnum) % NHASH; } /**************************************************************** * ERRSYMBOLADD * adds symbols to the master errnumlist as compiled from errSymTbl.c ***************************************************************/ int errSymbolAdd(long errNum, const char *name) { ERRNUMNODE *pNew = (ERRNUMNODE*) callocMustSucceed(1, sizeof(ERRNUMNODE), "errSymbolAdd"); pNew->errNum = errNum; pNew->message = name; ellAdd(&errnumlist, (ELLNODE*)pNew); return 0; } /**************************************************************** * errRawCopy ***************************************************************/ static void errRawCopy(long statusToDecode, char *pBuf, size_t bufLength) { epicsUInt16 modnum = (statusToDecode >>= 16) & 0xffff; epicsUInt16 errnum = statusToDecode & 0xffff; assert(bufLength > 20); if (modnum == 0) { epicsSnprintf(pBuf, bufLength, "Error #%u", errnum); } else { epicsSnprintf(pBuf, bufLength, "Error (%u,%u)", modnum, errnum); } } static const char* errSymLookupInternal(long status) { unsigned modNum; ERRNUMNODE *pNextNode; ERRNUMNODE **phashnode = NULL; if (!initialized) errSymBld(); modNum = (unsigned) status; modNum >>= 16; modNum &= 0xffff; if (modNum <= 500) { const char * pStr = strerror ((int) status); if (pStr) { return pStr; } } else { unsigned hashInd = errhash(status); phashnode = (ERRNUMNODE**)&hashtable[hashInd]; pNextNode = *phashnode; while (pNextNode) { if (pNextNode->errNum==status){ return pNextNode->message; } phashnode = &pNextNode->hashnode; pNextNode = *phashnode; } } return NULL; } const char* errSymMsg(long status) { const char* msg = errSymLookupInternal(status); return msg ? msg : ""; } /**************************************************************** * errSymLookup ***************************************************************/ void errSymLookup(long status, char * pBuf, size_t bufLength) { const char* msg = errSymLookupInternal(status); if(msg) { strncpy(pBuf, msg, bufLength); pBuf[bufLength-1] = '\0'; return; } errRawCopy(status, pBuf, bufLength); } /**************************************************************** * errSymDump ***************************************************************/ void errSymDump(void) { int i; int msgcount = 0; if (!initialized) errSymBld(); msgcount = 0; printf("errSymDump: number of hash slots = %d\n", NHASH); for (i = 0; i < NHASH; i++) { ERRNUMNODE **phashnode = &hashtable[i]; ERRNUMNODE *pNextNode = *phashnode; int count = 0; while (pNextNode) { int modnum = pNextNode->errNum >> 16; int errnum = pNextNode->errNum & 0xffff; if (!count++) { printf("HASHNODE = %d\n", i); } printf("\tmod %d num %d \"%s\"\n", modnum , errnum , pNextNode->message); phashnode = &pNextNode->hashnode; pNextNode = *phashnode; } msgcount += count; } printf("\nerrSymDump: total number of error messages = %d\n", msgcount); } /**************************************************************** * errSymTestPrint ***************************************************************/ void errSymTestPrint(long errNum) { char message[256]; epicsUInt16 modnum; epicsUInt16 errnum; if (!initialized) errSymBld(); message[0] = '\0'; modnum = (epicsUInt16) (errNum >> 16); errnum = (epicsUInt16) (errNum & 0xffff); if (modnum < 501) { fprintf(stderr, "Usage: errSymTestPrint(long errNum) \n"); fprintf(stderr, "errSymTestPrint: module number < 501 \n"); return; } errSymLookup(errNum, message, sizeof(message)); if ( message[0] == '\0' ) return; printf("module %hu number %hu message=\"%s\"\n", modnum, errnum, message); return; } /**************************************************************** * ERRSYMTEST ****************************************************************/ void errSymTest(epicsUInt16 modnum, epicsUInt16 begErrNum, epicsUInt16 endErrNum) { long errNum; epicsUInt16 errnum; if (!initialized) errSymBld(); if (modnum < 501) return; /* print range of error messages */ for (errnum = begErrNum; errnum <= endErrNum; errnum++) { errNum = modnum << 16; errNum |= (errnum & 0xffff); errSymTestPrint(errNum); } } base-7.0.3.1/modules/libcom/src/error/errSymTbl.h0000664000577000060420000000300513557101274020342 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_errSymTbl_H #define INC_errSymTbl_H #include #include "shareLib.h" #include "epicsTypes.h" /* ERRSYMBOL - entry in symbol table */ typedef struct { long errNum; /* errMessage symbol number */ const char *name; /* pointer to symbol name */ } ERRSYMBOL; /* ERRSYMTAB - symbol table */ typedef struct { int nsymbols; /* current number of symbols in table */ ERRSYMBOL *symbols; /* ptr to array of symbol entries */ } ERRSYMTAB; typedef ERRSYMTAB *ERRSYMTAB_ID; #ifdef __cplusplus extern "C" { #endif epicsShareFunc void errSymLookup(long status, char *pBuf, size_t bufLength); epicsShareFunc const char* errSymMsg(long status); epicsShareFunc void errSymTest(epicsUInt16 modnum, epicsUInt16 begErrNum, epicsUInt16 endErrNum); epicsShareFunc void errSymTestPrint(long errNum); epicsShareFunc int errSymBld(void); epicsShareFunc int errSymbolAdd(long errNum, const char *name); epicsShareFunc void errSymDump(void); #ifdef __cplusplus } #endif #endif /* INC_errSymTbl_H */ base-7.0.3.1/modules/libcom/src/error/errlog.c0000664000577000060420000004357513557101274017724 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Author: Marty Kraimer * Date: 07JAN1998 */ #include #include #include #include #include #include #define epicsExportSharedSymbols #define ERRLOG_INIT #include "adjustment.h" #include "dbDefs.h" #include "epicsThread.h" #include "cantProceed.h" #include "epicsMutex.h" #include "epicsEvent.h" #include "epicsInterrupt.h" #include "errMdef.h" #include "errSymTbl.h" #include "ellLib.h" #include "errlog.h" #include "epicsStdio.h" #include "epicsExit.h" #define BUFFER_SIZE 1280 #define MAX_MESSAGE_SIZE 256 /*Declare storage for errVerbose */ epicsShareDef int errVerbose = 0; static void errlogExitHandler(void *); static void errlogThread(void); static char *msgbufGetFree(int noConsoleMessage); static void msgbufSetSize(int size); /* Send 'size' chars plus trailing '\0' */ static char *msgbufGetSend(int *noConsoleMessage); static void msgbufFreeSend(void); typedef struct listenerNode{ ELLNODE node; errlogListener listener; void *pPrivate; } listenerNode; /*each message consists of a msgNode immediately followed by the message */ typedef struct msgNode { ELLNODE node; char *message; int length; int noConsoleMessage; } msgNode; static struct { epicsEventId waitForWork; /*errlogThread waits for this*/ epicsMutexId msgQueueLock; epicsMutexId listenerLock; epicsEventId waitForFlush; /*errlogFlush waits for this*/ epicsEventId flush; /*errlogFlush sets errlogThread does a Try*/ epicsMutexId flushLock; epicsEventId waitForExit; /*errlogExitHandler waits for this*/ int atExit; /*TRUE when errlogExitHandler is active*/ ELLLIST listenerList; ELLLIST msgQueue; msgNode *pnextSend; int errlogInitFailed; int buffersize; int maxMsgSize; int msgNeeded; int sevToLog; int toConsole; FILE *console; int missedMessages; char *pbuffer; } pvtData; /* * vsnprintf with truncation message */ static int tvsnPrint(char *str, size_t size, const char *format, va_list ap) { static const char tmsg[] = "<>\n"; int nchar = epicsVsnprintf(str, size, format ? format : "", ap); if (nchar >= size) { if (size > sizeof tmsg) strcpy(str + size - sizeof tmsg, tmsg); nchar = size - 1; } return nchar; } int errlogPrintf(const char *pFormat, ...) { va_list pvar; char *pbuffer; int nchar; int isOkToBlock; if (epicsInterruptIsInterruptContext()) { epicsInterruptContextMessage ("errlogPrintf called from interrupt level\n"); return 0; } errlogInit(0); isOkToBlock = epicsThreadIsOkToBlock(); if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { FILE *console = pvtData.console ? pvtData.console : stderr; va_start(pvar, pFormat); nchar = vfprintf(console, pFormat, pvar); va_end (pvar); fflush(console); } if (pvtData.atExit) return nchar; pbuffer = msgbufGetFree(isOkToBlock); if (!pbuffer) return 0; va_start(pvar, pFormat); nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar); va_end(pvar); msgbufSetSize(nchar); return nchar; } int errlogVprintf(const char *pFormat,va_list pvar) { int nchar; char *pbuffer; int isOkToBlock; FILE *console; if (epicsInterruptIsInterruptContext()) { epicsInterruptContextMessage ("errlogVprintf called from interrupt level\n"); return 0; } errlogInit(0); if (pvtData.atExit) return 0; isOkToBlock = epicsThreadIsOkToBlock(); pbuffer = msgbufGetFree(isOkToBlock); if (!pbuffer) { console = pvtData.console ? pvtData.console : stderr; vfprintf(console, pFormat, pvar); fflush(console); return 0; } nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar); if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { console = pvtData.console ? pvtData.console : stderr; fprintf(console, "%s", pbuffer); fflush(console); } msgbufSetSize(nchar); return nchar; } int errlogMessage(const char *message) { errlogPrintf("%s", message); return 0; } int errlogPrintfNoConsole(const char *pFormat, ...) { va_list pvar; int nchar; if (epicsInterruptIsInterruptContext()) { epicsInterruptContextMessage ("errlogPrintfNoConsole called from interrupt level\n"); return 0; } errlogInit(0); va_start(pvar, pFormat); nchar = errlogVprintfNoConsole(pFormat, pvar); va_end(pvar); return nchar; } int errlogVprintfNoConsole(const char *pFormat, va_list pvar) { int nchar; char *pbuffer; if (epicsInterruptIsInterruptContext()) { epicsInterruptContextMessage ("errlogVprintfNoConsole called from interrupt level\n"); return 0; } errlogInit(0); if (pvtData.atExit) return 0; pbuffer = msgbufGetFree(1); if (!pbuffer) return 0; nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar); msgbufSetSize(nchar); return nchar; } int errlogSevPrintf(errlogSevEnum severity, const char *pFormat, ...) { va_list pvar; int nchar; int isOkToBlock; if (epicsInterruptIsInterruptContext()) { epicsInterruptContextMessage ("errlogSevPrintf called from interrupt level\n"); return 0; } errlogInit(0); if (pvtData.sevToLog > severity) return 0; isOkToBlock = epicsThreadIsOkToBlock(); if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { FILE *console = pvtData.console ? pvtData.console : stderr; fprintf(console, "sevr=%s ", errlogGetSevEnumString(severity)); va_start(pvar, pFormat); vfprintf(console, pFormat, pvar); va_end(pvar); fflush(console); } va_start(pvar, pFormat); nchar = errlogSevVprintf(severity, pFormat, pvar); va_end(pvar); return nchar; } int errlogSevVprintf(errlogSevEnum severity, const char *pFormat, va_list pvar) { char *pnext; int nchar; int totalChar = 0; int isOkToBlock; if (epicsInterruptIsInterruptContext()) { epicsInterruptContextMessage ("errlogSevVprintf called from interrupt level\n"); return 0; } errlogInit(0); if (pvtData.atExit) return 0; isOkToBlock = epicsThreadIsOkToBlock(); pnext = msgbufGetFree(isOkToBlock); if (!pnext) return 0; nchar = sprintf(pnext, "sevr=%s ", errlogGetSevEnumString(severity)); pnext += nchar; totalChar += nchar; nchar = tvsnPrint(pnext, pvtData.maxMsgSize - totalChar - 1, pFormat, pvar); pnext += nchar; totalChar += nchar; if (pnext[-1] != '\n') { strcpy(pnext,"\n"); totalChar++; } msgbufSetSize(totalChar); return nchar; } const char * errlogGetSevEnumString(errlogSevEnum severity) { errlogInit(0); if (severity > 3) return "unknown"; return errlogSevEnumString[severity]; } void errlogSetSevToLog(errlogSevEnum severity) { errlogInit(0); pvtData.sevToLog = severity; } errlogSevEnum errlogGetSevToLog(void) { errlogInit(0); return pvtData.sevToLog; } void errlogAddListener(errlogListener listener, void *pPrivate) { listenerNode *plistenerNode; errlogInit(0); if (pvtData.atExit) return; plistenerNode = callocMustSucceed(1,sizeof(listenerNode), "errlogAddListener"); epicsMutexMustLock(pvtData.listenerLock); plistenerNode->listener = listener; plistenerNode->pPrivate = pPrivate; ellAdd(&pvtData.listenerList,&plistenerNode->node); epicsMutexUnlock(pvtData.listenerLock); } int errlogRemoveListeners(errlogListener listener, void *pPrivate) { listenerNode *plistenerNode; int count = 0; errlogInit(0); if (!pvtData.atExit) epicsMutexMustLock(pvtData.listenerLock); plistenerNode = (listenerNode *)ellFirst(&pvtData.listenerList); while (plistenerNode) { listenerNode *pnext = (listenerNode *)ellNext(&plistenerNode->node); if (plistenerNode->listener == listener && plistenerNode->pPrivate == pPrivate) { ellDelete(&pvtData.listenerList, &plistenerNode->node); free(plistenerNode); ++count; } plistenerNode = pnext; } if (!pvtData.atExit) epicsMutexUnlock(pvtData.listenerLock); if (count == 0) { FILE *console = pvtData.console ? pvtData.console : stderr; fprintf(console, "errlogRemoveListeners: No listeners found\n"); } return count; } int eltc(int yesno) { errlogInit(0); errlogFlush(); pvtData.toConsole = yesno; return 0; } int errlogSetConsole(FILE *stream) { errlogInit(0); pvtData.console = stream; return 0; } void errPrintf(long status, const char *pFileName, int lineno, const char *pformat, ...) { va_list pvar; char *pnext; int nchar; int totalChar=0; int isOkToBlock; char name[256]; if (epicsInterruptIsInterruptContext()) { epicsInterruptContextMessage("errPrintf called from interrupt level\n"); return; } errlogInit(0); isOkToBlock = epicsThreadIsOkToBlock(); if (status == 0) status = errno; if (status > 0) { errSymLookup(status, name, sizeof(name)); } if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { FILE *console = pvtData.console ? pvtData.console : stderr; if (pFileName) fprintf(console, "filename=\"%s\" line number=%d\n", pFileName, lineno); if (status > 0) fprintf(console, "%s ", name); va_start(pvar, pformat); vfprintf(console, pformat, pvar); va_end(pvar); fputc('\n', console); fflush(console); } if (pvtData.atExit) return; pnext = msgbufGetFree(isOkToBlock); if (!pnext) return; if (pFileName) { nchar = sprintf(pnext,"filename=\"%s\" line number=%d\n", pFileName, lineno); pnext += nchar; totalChar += nchar; } if (status > 0) { nchar = sprintf(pnext,"%s ",name); pnext += nchar; totalChar += nchar; } va_start(pvar, pformat); nchar = tvsnPrint(pnext, pvtData.maxMsgSize - totalChar - 1, pformat, pvar); va_end(pvar); if (nchar>0) { pnext += nchar; totalChar += nchar; } strcpy(pnext, "\n"); totalChar++ ; /*include the \n */ msgbufSetSize(totalChar); } static void errlogExitHandler(void *pvt) { pvtData.atExit = 1; epicsEventSignal(pvtData.waitForWork); epicsEventMustWait(pvtData.waitForExit); } struct initArgs { int bufsize; int maxMsgSize; }; static void errlogInitPvt(void *arg) { struct initArgs *pconfig = (struct initArgs *) arg; epicsThreadId tid; pvtData.errlogInitFailed = TRUE; pvtData.buffersize = pconfig->bufsize; pvtData.maxMsgSize = pconfig->maxMsgSize; pvtData.msgNeeded = adjustToWorstCaseAlignment(pvtData.maxMsgSize + sizeof(msgNode)); ellInit(&pvtData.listenerList); ellInit(&pvtData.msgQueue); pvtData.toConsole = TRUE; pvtData.console = NULL; pvtData.waitForWork = epicsEventMustCreate(epicsEventEmpty); pvtData.listenerLock = epicsMutexMustCreate(); pvtData.msgQueueLock = epicsMutexMustCreate(); pvtData.waitForFlush = epicsEventMustCreate(epicsEventEmpty); pvtData.flush = epicsEventMustCreate(epicsEventEmpty); pvtData.flushLock = epicsMutexMustCreate(); pvtData.waitForExit = epicsEventMustCreate(epicsEventEmpty); pvtData.pbuffer = callocMustSucceed(1, pvtData.buffersize, "errlogInitPvt"); errSymBld(); /* Better not to do this lazily... */ tid = epicsThreadCreate("errlog", epicsThreadPriorityLow, epicsThreadGetStackSize(epicsThreadStackSmall), (EPICSTHREADFUNC)errlogThread, 0); if (tid) { pvtData.errlogInitFailed = FALSE; } } int errlogInit2(int bufsize, int maxMsgSize) { static epicsThreadOnceId errlogOnceFlag = EPICS_THREAD_ONCE_INIT; struct initArgs config; if (pvtData.atExit) return 0; if (bufsize < BUFFER_SIZE) bufsize = BUFFER_SIZE; config.bufsize = bufsize; if (maxMsgSize < MAX_MESSAGE_SIZE) maxMsgSize = MAX_MESSAGE_SIZE; config.maxMsgSize = maxMsgSize; epicsThreadOnce(&errlogOnceFlag, errlogInitPvt, &config); if (pvtData.errlogInitFailed) { fprintf(stderr,"errlogInit failed\n"); exit(1); } return 0; } int errlogInit(int bufsize) { return errlogInit2(bufsize, MAX_MESSAGE_SIZE); } void errlogFlush(void) { int count; errlogInit(0); if (pvtData.atExit) return; /*If nothing in queue dont wake up errlogThread*/ epicsMutexMustLock(pvtData.msgQueueLock); count = ellCount(&pvtData.msgQueue); epicsMutexUnlock(pvtData.msgQueueLock); if (count <= 0) return; /*must let errlogThread empty queue*/ epicsMutexMustLock(pvtData.flushLock); epicsEventSignal(pvtData.flush); epicsEventSignal(pvtData.waitForWork); epicsEventMustWait(pvtData.waitForFlush); epicsMutexUnlock(pvtData.flushLock); } static void errlogThread(void) { listenerNode *plistenerNode; int noConsoleMessage; char *pmessage; epicsAtExit(errlogExitHandler,0); while (TRUE) { epicsEventMustWait(pvtData.waitForWork); while ((pmessage = msgbufGetSend(&noConsoleMessage))) { epicsMutexMustLock(pvtData.listenerLock); if (pvtData.toConsole && !noConsoleMessage) { FILE *console = pvtData.console ? pvtData.console : stderr; fprintf(console, "%s", pmessage); fflush(console); } plistenerNode = (listenerNode *)ellFirst(&pvtData.listenerList); while (plistenerNode) { (*plistenerNode->listener)(plistenerNode->pPrivate, pmessage); plistenerNode = (listenerNode *)ellNext(&plistenerNode->node); } epicsMutexUnlock(pvtData.listenerLock); msgbufFreeSend(); } if (pvtData.atExit) break; if (epicsEventTryWait(pvtData.flush) != epicsEventWaitOK) continue; epicsThreadSleep(.2); /*just wait an extra .2 seconds*/ epicsEventSignal(pvtData.waitForFlush); } epicsEventSignal(pvtData.waitForExit); } static msgNode * msgbufGetNode(void) { char *pbuffer = pvtData.pbuffer; char *pnextFree; msgNode *pnextSend; if (ellCount(&pvtData.msgQueue) == 0 ) { pnextFree = pbuffer; /* Reset if empty */ } else { msgNode *pfirst = (msgNode *)ellFirst(&pvtData.msgQueue); msgNode *plast = (msgNode *)ellLast(&pvtData.msgQueue); char *plimit = pbuffer + pvtData.buffersize; pnextFree = plast->message + adjustToWorstCaseAlignment(plast->length); if (pfirst > plast) { plimit = (char *)pfirst; } else if (pnextFree + pvtData.msgNeeded > plimit) { pnextFree = pbuffer; /* Hit end, wrap to start */ plimit = (char *)pfirst; } if (pnextFree + pvtData.msgNeeded > plimit) { return 0; /* No room */ } } pnextSend = (msgNode *)pnextFree; pnextSend->message = pnextFree + sizeof(msgNode); pnextSend->length = 0; return pnextSend; } static char * msgbufGetFree(int noConsoleMessage) { msgNode *pnextSend; if (epicsMutexLock(pvtData.msgQueueLock) != epicsMutexLockOK) return 0; if ((ellCount(&pvtData.msgQueue) == 0) && pvtData.missedMessages) { int nchar; pnextSend = msgbufGetNode(); nchar = sprintf(pnextSend->message, "errlog: %d messages were discarded\n", pvtData.missedMessages); pnextSend->length = nchar + 1; pvtData.missedMessages = 0; ellAdd(&pvtData.msgQueue, &pnextSend->node); } pvtData.pnextSend = pnextSend = msgbufGetNode(); if (pnextSend) { pnextSend->noConsoleMessage = noConsoleMessage; pnextSend->length = 0; return pnextSend->message; /* NB: msgQueueLock is still locked */ } ++pvtData.missedMessages; epicsMutexUnlock(pvtData.msgQueueLock); return 0; } static void msgbufSetSize(int size) { msgNode *pnextSend = pvtData.pnextSend; pnextSend->length = size+1; ellAdd(&pvtData.msgQueue, &pnextSend->node); epicsMutexUnlock(pvtData.msgQueueLock); epicsEventSignal(pvtData.waitForWork); } static char * msgbufGetSend(int *noConsoleMessage) { msgNode *pnextSend; epicsMutexMustLock(pvtData.msgQueueLock); pnextSend = (msgNode *)ellFirst(&pvtData.msgQueue); epicsMutexUnlock(pvtData.msgQueueLock); if (!pnextSend) return 0; *noConsoleMessage = pnextSend->noConsoleMessage; return pnextSend->message; } static void msgbufFreeSend(void) { msgNode *pnextSend; epicsMutexMustLock(pvtData.msgQueueLock); pnextSend = (msgNode *)ellFirst(&pvtData.msgQueue); if (!pnextSend) { FILE *console = pvtData.console ? pvtData.console : stderr; fprintf(console, "errlog: msgbufFreeSend logic error\n"); epicsThreadSuspendSelf(); } ellDelete(&pvtData.msgQueue, &pnextSend->node); epicsMutexUnlock(pvtData.msgQueueLock); } base-7.0.3.1/modules/libcom/src/error/errlog.h0000664000577000060420000000547213557101274017723 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_errlog_H #define INC_errlog_H #include #include #include #include "shareLib.h" #include "compilerDependencies.h" #ifdef __cplusplus extern "C" { #endif typedef void (*errlogListener)(void *pPrivate, const char *message); typedef enum { errlogInfo, errlogMinor, errlogMajor, errlogFatal } errlogSevEnum; epicsShareExtern int errVerbose; #ifdef ERRLOG_INIT epicsShareDef const char *errlogSevEnumString[] = { "info", "minor", "major", "fatal" }; #else epicsShareExtern const char * errlogSevEnumString[]; #endif /* errMessage is a macro so it can get the file and line number */ #define errMessage(S, PM) \ errPrintf(S, __FILE__, __LINE__, "%s", PM) /* epicsPrintf and epicsVprintf are old names for errlog routines*/ #define epicsPrintf errlogPrintf #define epicsVprintf errlogVprintf epicsShareFunc int errlogPrintf(const char *pformat, ...) EPICS_PRINTF_STYLE(1,2); epicsShareFunc int errlogVprintf(const char *pformat, va_list pvar); epicsShareFunc int errlogSevPrintf(const errlogSevEnum severity, const char *pformat, ...) EPICS_PRINTF_STYLE(2,3); epicsShareFunc int errlogSevVprintf(const errlogSevEnum severity, const char *pformat, va_list pvar); epicsShareFunc int errlogMessage(const char *message); epicsShareFunc const char * errlogGetSevEnumString(errlogSevEnum severity); epicsShareFunc void errlogSetSevToLog(errlogSevEnum severity); epicsShareFunc errlogSevEnum errlogGetSevToLog(void); epicsShareFunc void errlogAddListener(errlogListener listener, void *pPrivate); epicsShareFunc int errlogRemoveListeners(errlogListener listener, void *pPrivate); epicsShareFunc int eltc(int yesno); epicsShareFunc int errlogSetConsole(FILE *stream); epicsShareFunc int errlogInit(int bufsize); epicsShareFunc int errlogInit2(int bufsize, int maxMsgSize); epicsShareFunc void errlogFlush(void); epicsShareFunc void errPrintf(long status, const char *pFileName, int lineno, const char *pformat, ...) EPICS_PRINTF_STYLE(4,5); epicsShareFunc int errlogPrintfNoConsole(const char *pformat, ...) EPICS_PRINTF_STYLE(1,2); epicsShareFunc int errlogVprintfNoConsole(const char *pformat,va_list pvar); epicsShareFunc void errSymLookup(long status, char *pBuf, size_t bufLength); #ifdef __cplusplus } #endif #endif /*INC_errlog_H*/ base-7.0.3.1/modules/libcom/src/error/makeStatTbl.pl0000664000577000060420000000531113557101274021020 0ustar anjaesctl#!/usr/bin/env perl #************************************************************************* # Copyright (c) 2002 The University of Chicago, as Operator of Argonne # National Laboratory. # Copyright (c) 2014 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # # makeStatTbl.pl - Create Error Symbol Table # # Original Author: Kay-Uwe Kasemir, 1-31-97 # # SYNOPSIS # perl makeStatTbl.pl files.h... # # DESCRIPTION # This tool creates a symbol table (ERRSYMTAB) structure which contains the # names and values of all the status codes defined in the .h files named in # its input arguments. The status codes must be prefixed with "S_" # in order to be included in this table. # Module numbers definitions prefixed with "M_" are also read from the input # files and included in the output. # # This tool's primary use is for creating an error status table used # by errPrint, and errSymLookup. # # FILES # errMdef.h Module number file for each h directory # errSymTbl.c Source file generated by tool in the cwd # # SEE ALSO: errnoLib(1), symLib(1) use strict; use Getopt::Std; my $tool = 'makeStatTbl.pl'; our ($opt_h); our $opt_o = 'errSymTbl.c'; $Getopt::Std::OUTPUT_HELP_VERSION = 1; &HELP_MESSAGE unless getopts('ho:') && @ARGV; &HELP_MESSAGE if $opt_h; my (@syms, %vals, %msgs); # Extract names, values and comments from all S_ and M_ symbol definitions while (<>) { chomp; next unless m/^ \s* \# \s* define \s+ ([SM]_[A-Za-z0-9_]+) \s+ (.*?) \s* \/ \* \s* (.*?) \s* \* \/ \s* $/x; push @syms, $1; $vals{$1} = $2; $msgs{$1} = $3; } open my $out, '>', $opt_o or die "Can't create $opt_o: $!\n"; print $out <<"END"; /* Generated file $opt_o */ #include "errMdef.h" #include "errSymTbl.h" #include "dbDefs.h" END my @mods = grep {/^M_/} @syms; my @errs = grep {/^S_/} @syms; foreach my $mod (@mods) { my $val = $vals{$mod}; my $msg = $msgs{$mod}; print $out "#ifndef $mod\n", "#define $mod $val /* $msg */\n", "#endif\n"; } print $out "\n", "static ERRSYMBOL symbols[] = {\n"; foreach my $err (@errs) { my $msg = escape($msgs{$err}); my $val = $vals{$err}; print $out " { $val, \"$msg\"},\n"; } print $out <<"END"; }; static ERRSYMTAB symTbl = { NELEMENTS(symbols), symbols }; ERRSYMTAB_ID errSymTbl = &symTbl; END sub HELP_MESSAGE { print STDERR "Usage: $tool [-o file.c] files.h ...\n"; exit 2; } sub escape { $_ = shift; s/"/\\"/g; return $_; } base-7.0.3.1/modules/libcom/src/fdmgr/Makefile0000664000577000060420000000104113557101274017652 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/fdmgr INC += fdManager.h INC += fdmgr.h Com_SRCS += fdmgr.cpp Com_SRCS += fdManager.cpp base-7.0.3.1/modules/libcom/src/fdmgr/fdManager.cpp0000664000577000060420000002236713557101274020620 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ // // File descriptor management C++ class library // (for multiplexing IO in a single threaded environment) // // Author Jeffrey O. Hill // johill@lanl.gov // 505 665 1831 // // NOTES: // 1) This library is not thread safe // #include #define instantiateRecourceLib #define epicsExportSharedSymbols #include "epicsAssert.h" #include "epicsThread.h" #include "fdManager.h" #include "locationException.h" using std :: max; epicsShareDef fdManager fileDescriptorManager; const unsigned mSecPerSec = 1000u; const unsigned uSecPerSec = 1000u * mSecPerSec; // // fdManager::fdManager() // // hopefully its a reasonable guess that select() and epicsThreadSleep() // will have the same sleep quantum // epicsShareFunc fdManager::fdManager () : sleepQuantum ( epicsThreadSleepQuantum () ), fdSetsPtr ( new fd_set [fdrNEnums] ), pTimerQueue ( 0 ), maxFD ( 0 ), processInProg ( false ), pCBReg ( 0 ) { int status = osiSockAttach (); assert (status); for ( size_t i = 0u; i < fdrNEnums; i++ ) { FD_ZERO ( &fdSetsPtr[i] ); } } // // fdManager::~fdManager() // epicsShareFunc fdManager::~fdManager() { fdReg *pReg; while ( (pReg = this->regList.get()) ) { pReg->state = fdReg::limbo; pReg->destroy(); } while ( (pReg = this->activeList.get()) ) { pReg->state = fdReg::limbo; pReg->destroy(); } delete this->pTimerQueue; delete [] this->fdSetsPtr; osiSockRelease(); } // // fdManager::process() // epicsShareFunc void fdManager::process (double delay) { this->lazyInitTimerQueue (); // // no recursion // if (this->processInProg) { return; } this->processInProg = true; // // One shot at expired timers prior to going into // select. This allows zero delay timers to arm // fd writes. We will never process the timer queue // more than once here so that fd activity get serviced // in a reasonable length of time. // double minDelay = this->pTimerQueue->process(epicsTime::getMonotonic()); if ( minDelay >= delay ) { minDelay = delay; } bool ioPending = false; tsDLIter < fdReg > iter = this->regList.firstIter (); while ( iter.valid () ) { FD_SET(iter->getFD(), &this->fdSetsPtr[iter->getType()]); ioPending = true; ++iter; } if ( ioPending ) { struct timeval tv; tv.tv_sec = static_cast ( minDelay ); tv.tv_usec = static_cast ( (minDelay-tv.tv_sec) * uSecPerSec ); fd_set * pReadSet = & this->fdSetsPtr[fdrRead]; fd_set * pWriteSet = & this->fdSetsPtr[fdrWrite]; fd_set * pExceptSet = & this->fdSetsPtr[fdrException]; int status = select (this->maxFD, pReadSet, pWriteSet, pExceptSet, &tv); this->pTimerQueue->process(epicsTime::getMonotonic()); if ( status > 0 ) { // // Look for activity // iter=this->regList.firstIter (); while ( iter.valid () && status > 0 ) { tsDLIter < fdReg > tmp = iter; tmp++; if (FD_ISSET(iter->getFD(), &this->fdSetsPtr[iter->getType()])) { FD_CLR(iter->getFD(), &this->fdSetsPtr[iter->getType()]); this->regList.remove(*iter); this->activeList.add(*iter); iter->state = fdReg::active; status--; } iter = tmp; } // // I am careful to prevent problems if they access the // above list while in a "callBack()" routine // fdReg * pReg; while ( (pReg = this->activeList.get()) ) { pReg->state = fdReg::limbo; // // Tag current fdReg so that we // can detect if it was deleted // during the call back // this->pCBReg = pReg; pReg->callBack(); if (this->pCBReg != NULL) { // // check only after we see that it is non-null so // that we dont trigger bounds-checker dangling pointer // error // assert (this->pCBReg==pReg); this->pCBReg = 0; if (pReg->onceOnly) { pReg->destroy(); } else { this->regList.add(*pReg); pReg->state = fdReg::pending; } } } } else if ( status < 0 ) { int errnoCpy = SOCKERRNO; // dont depend on flags being properly set if // an error is retuned from select for ( size_t i = 0u; i < fdrNEnums; i++ ) { FD_ZERO ( &fdSetsPtr[i] ); } // // print a message if its an unexpected error // if ( errnoCpy != SOCK_EINTR ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf ( stderr, "fdManager: select failed because \"%s\"\n", sockErrBuf ); } } } else { /* * recover from subtle differences between * windows sockets and UNIX sockets implementation * of select() */ epicsThreadSleep(minDelay); this->pTimerQueue->process(epicsTime::getMonotonic()); } this->processInProg = false; return; } // // fdReg::destroy() // (default destroy method) // void fdReg::destroy() { delete this; } // // fdReg::~fdReg() // fdReg::~fdReg() { this->manager.removeReg(*this); } // // fdReg::show() // void fdReg::show(unsigned level) const { printf ("fdReg at %p\n", (void *) this); if (level>1u) { printf ("\tstate = %d, onceOnly = %d\n", this->state, this->onceOnly); } this->fdRegId::show(level); } // // fdRegId::show() // void fdRegId::show ( unsigned level ) const { printf ( "fdRegId at %p\n", static_cast ( this ) ); if ( level > 1u ) { printf ( "\tfd = %d, type = %d\n", this->fd, this->type ); } } // // fdManager::installReg () // void fdManager::installReg (fdReg ®) { this->maxFD = max ( this->maxFD, reg.getFD()+1 ); // Most applications will find that its important to push here to // the front of the list so that transient writes get executed // first allowing incoming read protocol to find that outgoing // buffer space is newly available. this->regList.push ( reg ); reg.state = fdReg::pending; int status = this->fdTbl.add ( reg ); if ( status != 0 ) { throwWithLocation ( fdInterestSubscriptionAlreadyExits () ); } } // // fdManager::removeReg () // void fdManager::removeReg (fdReg ®In) { fdReg *pItemFound; pItemFound = this->fdTbl.remove (regIn); if (pItemFound!=®In) { fprintf(stderr, "fdManager::removeReg() bad fd registration object\n"); return; } // // signal fdManager that the fdReg was deleted // during the call back // if (this->pCBReg == ®In) { this->pCBReg = 0; } switch (regIn.state) { case fdReg::active: this->activeList.remove (regIn); break; case fdReg::pending: this->regList.remove (regIn); break; case fdReg::limbo: break; default: // // here if memory corrupted // assert(0); } regIn.state = fdReg::limbo; FD_CLR(regIn.getFD(), &this->fdSetsPtr[regIn.getType()]); } // // fdManager::reschedule () // NOOP - this only runs single threaded, and therefore they can only // add a new timer from places that will always end up in a reschedule // void fdManager::reschedule () { } double fdManager::quantum () { return this->sleepQuantum; } // // lookUpFD() // epicsShareFunc fdReg *fdManager::lookUpFD (const SOCKET fd, const fdRegType type) { if (fd<0) { return NULL; } fdRegId id (fd,type); return this->fdTbl.lookup(id); } // // fdReg::fdReg() // fdReg::fdReg (const SOCKET fdIn, const fdRegType typIn, const bool onceOnlyIn, fdManager &managerIn) : fdRegId (fdIn,typIn), state (limbo), onceOnly (onceOnlyIn), manager (managerIn) { if (!FD_IN_FDSET(fdIn)) { fprintf (stderr, "%s: fd > FD_SETSIZE ignored\n", __FILE__); return; } this->manager.installReg (*this); } template class resTable; base-7.0.3.1/modules/libcom/src/fdmgr/fdManager.h0000664000577000060420000001155013557101274020255 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * File descriptor management C++ class library * (for multiplexing IO in a single threaded environment) * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef fdManagerH_included #define fdManagerH_included #include "shareLib.h" // reset share lib defines #include "tsDLList.h" #include "resourceLib.h" #include "epicsTime.h" #include "osiSock.h" #include "epicsTimer.h" enum fdRegType {fdrRead, fdrWrite, fdrException, fdrNEnums}; // // fdRegId // // file descriptor interest id // class epicsShareClass fdRegId { public: fdRegId (const SOCKET fdIn, const fdRegType typeIn) : fd(fdIn), type(typeIn) {} SOCKET getFD () const { return this->fd; } fdRegType getType () const { return this->type; } bool operator == (const fdRegId &idIn) const { return this->fd == idIn.fd && this->type==idIn.type; } resTableIndex hash () const; virtual void show (unsigned level) const; virtual ~fdRegId() {} private: SOCKET fd; fdRegType type; }; // // fdManager // // file descriptor manager // class fdManager : public epicsTimerQueueNotify { public: // // exceptions // class fdInterestSubscriptionAlreadyExits {}; epicsShareFunc fdManager (); epicsShareFunc virtual ~fdManager (); epicsShareFunc void process ( double delay ); // delay parameter is in seconds // returns NULL if the fd is unknown epicsShareFunc class fdReg *lookUpFD (const SOCKET fd, const fdRegType type); epicsTimer & createTimer (); private: tsDLList < fdReg > regList; tsDLList < fdReg > activeList; resTable < fdReg, fdRegId > fdTbl; const double sleepQuantum; fd_set * fdSetsPtr; epicsTimerQueuePassive * pTimerQueue; SOCKET maxFD; bool processInProg; // // Set to fdreg when in call back // and nill otherwise // fdReg * pCBReg; void reschedule (); double quantum (); void installReg (fdReg ®); void removeReg (fdReg ®); void lazyInitTimerQueue (); fdManager ( const fdManager & ); fdManager & operator = ( const fdManager & ); friend class fdReg; }; // // default file descriptor manager // epicsShareExtern fdManager fileDescriptorManager; // // fdReg // // file descriptor registration // class epicsShareClass fdReg : public fdRegId, public tsDLNode, public tsSLNode { friend class fdManager; public: fdReg (const SOCKET fdIn, const fdRegType type, const bool onceOnly=false, fdManager &manager = fileDescriptorManager); virtual ~fdReg (); virtual void show (unsigned level) const; // // Called by the file descriptor manager: // 1) If the fdManager is deleted and there are still // fdReg objects attached // 2) Immediately after calling "callBack()" if // the constructor specified "onceOnly" // // fdReg::destroy() does a "delete this" // virtual void destroy (); private: enum state {active, pending, limbo}; // // called when there is activity on the fd // NOTES // 1) the fdManager will call this only once during the // lifetime of a fdReg object if the constructor // specified "onceOnly" // virtual void callBack ()=0; unsigned char state; // state enums go here unsigned char onceOnly; fdManager &manager; fdReg ( const fdReg & ); fdReg & operator = ( const fdReg & ); }; // // fdRegId::hash() // inline resTableIndex fdRegId::hash () const { const unsigned fdManagerHashTableMinIndexBits = 8; const unsigned fdManagerHashTableMaxIndexBits = sizeof(SOCKET)*CHAR_BIT; resTableIndex hashid; hashid = integerHash ( fdManagerHashTableMinIndexBits, fdManagerHashTableMaxIndexBits, this->fd ); // // also evenly distribute based on the type of fdRegType // hashid ^= this->type; // // the result here is always masked to the // proper size after it is returned to the resource class // return hashid; } inline void fdManager::lazyInitTimerQueue () { if ( ! this->pTimerQueue ) { this->pTimerQueue = & epicsTimerQueuePassive::create ( *this ); } } inline epicsTimer & fdManager::createTimer () { this->lazyInitTimerQueue (); return this->pTimerQueue->createTimer (); } #endif // fdManagerH_included base-7.0.3.1/modules/libcom/src/fdmgr/fdmgr.cpp0000664000577000060420000001774513557101274020037 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ // // File descriptor management C++ class library // (for multiplexing IO in a single threaded environment) // // Author Jeffrey O. Hill // johill@lanl.gov // 505 665 1831 // // NOTES: // 1) the routines in this file provide backward compatibility with the original // "C" based file descriptor manager API // 2) This library is _not_ thread safe // #include #define epicsExportSharedSymbols #include "locationException.h" #include "epicsAssert.h" #include "fdManager.h" #include "fdmgr.h" static const fdRegType fdiToFdRegType[] = {fdrRead, fdrWrite, fdrException}; static const unsigned fdiToFdRegTypeNElements = sizeof (fdiToFdRegType) / sizeof (fdiToFdRegType[0]); const unsigned mSecPerSec = 1000u; const unsigned uSecPerSec = 1000u * mSecPerSec; class fdRegForOldFdmgr : public fdReg { public: // // exceptions // class noFunctionSpecified {}; class doubleDelete {}; epicsShareFunc fdRegForOldFdmgr (const SOCKET fdIn, const fdRegType type, const bool onceOnly, fdManager &manager, pCallBackFDMgr pFunc, void *pParam); epicsShareFunc ~fdRegForOldFdmgr (); private: pCallBackFDMgr pFunc; void *pParam; epicsShareFunc virtual void callBack (); fdRegForOldFdmgr ( const fdRegForOldFdmgr & ); fdRegForOldFdmgr & operator = ( const fdRegForOldFdmgr & ); }; class oldFdmgr; // // timerForOldFdmgr // class timerForOldFdmgr : public epicsTimerNotify, public chronIntIdRes { public: epicsShareFunc timerForOldFdmgr (oldFdmgr &fdmgr, double delay, pCallBackFDMgr pFunc, void *pParam); epicsShareFunc virtual ~timerForOldFdmgr (); // // exceptions // class noFunctionSpecified {}; class doubleDelete {}; private: epicsTimer &timer; oldFdmgr &fdmgr; pCallBackFDMgr pFunc; void *pParam; unsigned id; epicsShareFunc expireStatus expire ( const epicsTime & currentTime ); timerForOldFdmgr ( const timerForOldFdmgr & ); timerForOldFdmgr & operator = ( const timerForOldFdmgr & ); }; class oldFdmgr : public fdManager { friend class timerForOldFdmgr; friend epicsShareFunc int epicsShareAPI fdmgr_clear_timeout (fdctx *pfdctx, fdmgrAlarmId id); public: oldFdmgr (); private: chronIntIdResTable resTbl; oldFdmgr ( const oldFdmgr & ); oldFdmgr & operator = ( const oldFdmgr & ); }; #ifdef _MSC_VER # pragma warning ( push ) # pragma warning ( disable:4660 ) #endif template class chronIntIdResTable ; template class resTable; #ifdef _MSC_VER # pragma warning ( pop ) #endif epicsShareFunc fdRegForOldFdmgr::fdRegForOldFdmgr (const SOCKET fdIn, const fdRegType typeIn, const bool onceOnlyIn, fdManager &managerIn, pCallBackFDMgr pFuncIn, void *pParamIn) : fdReg (fdIn, typeIn, onceOnlyIn, managerIn), pFunc (pFuncIn), pParam (pParamIn) { if (pFuncIn==NULL) { throwWithLocation ( noFunctionSpecified () ); } } epicsShareFunc fdRegForOldFdmgr::~fdRegForOldFdmgr () { if (this->pFunc==NULL) { throwWithLocation ( doubleDelete () ); } } epicsShareFunc void fdRegForOldFdmgr::callBack () { (*this->pFunc) (this->pParam); } timerForOldFdmgr::timerForOldFdmgr ( oldFdmgr &fdmgrIn, double delayIn, pCallBackFDMgr pFuncIn, void * pParamIn ) : timer ( fdmgrIn.createTimer() ), fdmgr ( fdmgrIn ), pFunc ( pFuncIn ), pParam( pParamIn ) { if ( pFuncIn == NULL ) { throwWithLocation ( noFunctionSpecified () ); } this->fdmgr.resTbl.idAssignAdd (*this); this->timer.start ( *this, delayIn ); } timerForOldFdmgr::~timerForOldFdmgr () { this->fdmgr.resTbl.remove ( this->getId() ); this->timer.destroy (); } epicsTimerNotify::expireStatus timerForOldFdmgr::expire ( const epicsTime & ) { (*this->pFunc) (this->pParam); return noRestart; } oldFdmgr::oldFdmgr () {} extern "C" epicsShareFunc fdctx * epicsShareAPI fdmgr_init (void) { oldFdmgr *pfdm; try { pfdm = new oldFdmgr(); } catch (...) { pfdm = NULL; } return (fdctx *) pfdm; } extern "C" epicsShareFunc fdmgrAlarmId epicsShareAPI fdmgr_add_timeout ( fdctx *pfdctx, struct timeval *ptimeout, pCallBackFDMgr pFunc, void *pParam) { double delay = ptimeout->tv_sec + ptimeout->tv_usec / static_cast (uSecPerSec); oldFdmgr *pfdm = static_cast (pfdctx); timerForOldFdmgr *pTimer; unsigned id = fdmgrNoAlarm; if (!pfdm) { return fdmgrNoAlarm; } while (true) { try { pTimer = new timerForOldFdmgr (*pfdm, delay, pFunc, pParam); } catch (...) { pTimer = NULL; } if (pTimer) { id = pTimer->getId (); if (id!=fdmgrNoAlarm) { break; } else { delete pTimer; } } else { break; } } return id; } extern "C" epicsShareFunc int epicsShareAPI fdmgr_clear_timeout (fdctx *pfdctx, fdmgrAlarmId id) { oldFdmgr *pfdm = static_cast (pfdctx); timerForOldFdmgr *pTimer; try { pTimer = pfdm->resTbl.remove (id); } catch (...) { pTimer = NULL; } if (pTimer==NULL) { return -1; } delete pTimer; return 0; } extern "C" epicsShareFunc int epicsShareAPI fdmgr_add_callback ( fdctx *pfdctx, SOCKET fd, enum fdi_type fdi, pCallBackFDMgr pFunc, void *pParam) { oldFdmgr *pfdm = static_cast (pfdctx); fdRegForOldFdmgr *pfdrbc; bool onceOnly = (fdi==fdi_write); unsigned fdiType; if (pfdm==NULL) { return -1; } if (pFunc==NULL) { return -1; } if (fdi<0) { return -1; } fdiType = (unsigned) fdi; if (fdiType>=fdiToFdRegTypeNElements) { return -1; } try { pfdrbc = new fdRegForOldFdmgr (fd, fdiToFdRegType[fdiType], onceOnly, *pfdm, pFunc, pParam); } catch (...) { pfdrbc = NULL; } if (pfdrbc==NULL) { return -1; } else { return 0; } } extern "C" epicsShareFunc int epicsShareAPI fdmgr_clear_callback ( fdctx *pfdctx, SOCKET fd, enum fdi_type fdi) { oldFdmgr *pfdm = static_cast (pfdctx); fdReg *pFDR; if (pfdm==NULL) { return -1; } try { pFDR = pfdm->lookUpFD (fd, fdiToFdRegType[fdi]); } catch (...) { pFDR = NULL; } if (pFDR==NULL) { return -1; } else { delete pFDR; return 0; } } extern "C" epicsShareFunc int epicsShareAPI fdmgr_pend_event (fdctx *pfdctx, struct timeval *ptimeout) { oldFdmgr *pfdm = static_cast (pfdctx); double delay = ptimeout->tv_sec + ptimeout->tv_usec / static_cast (uSecPerSec); try { pfdm->process (delay); } catch (...) { return -1; } return 0; } extern "C" epicsShareFunc int epicsShareAPI fdmgr_delete (fdctx *pfdctx) { oldFdmgr *pfdm = static_cast (pfdctx); delete pfdm; return 0; } /* * depricated interface */ extern "C" epicsShareFunc int epicsShareAPI fdmgr_clear_fd (fdctx *pfdctx, SOCKET fd) { return fdmgr_clear_callback(pfdctx, fd, fdi_read); } /* * depricated interface */ extern "C" epicsShareFunc int epicsShareAPI fdmgr_add_fd ( fdctx *pfdctx, SOCKET fd, void (*pfunc)(void *pParam), void *param) { return fdmgr_add_callback (pfdctx, fd, fdi_read, pfunc, param); } base-7.0.3.1/modules/libcom/src/fdmgr/fdmgr.h0000664000577000060420000001145013557101274017467 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* fdmgr.h * * Header file associated with a file descriptor manager * for use with the UNIX system call select * * Author Jeffrey O. Hill * hill@atdiv.lanl.gov * 505 665 1831 */ #ifndef includeFdmgrH #define includeFdmgrH #include "ellLib.h" #include "bucketLib.h" #include "osiSock.h" #include "epicsThread.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif enum fdi_type {fdi_read, fdi_write, fdi_excp}; enum alarm_list_type {alt_invalid, alt_alarm, alt_expired, alt_free}; typedef void fdctx; typedef void (*pCallBackFDMgr)(void *); /* * C "typedef" name "alarm" was changed to "fdmgrAlarm" to avoid collisions * with other libraries. Next the identifier was changed again to * an unsigned integer type "fdmgrAlarmId". * * This "#define" is for codes that used to use a pointer to the old typedef * "alarm" or "fdmgrAlarm" types to identify an alarm. * * ie the following code will allow compilation against * all versions: * * #if defined (NEW_FDMGR_ALARMID) * fdmgrAlarmId XXXX * #elif defined (NEW_FDMGR_ALARM) * fdmgrAlarm *XXXX; * #else * alarm *XXXX; * #endif * * XXXX = fdmgrAlarmId fdmgr_add_timeout() */ typedef unsigned fdmgrAlarmId; #define NEW_FDMGR_ALARMID /* * * Initialize a file descriptor manager session * */ epicsShareFunc fdctx * epicsShareAPI fdmgr_init(void); /* * Specify a function to be called with a specified parameter * after a specified delay relative to the current time * * Returns fdmgrNoAlarm (zero) if alarm cant be created */ #define fdmgrNoAlarm 0 epicsShareFunc fdmgrAlarmId epicsShareAPI fdmgr_add_timeout( fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ struct timeval *ptimeout, /* relative delay from current time */ pCallBackFDMgr pfunc, /* function (handler) to call */ void *param /* first parameter passed to the func */ ); /* * Clear a timeout which has not executed its function (handler) * yet. */ epicsShareFunc int epicsShareAPI fdmgr_clear_timeout( fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ fdmgrAlarmId id /* alarm to delete */ ); /* * * Specify a function (handler) to be called with a specified parameter * when a file descriptor becomes active. The parameter fdi (file * descriptor interest) specifies the type of activity (IO) we wish * to be informed of: read, write, or exception. For more * info on this see the man pages for the UNIX system call select(). * * read and exception callbacks are permanent( ie the application's * function (handler) continues to be called each time the * file descriptor becomes active until fdmgr_add_callback() * is called). * * write callbacks are called only once after each call to * fdmgr_add_callback() * */ epicsShareFunc int epicsShareAPI fdmgr_add_callback( fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ SOCKET fd, /* file descriptor */ enum fdi_type fdi, /* file descriptor interest type */ pCallBackFDMgr pfunc, /* function (handler) to call */ void *param /* first parameter passed to the func */ ); /* * * Clear nterest in a type of file descriptor activity (IO). * */ epicsShareFunc int epicsShareAPI fdmgr_clear_callback( fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ SOCKET fd, /* file descriptor */ enum fdi_type fdi /* file descriptor interest type */ ); /* * * Wait a specified delay relative from the current time for file * descriptor activity (IO) or timeouts (timer expiration). Application * specified functions (handlers) will not be called unless the * application waits in this function or polls it frequently * enough. * */ epicsShareFunc int epicsShareAPI fdmgr_pend_event( fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ struct timeval *ptimeout ); /* * obsolete interface */ epicsShareFunc int epicsShareAPI fdmgr_clear_fd( fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ SOCKET fd ); /* * obsolete interface */ epicsShareFunc int epicsShareAPI fdmgr_add_fd( fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ SOCKET fd, pCallBackFDMgr pfunc, /* function (handler) to call */ void *param ); epicsShareFunc int epicsShareAPI fdmgr_delete(fdctx *pfdctx); #ifdef __cplusplus } #endif #endif /* ifndef includeFdmgrH (last line in this file) */ base-7.0.3.1/modules/libcom/src/flex/COPYING0000664000577000060420000000341213557101274017110 0ustar anjaesctlFlex carries the copyright used for BSD software, slightly modified because it originated at the Lawrence Berkeley (not Livermore!) Laboratory, which operates under a contract with the Department of Energy: Copyright (c) 1990 The Regents of the University of California. All rights reserved. This code is derived from software contributed to Berkeley by Vern Paxson. The United States Government has rights in this work pursuant to contract no. DE-AC03-76SF00098 between the United States Department of Energy and the University of California. Redistribution and use in source and binary forms are permitted provided that: (1) source distributions retain this entire copyright notice and comment, and (2) distributions including binaries display the following acknowledgement: ``This product includes software developed by the University of California, Berkeley and its contributors'' in the documentation or other materials provided with the distribution and in all advertising materials mentioning features or use of this software. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. This basically says "do whatever you please with this software except remove this notice or take advantage of the University's (or the flex authors') name". Note that the "flex.skel" scanner skeleton carries no copyright notice. You are free to do whatever you please with scanners generated using flex; for them, you are not even bound by the above copyright. base-7.0.3.1/modules/libcom/src/flex/Changes0000664000577000060420000003050613557101274017354 0ustar anjaesctlChanges between 2.3 Patch #7 (28Mar91) and 2.3 Patch #6: - Fixed out-of-bounds array access that caused bad tables to be produced on machines where the bad reference happened to yield a 1. This caused problems installing or running flex on some Suns, in particular. Changes between 2.3 Patch #6 (29Aug90) and 2.3 Patch #5: - Fixed a serious bug in yymore() which basically made it completely broken. Thanks goes to Jean Christophe of the Nethack development team for finding the problem and passing along the fix. Changes between 2.3 Patch #5 (16Aug90) and 2.3 Patch #4: - An up-to-date version of initscan.c so "make test" will work after applying the previous patches Changes between 2.3 Patch #4 (14Aug90) and 2.3 Patch #3: - Fixed bug in hexadecimal escapes which allowed only digits, not letters, in escapes - Fixed bug in previous "Changes" file! Changes between 2.3 Patch #3 (03Aug90) and 2.3 Patch #2: - Correction to patch #2 for gcc compilation; thanks goes to Paul Eggert for catching this. Changes between 2.3 Patch #2 (02Aug90) and original 2.3 release: - Fixed (hopefully) headaches involving declaring malloc() and free() for gcc, which defines __STDC__ but (often) doesn't come with the standard include files such as . Reordered #ifdef maze in the scanner skeleton in the hope of getting the declarations right for cfront and g++, too. - Note that this patch supercedes patch #1 for release 2.3, which was never announced but was available briefly for anonymous ftp. Changes between 2.3 (full) release of 28Jun90 and 2.2 (alpha) release: User-visible: - A lone <> rule (that is, one which is not qualified with a list of start conditions) now specifies the EOF action for *all* start conditions which haven't already had <> actions given. To specify an end-of-file action for just the initial state, use <>. - -d debug output is now contigent on the global yy_flex_debug being set to a non-zero value, which it is by default. - A new macro, YY_USER_INIT, is provided for the user to specify initialization action to be taken on the first call to the scanner. This action is done before the scanner does its own initialization. - yy_new_buffer() has been added as an alias for yy_create_buffer() - Comments beginning with '#' and extending to the end of the line now work, but have been deprecated (in anticipation of making flex recognize #line directives). - The funky restrictions on when semi-colons could follow the YY_NEW_FILE and yyless macros have been removed. They now behave identically to functions. - A bug in the sample redefinition of YY_INPUT in the documentation has been corrected. - A bug in the sample simple tokener in the documentation has been corrected. - The documentation on the incompatibilities between flex and lex has been reordered so that the discussion of yylineno and input() come first, as it's anticipated that these will be the most common source of headaches. Things which didn't used to be documented but now are: - flex interprets "^foo|bar" differently from lex. flex interprets it as "match either a 'foo' or a 'bar', providing it comes at the beginning of a line", whereas lex interprets it as "match either a 'foo' at the beginning of a line, or a 'bar' anywhere". - flex initializes the global "yyin" on the first call to the scanner, while lex initializes it at compile-time. - yy_switch_to_buffer() can be used in the yywrap() macro/routine. - flex scanners do not use stdio for their input, and hence when writing an interactive scanner one must explictly call fflush() after writing out a prompt. - flex scanner can be made reentrant (after a fashion) by using "yyrestart( yyin );". This is useful for interactive scanners which have interrupt handlers that long-jump out of the scanner. - a defense of why yylineno is not supported is included, along with a suggestion on how to convert scanners which rely on it. Other changes: - Prototypes and proper declarations of void routines have been added to the flex source code, courtesy of Kevin B. Kenny. - Routines dealing with memory allocation now use void* pointers instead of char* - see Makefile for porting implications. - Error-checking is now done when flex closes a file. - Various lint tweaks were added to reduce the number of gripes. - Makefile has been further parameterized to aid in porting. - Support for SCO Unix added. - Flex now sports the latest & greatest UC copyright notice (which is only slightly different from the previous one). - A note has been added to flexdoc.1 mentioning work in progress on modifying flex to generate straight C code rather than a table-driven automaton, with an email address of whom to contact if you are working along similar lines. Changes between 2.2 Patch #3 (30Mar90) and 2.2 Patch #2: - fixed bug which caused -I scanners to bomb Changes between 2.2 Patch #2 (27Mar90) and 2.2 Patch #1: - fixed bug writing past end of input buffer in yyunput() - fixed bug detecting NUL's at the end of a buffer Changes between 2.2 Patch #1 (23Mar90) and 2.2 (alpha) release: - Makefile fixes: definition of MAKE variable for systems which don't have it; installation of flexdoc.1 along with flex.1; fixed two bugs which could cause "bigtest" to fail. - flex.skel fix for compiling with g++. - README and flexdoc.1 no longer list an out-of-date BITNET address for contacting me. - minor typos and formatting changes to flex.1 and flexdoc.1. Changes between 2.2 (alpha) release of March '90 and previous release: User-visible: - Full user documentation now available. - Support for 8-bit scanners. - Scanners now accept NUL's. - A facility has been added for dealing with multiple input buffers. - Two manual entries now. One which fully describes flex (rather than just its differences from lex), and the other for quick(er) reference. - A number of changes to bring flex closer into compliance with the latest POSIX lex draft: %t support flex now accepts multiple input files and concatenates them together to form its input previous -c (compress) flag renamed -C do-nothing -c and -n flags added Any indented code or code within %{}'s in section 2 is now copied to the output - yyleng is now a bona fide global integer. - -d debug information now gives the line number of the matched rule instead of which number rule it was from the beginning of the file. - -v output now includes a summary of the flags used to generate the scanner. - unput() and yyrestart() are now globally callable. - yyrestart() no longer closes the previous value of yyin. - C++ support; generated scanners can be compiled with C++ compiler. - Primitive -lfl library added, containing default main() which calls yylex(). A number of routines currently living in the scanner skeleton will probably migrate to here in the future (in particular, yywrap() will probably cease to be a macro and instead be a function in the -lfl library). - Hexadecimal (\x) escape sequences added. - Support for MS-DOS, VMS, and Turbo-C integrated. - The %used/%unused operators have been deprecated. They may go away soon. Other changes: - Makefile enhanced for easier testing and installation. - The parser has been tweaked to detect some erroneous constructions which previously were missed. - Scanner input buffer overflow is now detected. - Bugs with missing "const" declarations fixed. - Out-of-date Minix/Atari patches provided. - Scanners no longer require printf() unless FLEX_DEBUG is being used. - A subtle input() bug has been fixed. - Line numbers for "continued action" rules (those following the special '|' action) are now correct. - unput() bug fixed; had been causing problems porting flex to VMS. - yymore() handling rewritten to fix bug with interaction between yymore() and trailing context. - EOF in actions now generates an error message. - Bug involving -CFe and generating equivalence classes fixed. - Bug which made -CF be treated as -Cf fixed. - Support for SysV tmpnam() added. - Unused #define's for scanner no longer generated. - Error messages which are associated with a particular input line are now all identified with their input line in standard format. - % directives which are valid to lex but not to flex are now ignored instead of generating warnings. - -DSYS_V flag can now also be specified -DUSG for System V compilation. Changes between 2.1 beta-test release of June '89 and previous release: User-visible: - -p flag generates a performance report to stderr. The report consists of comments regarding features of the scanner rules which result in slower scanners. - -b flag generates backtracking information to lex.backtrack. This is a list of scanner states which require backtracking and the characters on which they do so. By adding rules one can remove backtracking states. If all backtracking states are eliminated, the generated scanner will run faster. Backtracking is not yet documented in the manual entry. - Variable trailing context now works, i.e., one can have rules like "(foo)*/[ \t]*bletch". Some trailing context patterns still cannot be properly matched and generate error messages. These are patterns where the ending of the first part of the rule matches the beginning of the second part, such as "zx*/xy*", where the 'x*' matches the 'x' at the beginning of the trailing context. Lex won't get these patterns right either. - Faster scanners. - End-of-file rules. The special rule "<>" indicates actions which are to be taken when an end-of-file is encountered and yywrap() returns non-zero (i.e., indicates no further files to process). See manual entry for example. - The -r (reject used) flag is gone. flex now scans the input for occurrences of the string "REJECT" to determine if the action is needed. It tries to be intelligent about this but can be fooled. One can force the presence or absence of REJECT by adding a line in the first section of the form "%used REJECT" or "%unused REJECT". - yymore() has been implemented. Similarly to REJECT, flex detects the use of yymore(), which can be overridden using "%used" or "%unused". - Patterns like "x{0,3}" now work (i.e., with lower-limit == 0). - Removed '\^x' for ctrl-x misfeature. - Added '\a' and '\v' escape sequences. - \ now works for octal escape sequences; previously \0 was required. - Better error reporting; line numbers are associated with rules. - yyleng is a macro; it cannot be accessed outside of the scanner source file. - yytext and yyleng should not be modified within a flex action. - Generated scanners #define the name FLEX_SCANNER. - Rules are internally separated by YY_BREAK in lex.yy.c rather than break, to allow redefinition. - The macro YY_USER_ACTION can be redefined to provide an action which is always executed prior to the matched rule's action. - yyrestart() is a new action which can be used to restart the scanner after it has seen an end-of-file (a "real" one, that is, one for which yywrap() returned non-zero). It takes a FILE* argument indicating a new file to scan and sets things up so that a subsequent call to yylex() will start scanning that file. - Internal scanner names all preceded by "yy_" - lex.yy.c is deleted if errors are encountered during processing. - Comments may be put in the first section of the input by preceding them with '#'. Other changes: - Some portability-related bugs fixed, in particular for machines with unsigned characters or sizeof( int* ) != sizeof( int ). Also, tweaks for VMS and Microsoft C (MS-DOS), and identifiers all trimmed to be 31 or fewer characters. Shortened file names for dinosaur OS's. Checks for allocating > 64K memory on 16 bit'ers. Amiga tweaks. Compiles using gcc on a Sun-3. - Compressed and fast scanner skeletons merged. - Skeleton header files done away with. - Generated scanner uses prototypes and "const" for __STDC__. - -DSV flag is now -DSYS_V for System V compilation. - Removed all references to FTL language. - Software now covered by BSD Copyright. - flex will replace lex in subsequent BSD releases. base-7.0.3.1/modules/libcom/src/flex/EPICS_READ_THIS0000664000577000060420000000223113557101274020263 0ustar anjaesctlThis is a version of the BSD flex that has had its skeleton file munged in order to force it to build lex programs that have all their functions and variables defined as static. The file flex.skel.static is simply a copy of flex.skel that has been altered to make all the components into static variables. In order to be able to actually use the lex files produced by this flavor of flex, you must #include them into your C programs. Otherwise they will be uncallable (all functions are static). This is typical of lex programs that are used by yacc programs anyway. The scan.c file is actually the output of scan.l.DISTRIB when run through itself, using the regular flex.skel skeleton with the -i option. To regenerate scan.c, make sure you have a build of a working e_flex binary somewhere, then in this directory (not an O. build directory): % mv scan.l.DISTRIB scan.l % /path/to/e_flex -Sflex.skel -8 -i scan.l % mv lex.yy.c scan.c % make Then use the new binary to make sure it can build itself: % O./e_flex -Sflex.skel -8 -i scan.l % mv lex.yy.c scan.c % make If that succeeds, don't forget to rename scan.l back again: % mv scan.l scan.l.DISTRIB base-7.0.3.1/modules/libcom/src/flex/Flex.doc0000664000577000060420000022503413557101274017450 0ustar anjaesctl 26 May 1990 FLEX(1) NAME flex - fast lexical analyzer generator SYNOPSIS flex [-bcdfinpstvFILT8 -C[efmF] -Sskeleton] [filename ...] DESCRIPTION flex is a tool for generating scanners: programs which recognized lexi- cal patterns in text. flex reads the given input files, or its standard input if no file names are given, for a description of a scanner to gen- erate. The description is in the form of pairs of regular expressions and C code, called rules. flex generates as output a C source file, lex.yy.c, which defines a routine yylex(). This file is compiled and linked with the -lfl library to produce an executable. When the execut- able is run, it analyzes its input for occurrences of the regular expressions. Whenever it finds one, it executes the corresponding C code. SOME SIMPLE EXAMPLES First some simple examples to get the flavor of how one uses flex. The following flex input specifies a scanner which whenever it encounters the string "username" will replace it with the user's login name: %% username printf( "%s", getlogin() ); By default, any text not matched by a flex scanner is copied to the out- put, so the net effect of this scanner is to copy its input file to its output with each occurrence of "username" expanded. In this input, there is just one rule. "username" is the pattern and the "printf" is the action. The "%%" marks the beginning of the rules. Here's another simple example: int num_lines = 0, num_chars = 0; %% \n ++num_lines; ++num_chars; . ++num_chars; %% main() { yylex(); printf( "# of lines = %d, # of chars = %d\n", num_lines, num_chars ); } This scanner counts the number of characters and the number of lines in its input (it produces no output other than the final report on the counts). The first line declares two globals, "num_lines" and "num_chars", which are accessible both inside yylex() and in the main() Version 2.3 1 FLEX(1) 26 May 1990 routine declared after the second "%%". There are two rules, one which matches a newline ("\n") and increments both the line count and the character count, and one which matches any character other than a new- line (indicated by the "." regular expression). A somewhat more complicated example: /* scanner for a toy Pascal-like language */ %{ /* need this for the call to atof() below */ #include %} DIGIT [0-9] ID [a-z][a-z0-9]* %% {DIGIT}+ { printf( "An integer: %s (%d)\n", yytext, atoi( yytext ) ); } {DIGIT}+"."{DIGIT}* { printf( "A float: %s (%g)\n", yytext, atof( yytext ) ); } if|then|begin|end|procedure|function { printf( "A keyword: %s\n", yytext ); } {ID} printf( "An identifier: %s\n", yytext ); "+"|"-"|"*"|"/" printf( "An operator: %s\n", yytext ); "{"[^}\n]*"}" /* eat up one-line comments */ [ \t\n]+ /* eat up whitespace */ . printf( "Unrecognized character: %s\n", yytext ); %% main( argc, argv ) int argc; char **argv; { ++argv, --argc; /* skip over program name */ if ( argc > 0 ) yyin = fopen( argv[0], "r" ); else yyin = stdin; 2 Version 2.3 26 May 1990 FLEX(1) yylex(); } This is the beginnings of a simple scanner for a language like Pascal. It identifies different types of tokens and reports on what it has seen. The details of this example will be explained in the following sections. FORMAT OF THE INPUT FILE The flex input file consists of three sections, separated by a line with just %% in it: definitions %% rules %% user code The definitions section contains declarations of simple name definitions to simplify the scanner specification, and declarations of start condi- tions, which are explained in a later section. Name definitions have the form: name definition The "name" is a word beginning with a letter or an underscore ('_') fol- lowed by zero or more letters, digits, '_', or '-' (dash). The defini- tion is taken to begin at the first non-white-space character following the name and continuing to the end of the line. The definition can sub- sequently be referred to using "{name}", which will expand to "(defini- tion)". For example, DIGIT [0-9] ID [a-z][a-z0-9]* defines "DIGIT" to be a regular expression which matches a single digit, and "ID" to be a regular expression which matches a letter followed by zero-or-more letters-or-digits. A subsequent reference to {DIGIT}+"."{DIGIT}* is identical to ([0-9])+"."([0-9])* and matches one-or-more digits followed by a '.' followed by zero-or- more digits. The rules section of the flex input contains a series of rules of the form: pattern action Version 2.3 3 FLEX(1) 26 May 1990 where the pattern must be unindented and the action must begin on the same line. See below for a further description of patterns and actions. Finally, the user code section is simply copied to lex.yy.c verbatim. It is used for companion routines which call or are called by the scanner. The presence of this section is optional; if it is missing, the second %% in the input file may be skipped, too. In the definitions and rules sections, any indented text or text enclosed in %{ and %} is copied verbatim to the output (with the %{}'s removed). The %{}'s must appear unindented on lines by themselves. In the rules section, any indented or %{} text appearing before the first rule may be used to declare variables which are local to the scan- ning routine and (after the declarations) code which is to be executed whenever the scanning routine is entered. Other indented or %{} text in the rule section is still copied to the output, but its meaning is not well-defined and it may well cause compile-time errors (this feature is present for POSIX compliance; see below for other such features). In the definitions section, an unindented comment (i.e., a line begin- ning with "/*") is also copied verbatim to the output up to the next "*/". Also, any line in the definitions section beginning with '#' is ignored, though this style of comment is deprecated and may go away in the future. PATTERNS The patterns in the input are written using an extended set of regular expressions. These are: x match the character 'x' . any character except newline [xyz] a "character class"; in this case, the pattern matches either an 'x', a 'y', or a 'z' [abj-oZ] a "character class" with a range in it; matches an 'a', a 'b', any letter from 'j' through 'o', or a 'Z' [^A-Z] a "negated character class", i.e., any character but those in the class. In this case, any character EXCEPT an uppercase letter. [^A-Z\n] any character EXCEPT an uppercase letter or a newline r* zero or more r's, where r is any regular expression r+ one or more r's r? zero or one r's (that is, "an optional r") r{2,5} anywhere from two to five r's r{2,} two or more r's r{4} exactly 4 r's {name} the expansion of the "name" definition (see above) "[xyz]\"foo" 4 Version 2.3 26 May 1990 FLEX(1) the literal string: [xyz]"foo \X if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v', then the ANSI-C interpretation of \x. Otherwise, a literal 'X' (used to escape operators such as '*') \123 the character with octal value 123 \x2a the character with hexadecimal value 2a (r) match an r; parentheses are used to override precedence (see below) rs the regular expression r followed by the regular expression s; called "concatenation" r|s either an r or an s r/s an r but only if it is followed by an s. The s is not part of the matched text. This type of pattern is called as "trailing context". ^r an r, but only at the beginning of a line r$ an r, but only at the end of a line. Equivalent to "r/\n". r an r, but only in start condition s (see below for discussion of start conditions) r same, but in any of start conditions s1, s2, or s3 <> an end-of-file <> an end-of-file when in start condition s1 or s2 The regular expressions listed above are grouped according to pre- cedence, from highest precedence at the top to lowest at the bottom. Those grouped together have equal precedence. For example, foo|bar* is the same as (foo)|(ba(r*)) since the '*' operator has higher precedence than concatenation, and concatenation higher than alternation ('|'). This pattern therefore matches either the string "foo" or the string "ba" followed by zero-or- more r's. To match "foo" or zero-or-more "bar"'s, use: foo|(bar)* Version 2.3 5 FLEX(1) 26 May 1990 and to match zero-or-more "foo"'s-or-"bar"'s: (foo|bar)* Some notes on patterns: - A negated character class such as the example "[^A-Z]" above will match a newline unless "\n" (or an equivalent escape sequence) is one of the characters explicitly present in the negated character class (e.g., "[^A-Z\n]"). This is unlike how many other regular expression tools treat negated character classes, but unfortunately the inconsistency is historically entrenched. Matching newlines means that a pattern like [^"]* can match an entire input (over- flowing the scanner's input buffer) unless there's another quote in the input. - A rule can have at most one instance of trailing context (the '/' operator or the '$' operator). The start condition, '^', and "<>" patterns can only occur at the beginning of a pattern, and, as well as with '/' and '$', cannot be grouped inside parentheses. A '^' which does not occur at the beginning of a rule or a '$' which does not occur at the end of a rule loses its spe- cial properties and is treated as a normal character. The following are illegal: foo/bar$ foobar Note that the first of these, can be written "foo/bar\n". The following will result in '$' or '^' being treated as a normal character: foo|(bar$) foo|^bar If what's wanted is a "foo" or a bar-followed-by-a-newline, the following could be used (the special '|' action is explained below): foo | bar$ /* action goes here */ A similar trick will work for matching a foo or a bar-at-the- beginning-of-a-line. HOW THE INPUT IS MATCHED When the generated scanner is run, it analyzes its input looking for strings which match any of its patterns. If it finds more than one match, it takes the one matching the most text (for trailing context rules, this includes the length of the trailing part, even though it will then be returned to the input). If it finds two or more matches of 6 Version 2.3 26 May 1990 FLEX(1) the same length, the rule listed first in the flex input file is chosen. Once the match is determined, the text corresponding to the match (called the token) is made available in the global character pointer yytext, and its length in the global integer yyleng. The action corresponding to the matched pattern is then executed (a more detailed description of actions follows), and then the remaining input is scanned for another match. If no match is found, then the default rule is executed: the next char- acter in the input is considered matched and copied to the standard out- put. Thus, the simplest legal flex input is: %% which generates a scanner that simply copies its input (one character at a time) to its output. ACTIONS Each pattern in a rule has a corresponding action, which can be any arbitrary C statement. The pattern ends at the first non-escaped whi- tespace character; the remainder of the line is its action. If the action is empty, then when the pattern is matched the input token is simply discarded. For example, here is the specification for a program which deletes all occurrences of "zap me" from its input: %% "zap me" (It will copy all other characters in the input to the output since they will be matched by the default rule.) Here is a program which compresses multiple blanks and tabs down to a single blank, and throws away whitespace found at the end of a line: %% [ \t]+ putchar( ' ' ); [ \t]+$ /* ignore this token */ If the action contains a '{', then the action spans till the balancing '}' is found, and the action may cross multiple lines. flex knows about C strings and comments and won't be fooled by braces found within them, but also allows actions to begin with %{ and will consider the action to be all the text up to the next %} (regardless of ordinary braces inside the action). An action consisting solely of a vertical bar ('|') means "same as the action for the next rule." See below for an illustration. Actions can include arbitrary C code, including return statements to return a value to whatever routine called yylex(). Each time yylex() is called it continues processing tokens from where it last left off until it either reaches the end of the file or executes a return. Once it Version 2.3 7 FLEX(1) 26 May 1990 reaches an end-of-file, however, then any subsequent call to yylex() will simply immediately return, unless yyrestart() is first called (see below). Actions are not allowed to modify yytext or yyleng. There are a number of special directives which can be included within an action: - ECHO copies yytext to the scanner's output. - BEGIN followed by the name of a start condition places the scanner in the corresponding start condition (see below). - REJECT directs the scanner to proceed on to the "second best" rule which matched the input (or a prefix of the input). The rule is chosen as described above in "How the Input is Matched", and yytext and yyleng set up appropriately. It may either be one which matched as much text as the originally chosen rule but came later in the flex input file, or one which matched less text. For exam- ple, the following will both count the words in the input and call the routine special() whenever "frob" is seen: int word_count = 0; %% frob special(); REJECT; [^ \t\n]+ ++word_count; Without the REJECT, any "frob"'s in the input would not be counted as words, since the scanner normally executes only one action per token. Multiple REJECT's are allowed, each one finding the next best choice to the currently active rule. For example, when the following scanner scans the token "abcd", it will write "abcdab- caba" to the output: %% a | ab | abc | abcd ECHO; REJECT; .|\n /* eat up any unmatched character */ (The first three rules share the fourth's action since they use the special '|' action.) REJECT is a particularly expensive feature in terms scanner performance; if it is used in any of the scanner's actions it will slow down all of the scanner's matching. Further- more, REJECT cannot be used with the -f or -F options (see below). Note also that unlike the other special actions, REJECT is a branch; code immediately following it in the action will not be executed. - yymore() tells the scanner that the next time it matches a rule, 8 Version 2.3 26 May 1990 FLEX(1) the corresponding token should be appended onto the current value of yytext rather than replacing it. For example, given the input "mega-kludge" the following will write "mega-mega-kludge" to the output: %% mega- ECHO; yymore(); kludge ECHO; First "mega-" is matched and echoed to the output. Then "kludge" is matched, but the previous "mega-" is still hanging around at the beginning of yytext so the ECHO for the "kludge" rule will actually write "mega-kludge". The presence of yymore() in the scanner's action entails a minor performance penalty in the scanner's match- ing speed. - yyless(n) returns all but the first n characters of the current token back to the input stream, where they will be rescanned when the scanner looks for the next match. yytext and yyleng are adjusted appropriately (e.g., yyleng will now be equal to n ). For example, on the input "foobar" the following will write out "foobarbar": %% foobar ECHO; yyless(3); [a-z]+ ECHO; An argument of 0 to yyless will cause the entire current input string to be scanned again. Unless you've changed how the scanner will subsequently process its input (using BEGIN, for example), this will result in an endless loop. - unput(c) puts the character c back onto the input stream. It will be the next character scanned. The following action will take the current token and cause it to be rescanned enclosed in parentheses. { int i; unput( ')' ); for ( i = yyleng - 1; i >= 0; --i ) unput( yytext[i] ); unput( '(' ); } Note that since each unput() puts the given character back at the beginning of the input stream, pushing back strings must be done back-to-front. - input() reads the next character from the input stream. For exam- ple, the following is one way to eat up C comments: %% "/*" { register int c; Version 2.3 9 FLEX(1) 26 May 1990 for ( ; ; ) { while ( (c = input()) != '*' && c != EOF ) ; /* eat up text of comment */ if ( c == '*' ) { while ( (c = input()) == '*' ) ; if ( c == '/' ) break; /* found the end */ } if ( c == EOF ) { error( "EOF in comment" ); break; } } } (Note that if the scanner is compiled using C++, then input() is instead referred to as yyinput(), in order to avoid a name clash with the C++ stream by the name of input.) - yyterminate() can be used in lieu of a return statement in an action. It terminates the scanner and returns a 0 to the scanner's caller, indicating "all done". Subsequent calls to the scanner will immediately return unless preceded by a call to yyrestart() (see below). By default, yyterminate() is also called when an end-of-file is encountered. It is a macro and may be redefined. THE GENERATED SCANNER The output of flex is the file lex.yy.c, which contains the scanning routine yylex(), a number of tables used by it for matching tokens, and a number of auxiliary routines and macros. By default, yylex() is declared as follows: int yylex() { ... various definitions and the actions in here ... } (If your environment supports function prototypes, then it will be "int yylex( void )".) This definition may be changed by redefining the "YY_DECL" macro. For example, you could use: #undef YY_DECL #define YY_DECL float lexscan( a, b ) float a, b; to give the scanning routine the name lexscan, returning a float, and taking two floats as arguments. Note that if you give arguments to the 10 Version 2.3 26 May 1990 FLEX(1) scanning routine using a K&R-style/non-prototyped function declaration, you must terminate the definition with a semi-colon (;). Whenever yylex() is called, it scans tokens from the global input file yyin (which defaults to stdin). It continues until it either reaches an end-of-file (at which point it returns the value 0) or one of its actions executes a return statement. In the former case, when called again the scanner will immediately return unless yyrestart() is called to point yyin at the new input file. ( yyrestart() takes one argument, a FILE * pointer.) In the latter case (i.e., when an action executes a return), the scanner may then be called again and it will resume scan- ning where it left off. By default (and for purposes of efficiency), the scanner uses block- reads rather than simple getc() calls to read characters from yyin. The nature of how it gets its input can be controlled by redefining the YY_INPUT macro. YY_INPUT's calling sequence is "YY_INPUT(buf,result,max_size)". Its action is to place up to max_size characters in the character array buf and return in the integer variable result either the number of characters read or the constant YY_NULL (0 on Unix systems) to indicate EOF. The default YY_INPUT reads from the global file-pointer "yyin". A sample redefinition of YY_INPUT (in the definitions section of the input file): %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) \ { \ int c = getchar(); \ result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \ } %} This definition will change the input processing to occur one character at a time. You also can add in things like keeping track of the input line number this way; but don't expect your scanner to go very fast. When the scanner receives an end-of-file indication from YY_INPUT, it then checks the yywrap() function. If yywrap() returns false (zero), then it is assumed that the function has gone ahead and set up yyin to point to another input file, and scanning continues. If it returns true (non-zero), then the scanner terminates, returning 0 to its caller. The default yywrap() always returns 1. Presently, to redefine it you must first "#undef yywrap", as it is currently implemented as a macro. As indicated by the hedging in the previous sentence, it may be changed to a true function in the near future. The scanner writes its ECHO output to the yyout global (default, stdout), which may be redefined by the user simply by assigning it to Version 2.3 11 FLEX(1) 26 May 1990 some other FILE pointer. START CONDITIONS flex provides a mechanism for conditionally activating rules. Any rule whose pattern is prefixed with "" will only be active when the scanner is in the start condition named "sc". For example, [^"]* { /* eat up the string body ... */ ... } will be active only when the scanner is in the "STRING" start condition, and \. { /* handle an escape ... */ ... } will be active only when the current start condition is either "INI- TIAL", "STRING", or "QUOTE". Start conditions are declared in the definitions (first) section of the input using unindented lines beginning with either %s or %x followed by a list of names. The former declares inclusive start conditions, the latter exclusive start conditions. A start condition is activated using the BEGIN action. Until the next BEGIN action is executed, rules with the given start condition will be active and rules with other start con- ditions will be inactive. If the start condition is inclusive, then rules with no start conditions at all will also be active. If it is exclusive, then only rules qualified with the start condition will be active. A set of rules contingent on the same exclusive start condition describe a scanner which is independent of any of the other rules in the flex input. Because of this, exclusive start conditions make it easy to specify "mini-scanners" which scan portions of the input that are syn- tactically different from the rest (e.g., comments). If the distinction between inclusive and exclusive start conditions is still a little vague, here's a simple example illustrating the connec- tion between the two. The set of rules: %s example %% foo /* do something */ is equivalent to %x example %% foo /* do something */ The default rule (to ECHO any unmatched character) remains active in start conditions. 12 Version 2.3 26 May 1990 FLEX(1) BEGIN(0) returns to the original state where only the rules with no start conditions are active. This state can also be referred to as the start-condition "INITIAL", so BEGIN(INITIAL) is equivalent to BEGIN(0). (The parentheses around the start condition name are not required but are considered good style.) BEGIN actions can also be given as indented code at the beginning of the rules section. For example, the following will cause the scanner to enter the "SPECIAL" start condition whenever yylex() is called and the global variable enter_special is true: int enter_special; %x SPECIAL %% if ( enter_special ) BEGIN(SPECIAL); blahblahblah ...more rules follow... To illustrate the uses of start conditions, here is a scanner which pro- vides two different interpretations of a string like "123.456". By default it will treat it as as three tokens, the integer "123", a dot ('.'), and the integer "456". But if the string is preceded earlier in the line by the string "expect-floats" it will treat it as a single token, the floating-point number 123.456: %{ #include %} %s expect %% expect-floats BEGIN(expect); [0-9]+"."[0-9]+ { printf( "found a float, = %f\n", atof( yytext ) ); } \n { /* that's the end of the line, so * we need another "expect-number" * before we'll recognize any more * numbers */ BEGIN(INITIAL); } [0-9]+ { printf( "found an integer, = %d\n", atoi( yytext ) ); } Version 2.3 13 FLEX(1) 26 May 1990 "." printf( "found a dot\n" ); Here is a scanner which recognizes (and discards) C comments while main- taining a count of the current input line. %x comment %% int line_num = 1; "/*" BEGIN(comment); [^*\n]* /* eat anything that's not a '*' */ "*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ \n ++line_num; "*"+"/" BEGIN(INITIAL); Note that start-conditions names are really integer values and can be stored as such. Thus, the above could be extended in the following fashion: %x comment foo %% int line_num = 1; int comment_caller; "/*" { comment_caller = INITIAL; BEGIN(comment); } ... "/*" { comment_caller = foo; BEGIN(comment); } [^*\n]* /* eat anything that's not a '*' */ "*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ \n ++line_num; "*"+"/" BEGIN(comment_caller); One can then implement a "stack" of start conditions using an array of integers. (It is likely that such stacks will become a full-fledged flex feature in the future.) Note, though, that start conditions do not have their own name-space; %s's and %x's declare names in the same fashion as #define's. MULTIPLE INPUT BUFFERS Some scanners (such as those which support "include" files) require reading from several input streams. As flex scanners do a large amount of buffering, one cannot control where the next input will be read from by simply writing a YY_INPUT which is sensitive to the scanning context. 14 Version 2.3 26 May 1990 FLEX(1) YY_INPUT is only called when the scanner reaches the end of its buffer, which may be a long time after scanning a statement such as an "include" which requires switching the input source. To negotiate these sorts of problems, flex provides a mechanism for creating and switching between multiple input buffers. An input buffer is created by using: YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) which takes a FILE pointer and a size and creates a buffer associated with the given file and large enough to hold size characters (when in doubt, use YY_BUF_SIZE for the size). It returns a YY_BUFFER_STATE han- dle, which may then be passed to other routines: void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) switches the scanner's input buffer so subsequent tokens will come from new_buffer. Note that yy_switch_to_buffer() may be used by yywrap() to sets things up for continued scanning, instead of opening a new file and pointing yyin at it. void yy_delete_buffer( YY_BUFFER_STATE buffer ) is used to reclaim the storage associated with a buffer. yy_new_buffer() is an alias for yy_create_buffer(), provided for compa- tibility with the C++ use of new and delete for creating and destroying dynamic objects. Finally, the YY_CURRENT_BUFFER macro returns a YY_BUFFER_STATE handle to the current buffer. Here is an example of using these features for writing a scanner which expands include files (the <> feature is discussed below): /* the "incl" state is used for picking up the name * of an include file */ %x incl %{ #define MAX_INCLUDE_DEPTH 10 YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; int include_stack_ptr = 0; %} %% include BEGIN(incl); [a-z]+ ECHO; [^a-z\n]*\n? ECHO; [ \t]* /* eat the whitespace */ Version 2.3 15 FLEX(1) 26 May 1990 [^ \t\n]+ { /* got the include file name */ if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) { fprintf( stderr, "Includes nested too deeply" ); exit( 1 ); } include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER; yyin = fopen( yytext, "r" ); if ( ! yyin ) error( ... ); yy_switch_to_buffer( yy_create_buffer( yyin, YY_BUF_SIZE ) ); BEGIN(INITIAL); } <> { if ( --include_stack_ptr < 0 ) { yyterminate(); } else yy_switch_to_buffer( include_stack[include_stack_ptr] ); } END-OF-FILE RULES The special rule "<>" indicates actions which are to be taken when an end-of-file is encountered and yywrap() returns non-zero (i.e., indi- cates no further files to process). The action must finish by doing one of four things: - the special YY_NEW_FILE action, if yyin has been pointed at a new file to process; - a return statement; - the special yyterminate() action; - or, switching to a new buffer using yy_switch_to_buffer() as shown in the example above. <> rules may not be used with other patterns; they may only be qualified with a list of start conditions. If an unqualified <> rule is given, it applies to all start conditions which do not already have <> actions. To specify an <> rule for only the initial start condition, use 16 Version 2.3 26 May 1990 FLEX(1) <> These rules are useful for catching things like unclosed comments. An example: %x quote %% ...other rules for dealing with quotes... <> { error( "unterminated quote" ); yyterminate(); } <> { if ( *++filelist ) { yyin = fopen( *filelist, "r" ); YY_NEW_FILE; } else yyterminate(); } MISCELLANEOUS MACROS The macro YY_USER_ACTION can be redefined to provide an action which is always executed prior to the matched rule's action. For example, it could be #define'd to call a routine to convert yytext to lower-case. The macro YY_USER_INIT may be redefined to provide an action which is always executed before the first scan (and before the scanner's internal initializations are done). For example, it could be used to call a rou- tine to read in a data table or open a logging file. In the generated scanner, the actions are all gathered in one large switch statement and separated using YY_BREAK, which may be redefined. By default, it is simply a "break", to separate each rule's action from the following rule's. Redefining YY_BREAK allows, for example, C++ users to #define YY_BREAK to do nothing (while being very careful that every rule ends with a "break" or a "return"!) to avoid suffering from unreachable statement warnings where because a rule's action ends with "return", the YY_BREAK is inaccessible. INTERFACING WITH YACC One of the main uses of flex is as a companion to the yacc parser- generator. yacc parsers expect to call a routine named yylex() to find the next input token. The routine is supposed to return the type of the next token as well as putting any associated value in the global yylval. To use flex with yacc, one specifies the -d option to yacc to instruct it to generate the file y.tab.h containing definitions of all the %tokens appearing in the yacc input. This file is then included in the Version 2.3 17 FLEX(1) 26 May 1990 flex scanner. For example, if one of the tokens is "TOK_NUMBER", part of the scanner might look like: %{ #include "y.tab.h" %} %% [0-9]+ yylval = atoi( yytext ); return TOK_NUMBER; TRANSLATION TABLE In the name of POSIX compliance, flex supports a translation table for mapping input characters into groups. The table is specified in the first section, and its format looks like: %t 1 abcd 2 ABCDEFGHIJKLMNOPQRSTUVWXYZ 52 0123456789 6 \t\ \n %t This example specifies that the characters 'a', 'b', 'c', and 'd' are to all be lumped into group #1, upper-case letters in group #2, digits in group #52, tabs, blanks, and newlines into group #6, and no other char- acters will appear in the patterns. The group numbers are actually disregarded by flex; %t serves, though, to lump characters together. Given the above table, for example, the pattern "a(AA)*5" is equivalent to "d(ZQ)*0". They both say, "match any character in group #1, followed by zero-or-more pairs of characters from group #2, followed by a charac- ter from group #52." Thus %t provides a crude way for introducing equivalence classes into the scanner specification. Note that the -i option (see below) coupled with the equivalence classes which flex automatically generates take care of virtually all the instances when one might consider using %t. But what the hell, it's there if you want it. OPTIONS flex has the following options: -b Generate backtracking information to lex.backtrack. This is a list of scanner states which require backtracking and the input charac- ters on which they do so. By adding rules one can remove back- tracking states. If all backtracking states are eliminated and -f or -F is used, the generated scanner will run faster (see the -p flag). Only users who wish to squeeze every last cycle out of their scanners need worry about this option. (See the section on PERFORMANCE CONSIDERATIONS below.) -c is a do-nothing, deprecated option included for POSIX compliance. 18 Version 2.3 26 May 1990 FLEX(1) NOTE: in previous releases of flex -c specified table-compression options. This functionality is now given by the -C flag. To ease the the impact of this change, when flex encounters -c, it currently issues a warning message and assumes that -C was desired instead. In the future this "promotion" of -c to -C will go away in the name of full POSIX compliance (unless the POSIX meaning is removed first). -d makes the generated scanner run in debug mode. Whenever a pattern is recognized and the global yy_flex_debug is non-zero (which is the default), the scanner will write to stderr a line of the form: --accepting rule at line 53 ("the matched text") The line number refers to the location of the rule in the file defining the scanner (i.e., the file that was fed to flex). Mes- sages are also generated when the scanner backtracks, accepts the default rule, reaches the end of its input buffer (or encounters a NUL; at this point, the two look the same as far as the scanner's concerned), or reaches an end-of-file. -f specifies (take your pick) full table or fast scanner. No table compression is done. The result is large but fast. This option is equivalent to -Cf (see below). -i instructs flex to generate a case-insensitive scanner. The case of letters given in the flex input patterns will be ignored, and tokens in the input will be matched regardless of case. The matched text given in yytext will have the preserved case (i.e., it will not be folded). -n is another do-nothing, deprecated option included only for POSIX compliance. -p generates a performance report to stderr. The report consists of comments regarding features of the flex input file which will cause a loss of performance in the resulting scanner. Note that the use of REJECT and variable trailing context (see the BUGS section in flex(1)) entails a substantial performance penalty; use of yymore(), the ^ operator, and the -I flag entail minor performance penalties. -s causes the default rule (that unmatched scanner input is echoed to stdout) to be suppressed. If the scanner encounters input that does not match any of its rules, it aborts with an error. This option is useful for finding holes in a scanner's rule set. -t instructs flex to write the scanner it generates to standard output instead of lex.yy.c. -v specifies that flex should write to stderr a summary of statistics regarding the scanner it generates. Most of the statistics are meaningless to the casual flex user, but the first line identifies the version of flex, which is useful for figuring out where you Version 2.3 19 FLEX(1) 26 May 1990 stand with respect to patches and new releases, and the next two lines give the date when the scanner was created and a summary of the flags which were in effect. -F specifies that the fast scanner table representation should be used. This representation is about as fast as the full table representation (-f), and for some sets of patterns will be consid- erably smaller (and for others, larger). In general, if the pat- tern set contains both "keywords" and a catch-all, "identifier" rule, such as in the set: "case" return TOK_CASE; "switch" return TOK_SWITCH; ... "default" return TOK_DEFAULT; [a-z]+ return TOK_ID; then you're better off using the full table representation. If only the "identifier" rule is present and you then use a hash table or some such to detect the keywords, you're better off using -F. This option is equivalent to -CF (see below). -I instructs flex to generate an interactive scanner. Normally, scanners generated by flex always look ahead one character before deciding that a rule has been matched. At the cost of some scan- ning overhead, flex will generate a scanner which only looks ahead when needed. Such scanners are called interactive because if you want to write a scanner for an interactive system such as a command shell, you will probably want the user's input to be terminated with a newline, and without -I the user will have to type a charac- ter in addition to the newline in order to have the newline recog- nized. This leads to dreadful interactive performance. If all this seems to confusing, here's the general rule: if a human will be typing in input to your scanner, use -I, otherwise don't; if you don't care about squeezing the utmost performance from your scanner and you don't want to make any assumptions about the input to your scanner, use -I. Note, -I cannot be used in conjunction with full or fast tables, i.e., the -f, -F, -Cf, or -CF flags. -L instructs flex not to generate #line directives. Without this option, flex peppers the generated scanner with #line directives so error messages in the actions will be correctly located with respect to the original flex input file, and not to the fairly meaningless line numbers of lex.yy.c. (Unfortunately flex does not presently generate the necessary directives to "retarget" the line numbers for those parts of lex.yy.c which it generated. So if there is an error in the generated code, a meaningless line number is reported.) -T makes flex run in trace mode. It will generate a lot of messages 20 Version 2.3 26 May 1990 FLEX(1) to stdout concerning the form of the input and the resultant non- deterministic and deterministic finite automata. This option is mostly for use in maintaining flex. -8 instructs flex to generate an 8-bit scanner, i.e., one which can recognize 8-bit characters. On some sites, flex is installed with this option as the default. On others, the default is 7-bit char- acters. To see which is the case, check the verbose (-v) output for "equivalence classes created". If the denominator of the number shown is 128, then by default flex is generating 7-bit char- acters. If it is 256, then the default is 8-bit characters and the -8 flag is not required (but may be a good idea to keep the scanner specification portable). Feeding a 7-bit scanner 8-bit characters will result in infinite loops, bus errors, or other such fireworks, so when in doubt, use the flag. Note that if equivalence classes are used, 8-bit scanners take only slightly more table space than 7-bit scanners (128 bytes, to be exact); if equivalence classes are not used, however, then the tables may grow up to twice their 7-bit size. -C[efmF] controls the degree of table compression. -Ce directs flex to construct equivalence classes, i.e., sets of characters which have identical lexical properties (for example, if the only appearance of digits in the flex input is in the character class "[0-9]" then the digits '0', '1', ..., '9' will all be put in the same equivalence class). Equivalence classes usually give dramatic reductions in the final table/object file sizes (typically a factor of 2-5) and are pretty cheap performance-wise (one array look-up per character scanned). -Cf specifies that the full scanner tables should be generated - flex should not compress the tables by taking advantages of similar transition functions for different states. -CF specifies that the alternate fast scanner representation (described above under the -F flag) should be used. -Cm directs flex to construct meta-equivalence classes, which are sets of equivalence classes (or characters, if equivalence classes are not being used) that are commonly used together. Meta- equivalence classes are often a big win when using compressed tables, but they have a moderate performance impact (one or two "if" tests and one array look-up per character scanned). A lone -C specifies that the scanner tables should be compressed but neither equivalence classes nor meta-equivalence classes should be used. The options -Cf or -CF and -Cm do not make sense together - there is no opportunity for meta-equivalence classes if the table is not being compressed. Otherwise the options may be freely mixed. Version 2.3 21 FLEX(1) 26 May 1990 The default setting is -Cem, which specifies that flex should gen- erate equivalence classes and meta-equivalence classes. This set- ting provides the highest degree of table compression. You can trade off faster-executing scanners at the cost of larger tables with the following generally being true: slowest & smallest -Cem -Cm -Ce -C -C{f,F}e -C{f,F} fastest & largest Note that scanners with the smallest tables are usually generated and compiled the quickest, so during development you will usually want to use the default, maximal compression. -Cfe is often a good compromise between speed and size for produc- tion scanners. -C options are not cumulative; whenever the flag is encountered, the previous -C settings are forgotten. -Sskeleton_file overrides the default skeleton file from which flex constructs its scanners. You'll never need this option unless you are doing flex maintenance or development. PERFORMANCE CONSIDERATIONS The main design goal of flex is that it generate high-performance scanners. It has been optimized for dealing well with large sets of rules. Aside from the effects of table compression on scanner speed outlined above, there are a number of options/actions which degrade per- formance. These are, from most expensive to least: REJECT pattern sets that require backtracking arbitrary trailing context '^' beginning-of-line operator yymore() with the first three all being quite expensive and the last two being quite cheap. REJECT should be avoided at all costs when performance is important. It is a particularly expensive option. Getting rid of backtracking is messy and often may be an enormous amount of work for a complicated scanner. In principal, one begins by using the -b flag to generate a lex.backtrack file. For example, on the input 22 Version 2.3 26 May 1990 FLEX(1) %% foo return TOK_KEYWORD; foobar return TOK_KEYWORD; the file looks like: State #6 is non-accepting - associated rule line numbers: 2 3 out-transitions: [ o ] jam-transitions: EOF [ \001-n p-\177 ] State #8 is non-accepting - associated rule line numbers: 3 out-transitions: [ a ] jam-transitions: EOF [ \001-` b-\177 ] State #9 is non-accepting - associated rule line numbers: 3 out-transitions: [ r ] jam-transitions: EOF [ \001-q s-\177 ] Compressed tables always backtrack. The first few lines tell us that there's a scanner state in which it can make a transition on an 'o' but not on any other character, and that in that state the currently scanned text does not match any rule. The state occurs when trying to match the rules found at lines 2 and 3 in the input file. If the scanner is in that state and then reads some- thing other than an 'o', it will have to backtrack to find a rule which is matched. With a bit of headscratching one can see that this must be the state it's in when it has seen "fo". When this has happened, if anything other than another 'o' is seen, the scanner will have to back up to simply match the 'f' (by the default rule). The comment regarding State #8 indicates there's a problem when "foob" has been scanned. Indeed, on any character other than a 'b', the scanner will have to back up to accept "foo". Similarly, the comment for State #9 concerns when "fooba" has been scanned. The final comment reminds us that there's no point going to all the trouble of removing backtracking from the rules unless we're using -f or -F, since there's no performance gain doing so with compressed scanners. The way to remove the backtracking is to add "error" rules: %% foo return TOK_KEYWORD; foobar return TOK_KEYWORD; fooba | Version 2.3 23 FLEX(1) 26 May 1990 foob | fo { /* false alarm, not really a keyword */ return TOK_ID; } Eliminating backtracking among a list of keywords can also be done using a "catch-all" rule: %% foo return TOK_KEYWORD; foobar return TOK_KEYWORD; [a-z]+ return TOK_ID; This is usually the best solution when appropriate. Backtracking messages tend to cascade. With a complicated set of rules it's not uncommon to get hundreds of messages. If one can decipher them, though, it often only takes a dozen or so rules to eliminate the backtracking (though it's easy to make a mistake and have an error rule accidentally match a valid token. A possible future flex feature will be to automatically add rules to eliminate backtracking). Variable trailing context (where both the leading and trailing parts do not have a fixed length) entails almost the same performance loss as REJECT (i.e., substantial). So when possible a rule like: %% mouse|rat/(cat|dog) run(); is better written: %% mouse/cat|dog run(); rat/cat|dog run(); or as %% mouse|rat/cat run(); mouse|rat/dog run(); Note that here the special '|' action does not provide any savings, and can even make things worse (see BUGS in flex(1)). Another area where the user can increase a scanner's performance (and one that's easier to implement) arises from the fact that the longer the tokens matched, the faster the scanner will run. This is because with long tokens the processing of most input characters takes place in the (short) inner scanning loop, and does not often have to go through the additional work of setting up the scanning environment (e.g., yytext) for the action. Recall the scanner for C comments: 24 Version 2.3 26 May 1990 FLEX(1) %x comment %% int line_num = 1; "/*" BEGIN(comment); [^*\n]* "*"+[^*/\n]* \n ++line_num; "*"+"/" BEGIN(INITIAL); This could be sped up by writing it as: %x comment %% int line_num = 1; "/*" BEGIN(comment); [^*\n]* [^*\n]*\n ++line_num; "*"+[^*/\n]* "*"+[^*/\n]*\n ++line_num; "*"+"/" BEGIN(INITIAL); Now instead of each newline requiring the processing of another action, recognizing the newlines is "distributed" over the other rules to keep the matched text as long as possible. Note that adding rules does not slow down the scanner! The speed of the scanner is independent of the number of rules or (modulo the considerations given at the beginning of this section) how complicated the rules are with regard to operators such as '*' and '|'. A final example in speeding up a scanner: suppose you want to scan through a file containing identifiers and keywords, one per line and with no other extraneous characters, and recognize all the keywords. A natural first approach is: %% asm | auto | break | ... etc ... volatile | while /* it's a keyword */ .|\n /* it's not a keyword */ To eliminate the back-tracking, introduce a catch-all rule: %% asm | auto | Version 2.3 25 FLEX(1) 26 May 1990 break | ... etc ... volatile | while /* it's a keyword */ [a-z]+ | .|\n /* it's not a keyword */ Now, if it's guaranteed that there's exactly one word per line, then we can reduce the total number of matches by a half by merging in the recognition of newlines with that of the other tokens: %% asm\n | auto\n | break\n | ... etc ... volatile\n | while\n /* it's a keyword */ [a-z]+\n | .|\n /* it's not a keyword */ One has to be careful here, as we have now reintroduced backtracking into the scanner. In particular, while we know that there will never be any characters in the input stream other than letters or newlines, flex can't figure this out, and it will plan for possibly needing backtrack- ing when it has scanned a token like "auto" and then the next character is something other than a newline or a letter. Previously it would then just match the "auto" rule and be done, but now it has no "auto" rule, only a "auto\n" rule. To eliminate the possibility of backtracking, we could either duplicate all rules but without final newlines, or, since we never expect to encounter such an input and therefore don't how it's classified, we can introduce one more catch-all rule, this one which doesn't include a newline: %% asm\n | auto\n | break\n | ... etc ... volatile\n | while\n /* it's a keyword */ [a-z]+\n | [a-z]+ | .|\n /* it's not a keyword */ Compiled with -Cf, this is about as fast as one can get a flex scanner to go for this particular problem. A final note: flex is slow when matching NUL's, particularly when a token contains multiple NUL's. It's best to write rules which match short amounts of text if it's anticipated that the text will often 26 Version 2.3 26 May 1990 FLEX(1) include NUL's. INCOMPATIBILITIES WITH LEX AND POSIX flex is a rewrite of the Unix lex tool (the two implementations do not share any code, though), with some extensions and incompatibilities, both of which are of concern to those who wish to write scanners accept- able to either implementation. At present, the POSIX lex draft is very close to the original lex implementation, so some of these incompatibil- ities are also in conflict with the POSIX draft. But the intent is that except as noted below, flex as it presently stands will ultimately be POSIX conformant (i.e., that those areas of conflict with the POSIX draft will be resolved in flex's favor). Please bear in mind that all the comments which follow are with regard to the POSIX draft standard of Summer 1989, and not the final document (or subsequent drafts); they are included so flex users can be aware of the standardization issues and those areas where flex may in the near future undergo changes incompati- ble with its current definition. flex is fully compatible with lex with the following exceptions: - The undocumented lex scanner internal variable yylineno is not sup- ported. It is difficult to support this option efficiently, since it requires examining every character scanned and reexamining the characters when the scanner backs up. Things get more complicated when the end of buffer or file is reached or a NUL is scanned (since the scan must then be restarted with the proper line number count), or the user uses the yyless(), unput(), or REJECT actions, or the multiple input buffer functions. The fix is to add rules which, upon seeing a newline, increment yylineno. This is usually an easy process, though it can be a drag if some of the patterns can match multiple newlines along with other characters. yylineno is not part of the POSIX draft. - The input() routine is not redefinable, though it may be called to read characters following whatever has been matched by a rule. If input() encounters an end-of-file the normal yywrap() processing is done. A ``real'' end-of-file is returned by input() as EOF. Input is instead controlled by redefining the YY_INPUT macro. The flex restriction that input() cannot be redefined is in accor- dance with the POSIX draft, but YY_INPUT has not yet been accepted into the draft (and probably won't; it looks like the draft will simply not specify any way of controlling the scanner's input other than by making an initial assignment to yyin). - flex scanners do not use stdio for input. Because of this, when writing an interactive scanner one must explicitly call fflush() on the stream associated with the terminal after writing out a prompt. With lex such writes are automatically flushed since lex scanners use getchar() for their input. Also, when writing interactive Version 2.3 27 FLEX(1) 26 May 1990 scanners with flex, the -I flag must be used. - flex scanners are not as reentrant as lex scanners. In particular, if you have an interactive scanner and an interrupt handler which long-jumps out of the scanner, and the scanner is subsequently called again, you may get the following message: fatal flex scanner internal error--end of buffer missed To reenter the scanner, first use yyrestart( yyin ); - output() is not supported. Output from the ECHO macro is done to the file-pointer yyout (default stdout). The POSIX draft mentions that an output() routine exists but currently gives no details as to what it does. - lex does not support exclusive start conditions (%x), though they are in the current POSIX draft. - When definitions are expanded, flex encloses them in parentheses. With lex, the following: NAME [A-Z][A-Z0-9]* %% foo{NAME}? printf( "Found it\n" ); %% will not match the string "foo" because when the macro is expanded the rule is equivalent to "foo[A-Z][A-Z0-9]*?" and the precedence is such that the '?' is associated with "[A-Z0-9]*". With flex, the rule will be expanded to "foo([A-Z][A-Z0-9]*)?" and so the string "foo" will match. Note that because of this, the ^, $, , /, and <> operators cannot be used in a flex definition. The POSIX draft interpretation is the same as flex's. - To specify a character class which matches anything but a left bracket (']'), in lex one can use "[^]]" but with flex one must use "[^\]]". The latter works with lex, too. - The lex %r (generate a Ratfor scanner) option is not supported. It is not part of the POSIX draft. - If you are providing your own yywrap() routine, you must include a "#undef yywrap" in the definitions section (section 1). Note that the "#undef" will have to be enclosed in %{}'s. The POSIX draft specifies that yywrap() is a function and this is very unlikely to change; so flex users are warned that yywrap() is likely to be changed to a function in the near future. 28 Version 2.3 26 May 1990 FLEX(1) - After a call to unput(), yytext and yyleng are undefined until the next token is matched. This is not the case with lex or the present POSIX draft. - The precedence of the {} (numeric range) operator is different. lex interprets "abc{1,3}" as "match one, two, or three occurrences of 'abc'", whereas flex interprets it as "match 'ab' followed by one, two, or three occurrences of 'c'". The latter is in agreement with the current POSIX draft. - The precedence of the ^ operator is different. lex interprets "^foo|bar" as "match either 'foo' at the beginning of a line, or 'bar' anywhere", whereas flex interprets it as "match either 'foo' or 'bar' if they come at the beginning of a line". The latter is in agreement with the current POSIX draft. - To refer to yytext outside of the scanner source file, the correct definition with flex is "extern char *yytext" rather than "extern char yytext[]". This is contrary to the current POSIX draft but a point on which flex will not be changing, as the array representa- tion entails a serious performance penalty. It is hoped that the POSIX draft will be emended to support the flex variety of declara- tion (as this is a fairly painless change to require of lex users). - yyin is initialized by lex to be stdin; flex, on the other hand, initializes yyin to NULL and then assigns it to stdin the first time the scanner is called, providing yyin has not already been assigned to a non-NULL value. The difference is subtle, but the net effect is that with flex scanners, yyin does not have a valid value until the scanner has been called. - The special table-size declarations such as %a supported by lex are not required by flex scanners; flex ignores them. - The name FLEX_SCANNER is #define'd so scanners may be written for use with either flex or lex. The following flex features are not included in lex or the POSIX draft standard: yyterminate() <> YY_DECL #line directives %{}'s around actions yyrestart() comments beginning with '#' (deprecated) multiple actions on a line This last feature refers to the fact that with flex you can put multiple actions on the same line, separated with semi-colons, while with lex, the following foo handle_foo(); ++num_foos_seen; Version 2.3 29 FLEX(1) 26 May 1990 is (rather surprisingly) truncated to foo handle_foo(); flex does not truncate the action. Actions that are not enclosed in braces are simply terminated at the end of the line. DIAGNOSTICS reject_used_but_not_detected undefined or yymore_used_but_not_detected undefined - These errors can occur at compile time. They indicate that the scanner uses REJECT or yymore() but that flex failed to notice the fact, meaning that flex scanned the first two sections looking for occurrences of these actions and failed to find any, but somehow you snuck some in (via a #include file, for example). Make an explicit reference to the action in your flex input file. (Note that previously flex supported a %used/%unused mechanism for dealing with this problem; this feature is still supported but now deprecated, and will go away soon unless the author hears from people who can argue compellingly that they need it.) flex scanner jammed - a scanner compiled with -s has encountered an input string which wasn't matched by any of its rules. flex input buffer overflowed - a scanner rule matched a string long enough to overflow the scanner's internal input buffer (16K bytes by default - controlled by YY_BUF_SIZE in "flex.skel". Note that to rede- fine this macro, you must first #undefine it). scanner requires -8 flag - Your scanner specification includes recogniz- ing 8-bit characters and you did not specify the -8 flag (and your site has not installed flex with -8 as the default). fatal flex scanner internal error--end of buffer missed - This can occur in an scanner which is reentered after a long-jump has jumped out (or over) the scanner's activation frame. Before reentering the scanner, use: yyrestart( yyin ); too many %t classes! - You managed to put every single character into its own %t class. flex requires that at least one of the classes share characters. DEFICIENCIES / BUGS See flex(1). SEE ALSO flex(1), lex(1), yacc(1), sed(1), awk(1). M. E. Lesk and E. Schmidt, LEX - Lexical Analyzer Generator 30 Version 2.3 26 May 1990 FLEX(1) AUTHOR Vern Paxson, with the help of many ideas and much inspiration from Van Jacobson. Original version by Jef Poskanzer. The fast table represen- tation is a partial implementation of a design done by Van Jacobson. The implementation was done by Kevin Gong and Vern Paxson. Thanks to the many flex beta-testers, feedbackers, and contributors, especially Casey Leedom, benson@odi.com, Keith Bostic, Frederic Brehm, Nick Christopher, Jason Coughlin, Scott David Daniels, Leo Eskin, Chris Faylor, Eric Goldman, Eric Hughes, Jeffrey R. Jones, Kevin B. Kenny, Ronald Lamprecht, Greg Lee, Craig Leres, Mohamed el Lozy, Jim Meyering, Marc Nozell, Esmond Pitt, Jef Poskanzer, Jim Roskind, Dave Tallman, Frank Whaley, Ken Yap, and those whose names have slipped my marginal mail-archiving skills but whose contributions are appreciated all the same. Thanks to Keith Bostic, John Gilmore, Craig Leres, Bob Mulcahy, Rich Salz, and Richard Stallman for help with various distribution headaches. Thanks to Esmond Pitt and Earle Horton for 8-bit character support; to Benson Margulies and Fred Burke for C++ support; to Ove Ewerlid for the basics of support for NUL's; and to Eric Hughes for the basics of sup- port for multiple buffers. Work is being done on extending flex to generate scanners in which the state machine is directly represented in C code rather than tables. These scanners may well be substantially faster than those generated using -f or -F. If you are working in this area and are interested in comparing notes and seeing whether redundant work can be avoided, con- tact Ove Ewerlid (ewerlid@mizar.DoCS.UU.SE). This work was primarily done when I was at the Real Time Systems Group at the Lawrence Berkeley Laboratory in Berkeley, CA. Many thanks to all there for the support I received. Send comments to: Vern Paxson Computer Science Department 4126 Upson Hall Cornell University Ithaca, NY 14853-7501 vern@cs.cornell.edu decvax!cornell!vern Version 2.3 31 99 base-7.0.3.1/modules/libcom/src/flex/Makefile0000664000577000060420000000201113557101274017507 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/flex parse_YACCOPT = -l -d SKELETON_FILE = flex.skel.static parse_CPPFLAGS = -DDEFAULT_SKELETON_FILE=$(SKELETON_FILE) INC += flex.skel.static # flex.c is included in parse.c e_flex_SRCS += ccl.c e_flex_SRCS += dfa.c e_flex_SRCS += ecs.c e_flex_SRCS += gen.c e_flex_SRCS += misc.c e_flex_SRCS += nfa.c e_flex_SRCS += sym.c e_flex_SRCS += tblcmp.c e_flex_SRCS += parse.c e_flex_OBJS += epicsTempFile$(OBJ) PROD_HOST += e_flex CLEANS += parse.c parse.h base-7.0.3.1/modules/libcom/src/flex/README0000664000577000060420000000275113557101274016742 0ustar anjaesctlThis is release 2.3 of flex - a full release. The flex distribution consists of the following files: README This message Makefile flexdef.h parse.y scan.l ccl.c dfa.c ecs.c flex sources gen.c main.c misc.c nfa.c sym.c tblcmp.c yylex.c libmain.c flex library (-lfl) source initscan.c pre-flex'd version of scan.l flex.skel skeleton for generated scanners flexdoc.1 full user documentation flex.1 reference documentation Changes Differences between this release and the previous one COPYING flex's copyright MISC/ a directory containing miscellaneous porting-related notes (for Atari, MS-DOS, Turbo-C, and VMS) Decide where you want to keep flex.skel (suggestion: /usr/local/lib), but don't move it there yet. Edit "Makefile" and change the definition of SKELETON_FILE to reflect the full pathname of flex.skel. Read the "Porting considerations" note in the Makefile and make the necessary changes. To make flex for the first time, use: make first_flex which uses the pre-generated copy of the flex scanner (the scanner itself is written using flex). Assuming it builds successfully, you can test it using make test The "diff" should not show any differences. If you're feeling adventurous, issue "make bigtest" and be prepared to wait a while. Install flex using: make install Please send problems and feedback to: vern@cs.cornell.edu decvax!cornell!vern Vern Paxson CS Department 4126 Upson Hall Cornell University Ithaca, NY 14853-7501 base-7.0.3.1/modules/libcom/src/flex/RULES0000664000577000060420000000121613557101274016672 0ustar anjaesctl#************************************************************************* # Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. # Ensure that the lexer is built before it is needed parse.c: $(INSTALL_HOST_BIN)/antelope$(HOSTEXE) base-7.0.3.1/modules/libcom/src/flex/ccl.c0000664000577000060420000001047013557101274016764 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* ccl - routines for character classes */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "flexdef.h" /* ccladd - add a single character to a ccl * * synopsis * int cclp; * int ch; * ccladd( cclp, ch ); */ void ccladd(int cclp, int ch) { int ind, len, newpos, i; len = ccllen[cclp]; ind = cclmap[cclp]; /* check to see if the character is already in the ccl */ for ( i = 0; i < len; ++i ) if ( ccltbl[ind + i] == ch ) return; newpos = ind + len; if ( newpos >= current_max_ccl_tbl_size ) { current_max_ccl_tbl_size += MAX_CCL_TBL_SIZE_INCREMENT; ++num_reallocs; ccltbl = reallocate_character_array( ccltbl, current_max_ccl_tbl_size ); } ccllen[cclp] = len + 1; ccltbl[newpos] = ch; } /* cclinit - make an empty ccl * * synopsis * int cclinit(); * new_ccl = cclinit(); */ int cclinit(void) { if ( ++lastccl >= current_maxccls ) { current_maxccls += MAX_CCLS_INCREMENT; ++num_reallocs; cclmap = reallocate_integer_array( cclmap, current_maxccls ); ccllen = reallocate_integer_array( ccllen, current_maxccls ); cclng = reallocate_integer_array( cclng, current_maxccls ); } if ( lastccl == 1 ) /* we're making the first ccl */ cclmap[lastccl] = 0; else /* the new pointer is just past the end of the last ccl. Since * the cclmap points to the \first/ character of a ccl, adding the * length of the ccl to the cclmap pointer will produce a cursor * to the first free space */ cclmap[lastccl] = cclmap[lastccl - 1] + ccllen[lastccl - 1]; ccllen[lastccl] = 0; cclng[lastccl] = 0; /* ccl's start out life un-negated */ return ( lastccl ); } /* cclnegate - negate a ccl * * synopsis * int cclp; * cclnegate( ccl ); */ void cclnegate(int cclp) { cclng[cclp] = 1; } /* list_character_set - list the members of a set of characters in CCL form * * synopsis * int cset[CSIZE]; * FILE *file; * list_character_set( cset ); * * writes to the given file a character-class representation of those * characters present in the given set. A character is present if it * has a non-zero value in the set array. */ void list_character_set(FILE *file, int cset[]) { int i; char *readable_form(); putc( '[', file ); for ( i = 0; i < csize; ++i ) { if ( cset[i] ) { int start_char = i; putc( ' ', file ); fputs( readable_form( i ), file ); while ( ++i < csize && cset[i] ) ; if ( i - 1 > start_char ) /* this was a run */ fprintf( file, "-%s", readable_form( i - 1 ) ); putc( ' ', file ); } } putc( ']', file ); } base-7.0.3.1/modules/libcom/src/flex/dfa.c0000664000577000060420000006472713557101274016773 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dfa - DFA construction routines */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "flexdef.h" /* declare functions that have forward references */ void dump_associated_rules (FILE*, int); void dump_transitions (FILE*, int[]); void sympartition (int[], int, int[], int[]); int symfollowset (int[], int, int, int[]); /* check_for_backtracking - check a DFA state for backtracking * * synopsis * int ds, state[numecs]; * check_for_backtracking( ds, state ); * * ds is the number of the state to check and state[] is its out-transitions, * indexed by equivalence class, and state_rules[] is the set of rules * associated with this state */ void check_for_backtracking(int ds, int state[]) { if ( (reject && ! dfaacc[ds].dfaacc_set) || ! dfaacc[ds].dfaacc_state ) { /* state is non-accepting */ ++num_backtracking; if ( backtrack_report ) { fprintf( backtrack_file, "State #%d is non-accepting -\n", ds ); /* identify the state */ dump_associated_rules( backtrack_file, ds ); /* now identify it further using the out- and jam-transitions */ dump_transitions( backtrack_file, state ); putc( '\n', backtrack_file ); } } } /* check_trailing_context - check to see if NFA state set constitutes * "dangerous" trailing context * * synopsis * int nfa_states[num_states+1], num_states; * int accset[nacc+1], nacc; * check_trailing_context( nfa_states, num_states, accset, nacc ); * * NOTES * Trailing context is "dangerous" if both the head and the trailing * part are of variable size \and/ there's a DFA state which contains * both an accepting state for the head part of the rule and NFA states * which occur after the beginning of the trailing context. * When such a rule is matched, it's impossible to tell if having been * in the DFA state indicates the beginning of the trailing context * or further-along scanning of the pattern. In these cases, a warning * message is issued. * * nfa_states[1 .. num_states] is the list of NFA states in the DFA. * accset[1 .. nacc] is the list of accepting numbers for the DFA state. */ void check_trailing_context(int *nfa_states, int num_states, int *accset, int nacc) { int i, j; for ( i = 1; i <= num_states; ++i ) { int ns = nfa_states[i]; int type = state_type[ns]; int ar = assoc_rule[ns]; if ( type == STATE_NORMAL || rule_type[ar] != RULE_VARIABLE ) { /* do nothing */ } else if ( type == STATE_TRAILING_CONTEXT ) { /* potential trouble. Scan set of accepting numbers for * the one marking the end of the "head". We assume that * this looping will be fairly cheap since it's rare that * an accepting number set is large. */ for ( j = 1; j <= nacc; ++j ) if ( accset[j] & YY_TRAILING_HEAD_MASK ) { fprintf( stderr, "%s: Dangerous trailing context in rule at line %d\n", program_name, rule_linenum[ar] ); return; } } } } /* dump_associated_rules - list the rules associated with a DFA state * * synopisis * int ds; * FILE *file; * dump_associated_rules( file, ds ); * * goes through the set of NFA states associated with the DFA and * extracts the first MAX_ASSOC_RULES unique rules, sorts them, * and writes a report to the given file */ void dump_associated_rules(FILE *file, int ds) { int i, j; int num_associated_rules = 0; int rule_set[MAX_ASSOC_RULES + 1]; int *dset = dss[ds]; int size = dfasiz[ds]; for ( i = 1; i <= size; ++i ) { int rule_num = rule_linenum[assoc_rule[dset[i]]]; for ( j = 1; j <= num_associated_rules; ++j ) if ( rule_num == rule_set[j] ) break; if ( j > num_associated_rules ) { /* new rule */ if ( num_associated_rules < MAX_ASSOC_RULES ) rule_set[++num_associated_rules] = rule_num; } } bubble( rule_set, num_associated_rules ); fprintf( file, " associated rule line numbers:" ); for ( i = 1; i <= num_associated_rules; ++i ) { if ( i % 8 == 1 ) putc( '\n', file ); fprintf( file, "\t%d", rule_set[i] ); } putc( '\n', file ); } /* dump_transitions - list the transitions associated with a DFA state * * synopisis * int state[numecs]; * FILE *file; * dump_transitions( file, state ); * * goes through the set of out-transitions and lists them in human-readable * form (i.e., not as equivalence classes); also lists jam transitions * (i.e., all those which are not out-transitions, plus EOF). The dump * is done to the given file. */ void dump_transitions(FILE *file, int state[]) { int i, ec; int out_char_set[CSIZE]; for ( i = 0; i < csize; ++i ) { ec = abs( ecgroup[i] ); out_char_set[i] = state[ec]; } fprintf( file, " out-transitions: " ); list_character_set( file, out_char_set ); /* now invert the members of the set to get the jam transitions */ for ( i = 0; i < csize; ++i ) out_char_set[i] = ! out_char_set[i]; fprintf( file, "\n jam-transitions: EOF " ); list_character_set( file, out_char_set ); putc( '\n', file ); } /* epsclosure - construct the epsilon closure of a set of ndfa states * * synopsis * int t[current_max_dfa_size], numstates, accset[num_rules + 1], nacc; * int hashval; * int *epsclosure(); * t = epsclosure( t, &numstates, accset, &nacc, &hashval ); * * NOTES * the epsilon closure is the set of all states reachable by an arbitrary * number of epsilon transitions which themselves do not have epsilon * transitions going out, unioned with the set of states which have non-null * accepting numbers. t is an array of size numstates of nfa state numbers. * Upon return, t holds the epsilon closure and numstates is updated. accset * holds a list of the accepting numbers, and the size of accset is given * by nacc. t may be subjected to reallocation if it is not large enough * to hold the epsilon closure. * * hashval is the hash value for the dfa corresponding to the state set */ int *epsclosure(int *t, int *ns_addr, int accset[], int *nacc_addr, int *hv_addr) { int stkpos, ns, tsp; int numstates = *ns_addr, nacc, hashval, transsym, nfaccnum; int stkend, nstate; static int did_stk_init = false, *stk; #define MARK_STATE(state) \ trans1[state] = trans1[state] - MARKER_DIFFERENCE; #define IS_MARKED(state) (trans1[state] < 0) #define UNMARK_STATE(state) \ trans1[state] = trans1[state] + MARKER_DIFFERENCE; #define CHECK_ACCEPT(state) \ { \ nfaccnum = accptnum[state]; \ if ( nfaccnum != NIL ) \ accset[++nacc] = nfaccnum; \ } #define DO_REALLOCATION \ { \ current_max_dfa_size += MAX_DFA_SIZE_INCREMENT; \ ++num_reallocs; \ t = reallocate_integer_array( t, current_max_dfa_size ); \ stk = reallocate_integer_array( stk, current_max_dfa_size ); \ } \ #define PUT_ON_STACK(state) \ { \ if ( ++stkend >= current_max_dfa_size ) \ DO_REALLOCATION \ stk[stkend] = state; \ MARK_STATE(state) \ } #define ADD_STATE(state) \ { \ if ( ++numstates >= current_max_dfa_size ) \ DO_REALLOCATION \ t[numstates] = state; \ hashval = hashval + state; \ } #define STACK_STATE(state) \ { \ PUT_ON_STACK(state) \ CHECK_ACCEPT(state) \ if ( nfaccnum != NIL || transchar[state] != SYM_EPSILON ) \ ADD_STATE(state) \ } if ( ! did_stk_init ) { stk = allocate_integer_array( current_max_dfa_size ); did_stk_init = true; } nacc = stkend = hashval = 0; for ( nstate = 1; nstate <= numstates; ++nstate ) { ns = t[nstate]; /* the state could be marked if we've already pushed it onto * the stack */ if ( ! IS_MARKED(ns) ) PUT_ON_STACK(ns) CHECK_ACCEPT(ns) hashval = hashval + ns; } for ( stkpos = 1; stkpos <= stkend; ++stkpos ) { ns = stk[stkpos]; transsym = transchar[ns]; if ( transsym == SYM_EPSILON ) { tsp = trans1[ns] + MARKER_DIFFERENCE; if ( tsp != NO_TRANSITION ) { if ( ! IS_MARKED(tsp) ) STACK_STATE(tsp) tsp = trans2[ns]; if ( tsp != NO_TRANSITION ) if ( ! IS_MARKED(tsp) ) STACK_STATE(tsp) } } } /* clear out "visit" markers */ for ( stkpos = 1; stkpos <= stkend; ++stkpos ) { if ( IS_MARKED(stk[stkpos]) ) { UNMARK_STATE(stk[stkpos]) } else flexfatal( "consistency check failed in epsclosure()" ); } *ns_addr = numstates; *hv_addr = hashval; *nacc_addr = nacc; return ( t ); } /* increase_max_dfas - increase the maximum number of DFAs */ void increase_max_dfas(void) { current_max_dfas += MAX_DFAS_INCREMENT; ++num_reallocs; base = reallocate_integer_array( base, current_max_dfas ); def = reallocate_integer_array( def, current_max_dfas ); dfasiz = reallocate_integer_array( dfasiz, current_max_dfas ); accsiz = reallocate_integer_array( accsiz, current_max_dfas ); dhash = reallocate_integer_array( dhash, current_max_dfas ); dss = reallocate_int_ptr_array( dss, current_max_dfas ); dfaacc = reallocate_dfaacc_union( dfaacc, current_max_dfas ); if ( nultrans ) nultrans = reallocate_integer_array( nultrans, current_max_dfas ); } /* ntod - convert an ndfa to a dfa * * synopsis * ntod(); * * creates the dfa corresponding to the ndfa we've constructed. the * dfa starts out in state #1. */ void ntod(void) { int *accset, ds, nacc, newds; int sym, hashval, numstates, dsize; int num_full_table_rows = 0; /* used only for -f */ int *nset, *dset; int targptr, totaltrans, i, comstate, comfreq, targ; int *epsclosure(int *t, int *ns_addr, int *accset, int *nacc_addr, int *hv_addr); int snstods(int *sns, int numstates, int *accset, int nacc, int hashval, int *newds_addr); int symlist[CSIZE + 1]; int num_start_states; int todo_head, todo_next; /* note that the following are indexed by *equivalence classes* * and not by characters. Since equivalence classes are indexed * beginning with 1, even if the scanner accepts NUL's, this * means that (since every character is potentially in its own * equivalence class) these arrays must have room for indices * from 1 to CSIZE, so their size must be CSIZE + 1. */ int duplist[CSIZE + 1], state[CSIZE + 1]; int targfreq[CSIZE + 1], targstate[CSIZE + 1]; /* this is so find_table_space(...) will know where to start looking in * chk/nxt for unused records for space to put in the state */ if ( fullspd ) firstfree = 0; accset = allocate_integer_array( num_rules + 1 ); nset = allocate_integer_array( current_max_dfa_size ); /* the "todo" queue is represented by the head, which is the DFA * state currently being processed, and the "next", which is the * next DFA state number available (not in use). We depend on the * fact that snstods() returns DFA's \in increasing order/, and thus * need only know the bounds of the dfas to be processed. */ todo_head = todo_next = 0; for ( i = 0; i <= csize; ++i ) { duplist[i] = NIL; symlist[i] = false; } for ( i = 0; i <= num_rules; ++i ) accset[i] = NIL; if ( trace ) { dumpnfa( scset[1] ); fputs( "\n\nDFA Dump:\n\n", stderr ); } inittbl(); /* check to see whether we should build a separate table for transitions * on NUL characters. We don't do this for full-speed (-F) scanners, * since for them we don't have a simple state number lying around with * which to index the table. We also don't bother doing it for scanners * unless (1) NUL is in its own equivalence class (indicated by a * positive value of ecgroup[NUL]), (2) NUL's equilvalence class is * the last equivalence class, and (3) the number of equivalence classes * is the same as the number of characters. This latter case comes about * when useecs is false or when its true but every character still * manages to land in its own class (unlikely, but it's cheap to check * for). If all these things are true then the character code needed * to represent NUL's equivalence class for indexing the tables is * going to take one more bit than the number of characters, and therefore * we won't be assured of being able to fit it into a YY_CHAR variable. * This rules out storing the transitions in a compressed table, since * the code for interpreting them uses a YY_CHAR variable (perhaps it * should just use an integer, though; this is worth pondering ... ###). * * Finally, for full tables, we want the number of entries in the * table to be a power of two so the array references go fast (it * will just take a shift to compute the major index). If encoding * NUL's transitions in the table will spoil this, we give it its * own table (note that this will be the case if we're not using * equivalence classes). */ /* note that the test for ecgroup[0] == numecs below accomplishes * both (1) and (2) above */ if ( ! fullspd && ecgroup[0] == numecs ) { /* NUL is alone in its equivalence class, which is the last one */ int use_NUL_table = (numecs == csize); if ( fulltbl && ! use_NUL_table ) { /* we still may want to use the table if numecs is a power of 2 */ int power_of_two; for ( power_of_two = 1; power_of_two <= csize; power_of_two *= 2 ) if ( numecs == power_of_two ) { use_NUL_table = true; break; } } if ( use_NUL_table ) nultrans = allocate_integer_array( current_max_dfas ); /* from now on, nultrans != nil indicates that we're * saving null transitions for later, separate encoding */ } if ( fullspd ) { for ( i = 0; i <= numecs; ++i ) state[i] = 0; place_state( state, 0, 0 ); } else if ( fulltbl ) { if ( nultrans ) /* we won't be including NUL's transitions in the table, * so build it for entries from 0 .. numecs - 1 */ num_full_table_rows = numecs; else /* take into account the fact that we'll be including * the NUL entries in the transition table. Build it * from 0 .. numecs. */ num_full_table_rows = numecs + 1; /* declare it "short" because it's a real long-shot that that * won't be large enough. */ printf( "static short int yy_nxt[][%d] =\n {\n", /* '}' so vi doesn't get too confused */ num_full_table_rows ); /* generate 0 entries for state #0 */ for ( i = 0; i < num_full_table_rows; ++i ) mk2data( 0 ); /* force ',' and dataflush() next call to mk2data */ datapos = NUMDATAITEMS; /* force extra blank line next dataflush() */ dataline = NUMDATALINES; } /* create the first states */ num_start_states = lastsc * 2; for ( i = 1; i <= num_start_states; ++i ) { numstates = 1; /* for each start condition, make one state for the case when * we're at the beginning of the line (the '%' operator) and * one for the case when we're not */ if ( i % 2 == 1 ) nset[numstates] = scset[(i / 2) + 1]; else nset[numstates] = mkbranch( scbol[i / 2], scset[i / 2] ); nset = epsclosure( nset, &numstates, accset, &nacc, &hashval ); if ( snstods( nset, numstates, accset, nacc, hashval, &ds ) ) { numas += nacc; totnst += numstates; ++todo_next; if ( variable_trailing_context_rules && nacc > 0 ) check_trailing_context( nset, numstates, accset, nacc ); } } if ( ! fullspd ) { if ( ! snstods( nset, 0, accset, 0, 0, &end_of_buffer_state ) ) flexfatal( "could not create unique end-of-buffer state" ); ++numas; ++num_start_states; ++todo_next; } while ( todo_head < todo_next ) { targptr = 0; totaltrans = 0; for ( i = 1; i <= numecs; ++i ) state[i] = 0; ds = ++todo_head; dset = dss[ds]; dsize = dfasiz[ds]; if ( trace ) fprintf( stderr, "state # %d:\n", ds ); sympartition( dset, dsize, symlist, duplist ); for ( sym = 1; sym <= numecs; ++sym ) { if ( symlist[sym] ) { symlist[sym] = 0; if ( duplist[sym] == NIL ) { /* symbol has unique out-transitions */ numstates = symfollowset( dset, dsize, sym, nset ); nset = epsclosure( nset, &numstates, accset, &nacc, &hashval ); if ( snstods( nset, numstates, accset, nacc, hashval, &newds ) ) { totnst = totnst + numstates; ++todo_next; numas += nacc; if ( variable_trailing_context_rules && nacc > 0 ) check_trailing_context( nset, numstates, accset, nacc ); } state[sym] = newds; if ( trace ) fprintf( stderr, "\t%d\t%d\n", sym, newds ); targfreq[++targptr] = 1; targstate[targptr] = newds; ++numuniq; } else { /* sym's equivalence class has the same transitions * as duplist(sym)'s equivalence class */ targ = state[duplist[sym]]; state[sym] = targ; if ( trace ) fprintf( stderr, "\t%d\t%d\n", sym, targ ); /* update frequency count for destination state */ i = 0; while ( targstate[++i] != targ ) ; ++targfreq[i]; ++numdup; } ++totaltrans; duplist[sym] = NIL; } } numsnpairs = numsnpairs + totaltrans; if ( caseins && ! useecs ) { int j; for ( i = 'A', j = 'a'; i <= 'Z'; ++i, ++j ) state[i] = state[j]; } if ( ds > num_start_states ) check_for_backtracking( ds, state ); if ( nultrans ) { nultrans[ds] = state[NUL_ec]; state[NUL_ec] = 0; /* remove transition */ } if ( fulltbl ) { /* supply array's 0-element */ if ( ds == end_of_buffer_state ) mk2data( -end_of_buffer_state ); else mk2data( end_of_buffer_state ); for ( i = 1; i < num_full_table_rows; ++i ) /* jams are marked by negative of state number */ mk2data( state[i] ? state[i] : -ds ); /* force ',' and dataflush() next call to mk2data */ datapos = NUMDATAITEMS; /* force extra blank line next dataflush() */ dataline = NUMDATALINES; } else if ( fullspd ) place_state( state, ds, totaltrans ); else if ( ds == end_of_buffer_state ) /* special case this state to make sure it does what it's * supposed to, i.e., jam on end-of-buffer */ stack1( ds, 0, 0, JAMSTATE ); else /* normal, compressed state */ { /* determine which destination state is the most common, and * how many transitions to it there are */ comfreq = 0; comstate = 0; for ( i = 1; i <= targptr; ++i ) if ( targfreq[i] > comfreq ) { comfreq = targfreq[i]; comstate = targstate[i]; } bldtbl( state, ds, totaltrans, comstate, comfreq ); } } if ( fulltbl ) dataend(); else if ( ! fullspd ) { cmptmps(); /* create compressed template entries */ /* create tables for all the states with only one out-transition */ while ( onesp > 0 ) { mk1tbl( onestate[onesp], onesym[onesp], onenext[onesp], onedef[onesp] ); --onesp; } mkdeftbl(); } } /* snstods - converts a set of ndfa states into a dfa state * * synopsis * int sns[numstates], numstates, newds, accset[num_rules + 1], nacc, hashval; * int snstods(); * is_new_state = snstods( sns, numstates, accset, nacc, hashval, &newds ); * * on return, the dfa state number is in newds. */ int snstods(int sns[], int numstates, int accset[], int nacc, int hashval, int *newds_addr) { int didsort = 0; int i, j; int newds, *oldsns; for ( i = 1; i <= lastdfa; ++i ) if ( hashval == dhash[i] ) { if ( numstates == dfasiz[i] ) { oldsns = dss[i]; if ( ! didsort ) { /* we sort the states in sns so we can compare it to * oldsns quickly. we use bubble because there probably * aren't very many states */ bubble( sns, numstates ); didsort = 1; } for ( j = 1; j <= numstates; ++j ) if ( sns[j] != oldsns[j] ) break; if ( j > numstates ) { ++dfaeql; *newds_addr = i; return ( 0 ); } ++hshcol; } else ++hshsave; } /* make a new dfa */ if ( ++lastdfa >= current_max_dfas ) increase_max_dfas(); newds = lastdfa; dss[newds] = (int *) malloc( (unsigned) ((numstates + 1) * sizeof( int )) ); if ( ! dss[newds] ) flexfatal( "dynamic memory failure in snstods()" ); /* if we haven't already sorted the states in sns, we do so now, so that * future comparisons with it can be made quickly */ if ( ! didsort ) bubble( sns, numstates ); for ( i = 1; i <= numstates; ++i ) dss[newds][i] = sns[i]; dfasiz[newds] = numstates; dhash[newds] = hashval; if ( nacc == 0 ) { if ( reject ) dfaacc[newds].dfaacc_set = (int *) 0; else dfaacc[newds].dfaacc_state = 0; accsiz[newds] = 0; } else if ( reject ) { /* we sort the accepting set in increasing order so the disambiguating * rule that the first rule listed is considered match in the event of * ties will work. We use a bubble sort since the list is probably * quite small. */ bubble( accset, nacc ); dfaacc[newds].dfaacc_set = (int *) malloc( (unsigned) ((nacc + 1) * sizeof( int )) ); if ( ! dfaacc[newds].dfaacc_set ) flexfatal( "dynamic memory failure in snstods()" ); /* save the accepting set for later */ for ( i = 1; i <= nacc; ++i ) dfaacc[newds].dfaacc_set[i] = accset[i]; accsiz[newds] = nacc; } else { /* find lowest numbered rule so the disambiguating rule will work */ j = num_rules + 1; for ( i = 1; i <= nacc; ++i ) if ( accset[i] < j ) j = accset[i]; dfaacc[newds].dfaacc_state = j; } *newds_addr = newds; return ( 1 ); } /* symfollowset - follow the symbol transitions one step * * synopsis * int ds[current_max_dfa_size], dsize, transsym; * int nset[current_max_dfa_size], numstates; * numstates = symfollowset( ds, dsize, transsym, nset ); */ int symfollowset(int ds[], int dsize, int transsym, int nset[]) { int ns, tsp, sym, i, j, lenccl, ch, numstates; int ccllist; numstates = 0; for ( i = 1; i <= dsize; ++i ) { /* for each nfa state ns in the state set of ds */ ns = ds[i]; sym = transchar[ns]; tsp = trans1[ns]; if ( sym < 0 ) { /* it's a character class */ sym = -sym; ccllist = cclmap[sym]; lenccl = ccllen[sym]; if ( cclng[sym] ) { for ( j = 0; j < lenccl; ++j ) { /* loop through negated character class */ ch = ccltbl[ccllist + j]; if ( ch == 0 ) ch = NUL_ec; if ( ch > transsym ) break; /* transsym isn't in negated ccl */ else if ( ch == transsym ) /* next 2 */ goto bottom; } /* didn't find transsym in ccl */ nset[++numstates] = tsp; } else for ( j = 0; j < lenccl; ++j ) { ch = ccltbl[ccllist + j]; if ( ch == 0 ) ch = NUL_ec; if ( ch > transsym ) break; else if ( ch == transsym ) { nset[++numstates] = tsp; break; } } } else if ( sym >= 'A' && sym <= 'Z' && caseins ) flexfatal( "consistency check failed in symfollowset" ); else if ( sym == SYM_EPSILON ) { /* do nothing */ } else if ( abs( ecgroup[sym] ) == transsym ) nset[++numstates] = tsp; bottom: ; } return ( numstates ); } /* sympartition - partition characters with same out-transitions * * synopsis * integer ds[current_max_dfa_size], numstates, duplist[numecs]; * symlist[numecs]; * sympartition( ds, numstates, symlist, duplist ); */ void sympartition(int ds[], int numstates, int symlist[], int duplist[]) { int tch, i, j, k, ns, dupfwd[CSIZE + 1], lenccl, cclp, ich; /* partitioning is done by creating equivalence classes for those * characters which have out-transitions from the given state. Thus * we are really creating equivalence classes of equivalence classes. */ for ( i = 1; i <= numecs; ++i ) { /* initialize equivalence class list */ duplist[i] = i - 1; dupfwd[i] = i + 1; } duplist[1] = NIL; dupfwd[numecs] = NIL; for ( i = 1; i <= numstates; ++i ) { ns = ds[i]; tch = transchar[ns]; if ( tch != SYM_EPSILON ) { if ( tch < -lastccl || tch > csize ) { if ( tch > csize && tch <= CSIZE ) flexerror( "scanner requires -8 flag" ); else flexfatal( "bad transition character detected in sympartition()" ); } if ( tch >= 0 ) { /* character transition */ /* abs() needed for fake %t ec's */ int ec = abs( ecgroup[tch] ); mkechar( ec, dupfwd, duplist ); symlist[ec] = 1; } else { /* character class */ tch = -tch; lenccl = ccllen[tch]; cclp = cclmap[tch]; mkeccl( ccltbl + cclp, lenccl, dupfwd, duplist, numecs, NUL_ec ); if ( cclng[tch] ) { j = 0; for ( k = 0; k < lenccl; ++k ) { ich = ccltbl[cclp + k]; if ( ich == 0 ) ich = NUL_ec; for ( ++j; j < ich; ++j ) symlist[j] = 1; } for ( ++j; j <= numecs; ++j ) symlist[j] = 1; } else for ( k = 0; k < lenccl; ++k ) { ich = ccltbl[cclp + k]; if ( ich == 0 ) ich = NUL_ec; symlist[ich] = 1; } } } } } base-7.0.3.1/modules/libcom/src/flex/ecs.c0000664000577000060420000002152113557101274016774 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* ecs - equivalence class routines */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "flexdef.h" /* ccl2ecl - convert character classes to set of equivalence classes * * synopsis * ccl2ecl(); */ void ccl2ecl(void) { int i, ich, newlen, cclp, ccls, cclmec; for ( i = 1; i <= lastccl; ++i ) { /* we loop through each character class, and for each character * in the class, add the character's equivalence class to the * new "character" class we are creating. Thus when we are all * done, character classes will really consist of collections * of equivalence classes */ newlen = 0; cclp = cclmap[i]; for ( ccls = 0; ccls < ccllen[i]; ++ccls ) { ich = ccltbl[cclp + ccls]; cclmec = ecgroup[ich]; if ( xlation && cclmec < 0 ) { /* special hack--if we're doing %t tables then it's * possible that no representative of this character's * equivalence class is in the ccl. So waiting till * we see the representative would be disastrous. Instead, * we add this character's equivalence class anyway, if it's * not already present. */ int j; /* this loop makes this whole process n^2; but we don't * really care about %t performance anyway */ for ( j = 0; j < newlen; ++j ) if ( ccltbl[cclp + j] == -cclmec ) break; if ( j >= newlen ) { /* no representative yet, add this one in */ ccltbl[cclp + newlen] = -cclmec; ++newlen; } } else if ( cclmec > 0 ) { ccltbl[cclp + newlen] = cclmec; ++newlen; } } ccllen[i] = newlen; } } /* cre8ecs - associate equivalence class numbers with class members * * synopsis * int cre8ecs(); * number of classes = cre8ecs( fwd, bck, num ); * * fwd is the forward linked-list of equivalence class members. bck * is the backward linked-list, and num is the number of class members. * * Returned is the number of classes. */ int cre8ecs(int fwd[], int bck[], int num) { int i, j, numcl; numcl = 0; /* create equivalence class numbers. From now on, abs( bck(x) ) * is the equivalence class number for object x. If bck(x) * is positive, then x is the representative of its equivalence * class. */ for ( i = 1; i <= num; ++i ) if ( bck[i] == NIL ) { bck[i] = ++numcl; for ( j = fwd[i]; j != NIL; j = fwd[j] ) bck[j] = -numcl; } return ( numcl ); } /* ecs_from_xlation - associate equivalence class numbers using %t table * * synopsis * numecs = ecs_from_xlation( ecmap ); * * Upon return, ecmap will map each character code to its equivalence * class. The mapping will be positive if the character is the representative * of its class, negative otherwise. * * Returns the number of equivalence classes used. */ int ecs_from_xlation(int ecmap[]) { int i; int nul_is_alone = false; int did_default_xlation_class = false; if ( xlation[0] != 0 ) { /* if NUL shares its translation with other characters, choose one * of the other characters as the representative for the equivalence * class. This allows a cheap test later to see whether we can * do away with NUL's equivalence class. */ for ( i = 1; i < csize; ++i ) if ( xlation[i] == -xlation[0] ) { xlation[i] = xlation[0]; ecmap[0] = -xlation[0]; break; } if ( i >= csize ) /* didn't find a companion character--remember this fact */ nul_is_alone = true; } for ( i = 1; i < csize; ++i ) if ( xlation[i] == 0 ) { if ( did_default_xlation_class ) ecmap[i] = -num_xlations; else { /* make an equivalence class for those characters not * specified in the %t table */ ++num_xlations; ecmap[i] = num_xlations; did_default_xlation_class = true; } } else ecmap[i] = xlation[i]; if ( nul_is_alone ) /* force NUL's equivalence class to be the last one */ { ++num_xlations; ecmap[0] = num_xlations; /* there's actually a bug here: if someone is fanatic enough to * put every character in its own translation class, then right * now we just promoted NUL's equivalence class to be csize + 1; * we can handle NUL's class number being == csize (by instead * putting it in its own table), but we can't handle some *other* * character having to be put in its own table, too. So in * this case we bail out. */ if ( num_xlations > csize ) flexfatal( "too many %t classes!" ); } return num_xlations; } /* mkeccl - update equivalence classes based on character class xtions * * synopsis * Char ccls[]; * int lenccl, fwd[llsiz], bck[llsiz], llsiz, NUL_mapping; * mkeccl( ccls, lenccl, fwd, bck, llsiz, NUL_mapping ); * * where ccls contains the elements of the character class, lenccl is the * number of elements in the ccl, fwd is the forward link-list of equivalent * characters, bck is the backward link-list, and llsiz size of the link-list * * NUL_mapping is the value which NUL (0) should be mapped to. */ void mkeccl(unsigned char ccls[], int lenccl, int fwd[], int bck[], int llsiz, int NUL_mapping) { int cclp, oldec, newec; int cclm, i, j; static unsigned char cclflags[CSIZE]; /* initialized to all '\0' */ /* note that it doesn't matter whether or not the character class is * negated. The same results will be obtained in either case. */ cclp = 0; while ( cclp < lenccl ) { cclm = ccls[cclp]; if ( NUL_mapping && cclm == 0 ) cclm = NUL_mapping; oldec = bck[cclm]; newec = cclm; j = cclp + 1; for ( i = fwd[cclm]; i != NIL && i <= llsiz; i = fwd[i] ) { /* look for the symbol in the character class */ for ( ; j < lenccl; ++j ) { int ccl_char; if ( NUL_mapping && ccls[j] == 0 ) ccl_char = NUL_mapping; else ccl_char = ccls[j]; if ( ccl_char > i ) break; if ( ccl_char == i && ! cclflags[j] ) { /* we found an old companion of cclm in the ccl. * link it into the new equivalence class and flag it as * having been processed */ bck[i] = newec; fwd[newec] = i; newec = i; cclflags[j] = 1; /* set flag so we don't reprocess */ /* get next equivalence class member */ /* continue 2 */ goto next_pt; } } /* symbol isn't in character class. Put it in the old equivalence * class */ bck[i] = oldec; if ( oldec != NIL ) fwd[oldec] = i; oldec = i; next_pt: ; } if ( bck[cclm] != NIL || oldec != bck[cclm] ) { bck[cclm] = NIL; fwd[oldec] = NIL; } fwd[newec] = NIL; /* find next ccl member to process */ for ( ++cclp; cclflags[cclp] && cclp < lenccl; ++cclp ) { /* reset "doesn't need processing" flag */ cclflags[cclp] = 0; } } } /* mkechar - create equivalence class for single character * * synopsis * int tch, fwd[], bck[]; * mkechar( tch, fwd, bck ); */ void mkechar(int tch, int fwd[], int bck[]) { /* if until now the character has been a proper subset of * an equivalence class, break it away to create a new ec */ if ( fwd[tch] != NIL ) bck[fwd[tch]] = bck[tch]; if ( bck[tch] != NIL ) fwd[bck[tch]] = fwd[tch]; fwd[tch] = NIL; bck[tch] = NIL; } base-7.0.3.1/modules/libcom/src/flex/flex.c0000664000577000060420000004571413557101274017172 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* flex - tool to generate fast lexical analyzers */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #define ENQUOTE(path) #path #ifndef lint char copyright[] = "@(#) Copyright (c) 1990 The Regents of the University of California.\n\ All rights reserved.\n"; #endif /* not lint */ #define epicsExportSharedSymbols #include "epicsTempFile.h" #undef epicsExportSharedSymbols static char flex_version[] = "2.3"; /* declare functions that have forward references */ void flexinit (int, char**); void readin (void); void set_up_initial_allocations (void); /* these globals are all defined and commented in flexdef.h */ int printstats, syntaxerror, eofseen, ddebug, trace, spprdflt; int interactive, caseins, useecs, fulltbl, usemecs; int fullspd, gen_line_dirs, performance_report, backtrack_report, csize; int yymore_used, reject, real_reject, continued_action; int yymore_really_used, reject_really_used; int datapos, dataline, linenum; FILE *skelfile = NULL; char *infilename = NULL; int onestate[ONE_STACK_SIZE], onesym[ONE_STACK_SIZE]; int onenext[ONE_STACK_SIZE], onedef[ONE_STACK_SIZE], onesp; int current_mns, num_rules, current_max_rules, lastnfa; int *firstst, *lastst, *finalst, *transchar, *trans1, *trans2; int *accptnum, *assoc_rule, *state_type, *rule_type, *rule_linenum; int current_state_type; int variable_trailing_context_rules; int numtemps, numprots, protprev[MSP], protnext[MSP], prottbl[MSP]; int protcomst[MSP], firstprot, lastprot, protsave[PROT_SAVE_SIZE]; int numecs, nextecm[CSIZE + 1], ecgroup[CSIZE + 1], nummecs, tecfwd[CSIZE + 1]; int tecbck[CSIZE + 1]; int *xlation = (int *) 0; int num_xlations; int lastsc, current_max_scs, *scset, *scbol, *scxclu, *sceof, *actvsc; char **scname; int current_max_dfa_size, current_max_xpairs; int current_max_template_xpairs, current_max_dfas; int lastdfa, *nxt, *chk, *tnxt; int *base, *def, *nultrans, NUL_ec, tblend, firstfree, **dss, *dfasiz; union dfaacc_union *dfaacc; int *accsiz, *dhash, numas; int numsnpairs, jambase, jamstate; int lastccl, current_maxccls, *cclmap, *ccllen, *cclng, cclreuse; int current_max_ccl_tbl_size; Char *ccltbl; char *starttime, *endtime, nmstr[MAXLINE]; int sectnum, nummt, hshcol, dfaeql, numeps, eps2, num_reallocs; int tmpuses, totnst, peakpairs, numuniq, numdup, hshsave; int num_backtracking, bol_needed; FILE *temp_action_file; FILE *backtrack_file; int end_of_buffer_state; char **input_files; int num_input_files; char *program_name; #ifndef SHORT_FILE_NAMES static char *outfile = "lex.yy.c"; #else static char *outfile = "lexyy.c"; #endif static int outfile_created = 0; static int use_stdout; static char *skelname = NULL; int main(int argc, char *argv[]) { flexinit( argc, argv ); readin(); if ( syntaxerror ) flexend( 1 ); if ( yymore_really_used == REALLY_USED ) yymore_used = true; else if ( yymore_really_used == REALLY_NOT_USED ) yymore_used = false; if ( reject_really_used == REALLY_USED ) reject = true; else if ( reject_really_used == REALLY_NOT_USED ) reject = false; if ( performance_report ) { if ( interactive ) fprintf( stderr, "-I (interactive) entails a minor performance penalty\n" ); if ( yymore_used ) fprintf( stderr, "yymore() entails a minor performance penalty\n" ); if ( reject ) fprintf( stderr, "REJECT entails a large performance penalty\n" ); if ( variable_trailing_context_rules ) fprintf( stderr, "Variable trailing context rules entail a large performance penalty\n" ); } if ( reject ) real_reject = true; if ( variable_trailing_context_rules ) reject = true; if ( (fulltbl || fullspd) && reject ) { if ( real_reject ) flexerror( "REJECT cannot be used with -f or -F" ); else flexerror( "variable trailing context rules cannot be used with -f or -F" ); } ntod(); /* generate the C state transition tables from the DFA */ make_tables(); /* note, flexend does not return. It exits with its argument as status. */ flexend( 0 ); /*NOTREACHED*/ } /* flexend - terminate flex * * synopsis * int status; * flexend( status ); * * status is exit status. * * note * This routine does not return. */ void flexend(int status) { int tblsiz; char *flex_gettime(); if ( skelfile != NULL ) { if ( ferror( skelfile ) ) flexfatal( "error occurred when writing skeleton file" ); else if ( fclose( skelfile ) ) flexfatal( "error occurred when closing skeleton file" ); } if ( temp_action_file ) { if ( ferror( temp_action_file ) ) flexfatal( "error occurred when writing temporary action file" ); else if ( fclose( temp_action_file ) ) flexfatal( "error occurred when closing temporary action file" ); } if ( status != 0 && outfile_created ) { if ( ferror( stdout ) ) flexfatal( "error occurred when writing output file" ); else if ( fclose( stdout ) ) flexfatal( "error occurred when closing output file" ); else if ( unlink( outfile ) ) flexfatal( "error occurred when deleting output file" ); } if ( backtrack_report && backtrack_file ) { if ( num_backtracking == 0 ) fprintf( backtrack_file, "No backtracking.\n" ); else if ( fullspd || fulltbl ) fprintf( backtrack_file, "%d backtracking (non-accepting) states.\n", num_backtracking ); else fprintf( backtrack_file, "Compressed tables always backtrack.\n" ); if ( ferror( backtrack_file ) ) flexfatal( "error occurred when writing backtracking file" ); else if ( fclose( backtrack_file ) ) flexfatal( "error occurred when closing backtracking file" ); } if ( printstats ) { endtime = flex_gettime(); fprintf( stderr, "%s version %s usage statistics:\n", program_name, flex_version ); fprintf( stderr, " started at %s, finished at %s\n", starttime, endtime ); fprintf( stderr, " scanner options: -" ); if ( backtrack_report ) putc( 'b', stderr ); if ( ddebug ) putc( 'd', stderr ); if ( interactive ) putc( 'I', stderr ); if ( caseins ) putc( 'i', stderr ); if ( ! gen_line_dirs ) putc( 'L', stderr ); if ( performance_report ) putc( 'p', stderr ); if ( spprdflt ) putc( 's', stderr ); if ( use_stdout ) putc( 't', stderr ); if ( trace ) putc( 'T', stderr ); if ( printstats ) putc( 'v', stderr ); /* always true! */ if ( csize == 256 ) putc( '8', stderr ); fprintf( stderr, " -C" ); if ( fulltbl ) putc( 'f', stderr ); if ( fullspd ) putc( 'F', stderr ); if ( useecs ) putc( 'e', stderr ); if ( usemecs ) putc( 'm', stderr ); if ( strcmp( skelname, ENQUOTE(DEFAULT_SKELETON_FILE) ) ) fprintf( stderr, " -S%s", skelname ); putc( '\n', stderr ); fprintf( stderr, " %d/%d NFA states\n", lastnfa, current_mns ); fprintf( stderr, " %d/%d DFA states (%d words)\n", lastdfa, current_max_dfas, totnst ); fprintf( stderr, " %d rules\n", num_rules - 1 /* - 1 for def. rule */ ); if ( num_backtracking == 0 ) fprintf( stderr, " No backtracking\n" ); else if ( fullspd || fulltbl ) fprintf( stderr, " %d backtracking (non-accepting) states\n", num_backtracking ); else fprintf( stderr, " compressed tables always backtrack\n" ); if ( bol_needed ) fprintf( stderr, " Beginning-of-line patterns used\n" ); fprintf( stderr, " %d/%d start conditions\n", lastsc, current_max_scs ); fprintf( stderr, " %d epsilon states, %d double epsilon states\n", numeps, eps2 ); if ( lastccl == 0 ) fprintf( stderr, " no character classes\n" ); else fprintf( stderr, " %d/%d character classes needed %d/%d words of storage, %d reused\n", lastccl, current_maxccls, cclmap[lastccl] + ccllen[lastccl], current_max_ccl_tbl_size, cclreuse ); fprintf( stderr, " %d state/nextstate pairs created\n", numsnpairs ); fprintf( stderr, " %d/%d unique/duplicate transitions\n", numuniq, numdup ); if ( fulltbl ) { tblsiz = lastdfa * numecs; fprintf( stderr, " %d table entries\n", tblsiz ); } else { tblsiz = 2 * (lastdfa + numtemps) + 2 * tblend; fprintf( stderr, " %d/%d base-def entries created\n", lastdfa + numtemps, current_max_dfas ); fprintf( stderr, " %d/%d (peak %d) nxt-chk entries created\n", tblend, current_max_xpairs, peakpairs ); fprintf( stderr, " %d/%d (peak %d) template nxt-chk entries created\n", numtemps * nummecs, current_max_template_xpairs, numtemps * numecs ); fprintf( stderr, " %d empty table entries\n", nummt ); fprintf( stderr, " %d protos created\n", numprots ); fprintf( stderr, " %d templates created, %d uses\n", numtemps, tmpuses ); } if ( useecs ) { tblsiz = tblsiz + csize; fprintf( stderr, " %d/%d equivalence classes created\n", numecs, csize ); } if ( usemecs ) { tblsiz = tblsiz + numecs; fprintf( stderr, " %d/%d meta-equivalence classes created\n", nummecs, csize ); } fprintf( stderr, " %d (%d saved) hash collisions, %d DFAs equal\n", hshcol, hshsave, dfaeql ); fprintf( stderr, " %d sets of reallocations needed\n", num_reallocs ); fprintf( stderr, " %d total table entries needed\n", tblsiz ); } exit( status ); } /* flexinit - initialize flex * * synopsis * int argc; * char **argv; * flexinit( argc, argv ); */ void flexinit(int argc, char **argv) { int i, sawcmpflag; char *arg, *flex_gettime(), *mktemp(); printstats = syntaxerror = trace = spprdflt = interactive = caseins = false; backtrack_report = performance_report = ddebug = fulltbl = fullspd = false; yymore_used = continued_action = reject = false; yymore_really_used = reject_really_used = false; gen_line_dirs = usemecs = useecs = true; sawcmpflag = false; use_stdout = false; csize = DEFAULT_CSIZE; program_name = argv[0]; /* read flags */ for ( --argc, ++argv; argc ; --argc, ++argv ) { if ( argv[0][0] != '-' || argv[0][1] == '\0' ) break; arg = argv[0]; for ( i = 1; arg[i] != '\0'; ++i ) switch ( arg[i] ) { case 'b': backtrack_report = true; break; case 'c': fprintf( stderr, "%s: Assuming use of deprecated -c flag is really intended to be -C\n", program_name ); /* fall through */ case 'C': if ( i != 1 ) flexerror( "-C flag must be given separately" ); if ( ! sawcmpflag ) { useecs = false; usemecs = false; fulltbl = false; sawcmpflag = true; } for ( ++i; arg[i] != '\0'; ++i ) switch ( arg[i] ) { case 'e': useecs = true; break; case 'F': fullspd = true; break; case 'f': fulltbl = true; break; case 'm': usemecs = true; break; default: lerrif( "unknown -C option '%c'", (int) arg[i] ); break; } goto get_next_arg; case 'd': ddebug = true; break; case 'f': useecs = usemecs = false; fulltbl = true; break; case 'F': useecs = usemecs = false; fullspd = true; break; case 'I': interactive = true; break; case 'i': caseins = true; break; case 'L': gen_line_dirs = false; break; case 'n': /* stupid do-nothing deprecated option */ break; case 'o': if ( i != 1 ) flexerror( "-o flag must be given separately" ); outfile = arg + i + 1; goto get_next_arg; case 'p': performance_report = true; break; case 'S': if ( i != 1 ) flexerror( "-S flag must be given separately" ); skelname = arg + i + 1; goto get_next_arg; case 's': spprdflt = true; break; case 't': use_stdout = true; break; case 'T': trace = true; break; case 'v': printstats = true; break; case '8': csize = CSIZE; break; default: lerrif( "unknown flag '%c'", (int) arg[i] ); break; } get_next_arg: /* used by -C and -S flags in lieu of a "continue 2" control */ ; } if ( (fulltbl || fullspd) && usemecs ) flexerror( "full table and -Cm don't make sense together" ); if ( (fulltbl || fullspd) && interactive ) flexerror( "full table and -I are (currently) incompatible" ); if ( fulltbl && fullspd ) flexerror( "full table and -F are mutually exclusive" ); if ( ! skelname ) { static char skeleton_name_storage[400]; skelname = skeleton_name_storage; (void) strcpy( skelname, ENQUOTE(DEFAULT_SKELETON_FILE) ); } if ( ! use_stdout ) { FILE *prev_stdout = freopen( outfile, "w", stdout ); if ( prev_stdout == NULL ) lerrsf( "could not create %s", outfile ); outfile_created = 1; } num_input_files = argc; input_files = argv; set_input_file( num_input_files > 0 ? input_files[0] : NULL ); if ( backtrack_report ) { #ifndef SHORT_FILE_NAMES backtrack_file = fopen( "lex.backtrack", "w" ); #else backtrack_file = fopen( "lex.bck", "w" ); #endif if ( backtrack_file == NULL ) flexerror( "could not create lex.backtrack" ); } else backtrack_file = NULL; lastccl = 0; lastsc = 0; /* initialize the statistics */ starttime = flex_gettime(); if ( (skelfile = fopen( skelname, "r" )) == NULL ) lerrsf( "can't open skeleton file %s", skelname ); if ( ( temp_action_file = epicsTempFile () ) == NULL ) { lerrsf( "can't create temporary action file", "" ); } lastdfa = lastnfa = num_rules = numas = numsnpairs = tmpuses = 0; numecs = numeps = eps2 = num_reallocs = hshcol = dfaeql = totnst = 0; numuniq = numdup = hshsave = eofseen = datapos = dataline = 0; num_backtracking = onesp = numprots = 0; variable_trailing_context_rules = bol_needed = false; linenum = sectnum = 1; firstprot = NIL; /* used in mkprot() so that the first proto goes in slot 1 * of the proto queue */ lastprot = 1; if ( useecs ) { /* set up doubly-linked equivalence classes */ /* We loop all the way up to csize, since ecgroup[csize] is the * position used for NUL characters */ ecgroup[1] = NIL; for ( i = 2; i <= csize; ++i ) { ecgroup[i] = i - 1; nextecm[i - 1] = i; } nextecm[csize] = NIL; } else { /* put everything in its own equivalence class */ for ( i = 1; i <= csize; ++i ) { ecgroup[i] = i; nextecm[i] = BAD_SUBSCRIPT; /* to catch errors */ } } set_up_initial_allocations(); } /* readin - read in the rules section of the input file(s) * * synopsis * readin(); */ void readin(void) { skelout(); if ( ddebug ) puts( "#define FLEX_DEBUG" ); if ( csize == 256 ) puts( "#define YY_CHAR unsigned char" ); else puts( "#define YY_CHAR char" ); line_directive_out( stdout ); if ( yyparse() ) { pinpoint_message( "fatal parse error" ); flexend( 1 ); } if ( xlation ) { numecs = ecs_from_xlation( ecgroup ); useecs = true; } else if ( useecs ) numecs = cre8ecs( nextecm, ecgroup, csize ); else numecs = csize; /* now map the equivalence class for NUL to its expected place */ ecgroup[0] = ecgroup[csize]; NUL_ec = abs( ecgroup[0] ); if ( useecs ) ccl2ecl(); } /* set_up_initial_allocations - allocate memory for internal tables */ void set_up_initial_allocations(void) { current_mns = INITIAL_MNS; firstst = allocate_integer_array( current_mns ); lastst = allocate_integer_array( current_mns ); finalst = allocate_integer_array( current_mns ); transchar = allocate_integer_array( current_mns ); trans1 = allocate_integer_array( current_mns ); trans2 = allocate_integer_array( current_mns ); accptnum = allocate_integer_array( current_mns ); assoc_rule = allocate_integer_array( current_mns ); state_type = allocate_integer_array( current_mns ); current_max_rules = INITIAL_MAX_RULES; rule_type = allocate_integer_array( current_max_rules ); rule_linenum = allocate_integer_array( current_max_rules ); current_max_scs = INITIAL_MAX_SCS; scset = allocate_integer_array( current_max_scs ); scbol = allocate_integer_array( current_max_scs ); scxclu = allocate_integer_array( current_max_scs ); sceof = allocate_integer_array( current_max_scs ); scname = allocate_char_ptr_array( current_max_scs ); actvsc = allocate_integer_array( current_max_scs ); current_maxccls = INITIAL_MAX_CCLS; cclmap = allocate_integer_array( current_maxccls ); ccllen = allocate_integer_array( current_maxccls ); cclng = allocate_integer_array( current_maxccls ); current_max_ccl_tbl_size = INITIAL_MAX_CCL_TBL_SIZE; ccltbl = allocate_character_array( current_max_ccl_tbl_size ); current_max_dfa_size = INITIAL_MAX_DFA_SIZE; current_max_xpairs = INITIAL_MAX_XPAIRS; nxt = allocate_integer_array( current_max_xpairs ); chk = allocate_integer_array( current_max_xpairs ); current_max_template_xpairs = INITIAL_MAX_TEMPLATE_XPAIRS; tnxt = allocate_integer_array( current_max_template_xpairs ); current_max_dfas = INITIAL_MAX_DFAS; base = allocate_integer_array( current_max_dfas ); def = allocate_integer_array( current_max_dfas ); dfasiz = allocate_integer_array( current_max_dfas ); accsiz = allocate_integer_array( current_max_dfas ); dhash = allocate_integer_array( current_max_dfas ); dss = allocate_int_ptr_array( current_max_dfas ); dfaacc = allocate_dfaacc_union( current_max_dfas ); nultrans = (int *) 0; } base-7.0.3.1/modules/libcom/src/flex/flex.html0000664000577000060420000006157013557101274017712 0ustar anjaesctl


NAME

     flex - fast lexical analyzer generator


SYNOPSIS

     flex [-bcdfinpstvFILT8 -C[efmF] -Sskeleton] [filename ...]


DESCRIPTION

     flex is a  tool  for  generating  scanners:  programs  which
     recognized  lexical  patterns in text.  flex reads the given
     input files, or its standard input  if  no  file  names  are
     given,  for  a  description  of  a scanner to generate.  The
     description is in the form of pairs of  regular  expressions
     and  C  code,  called  rules.  flex  generates as output a C
     source file, lex.yy.c, which defines a routine yylex(). This
     file is compiled and linked with the -lfl library to produce
     an executable.  When the executable is run, it analyzes  its
     input  for occurrences of the regular expressions.  Whenever
     it finds one, it executes the corresponding C code.

     For full documentation, see flexdoc(1). This manual entry is
     intended for use as a quick reference.


OPTIONS

     flex has the following options:

     -b   Generate  backtracking  information  to  lex.backtrack.
          This  is  a  list of scanner states which require back-
          tracking and the input characters on which they do  so.
          By adding rules one can remove backtracking states.  If
          all backtracking states are eliminated and -f or -F  is
          used, the generated scanner will run faster.

     -c   is a do-nothing, deprecated option included  for  POSIX
          compliance.

          NOTE: in previous releases of flex -c specified  table-
          compression  options.   This functionality is now given
          by the -C flag.  To ease the the impact of this change,
          when  flex encounters -c, it currently issues a warning
          message and assumes that -C was  desired  instead.   In
          the future this "promotion" of -c to -C will go away in
          the name of full POSIX  compliance  (unless  the  POSIX
          meaning is removed first).

     -d   makes the generated scanner run in debug  mode.   When-
          ever   a   pattern   is   recognized   and  the  global
          yy_flex_debug is non-zero (which is the  default),  the
          scanner will write to stderr a line of the form:

              --accepting rule at line 53 ("the matched text")

          The line number refers to the location of the  rule  in
          the  file defining the scanner (i.e., the file that was
          fed to flex).  Messages are  also  generated  when  the
          scanner  backtracks,  accepts the default rule, reaches
          the end of its input buffer (or encounters a  NUL;  the
          two  look  the same as far as the scanner's concerned),
          or reaches an end-of-file.

     -f   specifies (take your pick) full table or fast  scanner.
          No  table compression is done.  The result is large but
          fast.  This option is equivalent to -Cf (see below).

     -i   instructs flex to generate a case-insensitive  scanner.
          The  case  of  letters given in the flex input patterns
          will be ignored,  and  tokens  in  the  input  will  be
          matched  regardless of case.  The matched text given in
          yytext will have the preserved case (i.e., it will  not
          be folded).

     -n   is another do-nothing, deprecated option included  only
          for POSIX compliance.

     -p   generates a performance report to stderr.   The  report
          consists  of  comments  regarding  features of the flex
          input file which will cause a loss  of  performance  in
          the resulting scanner.

     -s   causes the default rule (that unmatched  scanner  input
          is  echoed to stdout) to be suppressed.  If the scanner
          encounters input that does not match any of its  rules,
          it aborts with an error.

     -t   instructs flex to write the  scanner  it  generates  to
          standard output instead of lex.yy.c.

     -v   specifies that flex should write to stderr a summary of
          statistics regarding the scanner it generates.

     -F   specifies that the fast  scanner  table  representation
          should  be  used.  This representation is about as fast
          as the full table representation  (-f),  and  for  some
          sets  of patterns will be considerably smaller (and for
          others, larger).  See flexdoc(1) for details.

          This option is equivalent to -CF (see below).

     -I   instructs flex to generate an interactive scanner, that
          is, a scanner which stops immediately rather than look-
          ing ahead if it knows that the currently  scanned  text
          cannot  be  part  of a longer rule's match.  Again, see
          flexdoc(1) for details.

          Note, -I cannot be used in  conjunction  with  full  or
          fast tables, i.e., the -f, -F, -Cf, or -CF flags.

     -L   instructs flex not  to  generate  #line  directives  in
          lex.yy.c. The default is to generate such directives so
          error messages in the actions will be correctly located
          with  respect  to the original flex input file, and not
          to the fairly meaningless line numbers of lex.yy.c.

     -T   makes flex run in trace mode.  It will generate  a  lot
          of  messages to stdout concerning the form of the input
          and the resultant non-deterministic  and  deterministic
          finite  automata.   This  option  is  mostly for use in
          maintaining flex.

     -8   instructs flex to generate an 8-bit scanner.   On  some
          sites,  this is the default.  On others, the default is
          7-bit characters.  To see which is the case, check  the
          verbose  (-v) output for "equivalence classes created".
          If the denominator of the number shown is 128, then  by
          default  flex is generating 7-bit characters.  If it is
          256, then the default is 8-bit characters.

     -C[efmF]
          controls the degree of table compression.

          -Ce directs  flex  to  construct  equivalence  classes,
          i.e.,  sets  of characters which have identical lexical
          properties.  Equivalence classes usually give  dramatic
          reductions  in the final table/object file sizes (typi-
          cally  a  factor  of  2-5)   and   are   pretty   cheap
          performance-wise   (one  array  look-up  per  character
          scanned).

          -Cf specifies that the full scanner  tables  should  be
          generated - flex should not compress the tables by tak-
          ing advantages of similar transition functions for dif-
          ferent states.

          -CF specifies that the alternate fast scanner represen-
          tation (described in flexdoc(1)) should be used.

          -Cm directs flex to construct meta-equivalence classes,
          which  are  sets of equivalence classes (or characters,
          if equivalence classes are not  being  used)  that  are
          commonly  used  together.  Meta-equivalence classes are
          often a big win when using compressed tables, but  they
          have  a  moderate  performance  impact (one or two "if"
          tests and one array look-up per character scanned).

          A lone -C specifies that the scanner tables  should  be
          compressed  but  neither  equivalence classes nor meta-
          equivalence classes should be used.
          The options -Cf or  -CF  and  -Cm  do  not  make  sense
          together - there is no opportunity for meta-equivalence
          classes if the table is not being  compressed.   Other-
          wise the options may be freely mixed.

          The default setting is -Cem, which specifies that  flex
          should   generate   equivalence   classes   and   meta-
          equivalence classes.  This setting provides the highest
          degree   of  table  compression.   You  can  trade  off
          faster-executing scanners at the cost of larger  tables
          with the following generally being true:

              slowest & smallest
                    -Cem
                    -Cm
                    -Ce
                    -C
                    -C{f,F}e
                    -C{f,F}
              fastest & largest


          -C options are not cumulative;  whenever  the  flag  is
          encountered, the previous -C settings are forgotten.

     -Sskeleton_file
          overrides the default skeleton  file  from  which  flex
          constructs its scanners.  You'll never need this option
          unless you are doing flex maintenance or development.


SUMMARY OF FLEX REGULAR EXPRESSIONS

     The patterns in the input are written using an extended  set
     of regular expressions.  These are:

         x          match the character 'x'
         .          any character except newline
         [xyz]      a "character class"; in this case, the pattern
                      matches either an 'x', a 'y', or a 'z'
         [abj-oZ]   a "character class" with a range in it; matches
                      an 'a', a 'b', any letter from 'j' through 'o',
                      or a 'Z'
         [^A-Z]     a "negated character class", i.e., any character
                      but those in the class.  In this case, any
                      character EXCEPT an uppercase letter.
         [^A-Z\n]   any character EXCEPT an uppercase letter or
                      a newline
         r*         zero or more r's, where r is any regular expression
         r+         one or more r's
         r?         zero or one r's (that is, "an optional r")
         r{2,5}     anywhere from two to five r's
         r{2,}      two or more r's
         r{4}       exactly 4 r's
         {name}     the expansion of the "name" definition
                    (see above)
         "[xyz]\"foo"
                    the literal string: [xyz]"foo
         \X         if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v',
                      then the ANSI-C interpretation of \x.
                      Otherwise, a literal 'X' (used to escape
                      operators such as '*')
         \123       the character with octal value 123
         \x2a       the character with hexadecimal value 2a
         (r)        match an r; parentheses are used to override
                      precedence (see below)


         rs         the regular expression r followed by the
                      regular expression s; called "concatenation"


         r|s        either an r or an s


         r/s        an r but only if it is followed by an s.  The
                      s is not part of the matched text.  This type
                      of pattern is called as "trailing context".
         ^r         an r, but only at the beginning of a line
         r$         an r, but only at the end of a line.  Equivalent
                      to "r/\n".


         <s>r       an r, but only in start condition s (see
                    below for discussion of start conditions)
         <s1,s2,s3>r
                    same, but in any of start conditions s1,
                    s2, or s3


         <<EOF>>    an end-of-file
         <s1,s2><<EOF>>
                    an end-of-file when in start condition s1 or s2

     The regular expressions listed above are  grouped  according
     to  precedence, from highest precedence at the top to lowest
     at the bottom.   Those  grouped  together  have  equal  pre-
     cedence.

     Some notes on patterns:

     -    Negated character classes match  newlines  unless  "\n"
          (or  an equivalent escape sequence) is one of the char-
          acters explicitly  present  in  the  negated  character
          class (e.g., "[^A-Z\n]").

     -    A rule can have at most one instance of  trailing  con-
          text (the '/' operator or the '$' operator).  The start
          condition, '^', and "<<EOF>>" patterns can  only  occur
          at the beginning of a pattern, and, as well as with '/'
          and '$', cannot be  grouped  inside  parentheses.   The
          following are all illegal:

              foo/bar$
              foo|(bar$)
              foo|^bar
              <sc1>foo<sc2>bar



SUMMARY OF SPECIAL ACTIONS

     In addition to arbitrary C code, the following can appear in
     actions:

     -    ECHO copies yytext to the scanner's output.

     -    BEGIN followed by the name of a start condition  places
          the scanner in the corresponding start condition.

     -    REJECT directs the scanner to proceed on to the "second
          best"  rule which matched the input (or a prefix of the
          input).  yytext and yyleng are  set  up  appropriately.
          Note that REJECT is a particularly expensive feature in
          terms scanner performance; if it is used in any of  the
          scanner's   actions  it  will  slow  down  all  of  the
          scanner's matching.  Furthermore, REJECT cannot be used
          with the -f or -F options.

          Note also that unlike the other special actions, REJECT
          is  a  branch;  code  immediately  following  it in the
          action will not be executed.

     -    yymore() tells  the  scanner  that  the  next  time  it
          matches  a  rule,  the  corresponding  token  should be
          appended onto the current value of yytext  rather  than
          replacing it.

     -    yyless(n) returns all but the first n characters of the
          current token back to the input stream, where they will
          be rescanned when the scanner looks for the next match.
          yytext  and  yyleng  are  adjusted appropriately (e.g.,
          yyleng will now be equal to n ).

     -    unput(c) puts the  character  c  back  onto  the  input
          stream.  It will be the next character scanned.

     -    input() reads the next character from the input  stream
          (this  routine  is  called  yyinput() if the scanner is
          compiled using C++).

     -    yyterminate() can be used in lieu of a return statement
          in  an action.  It terminates the scanner and returns a
          0 to the scanner's caller, indicating "all done".

          By default, yyterminate() is also called when  an  end-
          of-file is encountered.  It is a macro and may be rede-
          fined.

     -    YY_NEW_FILE is an  action  available  only  in  <<EOF>>
          rules.   It  means "Okay, I've set up a new input file,
          continue scanning".

     -    yy_create_buffer( file, size ) takes a FILE pointer and
          an integer size. It returns a YY_BUFFER_STATE handle to
          a new input buffer  large  enough  to  accomodate  size
          characters and associated with the given file.  When in
          doubt, use YY_BUF_SIZE for the size.

     -    yy_switch_to_buffer(   new_buffer   )   switches    the
          scanner's  processing to scan for tokens from the given
          buffer, which must be a YY_BUFFER_STATE.

     -    yy_delete_buffer( buffer ) deletes the given buffer.


VALUES AVAILABLE TO THE USER

     -    char *yytext holds the text of the current  token.   It
          may not be modified.

     -    int yyleng holds the length of the current  token.   It
          may not be modified.

     -    FILE *yyin is the file  which  by  default  flex  reads
          from.   It  may  be  redefined  but doing so only makes
          sense before scanning begins.  Changing it in the  mid-
          dle of scanning will have unexpected results since flex
          buffers its input.  Once scanning terminates because an
          end-of-file   has   been  seen,  void  yyrestart(  FILE
          *new_file ) may be called to  point  yyin  at  the  new
          input file.

     -    FILE *yyout is the file to which ECHO actions are done.
          It can be reassigned by the user.

     -    YY_CURRENT_BUFFER returns a YY_BUFFER_STATE  handle  to
          the current buffer.


MACROS THE USER CAN REDEFINE

     -    YY_DECL controls how the scanning routine is  declared.
          By  default, it is "int yylex()", or, if prototypes are
          being used, "int yylex(void)".  This definition may  be
          changed  by  redefining the "YY_DECL" macro.  Note that
          if you give arguments to the scanning routine  using  a
          K&R-style/non-prototyped function declaration, you must
          terminate the definition with a semi-colon (;).

     -    The nature of how the scanner gets  its  input  can  be
          controlled    by   redefining   the   YY_INPUT   macro.
          YY_INPUT's         calling         sequence          is
          "YY_INPUT(buf,result,max_size)".    Its  action  is  to
          place up to max_size characters in the character  array
          buf  and  return  in the integer variable result either
          the number of characters read or the  constant  YY_NULL
          (0  on  Unix  systems)  to  indicate  EOF.  The default
          YY_INPUT reads from the global file-pointer "yyin".   A
          sample  redefinition  of  YY_INPUT  (in the definitions
          section of the input file):

              %{
              #undef YY_INPUT
              #define YY_INPUT(buf,result,max_size) \
                  { \
                  int c = getchar(); \
                  result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \
                  }
              %}


     -    When the scanner  receives  an  end-of-file  indication
          from  YY_INPUT,  it  then checks the yywrap() function.
          If yywrap() returns false (zero), then  it  is  assumed
          that  the  function  has  gone ahead and set up yyin to
          point to another input file,  and  scanning  continues.
          If  it  returns  true (non-zero), then the scanner ter-
          minates, returning 0 to its caller.

          The default yywrap() always returns 1.   Presently,  to
          redefine  it  you  must first "#undef yywrap", as it is
          currently implemented as a macro.  It  is  likely  that
          yywrap()  will  soon be defined to be a function rather
          than a macro.

     -    YY_USER_ACTION can be redefined to  provide  an  action
          which  is  always  executed prior to the matched rule's
          action.

     -    The macro YY_USER_INIT may be redefined to  provide  an
          action which is always executed before the first scan.

     -    In the generated scanner, the actions are all  gathered
          in  one  large  switch  statement  and  separated using
          YY_BREAK, which may be redefined.  By  default,  it  is
          simply  a  "break", to separate each rule's action from
          the following rule's.


FILES

     flex.skel
          skeleton scanner.

     lex.yy.c
          generated scanner (called lexyy.c on some systems).

     lex.backtrack
          backtracking information for -b flag (called lex.bck on
          some systems).

     -lfl library with which to link the scanners.


SEE ALSO

     flexdoc(1), lex(1), yacc(1), sed(1), awk(1).

     M. E. Lesk and E. Schmidt, LEX - Lexical Analyzer Generator


DIAGNOSTICS

     reject_used_but_not_detected undefined or

     yymore_used_but_not_detected undefined -  These  errors  can
     occur  at compile time.  They indicate that the scanner uses
     REJECT or yymore() but that flex failed to notice the  fact,
     meaning that flex scanned the first two sections looking for
     occurrences of these actions and failed  to  find  any,  but
     somehow  you  snuck  some in (via a #include file, for exam-
     ple).  Make an explicit reference to the action in your flex
     input   file.    (Note  that  previously  flex  supported  a
     %used/%unused mechanism for dealing with this problem;  this
     feature  is  still supported but now deprecated, and will go
     away soon unless the author hears from people who can  argue
     compellingly that they need it.)

     flex scanner jammed - a scanner compiled with -s has encoun-
     tered  an  input  string  which wasn't matched by any of its
     rules.

     flex input buffer overflowed -  a  scanner  rule  matched  a
     string  long enough to overflow the scanner's internal input
     buffer  (16K   bytes   -   controlled   by   YY_BUF_MAX   in
     "flex.skel").

     scanner  requires  -8  flag  -  Your  scanner  specification
     includes  recognizing  8-bit  characters  and  you  did  not
     specify the -8 flag (and your site has  not  installed  flex
     with -8 as the default).

     fatal flex scanner internal error--end of  buffer  missed  -
     This  can  occur  in  an  scanner which is reentered after a
     long-jump has jumped out (or over) the scanner's  activation
     frame.  Before reentering the scanner, use:
         yyrestart( yyin );


     too many %t classes! - You managed to put every single char-
     acter  into  its  own %t class.  flex requires that at least
     one of the classes share characters.


AUTHOR

     Vern Paxson, with the help of many ideas and  much  inspira-
     tion from Van Jacobson.  Original version by Jef Poskanzer.

     See flexdoc(1) for additional credits  and  the  address  to
     send comments to.


DEFICIENCIES / BUGS

     Some trailing context patterns cannot  be  properly  matched
     and  generate  warning  messages  ("Dangerous  trailing con-
     text").  These are patterns where the ending  of  the  first
     part  of  the rule matches the beginning of the second part,
     such as "zx*/xy*", where the 'x*' matches  the  'x'  at  the
     beginning  of  the  trailing  context.  (Note that the POSIX
     draft states that the text matched by such patterns is unde-
     fined.)

     For some trailing context rules, parts  which  are  actually
     fixed-length  are  not  recognized  as  such, leading to the
     abovementioned performance loss.  In particular, parts using
     '|'   or  {n}  (such  as  "foo{3}")  are  always  considered
     variable-length.

     Combining trailing context with the special '|'  action  can
     result  in fixed trailing context being turned into the more
     expensive variable trailing context.  For example, this hap-
     pens in the following example:

         %%
         abc      |
         xyz/def


     Use of unput() invalidates yytext and yyleng.

     Use of unput() to push back more text than was  matched  can
     result  in the pushed-back text matching a beginning-of-line
     ('^') rule even though it didn't come at  the  beginning  of
     the line (though this is rare!).

     Pattern-matching  of  NUL's  is  substantially  slower  than
     matching other characters.

     flex does not generate correct  #line  directives  for  code
     internal to the scanner; thus, bugs in flex.skel yield bogus
     line numbers.

     Due to both buffering of input and  read-ahead,  you  cannot
     intermix  calls to <stdio.h> routines, such as, for example,
     getchar(), with flex rules and  expect  it  to  work.   Call
     input() instead.

     The total table entries listed by the -v flag  excludes  the
     number  of  table  entries needed to determine what rule has
     been matched.  The number of entries is equal to the  number
     of  DFA states if the scanner does not use REJECT, and some-
     what greater than the number of states if it does.

     REJECT cannot be used with the -f or -F options.

     Some of the macros, such as  yywrap(),  may  in  the  future
     become  functions which live in the -lfl library.  This will
     doubtless break a lot of  code,  but  may  be  required  for
     POSIX-compliance.

     The flex internal algorithms need documentation.
































Man(1) output converted with man2html
base-7.0.3.1/modules/libcom/src/flex/flex.skel0000664000577000060420000004336413557101274017705 0ustar anjaesctl/* A lexical scanner generated by flex */ /* scanner skeleton */ #define FLEX_SCANNER #include /* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ #ifdef c_plusplus #ifndef __cplusplus #define __cplusplus #endif #endif #ifdef __cplusplus #include #include #else /* ! __cplusplus */ #ifdef __GNUC__ #include void *malloc( size_t ); void free( void* ); #else #include #endif /* __GNUC__ */ #endif /* ! __cplusplus */ /* amount of stuff to slurp up with each read */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* returned upon end-of-file */ #define YY_END_TOK 0 /* copy whatever the last rule matched to the standard output */ /* cast to (char *) is because for 8-bit chars, yytext is (unsigned char *) */ /* this used to be an fputs(), but since the string might contain NUL's, * we now use fwrite() */ #define ECHO (void) fwrite( (char *) yytext, yyleng, 1, yyout ) /* gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #define YY_INPUT(buf,result,max_size) \ if ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ YY_FATAL_ERROR( "read() in flex scanner failed" ); #define YY_NULL 0 /* no semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #define yyterminate() return ( YY_NULL ) /* report a fatal error */ /* The funky do-while is used to turn this macro definition into * a single C statement (which needs a semi-colon terminator). * This avoids problems with code like: * * if ( something_happens ) * YY_FATAL_ERROR( "oops, the something happened" ); * else * everything_okay(); * * Prior to using the do-while the compiler would get upset at the * "else" because it interpreted the "if" statement as being all * done when it reached the ';' after the YY_FATAL_ERROR() call. */ #define YY_FATAL_ERROR(msg) \ do \ { \ (void) fputs( msg, stderr ); \ (void) putc( '\n', stderr ); \ exit( 1 ); \ } \ while ( 0 ) /* default yywrap function - always treat EOF as an EOF */ #define yywrap() 1 /* enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN */ #define BEGIN yy_start = 1 + 2 * /* action number for EOF rule of a given start state */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* special action meaning "start processing a new file" */ #define YY_NEW_FILE \ do \ { \ yy_init_buffer( yy_current_buffer, yyin ); \ yy_load_buffer_state(); \ } \ while ( 0 ) /* default declaration of generated scanner - a define so the user can * easily add parameters */ #define YY_DECL int yylex ( void ) /* code executed at the end of each rule */ #define YY_BREAK break; #define YY_END_OF_BUFFER_CHAR 0 #ifndef YY_BUF_SIZE #define YY_BUF_SIZE (YY_READ_BUF_SIZE * 2) /* size of default input buffer */ #endif typedef struct yy_buffer_state *YY_BUFFER_STATE; %% section 1 definitions go here /* done after the current pattern has been matched and before the * corresponding action - sets up yytext */ #define YY_DO_BEFORE_ACTION \ yytext = yy_bp; \ %% code to fiddle yytext and yyleng for yymore() goes here yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yy_c_buf_p = yy_cp; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 /* return all but the first 'n' matched characters back to the input stream */ #define yyless(n) \ do \ { \ /* undo effects of setting up yytext */ \ *yy_cp = yy_hold_char; \ yy_c_buf_p = yy_cp = yy_bp + n; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yytext ) struct yy_buffer_state { FILE *yy_input_file; YY_CHAR *yy_ch_buf; /* input buffer */ YY_CHAR *yy_buf_pos; /* current position in input buffer */ /* size of input buffer in bytes, not including room for EOB characters*/ int yy_buf_size; /* number of characters read into yy_ch_buf, not including EOB characters */ int yy_n_chars; int yy_eof_status; /* whether we've seen an EOF on this buffer */ #define EOF_NOT_SEEN 0 /* "pending" happens when the EOF has been seen but there's still * some text process */ #define EOF_PENDING 1 #define EOF_DONE 2 }; static YY_BUFFER_STATE yy_current_buffer; /* we provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state" */ #define YY_CURRENT_BUFFER yy_current_buffer /* yy_hold_char holds the character lost when yytext is formed */ static YY_CHAR yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif #ifndef YY_USER_INIT #define YY_USER_INIT #endif extern YY_CHAR *yytext; extern int yyleng; extern FILE *yyin, *yyout; YY_CHAR *yytext; int yyleng; FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; %% data tables for the DFA go here /* these variables are all declared out here so that section 3 code can * manipulate them */ /* points to current character in buffer */ static YY_CHAR *yy_c_buf_p = (YY_CHAR *) 0; static int yy_init = 1; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* flag which is used to allow yywrap()'s to do buffer switches * instead of setting up a fresh yyin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; static yy_state_type yy_get_previous_state ( void ); static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); static int yy_get_next_buffer ( void ); static void yyunput ( YY_CHAR c, YY_CHAR *buf_ptr ); void yyrestart ( FILE *input_file ); void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); void yy_load_buffer_state ( void ); YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); void yy_delete_buffer ( YY_BUFFER_STATE b ); void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); #define yy_new_buffer yy_create_buffer #ifdef __cplusplus static int yyinput ( void ); #else static int input ( void ); #endif YY_DECL { register yy_state_type yy_current_state; register YY_CHAR *yy_cp, *yy_bp; register int yy_act; %% user's declarations go here if ( yy_init ) { YY_USER_INIT; if ( ! yy_start ) yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( yy_current_buffer ) yy_init_buffer( yy_current_buffer, yyin ); else yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); yy_load_buffer_state(); yy_init = 0; } while ( 1 ) /* loops until end-of-file is reached */ { %% yymore()-related code goes here yy_cp = yy_c_buf_p; /* support of yytext */ *yy_cp = yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of the * current run. */ yy_bp = yy_cp; %% code to set up and find next match goes here yy_find_action: %% code to find the action number goes here YY_DO_BEFORE_ACTION; YY_USER_ACTION; do_action: /* this label is used only to access EOF actions */ %% debug code goes here switch ( yy_act ) { %% actions go here case YY_END_OF_BUFFER: { /* amount of text matched not including the EOB char */ int yy_amount_of_matched_text = yy_cp - yytext - 1; /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yy_hold_char; /* note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the end- * of-buffer state). Contrast this with the test in yyinput(). */ if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) /* this was really a NUL */ { yy_state_type yy_next_state; yy_c_buf_p = yytext + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state(); /* okay, we're now positioned to make the * NUL transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we * don't want to build jamming into it because * then it will run more slowly) */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = yytext + YY_MORE_ADJ; if ( yy_next_state ) { /* consume the NUL */ yy_cp = ++yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { %% code to do backtracking for compressed tables and set up yy_cp goes here goto yy_find_action; } } else switch ( yy_get_next_buffer() ) { case EOB_ACT_END_OF_FILE: { yy_did_buffer_switch_on_eof = 0; if ( yywrap() ) { /* note: because we've taken care in * yy_get_next_buffer() to have set up yytext, * we can now set up yy_c_buf_p so that if some * total hoser (like flex itself) wants * to call the scanner after we return the * YY_NULL, it'll still work - another YY_NULL * will get returned. */ yy_c_buf_p = yytext + YY_MORE_ADJ; yy_act = YY_STATE_EOF((yy_start - 1) / 2); goto do_action; } else { if ( ! yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } } break; case EOB_ACT_CONTINUE_SCAN: yy_c_buf_p = yytext + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state(); yy_cp = yy_c_buf_p; yy_bp = yytext + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yy_c_buf_p = &yy_current_buffer->yy_ch_buf[yy_n_chars]; yy_current_state = yy_get_previous_state(); yy_cp = yy_c_buf_p; yy_bp = yytext + YY_MORE_ADJ; goto yy_find_action; } break; } default: #ifdef FLEX_DEBUG printf( "action # %d\n", yy_act ); #endif YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } } } /* yy_get_next_buffer - try to read in a new buffer * * synopsis * int yy_get_next_buffer(); * * returns a code representing an action * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer() { register YY_CHAR *dest = yy_current_buffer->yy_ch_buf; register YY_CHAR *source = yytext - 1; /* copy prev. char, too */ register int number_to_move, i; int ret_val; if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); /* try to read more data */ /* first move last chars to start of buffer */ number_to_move = yy_c_buf_p - yytext; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( yy_current_buffer->yy_eof_status != EOF_NOT_SEEN ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ yy_n_chars = 0; else { int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; else if ( num_to_read <= 0 ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); /* read in more data */ YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), yy_n_chars, num_to_read ); } if ( yy_n_chars == 0 ) { if ( number_to_move == 1 ) { ret_val = EOB_ACT_END_OF_FILE; yy_current_buffer->yy_eof_status = EOF_DONE; } else { ret_val = EOB_ACT_LAST_MATCH; yy_current_buffer->yy_eof_status = EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; yy_n_chars += number_to_move; yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; /* yytext begins at the second character in yy_ch_buf; the first * character is the one which preceded it before reading in the latest * buffer; it needs to be kept around in case it's a newline, so * yy_get_previous_state() will have with '^' rules active */ yytext = &yy_current_buffer->yy_ch_buf[1]; return ( ret_val ); } /* yy_get_previous_state - get the state just before the EOB char was reached * * synopsis * yy_state_type yy_get_previous_state(); */ static yy_state_type yy_get_previous_state() { register yy_state_type yy_current_state; register YY_CHAR *yy_cp; %% code to get the start state into yy_current_state goes here for ( yy_cp = yytext + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) { %% code to find the next state goes here } return ( yy_current_state ); } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans( register yy_state_type yy_current_state ) { register int yy_is_jam; %% code to find the next state, and perhaps do backtracking, goes here return ( yy_is_jam ? 0 : yy_current_state ); } static void yyunput( YY_CHAR c, register YY_CHAR *yy_bp ) { register YY_CHAR *yy_cp = yy_c_buf_p; /* undo effects of setting up yytext */ *yy_cp = yy_hold_char; if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) { /* need to shift things up to make room */ register int number_to_move = yy_n_chars + 2; /* +2 for EOB chars */ register YY_CHAR *dest = &yy_current_buffer->yy_ch_buf[yy_current_buffer->yy_buf_size + 2]; register YY_CHAR *source = &yy_current_buffer->yy_ch_buf[number_to_move]; while ( source > yy_current_buffer->yy_ch_buf ) *--dest = *--source; yy_cp += dest - source; yy_bp += dest - source; yy_n_chars = yy_current_buffer->yy_buf_size; if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); } if ( yy_cp > yy_bp && yy_cp[-1] == '\n' ) yy_cp[-2] = '\n'; *--yy_cp = c; /* note: the formal parameter *must* be called "yy_bp" for this * macro to now work correctly */ YY_DO_BEFORE_ACTION; /* set up yytext again */ } #ifdef __cplusplus static int yyinput() #else static int input(void) #endif { int c; YY_CHAR *yy_cp = yy_c_buf_p; *yy_cp = yy_hold_char; if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) /* this was really a NUL */ *yy_c_buf_p = '\0'; else { /* need more input */ yytext = yy_c_buf_p; ++yy_c_buf_p; switch ( yy_get_next_buffer() ) { case EOB_ACT_END_OF_FILE: { if ( yywrap() ) { yy_c_buf_p = yytext + YY_MORE_ADJ; return ( EOF ); } YY_NEW_FILE; #ifdef __cplusplus return ( yyinput() ); #else return ( input() ); #endif } break; case EOB_ACT_CONTINUE_SCAN: yy_c_buf_p = yytext + YY_MORE_ADJ; break; case EOB_ACT_LAST_MATCH: #ifdef __cplusplus YY_FATAL_ERROR( "unexpected last match in yyinput()" ); #else YY_FATAL_ERROR( "unexpected last match in input()" ); #endif } } } c = *yy_c_buf_p; yy_hold_char = *++yy_c_buf_p; return ( c ); } void yyrestart( FILE *input_file ) { yy_init_buffer( yy_current_buffer, input_file ); yy_load_buffer_state(); } void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) { if ( yy_current_buffer == new_buffer ) return; if ( yy_current_buffer ) { /* flush out information for old buffer */ *yy_c_buf_p = yy_hold_char; yy_current_buffer->yy_buf_pos = yy_c_buf_p; yy_current_buffer->yy_n_chars = yy_n_chars; } yy_current_buffer = new_buffer; yy_load_buffer_state(); /* we don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ yy_did_buffer_switch_on_eof = 1; } void yy_load_buffer_state( void ) { yy_n_chars = yy_current_buffer->yy_n_chars; yytext = yy_c_buf_p = yy_current_buffer->yy_buf_pos; yyin = yy_current_buffer->yy_input_file; yy_hold_char = *yy_c_buf_p; } YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) malloc( sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (YY_CHAR *) malloc( (unsigned) (b->yy_buf_size + 2) ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); yy_init_buffer( b, file ); return ( b ); } void yy_delete_buffer( YY_BUFFER_STATE b ) { if ( b == yy_current_buffer ) yy_current_buffer = (YY_BUFFER_STATE) 0; free( (char *) b->yy_ch_buf ); free( (char *) b ); } void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) { b->yy_input_file = file; /* we put in the '\n' and start reading from [1] so that an * initial match-at-newline will be true. */ b->yy_ch_buf[0] = '\n'; b->yy_n_chars = 1; /* we always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[1]; b->yy_eof_status = EOF_NOT_SEEN; } base-7.0.3.1/modules/libcom/src/flex/flex.skel.static0000664000577000060420000004473513557101274021176 0ustar anjaesctl/* A lexical scanner generated by flex */ /* scanner skeleton */ /* modified by Jim Kowalkowski to have everything declared static */ #define FLEX_SCANNER #include #include /* amount of stuff to slurp up with each read */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* returned upon end-of-file */ #define YY_END_TOK 0 /* copy whatever the last rule matched to the standard output */ /* cast to (char *) is because for 8-bit chars, yytext is (unsigned char *) */ /* this used to be an fputs(), but since the string might contain NUL's, * we now use fwrite() */ #define ECHO (void) fwrite( (char *) yytext, yyleng, 1, yyout ) /* gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #define YY_INPUT(buf,result,max_size) \ if ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ YY_FATAL_ERROR( "read() in flex scanner failed" ); #define YY_NULL 0 /* no semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ /* #define yyterminate() return ( YY_NULL ) replaced by jbk */ static int yyterminate_internal( void ); #define yyterminate() return yyterminate_internal() /* report a fatal error */ /* The funky do-while is used to turn this macro definition into * a single C statement (which needs a semi-colon terminator). * This avoids problems with code like: * * if ( something_happens ) * YY_FATAL_ERROR( "oops, the something happened" ); * else * everything_okay(); * * Prior to using the do-while the compiler would get upset at the * "else" because it interpreted the "if" statement as being all * done when it reached the ';' after the YY_FATAL_ERROR() call. */ #define YY_FATAL_ERROR(msg) \ do \ { \ (void) fputs( msg, stderr ); \ (void) putc( '\n', stderr ); \ exit( 1 ); \ } \ while ( 0 ) /* default yywrap function - always treat EOF as an EOF */ #define yywrap() 1 /* enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN */ #define BEGIN yy_start = 1 + 2 * /* action number for EOF rule of a given start state */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* special action meaning "start processing a new file" */ #define YY_NEW_FILE \ do \ { \ yy_init_buffer( yy_current_buffer, yyin ); \ yy_load_buffer_state(); \ } \ while ( 0 ) /* default declaration of generated scanner - a define so the user can * easily add parameters - jbk added the static to YY_DECL */ #define YY_DECL static int yylex ( void ) /* code executed at the end of each rule */ #define YY_BREAK break; #define YY_END_OF_BUFFER_CHAR 0 #ifndef YY_BUF_SIZE #define YY_BUF_SIZE (YY_READ_BUF_SIZE * 2) /* size of default input buffer */ #endif typedef struct yy_buffer_state *YY_BUFFER_STATE; %% section 1 definitions go here /* done after the current pattern has been matched and before the * corresponding action - sets up yytext */ #define YY_DO_BEFORE_ACTION \ yytext = yy_bp; \ %% code to fiddle yytext and yyleng for yymore() goes here yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yy_c_buf_p = yy_cp; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 /* return all but the first 'n' matched characters back to the input stream */ #define yyless(n) \ do \ { \ /* undo effects of setting up yytext */ \ *yy_cp = yy_hold_char; \ yy_c_buf_p = yy_cp = yy_bp + n; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yytext ) struct yy_buffer_state { FILE *yy_input_file; YY_CHAR *yy_ch_buf; /* input buffer */ YY_CHAR *yy_buf_pos; /* current position in input buffer */ /* size of input buffer in bytes, not including room for EOB characters */ int yy_buf_size; /* number of characters read into yy_ch_buf, not including EOB characters */ int yy_n_chars; int yy_eof_status; /* whether we've seen an EOF on this buffer */ #define EOF_NOT_SEEN 0 /* "pending" happens when the EOF has been seen but there's still * some text process */ #define EOF_PENDING 1 #define EOF_DONE 2 }; static YY_BUFFER_STATE yy_current_buffer; /* we provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state" */ #define YY_CURRENT_BUFFER yy_current_buffer /* yy_hold_char holds the character lost when yytext is formed */ static YY_CHAR yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif #ifndef YY_USER_INIT #define YY_USER_INIT #endif /* jbk update extern YY_CHAR *yytext; extern int yyleng; extern FILE *yyin, *yyout; */ static YY_CHAR *yytext; /* jbk added static */ static int yyleng; /* jbk added static */ static FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; /* jbk added static */ %% data tables for the DFA go here /* these variables are all declared out here so that section 3 code can * manipulate them */ /* points to current character in buffer */ static YY_CHAR *yy_c_buf_p = (YY_CHAR *) 0; static int yy_init = 1; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* flag which is used to allow yywrap()'s to do buffer switches * instead of setting up a fresh yyin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; static yy_state_type yy_get_previous_state ( void ); static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); static int yy_get_next_buffer ( void ); static void yyunput ( YY_CHAR c, YY_CHAR *buf_ptr ); /* jbk added static in front all these */ static void yyrestart ( FILE *input_file ); static void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); static void yy_load_buffer_state ( void ); static YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); static void yy_delete_buffer ( YY_BUFFER_STATE b ); static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); #define yy_new_buffer yy_create_buffer #ifdef yyneed_input #ifdef __cplusplus static int yyinput ( void ); #else static int input ( void ); #endif #endif YY_DECL { register yy_state_type yy_current_state; register YY_CHAR *yy_cp, *yy_bp; register int yy_act; %% user's declarations go here if ( yy_init ) { YY_USER_INIT; if ( ! yy_start ) yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( yy_current_buffer ) yy_init_buffer( yy_current_buffer, yyin ); else yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); yy_load_buffer_state(); yy_init = 0; } while ( 1 ) /* loops until end-of-file is reached */ { %% yymore()-related code goes here yy_cp = yy_c_buf_p; /* support of yytext */ *yy_cp = yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of the * current run. */ yy_bp = yy_cp; %% code to set up and find next match goes here yy_find_action: %% code to find the action number goes here YY_DO_BEFORE_ACTION; YY_USER_ACTION; do_action: /* this label is used only to access EOF actions */ %% debug code goes here switch ( yy_act ) { %% actions go here case YY_END_OF_BUFFER: { /* amount of text matched not including the EOB char */ int yy_amount_of_matched_text = yy_cp - yytext - 1; /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yy_hold_char; /* note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the end- * of-buffer state). Contrast this with the test in yyinput(). */ if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) /* this was really a NUL */ { yy_state_type yy_next_state; yy_c_buf_p = yytext + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state(); /* okay, we're now positioned to make the * NUL transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we * don't want to build jamming into it because * then it will run more slowly) */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = yytext + YY_MORE_ADJ; if ( yy_next_state ) { /* consume the NUL */ yy_cp = ++yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { %% code to do backtracking for compressed tables and set up yy_cp goes here goto yy_find_action; } } else switch ( yy_get_next_buffer() ) { case EOB_ACT_END_OF_FILE: { yy_did_buffer_switch_on_eof = 0; if ( yywrap() ) { /* note: because we've taken care in * yy_get_next_buffer() to have set up yytext, * we can now set up yy_c_buf_p so that if some * total hoser (like flex itself) wants * to call the scanner after we return the * YY_NULL, it'll still work - another YY_NULL * will get returned. */ yy_c_buf_p = yytext + YY_MORE_ADJ; yy_act = YY_STATE_EOF((yy_start - 1) / 2); goto do_action; } else { if ( ! yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } } break; case EOB_ACT_CONTINUE_SCAN: yy_c_buf_p = yytext + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state(); yy_cp = yy_c_buf_p; yy_bp = yytext + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yy_c_buf_p = &yy_current_buffer->yy_ch_buf[yy_n_chars]; yy_current_state = yy_get_previous_state(); yy_cp = yy_c_buf_p; yy_bp = yytext + YY_MORE_ADJ; goto yy_find_action; } break; } default: #ifdef FLEX_DEBUG printf( "action # %d\n", yy_act ); #endif YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } } } /* yy_get_next_buffer - try to read in a new buffer * * synopsis * int yy_get_next_buffer(); * * returns a code representing an action * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer( void ) { register YY_CHAR *dest = yy_current_buffer->yy_ch_buf; register YY_CHAR *source = yytext - 1; /* copy prev. char, too */ register int number_to_move, i; int ret_val; if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); /* try to read more data */ /* first move last chars to start of buffer */ number_to_move = yy_c_buf_p - yytext; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( yy_current_buffer->yy_eof_status != EOF_NOT_SEEN ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ yy_n_chars = 0; else { int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; else if ( num_to_read <= 0 ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); /* read in more data */ YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), yy_n_chars, num_to_read ); } if ( yy_n_chars == 0 ) { if ( number_to_move - YY_MORE_ADJ == 1 ) { ret_val = EOB_ACT_END_OF_FILE; yy_current_buffer->yy_eof_status = EOF_DONE; } else { ret_val = EOB_ACT_LAST_MATCH; yy_current_buffer->yy_eof_status = EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; yy_n_chars += number_to_move; yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; /* yytext begins at the second character in yy_ch_buf; the first * character is the one which preceded it before reading in the latest * buffer; it needs to be kept around in case it's a newline, so * yy_get_previous_state() will have with '^' rules active */ yytext = &yy_current_buffer->yy_ch_buf[1]; return ( ret_val ); } /* yy_get_previous_state - get the state just before the EOB char was reached * * synopsis * yy_state_type yy_get_previous_state(); */ static yy_state_type yy_get_previous_state( void ) { register yy_state_type yy_current_state; register YY_CHAR *yy_cp; %% code to get the start state into yy_current_state goes here for ( yy_cp = yytext + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) { %% code to find the next state goes here } return ( yy_current_state ); } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans( register yy_state_type yy_current_state ) { register int yy_is_jam; %% code to find the next state, and perhaps do backtracking, goes here return ( yy_is_jam ? 0 : yy_current_state ); } static void yyunput( YY_CHAR c, register YY_CHAR *yy_bp ) { register YY_CHAR *yy_cp = yy_c_buf_p; /* undo effects of setting up yytext */ *yy_cp = yy_hold_char; if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) { /* need to shift things up to make room */ register int number_to_move = yy_n_chars + 2; /* +2 for EOB chars */ register YY_CHAR *dest = &yy_current_buffer->yy_ch_buf[yy_current_buffer->yy_buf_size + 2]; register YY_CHAR *source = &yy_current_buffer->yy_ch_buf[number_to_move]; while ( source > yy_current_buffer->yy_ch_buf ) *--dest = *--source; yy_cp += dest - source; yy_bp += dest - source; yy_n_chars = yy_current_buffer->yy_buf_size; if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); } if ( yy_cp > yy_bp && yy_cp[-1] == '\n' ) yy_cp[-2] = '\n'; *--yy_cp = c; /* note: the formal parameter *must* be called "yy_bp" for this * macro to now work correctly */ YY_DO_BEFORE_ACTION; /* set up yytext again */ } #ifdef yyneed_input #ifdef __cplusplus static int yyinput( void ) #else static int input( void ) #endif { int c; YY_CHAR *yy_cp = yy_c_buf_p; *yy_cp = yy_hold_char; if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) /* this was really a NUL */ *yy_c_buf_p = '\0'; else { /* need more input */ yytext = yy_c_buf_p; ++yy_c_buf_p; switch ( yy_get_next_buffer() ) { case EOB_ACT_END_OF_FILE: { if ( yywrap() ) { yy_c_buf_p = yytext + YY_MORE_ADJ; return ( EOF ); } YY_NEW_FILE; #ifdef __cplusplus return ( yyinput() ); #else return ( input() ); #endif } break; case EOB_ACT_CONTINUE_SCAN: yy_c_buf_p = yytext + YY_MORE_ADJ; break; case EOB_ACT_LAST_MATCH: #ifdef __cplusplus YY_FATAL_ERROR( "unexpected last match in yyinput()" ); #else YY_FATAL_ERROR( "unexpected last match in input()" ); #endif } } } c = *yy_c_buf_p; yy_hold_char = *++yy_c_buf_p; return ( c ); } #endif /* yyneed_input */ /* jbk added static in front of func */ static void yyrestart( FILE *input_file ) { if ( yy_current_buffer ) yy_init_buffer( yy_current_buffer, input_file ); else yy_current_buffer = yy_create_buffer( input_file, YY_BUF_SIZE ); yy_load_buffer_state(); } /* jbk added static in front of func */ static void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) { if ( yy_current_buffer == new_buffer ) return; if ( yy_current_buffer ) { /* flush out information for old buffer */ *yy_c_buf_p = yy_hold_char; yy_current_buffer->yy_buf_pos = yy_c_buf_p; yy_current_buffer->yy_n_chars = yy_n_chars; } yy_current_buffer = new_buffer; yy_load_buffer_state(); /* we don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ yy_did_buffer_switch_on_eof = 1; } /* jbk added static in front of func */ static void yy_load_buffer_state( void ) { yy_n_chars = yy_current_buffer->yy_n_chars; yytext = yy_c_buf_p = yy_current_buffer->yy_buf_pos; yyin = yy_current_buffer->yy_input_file; yy_hold_char = *yy_c_buf_p; } /* jbk added static in front of func */ static YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) malloc( sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (YY_CHAR *) malloc( (unsigned) (b->yy_buf_size + 2) ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); yy_init_buffer( b, file ); return ( b ); } /* jbk added static in front of func */ static void yy_delete_buffer( YY_BUFFER_STATE b ) { if ( b == yy_current_buffer ) yy_current_buffer = (YY_BUFFER_STATE) 0; free( (char *) b->yy_ch_buf ); free( (char *) b ); } /* jbk added static in front of func */ static void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) { b->yy_input_file = file; /* we put in the '\n' and start reading from [1] so that an * initial match-at-newline will be true. */ b->yy_ch_buf[0] = '\n'; b->yy_n_chars = 1; /* we always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[1]; b->yy_eof_status = EOF_NOT_SEEN; } static int yyterminate_internal( void ) { /* jbk fix - buffer created by yy_create_buffer needs to be freed */ yy_delete_buffer(yy_current_buffer); yy_current_buffer=NULL; return YY_NULL; } base-7.0.3.1/modules/libcom/src/flex/flexdef.h0000664000577000060420000006764613557101274017666 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* flexdef - definitions file for flex */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef INC_flexdef_H #define INC_flexdef_H #include #include #include #include #include #ifdef __GNUC__ #define NORETURN __attribute__((noreturn)) #else #define NORETURN #endif /* always be prepared to generate an 8-bit scanner */ #define FLEX_8_BIT_CHARS #ifdef FLEX_8_BIT_CHARS #define CSIZE 256 #define Char unsigned char #else #define Char char #define CSIZE 128 #endif /* size of input alphabet - should be size of ASCII set */ #ifndef DEFAULT_CSIZE #define DEFAULT_CSIZE 128 #endif /* maximum line length we'll have to deal with */ #define MAXLINE BUFSIZ /* maximum size of file name */ #define FILENAMESIZE 1024 #ifndef min #define min(x,y) ((x) < (y) ? (x) : (y)) #endif #ifndef max #define max(x,y) ((x) > (y) ? (x) : (y)) #endif #define true 1 #define false 0 #ifndef DEFAULT_SKELETON_FILE #define DEFAULT_SKELETON_FILE "flex.skel" #endif /* special chk[] values marking the slots taking by end-of-buffer and action * numbers */ #define EOB_POSITION -1 #define ACTION_POSITION -2 /* number of data items per line for -f output */ #define NUMDATAITEMS 10 /* number of lines of data in -f output before inserting a blank line for * readability. */ #define NUMDATALINES 10 /* transition_struct_out() definitions */ #define TRANS_STRUCT_PRINT_LENGTH 15 /* returns true if an nfa state has an epsilon out-transition slot * that can be used. This definition is currently not used. */ #define FREE_EPSILON(state) \ (transchar[state] == SYM_EPSILON && \ trans2[state] == NO_TRANSITION && \ finalst[state] != state) /* returns true if an nfa state has an epsilon out-transition character * and both slots are free */ #define SUPER_FREE_EPSILON(state) \ (transchar[state] == SYM_EPSILON && \ trans1[state] == NO_TRANSITION) \ /* maximum number of NFA states that can comprise a DFA state. It's real * big because if there's a lot of rules, the initial state will have a * huge epsilon closure. */ #define INITIAL_MAX_DFA_SIZE 750 #define MAX_DFA_SIZE_INCREMENT 750 /* a note on the following masks. They are used to mark accepting numbers * as being special. As such, they implicitly limit the number of accepting * numbers (i.e., rules) because if there are too many rules the rule numbers * will overload the mask bits. Fortunately, this limit is \large/ (0x2000 == * 8192) so unlikely to actually cause any problems. A check is made in * new_rule() to ensure that this limit is not reached. */ /* mask to mark a trailing context accepting number */ #define YY_TRAILING_MASK 0x2000 /* mask to mark the accepting number of the "head" of a trailing context rule */ #define YY_TRAILING_HEAD_MASK 0x4000 /* maximum number of rules, as outlined in the above note */ #define MAX_RULE (YY_TRAILING_MASK - 1) /* NIL must be 0. If not, its special meaning when making equivalence classes * (it marks the representative of a given e.c.) will be unidentifiable */ #define NIL 0 #define JAM -1 /* to mark a missing DFA transition */ #define NO_TRANSITION NIL #define UNIQUE -1 /* marks a symbol as an e.c. representative */ #define INFINITY -1 /* for x{5,} constructions */ #define INITIAL_MAX_CCLS 100 /* max number of unique character classes */ #define MAX_CCLS_INCREMENT 100 /* size of table holding members of character classes */ #define INITIAL_MAX_CCL_TBL_SIZE 500 #define MAX_CCL_TBL_SIZE_INCREMENT 250 #define INITIAL_MAX_RULES 100 /* default maximum number of rules */ #define MAX_RULES_INCREMENT 100 #define INITIAL_MNS 2000 /* default maximum number of nfa states */ #define MNS_INCREMENT 1000 /* amount to bump above by if it's not enough */ #define INITIAL_MAX_DFAS 1000 /* default maximum number of dfa states */ #define MAX_DFAS_INCREMENT 1000 #define JAMSTATE -32766 /* marks a reference to the state that always jams */ /* enough so that if it's subtracted from an NFA state number, the result * is guaranteed to be negative */ #define MARKER_DIFFERENCE 32000 #define MAXIMUM_MNS 31999 /* maximum number of nxt/chk pairs for non-templates */ #define INITIAL_MAX_XPAIRS 2000 #define MAX_XPAIRS_INCREMENT 2000 /* maximum number of nxt/chk pairs needed for templates */ #define INITIAL_MAX_TEMPLATE_XPAIRS 2500 #define MAX_TEMPLATE_XPAIRS_INCREMENT 2500 #define SYM_EPSILON (CSIZE + 1) /* to mark transitions on the symbol epsilon */ #define INITIAL_MAX_SCS 40 /* maximum number of start conditions */ #define MAX_SCS_INCREMENT 40 /* amount to bump by if it's not enough */ #define ONE_STACK_SIZE 500 /* stack of states with only one out-transition */ #define SAME_TRANS -1 /* transition is the same as "default" entry for state */ /* the following percentages are used to tune table compression: * the percentage the number of out-transitions a state must be of the * number of equivalence classes in order to be considered for table * compaction by using protos */ #define PROTO_SIZE_PERCENTAGE 15 /* the percentage the number of homogeneous out-transitions of a state * must be of the number of total out-transitions of the state in order * that the state's transition table is first compared with a potential * template of the most common out-transition instead of with the first * proto in the proto queue */ #define CHECK_COM_PERCENTAGE 50 /* the percentage the number of differences between a state's transition * table and the proto it was first compared with must be of the total * number of out-transitions of the state in order to keep the first * proto as a good match and not search any further */ #define FIRST_MATCH_DIFF_PERCENTAGE 10 /* the percentage the number of differences between a state's transition * table and the most similar proto must be of the state's total number * of out-transitions to use the proto as an acceptable close match */ #define ACCEPTABLE_DIFF_PERCENTAGE 50 /* the percentage the number of homogeneous out-transitions of a state * must be of the number of total out-transitions of the state in order * to consider making a template from the state */ #define TEMPLATE_SAME_PERCENTAGE 60 /* the percentage the number of differences between a state's transition * table and the most similar proto must be of the state's total number * of out-transitions to create a new proto from the state */ #define NEW_PROTO_DIFF_PERCENTAGE 20 /* the percentage the total number of out-transitions of a state must be * of the number of equivalence classes in order to consider trying to * fit the transition table into "holes" inside the nxt/chk table. */ #define INTERIOR_FIT_PERCENTAGE 15 /* size of region set aside to cache the complete transition table of * protos on the proto queue to enable quick comparisons */ #define PROT_SAVE_SIZE 2000 #define MSP 50 /* maximum number of saved protos (protos on the proto queue) */ /* maximum number of out-transitions a state can have that we'll rummage * around through the interior of the internal fast table looking for a * spot for it */ #define MAX_XTIONS_FULL_INTERIOR_FIT 4 /* maximum number of rules which will be reported as being associated * with a DFA state */ #define MAX_ASSOC_RULES 100 /* number that, if used to subscript an array, has a good chance of producing * an error; should be small enough to fit into a short */ #define BAD_SUBSCRIPT -32767 /* absolute value of largest number that can be stored in a short, with a * bit of slop thrown in for general paranoia. */ #define MAX_SHORT 32766 /* Declarations for global variables. */ /* variables for symbol tables: * sctbl - start-condition symbol table * ndtbl - name-definition symbol table * ccltab - character class text symbol table */ struct hash_entry { struct hash_entry *prev, *next; char *name; char *str_val; int int_val; } ; typedef struct hash_entry *hash_table[]; #define NAME_TABLE_HASH_SIZE 101 #define START_COND_HASH_SIZE 101 #define CCL_HASH_SIZE 101 extern struct hash_entry *ndtbl[NAME_TABLE_HASH_SIZE]; extern struct hash_entry *sctbl[START_COND_HASH_SIZE]; extern struct hash_entry *ccltab[CCL_HASH_SIZE]; /* variables for flags: * printstats - if true (-v), dump statistics * syntaxerror - true if a syntax error has been found * eofseen - true if we've seen an eof in the input file * ddebug - if true (-d), make a "debug" scanner * trace - if true (-T), trace processing * spprdflt - if true (-s), suppress the default rule * interactive - if true (-I), generate an interactive scanner * caseins - if true (-i), generate a case-insensitive scanner * useecs - if true (-Ce flag), use equivalence classes * fulltbl - if true (-Cf flag), don't compress the DFA state table * usemecs - if true (-Cm flag), use meta-equivalence classes * fullspd - if true (-F flag), use Jacobson method of table representation * gen_line_dirs - if true (i.e., no -L flag), generate #line directives * performance_report - if true (i.e., -p flag), generate a report relating * to scanner performance * backtrack_report - if true (i.e., -b flag), generate "lex.backtrack" file * listing backtracking states * csize - size of character set for the scanner we're generating; * 128 for 7-bit chars and 256 for 8-bit * yymore_used - if true, yymore() is used in input rules * reject - if true, generate backtracking tables for REJECT macro * real_reject - if true, scanner really uses REJECT (as opposed to just * having "reject" set for variable trailing context) * continued_action - true if this rule's action is to "fall through" to * the next rule's action (i.e., the '|' action) * yymore_really_used - has a REALLY_xxx value indicating whether a * %used or %notused was used with yymore() * reject_really_used - same for REJECT */ extern int printstats, syntaxerror, eofseen, ddebug, trace, spprdflt; extern int interactive, caseins, useecs, fulltbl, usemecs; extern int fullspd, gen_line_dirs, performance_report, backtrack_report, csize; extern int yymore_used, reject, real_reject, continued_action; #define REALLY_NOT_DETERMINED 0 #define REALLY_USED 1 #define REALLY_NOT_USED 2 extern int yymore_really_used, reject_really_used; /* variables used in the flex input routines: * datapos - characters on current output line * dataline - number of contiguous lines of data in current data * statement. Used to generate readable -f output * linenum - current input line number * skelfile - the skeleton file * yyin - input file * temp_action_file - temporary file to hold actions * backtrack_file - file to summarize backtracking states to * infilename - name of input file * input_files - array holding names of input files * num_input_files - size of input_files array * program_name - name with which program was invoked */ extern int datapos, dataline, linenum; extern FILE *skelfile, *yyin, *temp_action_file, *backtrack_file; extern char *infilename; extern char **input_files; extern int num_input_files; extern char *program_name; /* variables for stack of states having only one out-transition: * onestate - state number * onesym - transition symbol * onenext - target state * onedef - default base entry * onesp - stack pointer */ extern int onestate[ONE_STACK_SIZE], onesym[ONE_STACK_SIZE]; extern int onenext[ONE_STACK_SIZE], onedef[ONE_STACK_SIZE], onesp; /* variables for nfa machine data: * current_mns - current maximum on number of NFA states * num_rules - number of the last accepting state; also is number of * rules created so far * current_max_rules - current maximum number of rules * lastnfa - last nfa state number created * firstst - physically the first state of a fragment * lastst - last physical state of fragment * finalst - last logical state of fragment * transchar - transition character * trans1 - transition state * trans2 - 2nd transition state for epsilons * accptnum - accepting number * assoc_rule - rule associated with this NFA state (or 0 if none) * state_type - a STATE_xxx type identifying whether the state is part * of a normal rule, the leading state in a trailing context * rule (i.e., the state which marks the transition from * recognizing the text-to-be-matched to the beginning of * the trailing context), or a subsequent state in a trailing * context rule * rule_type - a RULE_xxx type identifying whether this a a ho-hum * normal rule or one which has variable head & trailing * context * rule_linenum - line number associated with rule */ extern int current_mns, num_rules, current_max_rules, lastnfa; extern int *firstst, *lastst, *finalst, *transchar, *trans1, *trans2; extern int *accptnum, *assoc_rule, *state_type, *rule_type, *rule_linenum; /* different types of states; values are useful as masks, as well, for * routines like check_trailing_context() */ #define STATE_NORMAL 0x1 #define STATE_TRAILING_CONTEXT 0x2 /* global holding current type of state we're making */ extern int current_state_type; /* different types of rules */ #define RULE_NORMAL 0 #define RULE_VARIABLE 1 /* true if the input rules include a rule with both variable-length head * and trailing context, false otherwise */ extern int variable_trailing_context_rules; /* variables for protos: * numtemps - number of templates created * numprots - number of protos created * protprev - backlink to a more-recently used proto * protnext - forward link to a less-recently used proto * prottbl - base/def table entry for proto * protcomst - common state of proto * firstprot - number of the most recently used proto * lastprot - number of the least recently used proto * protsave contains the entire state array for protos */ extern int numtemps, numprots, protprev[MSP], protnext[MSP], prottbl[MSP]; extern int protcomst[MSP], firstprot, lastprot, protsave[PROT_SAVE_SIZE]; /* variables for managing equivalence classes: * numecs - number of equivalence classes * nextecm - forward link of Equivalence Class members * ecgroup - class number or backward link of EC members * nummecs - number of meta-equivalence classes (used to compress * templates) * tecfwd - forward link of meta-equivalence classes members * tecbck - backward link of MEC's * xlation - maps character codes to their translations, or nil if no %t table * num_xlations - number of different xlation values */ /* reserve enough room in the equivalence class arrays so that we * can use the CSIZE'th element to hold equivalence class information * for the NUL character. Later we'll move this information into * the 0th element. */ extern int numecs, nextecm[CSIZE + 1], ecgroup[CSIZE + 1], nummecs; /* meta-equivalence classes are indexed starting at 1, so it's possible * that they will require positions from 1 .. CSIZE, i.e., CSIZE + 1 * slots total (since the arrays are 0-based). nextecm[] and ecgroup[] * don't require the extra position since they're indexed from 1 .. CSIZE - 1. */ extern int tecfwd[CSIZE + 1], tecbck[CSIZE + 1]; extern int *xlation; extern int num_xlations; /* variables for start conditions: * lastsc - last start condition created * current_max_scs - current limit on number of start conditions * scset - set of rules active in start condition * scbol - set of rules active only at the beginning of line in a s.c. * scxclu - true if start condition is exclusive * sceof - true if start condition has EOF rule * scname - start condition name * actvsc - stack of active start conditions for the current rule */ extern int lastsc, current_max_scs, *scset, *scbol, *scxclu, *sceof, *actvsc; extern char **scname; /* variables for dfa machine data: * current_max_dfa_size - current maximum number of NFA states in DFA * current_max_xpairs - current maximum number of non-template xtion pairs * current_max_template_xpairs - current maximum number of template pairs * current_max_dfas - current maximum number DFA states * lastdfa - last dfa state number created * nxt - state to enter upon reading character * chk - check value to see if "nxt" applies * tnxt - internal nxt table for templates * base - offset into "nxt" for given state * def - where to go if "chk" disallows "nxt" entry * nultrans - NUL transition for each state * NUL_ec - equivalence class of the NUL character * tblend - last "nxt/chk" table entry being used * firstfree - first empty entry in "nxt/chk" table * dss - nfa state set for each dfa * dfasiz - size of nfa state set for each dfa * dfaacc - accepting set for each dfa state (or accepting number, if * -r is not given) * accsiz - size of accepting set for each dfa state * dhash - dfa state hash value * numas - number of DFA accepting states created; note that this * is not necessarily the same value as num_rules, which is the analogous * value for the NFA * numsnpairs - number of state/nextstate transition pairs * jambase - position in base/def where the default jam table starts * jamstate - state number corresponding to "jam" state * end_of_buffer_state - end-of-buffer dfa state number */ extern int current_max_dfa_size, current_max_xpairs; extern int current_max_template_xpairs, current_max_dfas; extern int lastdfa, lasttemp, *nxt, *chk, *tnxt; extern int *base, *def, *nultrans, NUL_ec, tblend, firstfree, **dss, *dfasiz; extern union dfaacc_union { int *dfaacc_set; int dfaacc_state; } *dfaacc; extern int *accsiz, *dhash, numas; extern int numsnpairs, jambase, jamstate; extern int end_of_buffer_state; /* variables for ccl information: * lastccl - ccl index of the last created ccl * current_maxccls - current limit on the maximum number of unique ccl's * cclmap - maps a ccl index to its set pointer * ccllen - gives the length of a ccl * cclng - true for a given ccl if the ccl is negated * cclreuse - counts how many times a ccl is re-used * current_max_ccl_tbl_size - current limit on number of characters needed * to represent the unique ccl's * ccltbl - holds the characters in each ccl - indexed by cclmap */ extern int lastccl, current_maxccls, *cclmap, *ccllen, *cclng, cclreuse; extern int current_max_ccl_tbl_size; extern Char *ccltbl; /* variables for miscellaneous information: * starttime - real-time when we started * endtime - real-time when we ended * nmstr - last NAME scanned by the scanner * sectnum - section number currently being parsed * nummt - number of empty nxt/chk table entries * hshcol - number of hash collisions detected by snstods * dfaeql - number of times a newly created dfa was equal to an old one * numeps - number of epsilon NFA states created * eps2 - number of epsilon states which have 2 out-transitions * num_reallocs - number of times it was necessary to realloc() a group * of arrays * tmpuses - number of DFA states that chain to templates * totnst - total number of NFA states used to make DFA states * peakpairs - peak number of transition pairs we had to store internally * numuniq - number of unique transitions * numdup - number of duplicate transitions * hshsave - number of hash collisions saved by checking number of states * num_backtracking - number of DFA states requiring back-tracking * bol_needed - whether scanner needs beginning-of-line recognition */ extern char *starttime, *endtime, nmstr[MAXLINE]; extern int sectnum, nummt, hshcol, dfaeql, numeps, eps2, num_reallocs; extern int tmpuses, totnst, peakpairs, numuniq, numdup, hshsave; extern int num_backtracking, bol_needed; void *allocate_array(int size, int element_size); void *reallocate_array(void *array, int size, int element_size); #define allocate_integer_array(size) \ (int *) allocate_array( size, sizeof( int ) ) #define reallocate_integer_array(array,size) \ (int *) reallocate_array( (void *) array, size, sizeof( int ) ) #define allocate_int_ptr_array(size) \ (int **) allocate_array( size, sizeof( int * ) ) #define allocate_char_ptr_array(size) \ (char **) allocate_array( size, sizeof( char * ) ) #define allocate_dfaacc_union(size) \ (union dfaacc_union *) \ allocate_array( size, sizeof( union dfaacc_union ) ) #define reallocate_int_ptr_array(array,size) \ (int **) reallocate_array( (void *) array, size, sizeof( int * ) ) #define reallocate_char_ptr_array(array,size) \ (char **) reallocate_array( (void *) array, size, sizeof( char * ) ) #define reallocate_dfaacc_union(array, size) \ (union dfaacc_union *) \ reallocate_array( (void *) array, size, sizeof( union dfaacc_union ) ) #define allocate_character_array(size) \ (Char *) allocate_array( size, sizeof( Char ) ) #define reallocate_character_array(array,size) \ (Char *) reallocate_array( (void *) array, size, sizeof( Char ) ) #if 0 /* JRW this might couse truuble... but not for IOC usage */ /* used to communicate between scanner and parser. The type should really * be YYSTYPE, but we can't easily get our hands on it. */ #ifdef __alpha /* inconsistency with parse.y, line 57... on Alpha */ extern long yylval; #else extern int yylval; #endif #endif /* external functions that are cross-referenced among the flex source files */ /* from file ccl.c */ extern void ccladd (int, int); /* Add a single character to a ccl */ extern int cclinit (void); /* make an empty ccl */ extern void cclnegate (int); /* negate a ccl */ /* list the members of a set of characters in CCL form */ extern void list_character_set (FILE*, int[]); /* from file dfa.c */ /* increase the maximum number of dfas */ extern void increase_max_dfas (void); extern void ntod (void); /* convert a ndfa to a dfa */ /* from file ecs.c */ /* convert character classes to set of equivalence classes */ extern void ccl2ecl (void); /* associate equivalence class numbers with class members */ extern int cre8ecs (int[], int[], int); /* associate equivalence class numbers using %t table */ extern int ecs_from_xlation (int[]); /* update equivalence classes based on character class transitions */ extern void mkeccl (Char[], int, int[], int[], int, int); /* create equivalence class for single character */ extern void mkechar (int, int[], int[]); /* from file gen.c */ extern void make_tables (void); /* generate transition tables */ /* from file main.c */ extern void flexend (int) NORETURN; /* from file misc.c */ /* write out the actions from the temporary file to lex.yy.c */ extern void action_out (void); /* true if a string is all lower case */ extern int all_lower (Char *); /* true if a string is all upper case */ extern int all_upper (Char *); /* bubble sort an integer array */ extern void bubble (int [], int); /* shell sort a character array */ extern void cshell (Char [], int, int); extern void dataend (void); /* finish up a block of data declarations */ /* report an error message and terminate */ extern void flexerror (char[]) NORETURN; /* report a fatal error message and terminate */ extern void flexfatal (char[]); /* report an error message formatted with one integer argument */ extern void lerrif (char[], int); /* report an error message formatted with one string argument */ extern void lerrsf (char[], char[]); /* spit out a "# line" statement */ extern void line_directive_out (FILE*); /* generate a data statment for a two-dimensional array */ extern void mk2data (int); extern void mkdata (int); /* generate a data statement */ /* return the integer represented by a string of digits */ extern int myctoi (Char []); /* write out one section of the skeleton file */ extern void skelout (void); /* output a yy_trans_info structure */ extern void transition_struct_out (int, int); /* from file nfa.c */ /* add an accepting state to a machine */ extern void add_accept (int, int); /* make a given number of copies of a singleton machine */ extern int copysingl (int, int); /* debugging routine to write out an nfa */ extern void dumpnfa (int); /* finish up the processing for a rule */ extern void finish_rule (int, int, int, int); /* connect two machines together */ extern int link_machines (int, int); /* mark each "beginning" state in a machine as being a "normal" (i.e., * not trailing context associated) state */ extern void mark_beginning_as_normal (int); /* make a machine that branches to two machines */ extern int mkbranch (int, int); extern int mkclos (int); /* convert a machine into a closure */ extern int mkopt (int); /* make a machine optional */ /* make a machine that matches either one of two machines */ extern int mkor (int, int); /* convert a machine into a positive closure */ extern int mkposcl (int); extern int mkrep (int, int, int); /* make a replicated machine */ /* create a state with a transition on a given symbol */ extern int mkstate (int); extern void new_rule (void); /* initialize for a new rule */ /* from file parse.y */ /* write out a message formatted with one string, pinpointing its location */ extern void format_pinpoint_message (char[], char[]); /* write out a message, pinpointing its location */ extern void pinpoint_message (char[]); extern void synerr (char []); /* report a syntax error */ /* extern int yyparse ();*/ /* the YACC parser */ /* from file scan.l */ extern int flexscan (); /* the Flex-generated scanner for flex */ /* open the given file (if NULL, stdin) for scanning */ extern void set_input_file (char*); extern int yywrap (); /* wrapup a file in the lexical analyzer */ /* from file sym.c */ /* save the text of a character class */ extern void cclinstal (Char [], int); /* lookup the number associated with character class */ extern int ccllookup (Char []); extern void ndinstal (char[], Char[]); /* install a name definition */ extern void scinstal (char[], int); /* make a start condition */ /* lookup the number associated with a start condition */ extern int sclookup (char[]); /* from file tblcmp.c */ /* build table entries for dfa state */ extern void bldtbl (int[], int, int, int, int); extern void cmptmps (void); /* compress template table entries */ extern void inittbl (void); /* initialize transition tables */ extern void mkdeftbl (void); /* make the default, "jam" table entries */ /* create table entries for a state (or state fragment) which has * only one out-transition */ extern void mk1tbl (int, int, int, int); /* place a state into full speed transition table */ extern void place_state (int*, int, int); /* save states with only one out-transition to be processed later */ extern void stack1 (int, int, int, int); /* from file yylex.c */ extern int yylex (); /* The Unix kernel calls used here */ extern int read (int, char*, int); #ifndef _WIN32 extern int unlink (char*); #endif extern int write (int, char*, int); #endif /* INC_flexdef_H */ base-7.0.3.1/modules/libcom/src/flex/flexdoc.html0000664000577000060420000022761713557101274020406 0ustar anjaesctl


NAME

     flex - fast lexical analyzer generator


SYNOPSIS

     flex [-bcdfinpstvFILT8 -C[efmF] -Sskeleton] [filename ...]


DESCRIPTION

     flex is a  tool  for  generating  scanners:  programs  which
     recognized  lexical  patterns in text.  flex reads the given
     input files, or its standard input  if  no  file  names  are
     given,  for  a  description  of  a scanner to generate.  The
     description is in the form of pairs of  regular  expressions
     and  C  code,  called  rules.  flex  generates as output a C
     source file, lex.yy.c, which defines a routine yylex(). This
     file is compiled and linked with the -lfl library to produce
     an executable.  When the executable is run, it analyzes  its
     input  for occurrences of the regular expressions.  Whenever
     it finds one, it executes the corresponding C code.


SOME SIMPLE EXAMPLES

     First some simple examples to get the flavor of how one uses
     flex.  The  following  flex  input specifies a scanner which
     whenever it encounters the string "username" will replace it
     with the user's login name:

         %%
         username    printf( "%s", getlogin() );

     By default, any text not matched by a flex scanner is copied
     to  the output, so the net effect of this scanner is to copy
     its input file to its output with each occurrence of  "user-
     name"  expanded.   In  this  input,  there is just one rule.
     "username" is the pattern and the "printf"  is  the  action.
     The "%%" marks the beginning of the rules.

     Here's another simple example:

             int num_lines = 0, num_chars = 0;

         %%
         \n    ++num_lines; ++num_chars;
         .     ++num_chars;

         %%
         main()
             {
             yylex();
             printf( "# of lines = %d, # of chars = %d\n",
                     num_lines, num_chars );
             }

     This scanner counts the number of characters and the  number
     of  lines in its input (it produces no output other than the
     final report on the counts).  The first  line  declares  two
     globals,  "num_lines"  and "num_chars", which are accessible
     both inside yylex() and in the main() routine declared after
     the  second  "%%".  There are two rules, one which matches a
     newline ("\n") and increments both the line  count  and  the
     character  count,  and one which matches any character other
     than a newline (indicated by the "." regular expression).

     A somewhat more complicated example:

         /* scanner for a toy Pascal-like language */

         %{
         /* need this for the call to atof() below */
         #include <math.h>
         %}

         DIGIT    [0-9]
         ID       [a-z][a-z0-9]*

         %%

         {DIGIT}+    {
                     printf( "An integer: %s (%d)\n", yytext,
                             atoi( yytext ) );
                     }

         {DIGIT}+"."{DIGIT}*        {
                     printf( "A float: %s (%g)\n", yytext,
                             atof( yytext ) );
                     }

         if|then|begin|end|procedure|function        {
                     printf( "A keyword: %s\n", yytext );
                     }

         {ID}        printf( "An identifier: %s\n", yytext );

         "+"|"-"|"*"|"/"   printf( "An operator: %s\n", yytext );

         "{"[^}\n]*"}"     /* eat up one-line comments */

         [ \t\n]+          /* eat up whitespace */

         .           printf( "Unrecognized character: %s\n", yytext );

         %%

         main( argc, argv )
         int argc;
         char **argv;
             {
             ++argv, --argc;  /* skip over program name */
             if ( argc > 0 )
                     yyin = fopen( argv[0], "r" );
             else
                     yyin = stdin;

             yylex();
             }

     This is the beginnings of a simple scanner  for  a  language
     like  Pascal.   It  identifies different types of tokens and
     reports on what it has seen.

     The details of this example will be explained in the follow-
     ing sections.


FORMAT OF THE INPUT FILE

     The flex input file consists of three sections, separated by
     a line with just %% in it:

         definitions
         %%
         rules
         %%
         user code

     The definitions section contains declarations of simple name
     definitions  to  simplify  the  scanner  specification,  and
     declarations of start conditions, which are explained  in  a
     later section.

     Name definitions have the form:

         name definition

     The "name" is a word beginning with a letter  or  an  under-
     score  ('_')  followed by zero or more letters, digits, '_',
     or '-' (dash).  The definition is  taken  to  begin  at  the
     first  non-white-space character following the name and con-
     tinuing to the end of the line.  The definition  can  subse-
     quently  be referred to using "{name}", which will expand to
     "(definition)".  For example,

         DIGIT    [0-9]
         ID       [a-z][a-z0-9]*

     defines "DIGIT" to be a regular expression which  matches  a
     single  digit,  and  "ID"  to  be a regular expression which
     matches a letter followed by zero-or-more letters-or-digits.
     A subsequent reference to

         {DIGIT}+"."{DIGIT}*

     is identical to

         ([0-9])+"."([0-9])*

     and matches one-or-more digits followed by a '.' followed by
     zero-or-more digits.

     The rules section of the flex input  contains  a  series  of
     rules of the form:

         pattern   action

     where the pattern must be unindented  and  the  action  must
     begin on the same line.

     See below for a further description of patterns and actions.

     Finally, the user code section is simply copied to  lex.yy.c
     verbatim.   It  is used for companion routines which call or
     are called by the scanner.  The presence of this section  is
     optional;  if it is missing, the second %% in the input file
     may be skipped, too.

     In the definitions and rules sections, any indented text  or
     text  enclosed in %{ and %} is copied verbatim to the output
     (with the %{}'s removed).  The %{}'s must appear  unindented
     on lines by themselves.

     In the rules section, any indented  or  %{}  text  appearing
     before the first rule may be used to declare variables which
     are local to the scanning routine and  (after  the  declara-
     tions)  code  which  is to be executed whenever the scanning
     routine is entered.  Other indented or %{} text in the  rule
     section  is  still  copied to the output, but its meaning is
     not well-defined and it may well cause  compile-time  errors
     (this feature is present for POSIX compliance; see below for
     other such features).

     In the definitions section, an unindented comment  (i.e.,  a
     line  beginning  with  "/*")  is also copied verbatim to the
     output up to the next "*/".  Also, any line in  the  defini-
     tions  section  beginning  with  '#' is ignored, though this
     style of comment is  deprecated  and  may  go  away  in  the
     future.


PATTERNS

     The patterns in the input are written using an extended  set
     of regular expressions.  These are:

         x          match the character 'x'
         .          any character except newline
         [xyz]      a "character class"; in this case, the pattern
                      matches either an 'x', a 'y', or a 'z'
         [abj-oZ]   a "character class" with a range in it; matches
                      an 'a', a 'b', any letter from 'j' through 'o',
                      or a 'Z'
         [^A-Z]     a "negated character class", i.e., any character
                      but those in the class.  In this case, any
                      character EXCEPT an uppercase letter.
         [^A-Z\n]   any character EXCEPT an uppercase letter or
                      a newline
         r*         zero or more r's, where r is any regular expression
         r+         one or more r's
         r?         zero or one r's (that is, "an optional r")
         r{2,5}     anywhere from two to five r's
         r{2,}      two or more r's
         r{4}       exactly 4 r's
         {name}     the expansion of the "name" definition
                    (see above)
         "[xyz]\"foo"
                    the literal string: [xyz]"foo
         \X         if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v',
                      then the ANSI-C interpretation of \x.
                      Otherwise, a literal 'X' (used to escape
                      operators such as '*')
         \123       the character with octal value 123
         \x2a       the character with hexadecimal value 2a
         (r)        match an r; parentheses are used to override
                      precedence (see below)


         rs         the regular expression r followed by the
                      regular expression s; called "concatenation"


         r|s        either an r or an s


         r/s        an r but only if it is followed by an s.  The
                      s is not part of the matched text.  This type
                      of pattern is called as "trailing context".
         ^r         an r, but only at the beginning of a line
         r$         an r, but only at the end of a line.  Equivalent
                      to "r/\n".


         <s>r       an r, but only in start condition s (see
                    below for discussion of start conditions)
         <s1,s2,s3>r
                    same, but in any of start conditions s1,
                    s2, or s3

         <<EOF>>    an end-of-file
         <s1,s2><<EOF>>
                    an end-of-file when in start condition s1 or s2

     The regular expressions listed above are  grouped  according
     to  precedence, from highest precedence at the top to lowest
     at the bottom.   Those  grouped  together  have  equal  pre-
     cedence.  For example,

         foo|bar*

     is the same as

         (foo)|(ba(r*))

     since the '*' operator has higher precedence than concatena-
     tion, and concatenation higher than alternation ('|').  This
     pattern therefore matches either the  string  "foo"  or  the
     string "ba" followed by zero-or-more r's.  To match "foo" or
     zero-or-more "bar"'s, use:

         foo|(bar)*

     and to match zero-or-more "foo"'s-or-"bar"'s:

         (foo|bar)*


     Some notes on patterns:

     -    A negated character class such as the example  "[^A-Z]"
          above   will   match  a  newline  unless  "\n"  (or  an
          equivalent escape sequence) is one  of  the  characters
          explicitly  present  in  the  negated  character  class
          (e.g., "[^A-Z\n]").  This is unlike how many other reg-
          ular  expression tools treat negated character classes,
          but unfortunately  the  inconsistency  is  historically
          entrenched.   Matching  newlines  means  that a pattern
          like [^"]* can match an entire input  (overflowing  the
          scanner's input buffer) unless there's another quote in
          the input.

     -    A rule can have at most one instance of  trailing  con-
          text (the '/' operator or the '$' operator).  The start
          condition, '^', and "<<EOF>>" patterns can  only  occur
          at the beginning of a pattern, and, as well as with '/'
          and '$', cannot be grouped inside parentheses.   A  '^'
          which  does  not  occur at the beginning of a rule or a
          '$' which does not occur at the end of a rule loses its
          special  properties  and is treated as a normal charac-
          ter.

          The following are illegal:

              foo/bar$
              <sc1>foo<sc2>bar

          Note  that  the  first  of  these,   can   be   written
          "foo/bar\n".

          The following will result in '$' or '^'  being  treated
          as a normal character:

              foo|(bar$)
              foo|^bar

          If what's wanted is a  "foo"  or  a  bar-followed-by-a-
          newline,  the  following could be used (the special '|'
          action is explained below):

              foo      |
              bar$     /* action goes here */

          A similar trick will work for matching a foo or a  bar-
          at-the-beginning-of-a-line.


HOW THE INPUT IS MATCHED

     When the generated scanner is run,  it  analyzes  its  input
     looking  for strings which match any of its patterns.  If it
     finds more than one match, it takes  the  one  matching  the
     most  text  (for  trailing  context rules, this includes the
     length of the trailing part, even though  it  will  then  be
     returned  to the input).  If it finds two or more matches of
     the same length, the rule listed first  in  the  flex  input
     file is chosen.

     Once the match is determined, the text corresponding to  the
     match  (called  the  token)  is made available in the global
     character pointer yytext,  and  its  length  in  the  global
     integer yyleng. The action corresponding to the matched pat-
     tern is  then  executed  (a  more  detailed  description  of
     actions  follows),  and  then the remaining input is scanned
     for another match.

     If no match is found, then the default rule is executed: the
     next character in the input is considered matched and copied
     to the standard output.  Thus, the simplest legal flex input
     is:

         %%

     which generates a scanner that simply copies its input  (one
     character at a time) to its output.


ACTIONS

     Each pattern in a rule has a corresponding action, which can
     be any arbitrary C statement.  The pattern ends at the first
     non-escaped whitespace character; the remainder of the  line
     is  its  action.  If the action is empty, then when the pat-
     tern is matched the input token is  simply  discarded.   For
     example,  here  is  the  specification  for  a program which
     deletes all occurrences of "zap me" from its input:

         %%
         "zap me"

     (It will copy all other characters in the input to the  out-
     put since they will be matched by the default rule.)

     Here is a program which compresses multiple blanks and  tabs
     down  to a single blank, and throws away whitespace found at
     the end of a line:

         %%
         [ \t]+        putchar( ' ' );
         [ \t]+$       /* ignore this token */


     If the action contains a '{', then the action spans till the
     balancing  '}'  is  found, and the action may cross multiple
     lines.  flex knows about C strings and comments and won't be
     fooled  by braces found within them, but also allows actions
     to begin with %{ and will consider the action to be all  the
     text up to the next %} (regardless of ordinary braces inside
     the action).

     An action consisting solely of a vertical  bar  ('|')  means
     "same  as  the  action for the next rule."  See below for an
     illustration.

     Actions can  include  arbitrary  C  code,  including  return
     statements  to  return  a  value  to whatever routine called
     yylex(). Each time yylex() is called it continues processing
     tokens  from  where it last left off until it either reaches
     the end of the file or executes a return.  Once  it  reaches
     an end-of-file, however, then any subsequent call to yylex()
     will simply immediately return, unless yyrestart() is  first
     called (see below).

     Actions are not allowed to modify yytext or yyleng.

     There are a  number  of  special  directives  which  can  be
     included within an action:

     -    ECHO copies yytext to the scanner's output.

     -    BEGIN followed by the name of a start condition  places
          the  scanner  in the corresponding start condition (see
          below).

     -    REJECT directs the scanner to proceed on to the "second
          best"  rule which matched the input (or a prefix of the
          input).  The rule is chosen as described above in  "How
          the  Input  is  Matched",  and yytext and yyleng set up
          appropriately.  It may either be one which  matched  as
          much  text as the originally chosen rule but came later
          in the flex input file, or one which matched less text.
          For example, the following will both count the words in
          the input  and  call  the  routine  special()  whenever
          "frob" is seen:

                      int word_count = 0;
              %%

              frob        special(); REJECT;
              [^ \t\n]+   ++word_count;

          Without the REJECT, any "frob"'s in the input would not
          be  counted  as  words, since the scanner normally exe-
          cutes only one action per token.  Multiple REJECT's are
          allowed,  each  one finding the next best choice to the
          currently active rule.  For example, when the following
          scanner  scans the token "abcd", it will write "abcdab-
          caba" to the output:

              %%
              a        |
              ab       |
              abc      |
              abcd     ECHO; REJECT;
              .|\n     /* eat up any unmatched character */

          (The first three rules share the fourth's action  since
          they use the special '|' action.)  REJECT is a particu-
          larly expensive feature in terms  scanner  performance;
          if  it  is used in any of the scanner's actions it will
          slow down all of the scanner's matching.   Furthermore,
          REJECT  cannot  be  used with the -f or -F options (see
          below).

          Note also that unlike the other special actions, REJECT
          is  a  branch;  code  immediately  following  it in the
          action will not be executed.

     -    yymore() tells  the  scanner  that  the  next  time  it
          matches  a  rule,  the  corresponding  token  should be
          appended onto the current value of yytext  rather  than
          replacing  it.   For  example,  given  the input "mega-
          kludge" the following will write "mega-mega-kludge"  to
          the output:

              %%
              mega-    ECHO; yymore();
              kludge   ECHO;

          First "mega-" is matched  and  echoed  to  the  output.
          Then  "kludge"  is matched, but the previous "mega-" is
          still hanging around at the beginning of yytext so  the
          ECHO  for  the "kludge" rule will actually write "mega-
          kludge".  The presence of  yymore()  in  the  scanner's
          action  entails  a  minor  performance  penalty  in the
          scanner's matching speed.

     -    yyless(n) returns all but the first n characters of the
          current token back to the input stream, where they will
          be rescanned when the scanner looks for the next match.
          yytext  and  yyleng  are  adjusted appropriately (e.g.,
          yyleng will now be equal to n ).  For example,  on  the
          input  "foobar"  the  following will write out "foobar-
          bar":

              %%
              foobar    ECHO; yyless(3);
              [a-z]+    ECHO;

          An argument of  0  to  yyless  will  cause  the  entire
          current  input  string  to  be  scanned  again.  Unless
          you've changed how the scanner will  subsequently  pro-
          cess  its  input  (using BEGIN, for example), this will
          result in an endless loop.

     -    unput(c) puts the  character  c  back  onto  the  input
          stream.   It  will  be the next character scanned.  The
          following action will take the current token and  cause
          it to be rescanned enclosed in parentheses.

              {
              int i;
              unput( ')' );
              for ( i = yyleng - 1; i >= 0; --i )
                  unput( yytext[i] );
              unput( '(' );
              }

          Note that since each unput() puts the  given  character
          back at the beginning of the input stream, pushing back
          strings must be done back-to-front.

     -    input() reads the next character from the input stream.
          For  example,  the  following  is  one  way to eat up C
          comments:

              %%
              "/*"        {
                          register int c;

                          for ( ; ; )
                              {
                              while ( (c = input()) != '*' &&
                                      c != EOF )
                                  ;    /* eat up text of comment */

                              if ( c == '*' )
                                  {
                                  while ( (c = input()) == '*' )
                                      ;
                                  if ( c == '/' )
                                      break;    /* found the end */
                                  }

                              if ( c == EOF )
                                  {
                                  error( "EOF in comment" );
                                  break;
                                  }
                              }
                          }

          (Note that if the scanner is compiled using  C++,  then
          input()  is  instead referred to as yyinput(), in order
          to avoid a name clash with the C++ stream by  the  name
          of input.)

     -    yyterminate() can be used in lieu of a return statement
          in  an action.  It terminates the scanner and returns a
          0 to the scanner's caller, indicating "all done".  Sub-
          sequent  calls  to  the scanner will immediately return
          unless preceded by a call to yyrestart()  (see  below).
          By  default,  yyterminate() is also called when an end-
          of-file is encountered.  It is a macro and may be rede-
          fined.


THE GENERATED SCANNER

     The output of flex is the file lex.yy.c, which contains  the
     scanning  routine yylex(), a number of tables used by it for
     matching tokens, and a number of auxiliary routines and mac-
     ros.  By default, yylex() is declared as follows:

         int yylex()
             {
             ... various definitions and the actions in here ...
             }

     (If your environment supports function prototypes,  then  it
     will  be  "int  yylex(  void  )".)   This  definition may be
     changed by redefining the "YY_DECL" macro.  For example, you
     could use:

         #undef YY_DECL
         #define YY_DECL float lexscan( a, b ) float a, b;

     to give the scanning routine the name lexscan,  returning  a
     float, and taking two floats as arguments.  Note that if you
     give  arguments  to  the  scanning  routine  using  a   K&R-
     style/non-prototyped  function  declaration,  you  must ter-
     minate the definition with a semi-colon (;).

     Whenever yylex() is called, it scans tokens from the  global
     input  file  yyin  (which  defaults to stdin).  It continues
     until it either reaches an end-of-file (at  which  point  it
     returns the value 0) or one of its actions executes a return
     statement.  In  the  former  case,  when  called  again  the
     scanner will immediately return unless yyrestart() is called
     to point yyin at the new input file.   (  yyrestart()  takes
     one  argument, a FILE * pointer.)  In the latter case (i.e.,
     when an action executes a return), the scanner may  then  be
     called again and it will resume scanning where it left off.

     By default (and for purposes  of  efficiency),  the  scanner
     uses  block-reads  rather  than  simple getc() calls to read
     characters from yyin. The nature of how it  gets  its  input
     can   be   controlled  by  redefining  the  YY_INPUT  macro.
     YY_INPUT's           calling           sequence           is
     "YY_INPUT(buf,result,max_size)".   Its action is to place up
     to max_size characters in the character array buf and return
     in  the integer variable result either the number of charac-
     ters read or the constant YY_NULL (0  on  Unix  systems)  to
     indicate  EOF.   The  default YY_INPUT reads from the global
     file-pointer "yyin".

     A sample redefinition of YY_INPUT (in the  definitions  sec-
     tion of the input file):

         %{
         #undef YY_INPUT
         #define YY_INPUT(buf,result,max_size) \
             { \
             int c = getchar(); \
             result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \
             }
         %}

     This definition will change the input  processing  to  occur
     one character at a time.

     You also can add in things like keeping track of  the  input
     line  number  this  way; but don't expect your scanner to go
     very fast.

     When the scanner receives  an  end-of-file  indication  from
     YY_INPUT, it then checks the yywrap() function.  If yywrap()
     returns false (zero), then it is assumed that  the  function
     has  gone  ahead  and  set up yyin to point to another input
     file, and scanning continues.   If  it  returns  true  (non-
     zero),  then  the  scanner  terminates,  returning  0 to its
     caller.

     The default yywrap() always returns 1.  Presently, to  rede-
     fine  it  you must first "#undef yywrap", as it is currently
     implemented as a macro.  As indicated by the hedging in  the
     previous  sentence,  it may be changed to a true function in
     the near future.

     The scanner writes its  ECHO  output  to  the  yyout  global
     (default, stdout), which may be redefined by the user simply
     by assigning it to some other FILE pointer.


START CONDITIONS

     flex  provides  a  mechanism  for  conditionally  activating
     rules.   Any rule whose pattern is prefixed with "<sc>" will
     only be active when the scanner is in  the  start  condition
     named "sc".  For example,

         <STRING>[^"]*        { /* eat up the string body ... */
                     ...
                     }

     will be active only when the  scanner  is  in  the  "STRING"
     start condition, and

         <INITIAL,STRING,QUOTE>\.        { /* handle an escape ... */
                     ...
                     }

     will be active only when  the  current  start  condition  is
     either "INITIAL", "STRING", or "QUOTE".

     Start conditions are declared  in  the  definitions  (first)
     section  of  the input using unindented lines beginning with
     either %s or %x followed by a list  of  names.   The  former
     declares  inclusive  start  conditions, the latter exclusive
     start conditions.  A start condition is activated using  the
     BEGIN  action.   Until  the  next  BEGIN action is executed,
     rules with the given start  condition  will  be  active  and
     rules  with other start conditions will be inactive.  If the
     start condition is inclusive, then rules with no start  con-
     ditions  at  all  will  also be active.  If it is exclusive,
     then only rules qualified with the start condition  will  be
     active.   A  set  of  rules contingent on the same exclusive
     start condition describe a scanner which is  independent  of
     any  of the other rules in the flex input.  Because of this,
     exclusive start conditions make it easy  to  specify  "mini-
     scanners"  which scan portions of the input that are syntac-
     tically different from the rest (e.g., comments).

     If the distinction between  inclusive  and  exclusive  start
     conditions  is still a little vague, here's a simple example
     illustrating the connection between the  two.   The  set  of
     rules:

         %s example
         %%
         <example>foo           /* do something */

     is equivalent to

         %x example
         %%
         <INITIAL,example>foo   /* do something */


     The default rule (to ECHO any unmatched  character)  remains
     active in start conditions.

     BEGIN(0) returns to the original state where only the  rules
     with no start conditions are active.  This state can also be
     referred   to   as   the   start-condition   "INITIAL",   so
     BEGIN(INITIAL)  is  equivalent to BEGIN(0). (The parentheses
     around the start condition name are  not  required  but  are
     considered good style.)

     BEGIN actions can also be given  as  indented  code  at  the
     beginning  of the rules section.  For example, the following
     will cause the scanner to enter the "SPECIAL"  start  condi-
     tion  whenever  yylex()  is  called  and the global variable
     enter_special is true:

                 int enter_special;

         %x SPECIAL
         %%
                 if ( enter_special )
                     BEGIN(SPECIAL);

         <SPECIAL>blahblahblah
         ...more rules follow...



     To illustrate the  uses  of  start  conditions,  here  is  a
     scanner  which  provides  two different interpretations of a
     string like "123.456".  By default it will treat  it  as  as
     three  tokens,  the  integer  "123",  a  dot  ('.'), and the
     integer "456".  But if the string is preceded earlier in the
     line  by  the  string  "expect-floats" it will treat it as a
     single token, the floating-point number 123.456:

         %{
         #include <math.h>
         %}
         %s expect

         %%
         expect-floats        BEGIN(expect);

         <expect>[0-9]+"."[0-9]+      {
                     printf( "found a float, = %f\n",
                             atof( yytext ) );
                     }
         <expect>\n           {
                     /* that's the end of the line, so
                      * we need another "expect-number"
                      * before we'll recognize any more
                      * numbers
                      */
                     BEGIN(INITIAL);
                     }

         [0-9]+      {
                     printf( "found an integer, = %d\n",
                             atoi( yytext ) );
                     }

         "."         printf( "found a dot\n" );

     Here is a scanner which recognizes (and discards) C comments
     while maintaining a count of the current input line.

         %x comment
         %%
                 int line_num = 1;

         "/*"         BEGIN(comment);

         <comment>[^*\n]*        /* eat anything that's not a '*' */
         <comment>"*"+[^*/\n]*   /* eat up '*'s not followed by '/'s */
         <comment>\n             ++line_num;
         <comment>"*"+"/"        BEGIN(INITIAL);

     Note that start-conditions names are really  integer  values
     and  can  be  stored  as  such.   Thus,  the  above could be
     extended in the following fashion:

         %x comment foo
         %%
                 int line_num = 1;
                 int comment_caller;

         "/*"         {
                      comment_caller = INITIAL;
                      BEGIN(comment);
                      }

         ...

         <foo>"/*"    {
                      comment_caller = foo;
                      BEGIN(comment);
                      }

         <comment>[^*\n]*        /* eat anything that's not a '*' */
         <comment>"*"+[^*/\n]*   /* eat up '*'s not followed by '/'s */
         <comment>\n             ++line_num;
         <comment>"*"+"/"        BEGIN(comment_caller);

     One can then implement a "stack" of start  conditions  using
     an  array  of integers.  (It is likely that such stacks will
     become a full-fledged flex feature in  the  future.)   Note,
     though,  that  start  conditions do not have their own name-
     space; %s's and %x's declare names in the  same  fashion  as
     #define's.


MULTIPLE INPUT BUFFERS

     Some scanners (such as those which support "include"  files)
     require   reading  from  several  input  streams.   As  flex
     scanners do a large amount of buffering, one cannot  control
     where  the  next input will be read from by simply writing a
     YY_INPUT  which  is  sensitive  to  the  scanning   context.
     YY_INPUT  is only called when the scanner reaches the end of
     its buffer, which may be a long time after scanning a state-
     ment such as an "include" which requires switching the input
     source.

     To negotiate  these  sorts  of  problems,  flex  provides  a
     mechanism  for creating and switching between multiple input
     buffers.  An input buffer is created by using:

         YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )

     which takes a FILE pointer and a size and creates  a  buffer
     associated with the given file and large enough to hold size
     characters (when in doubt, use YY_BUF_SIZE  for  the  size).
     It  returns  a  YY_BUFFER_STATE  handle,  which  may then be
     passed to other routines:

         void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )

     switches the scanner's input  buffer  so  subsequent  tokens
     will  come  from new_buffer. Note that yy_switch_to_buffer()
     may be used by yywrap() to  sets  things  up  for  continued
     scanning, instead of opening a new file and pointing yyin at
     it.

         void yy_delete_buffer( YY_BUFFER_STATE buffer )

     is used to reclaim the storage associated with a buffer.

     yy_new_buffer() is an alias for yy_create_buffer(), provided
     for  compatibility  with  the  C++ use of new and delete for
     creating and destroying dynamic objects.

     Finally,   the    YY_CURRENT_BUFFER    macro    returns    a
     YY_BUFFER_STATE handle to the current buffer.

     Here is an example of using these  features  for  writing  a
     scanner  which expands include files (the <<EOF>> feature is
     discussed below):

         /* the "incl" state is used for picking up the name
          * of an include file
          */
         %x incl

         %{
         #define MAX_INCLUDE_DEPTH 10
         YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
         int include_stack_ptr = 0;
         %}

         %%
         include             BEGIN(incl);

         [a-z]+              ECHO;
         [^a-z\n]*\n?        ECHO;

         <incl>[ \t]*      /* eat the whitespace */
         <incl>[^ \t\n]+   { /* got the include file name */
                 if ( include_stack_ptr >= MAX_INCLUDE_DEPTH )
                     {
                     fprintf( stderr, "Includes nested too deeply" );
                     exit( 1 );
                     }

                 include_stack[include_stack_ptr++] =
                     YY_CURRENT_BUFFER;

                 yyin = fopen( yytext, "r" );

                 if ( ! yyin )
                     error( ... );

                 yy_switch_to_buffer(
                     yy_create_buffer( yyin, YY_BUF_SIZE ) );

                 BEGIN(INITIAL);
                 }

         <<EOF>> {
                 if ( --include_stack_ptr < 0 )
                     {
                     yyterminate();
                     }

                 else
                     yy_switch_to_buffer(
                          include_stack[include_stack_ptr] );
                 }



END-OF-FILE RULES

     The special rule "<<EOF>>" indicates actions which are to be
     taken  when  an  end-of-file  is  encountered  and  yywrap()
     returns non-zero (i.e., indicates no further files  to  pro-
     cess).  The action must finish by doing one of four things:

     -    the  special  YY_NEW_FILE  action,  if  yyin  has  been
          pointed at a new file to process;

     -    a return statement;

     -    the special yyterminate() action;

     -    or,    switching    to    a    new     buffer     using
          yy_switch_to_buffer() as shown in the example above.

     <<EOF>> rules may not be used with other patterns; they  may
     only  be  qualified  with a list of start conditions.  If an
     unqualified <<EOF>> rule is given, it applies to  all  start
     conditions  which  do  not already have <<EOF>> actions.  To
     specify an <<EOF>> rule for only the  initial  start  condi-
     tion, use

         <INITIAL><<EOF>>


     These rules are useful for  catching  things  like  unclosed
     comments.  An example:

         %x quote
         %%

         ...other rules for dealing with quotes...

         <quote><<EOF>>   {
                  error( "unterminated quote" );
                  yyterminate();
                  }
         <<EOF>>  {
                  if ( *++filelist )
                      {
                      yyin = fopen( *filelist, "r" );
                      YY_NEW_FILE;
                      }
                  else
                     yyterminate();
                  }



MISCELLANEOUS MACROS

     The macro YY_USER_ACTION can  be  redefined  to  provide  an
     action  which is always executed prior to the matched rule's
     action.  For example, it could be #define'd to call  a  rou-
     tine to convert yytext to lower-case.

     The macro YY_USER_INIT may be redefined to provide an action
     which  is  always executed before the first scan (and before
     the scanner's internal initializations are done).  For exam-
     ple,  it  could  be used to call a routine to read in a data
     table or open a logging file.

     In the generated scanner, the actions are  all  gathered  in
     one  large  switch  statement  and separated using YY_BREAK,
     which may be redefined.  By default, it is simply a "break",
     to  separate  each  rule's action from the following rule's.
     Redefining  YY_BREAK  allows,  for  example,  C++  users  to
     #define  YY_BREAK  to  do  nothing (while being very careful
     that every rule ends with a "break" or a "return"!) to avoid
     suffering  from unreachable statement warnings where because
     a rule's action ends with "return", the YY_BREAK is inacces-
     sible.


INTERFACING WITH YACC

     One of the main uses of flex is as a companion to  the  yacc
     parser-generator.   yacc  parsers  expect  to call a routine
     named yylex() to find the next input token.  The routine  is
     supposed  to  return  the  type of the next token as well as
     putting any associated value in the global  yylval.  To  use
     flex  with  yacc,  one  specifies  the  -d option to yacc to
     instruct it to generate the file y.tab.h containing  defini-
     tions  of all the %tokens appearing in the yacc input.  This
     file is then included in the flex scanner.  For example,  if
     one of the tokens is "TOK_NUMBER", part of the scanner might
     look like:

         %{
         #include "y.tab.h"
         %}

         %%

         [0-9]+        yylval = atoi( yytext ); return TOK_NUMBER;



TRANSLATION TABLE

     In the name of POSIX compliance, flex supports a translation
     table  for  mapping input characters into groups.  The table
     is specified in the first  section,  and  its  format  looks
     like:

         %t
         1        abcd
         2        ABCDEFGHIJKLMNOPQRSTUVWXYZ
         52       0123456789
         6        \t\ \n
         %t

     This example specifies that the characters  'a',  'b',  'c',
     and  'd'  are  to  all  be  lumped into group #1, upper-case
     letters in group #2, digits in group #52, tabs, blanks,  and
     newlines  into group #6, and no other characters will appear
     in the patterns.  The group numbers are actually disregarded
     by  flex;  %t  serves,  though, to lump characters together.
     Given the above table, for example, the pattern "a(AA)*5" is
     equivalent  to "d(ZQ)*0".  They both say, "match any charac-
     ter in group #1, followed by zero-or-more pairs  of  charac-
     ters from group #2, followed by a character from group #52."
     Thus %t provides a crude  way  for  introducing  equivalence
     classes into the scanner specification.

     Note that  the  -i  option  (see  below)  coupled  with  the
     equivalence  classes which flex automatically generates take
     care of virtually all the instances when one might  consider
     using %t. But what the hell, it's there if you want it.


OPTIONS

     flex has the following options:

     -b   Generate  backtracking  information  to  lex.backtrack.
          This  is  a  list of scanner states which require back-
          tracking and the input characters on which they do  so.
          By adding rules one can remove backtracking states.  If
          all backtracking states are eliminated and -f or -F  is
          used, the generated scanner will run faster (see the -p
          flag).  Only users who wish to squeeze every last cycle
          out  of  their  scanners  need worry about this option.
          (See the section on PERFORMANCE CONSIDERATIONS below.)

     -c   is a do-nothing, deprecated option included  for  POSIX
          compliance.

          NOTE: in previous releases of flex -c specified  table-
          compression  options.   This functionality is now given
          by the -C flag.  To ease the the impact of this change,
          when  flex encounters -c, it currently issues a warning
          message and assumes that -C was  desired  instead.   In
          the future this "promotion" of -c to -C will go away in
          the name of full POSIX  compliance  (unless  the  POSIX
          meaning is removed first).

     -d   makes the generated scanner run in debug  mode.   When-
          ever   a   pattern   is   recognized   and  the  global
          yy_flex_debug is non-zero (which is the  default),  the
          scanner will write to stderr a line of the form:

              --accepting rule at line 53 ("the matched text")

          The line number refers to the location of the  rule  in
          the  file defining the scanner (i.e., the file that was
          fed to flex).  Messages are  also  generated  when  the
          scanner  backtracks,  accepts the default rule, reaches
          the end of its input buffer (or encounters  a  NUL;  at
          this  point,  the  two  look  the  same  as  far as the
          scanner's concerned), or reaches an end-of-file.

     -f   specifies (take your pick) full table or fast  scanner.
          No  table compression is done.  The result is large but
          fast.  This option is equivalent to -Cf (see below).

     -i   instructs flex to generate a case-insensitive  scanner.
          The  case  of  letters given in the flex input patterns
          will be ignored,  and  tokens  in  the  input  will  be
          matched  regardless of case.  The matched text given in
          yytext will have the preserved case (i.e., it will  not
          be folded).

     -n   is another do-nothing, deprecated option included  only
          for POSIX compliance.

     -p   generates a performance report to stderr.   The  report
          consists  of  comments  regarding  features of the flex
          input file which will cause a loss  of  performance  in
          the resulting scanner.  Note that the use of REJECT and
          variable trailing context  (see  the  BUGS  section  in
          flex(1)) entails a substantial performance penalty; use
          of yymore(), the ^ operator, and  the  -I  flag  entail
          minor performance penalties.

     -s   causes the default rule (that unmatched  scanner  input
          is  echoed to stdout) to be suppressed.  If the scanner
          encounters input that does not match any of its  rules,
          it  aborts  with  an  error.  This option is useful for
          finding holes in a scanner's rule set.

     -t   instructs flex to write the  scanner  it  generates  to
          standard output instead of lex.yy.c.

     -v   specifies that flex should write to stderr a summary of
          statistics regarding the scanner it generates.  Most of
          the statistics are meaningless to the casual flex user,
          but  the  first  line  identifies  the version of flex,
          which is useful for figuring out where you  stand  with
          respect  to  patches and new releases, and the next two
          lines give the date when the scanner was created and  a
          summary of the flags which were in effect.

     -F   specifies that the fast  scanner  table  representation
          should  be  used.  This representation is about as fast
          as the full table representation  (-f),  and  for  some
          sets  of patterns will be considerably smaller (and for
          others, larger).  In general, if the pattern  set  con-
          tains  both  "keywords"  and  a catch-all, "identifier"
          rule, such as in the set:

              "case"    return TOK_CASE;
              "switch"  return TOK_SWITCH;
              ...
              "default" return TOK_DEFAULT;
              [a-z]+    return TOK_ID;

          then you're better off using the full table representa-
          tion.  If only the "identifier" rule is present and you
          then use a hash table or some such to detect  the  key-
          words, you're better off using -F.

          This option is equivalent to -CF (see below).

     -I   instructs flex  to  generate  an  interactive  scanner.
          Normally,  scanners generated by flex always look ahead
          one character before deciding  that  a  rule  has  been
          matched.   At  the cost of some scanning overhead, flex
          will generate a scanner which  only  looks  ahead  when
          needed.   Such  scanners are called interactive because
          if you want to write a scanner for an interactive  sys-
          tem such as a command shell, you will probably want the
          user's input to  be  terminated  with  a  newline,  and
          without  -I  the  user will have to type a character in
          addition to the newline in order to  have  the  newline
          recognized.  This leads to dreadful interactive perfor-
          mance.

          If all this seems  to  confusing,  here's  the  general
          rule:  if  a  human  will  be  typing  in input to your
          scanner, use -I, otherwise don't;  if  you  don't  care
          about   squeezing  the  utmost  performance  from  your
          scanner and you don't  want  to  make  any  assumptions
          about the input to your scanner, use -I.

          Note, -I cannot be used in  conjunction  with  full  or
          fast tables, i.e., the -f, -F, -Cf, or -CF flags.

     -L   instructs  flex  not  to  generate  #line   directives.
          Without this option, flex peppers the generated scanner
          with #line directives so error messages in the  actions
          will  be correctly located with respect to the original
          flex input file, and not to the fairly meaningless line
          numbers  of  lex.yy.c.  (Unfortunately  flex  does  not
          presently generate the necessary directives to  "retar-
          get" the line numbers for those parts of lex.yy.c which
          it generated.  So if there is an error in the generated
          code, a meaningless line number is reported.)

     -T   makes flex run in trace mode.  It will generate  a  lot
          of  messages to stdout concerning the form of the input
          and the resultant non-deterministic  and  deterministic
          finite  automata.   This  option  is  mostly for use in
          maintaining flex.

     -8   instructs flex to generate an 8-bit scanner, i.e.,  one
          which  can  recognize 8-bit characters.  On some sites,
          flex is installed with this option as the default.   On
          others,  the default is 7-bit characters.  To see which
          is  the  case,  check  the  verbose  (-v)  output   for
          "equivalence  classes  created".  If the denominator of
          the number shown is 128, then by default flex  is  gen-
          erating  7-bit  characters.   If  it  is  256, then the
          default is 8-bit characters and  the  -8  flag  is  not
          required  (but  may  be a good idea to keep the scanner
          specification portable).  Feeding a 7-bit scanner 8-bit
          characters  will  result in infinite loops, bus errors,
          or other such fireworks, so  when  in  doubt,  use  the
          flag.  Note that if equivalence classes are used, 8-bit
          scanners take only slightly more table space than 7-bit
          scanners  (128  bytes,  to  be  exact);  if equivalence
          classes are not used, however, then the tables may grow
          up to twice their 7-bit size.

     -C[efmF]
          controls the degree of table compression.
          -Ce directs  flex  to  construct  equivalence  classes,
          i.e.,  sets  of characters which have identical lexical
          properties (for example,  if  the  only  appearance  of
          digits  in  the  flex  input  is in the character class
          "[0-9]" then the digits '0', '1', ..., '9' will all  be
          put   in  the  same  equivalence  class).   Equivalence
          classes usually give dramatic reductions in  the  final
          table/object file sizes (typically a factor of 2-5) and
          are pretty cheap performance-wise  (one  array  look-up
          per character scanned).

          -Cf specifies that the full scanner  tables  should  be
          generated - flex should not compress the tables by tak-
          ing advantages of similar transition functions for dif-
          ferent states.

          -CF specifies that the alternate fast scanner represen-
          tation  (described  above  under the -F flag) should be
          used.

          -Cm directs flex to construct meta-equivalence classes,
          which  are  sets of equivalence classes (or characters,
          if equivalence classes are not  being  used)  that  are
          commonly  used  together.  Meta-equivalence classes are
          often a big win when using compressed tables, but  they
          have  a  moderate  performance  impact (one or two "if"
          tests and one array look-up per character scanned).

          A lone -C specifies that the scanner tables  should  be
          compressed  but  neither  equivalence classes nor meta-
          equivalence classes should be used.

          The options -Cf or  -CF  and  -Cm  do  not  make  sense
          together - there is no opportunity for meta-equivalence
          classes if the table is not being  compressed.   Other-
          wise the options may be freely mixed.

          The default setting is -Cem, which specifies that  flex
          should   generate   equivalence   classes   and   meta-
          equivalence classes.  This setting provides the highest
          degree   of  table  compression.   You  can  trade  off
          faster-executing scanners at the cost of larger  tables
          with the following generally being true:

              slowest & smallest
                    -Cem
                    -Cm
                    -Ce
                    -C
                    -C{f,F}e
                    -C{f,F}
              fastest & largest

          Note that scanners with the smallest tables are usually
          generated and compiled the quickest, so during develop-
          ment you will usually want to use the default,  maximal
          compression.

          -Cfe is often a good compromise between speed and  size
          for production scanners.

          -C options are not cumulative;  whenever  the  flag  is
          encountered, the previous -C settings are forgotten.

     -Sskeleton_file
          overrides the default skeleton  file  from  which  flex
          constructs its scanners.  You'll never need this option
          unless you are doing flex maintenance or development.


PERFORMANCE CONSIDERATIONS

     The main design goal of  flex  is  that  it  generate  high-
     performance  scanners.   It  has  been optimized for dealing
     well with large sets of rules.  Aside from  the  effects  of
     table compression on scanner speed outlined above, there are
     a  number  of  options/actions  which  degrade  performance.
     These are, from most expensive to least:

         REJECT

         pattern sets that require backtracking
         arbitrary trailing context

         '^' beginning-of-line operator
         yymore()

     with the first three all being quite expensive and the  last
     two being quite cheap.

     REJECT should be avoided at all costs  when  performance  is
     important.  It is a particularly expensive option.

     Getting rid of backtracking is messy and  often  may  be  an
     enormous amount of work for a complicated scanner.  In prin-
     cipal, one begins  by  using  the  -b  flag  to  generate  a
     lex.backtrack file.  For example, on the input

         %%
         foo        return TOK_KEYWORD;
         foobar     return TOK_KEYWORD;

     the file looks like:

         State #6 is non-accepting -
          associated rule line numbers:
                2       3

          out-transitions: [ o ]
          jam-transitions: EOF [ \001-n  p-\177 ]

         State #8 is non-accepting -
          associated rule line numbers:
                3
          out-transitions: [ a ]
          jam-transitions: EOF [ \001-`  b-\177 ]

         State #9 is non-accepting -
          associated rule line numbers:
                3
          out-transitions: [ r ]
          jam-transitions: EOF [ \001-q  s-\177 ]

         Compressed tables always backtrack.

     The first few lines tell us that there's a scanner state  in
     which  it  can  make  a  transition on an 'o' but not on any
     other character,  and  that  in  that  state  the  currently
     scanned text does not match any rule.  The state occurs when
     trying to match the rules found at lines  2  and  3  in  the
     input  file.  If the scanner is in that state and then reads
     something other than an 'o', it will have  to  backtrack  to
     find  a rule which is matched.  With a bit of headscratching
     one can see that this must be the state it's in when it  has
     seen  "fo".   When this has happened, if anything other than
     another 'o' is seen, the scanner will have  to  back  up  to
     simply match the 'f' (by the default rule).

     The comment regarding State #8 indicates there's  a  problem
     when  "foob"  has  been  scanned.   Indeed, on any character
     other than a 'b', the scanner will have to back up to accept
     "foo".   Similarly,  the  comment for State #9 concerns when
     "fooba" has been scanned.

     The final comment reminds us that there's no point going  to
     all  the  trouble  of  removing  backtracking from the rules
     unless we're using -f or -F, since  there's  no  performance
     gain doing so with compressed scanners.

     The way to remove the backtracking is to add "error" rules:

         %%
         foo         return TOK_KEYWORD;
         foobar      return TOK_KEYWORD;

         fooba       |
         foob        |
         fo          {
                     /* false alarm, not really a keyword */
                     return TOK_ID;
                     }


     Eliminating backtracking among a list of keywords  can  also
     be done using a "catch-all" rule:

         %%
         foo         return TOK_KEYWORD;
         foobar      return TOK_KEYWORD;

         [a-z]+      return TOK_ID;

     This is usually the best solution when appropriate.

     Backtracking messages tend to cascade.  With  a  complicated
     set  of rules it's not uncommon to get hundreds of messages.
     If one can decipher them, though,  it  often  only  takes  a
     dozen or so rules to eliminate the backtracking (though it's
     easy to make a mistake and have an error  rule  accidentally
     match a valid token.  A possible future flex feature will be
     to automatically add rules to eliminate backtracking).

     Variable trailing context (where both the leading and trail-
     ing  parts  do  not  have a fixed length) entails almost the
     same performance loss as  REJECT  (i.e.,  substantial).   So
     when possible a rule like:

         %%
         mouse|rat/(cat|dog)   run();

     is better written:

         %%
         mouse/cat|dog         run();
         rat/cat|dog           run();

     or as

         %%
         mouse|rat/cat         run();
         mouse|rat/dog         run();

     Note that here the special '|' action does not  provide  any
     savings,  and  can  even  make  things  worse  (see  BUGS in
     flex(1)).

     Another area where the user can increase a scanner's perfor-
     mance  (and  one that's easier to implement) arises from the
     fact that the longer the  tokens  matched,  the  faster  the
     scanner will run.  This is because with long tokens the pro-
     cessing of most input characters takes place in the  (short)
     inner  scanning  loop, and does not often have to go through
     the additional work of setting up the  scanning  environment
     (e.g.,  yytext)  for  the  action.  Recall the scanner for C
     comments:

         %x comment
         %%
                 int line_num = 1;

         "/*"         BEGIN(comment);

         <comment>[^*\n]*
         <comment>"*"+[^*/\n]*
         <comment>\n             ++line_num;
         <comment>"*"+"/"        BEGIN(INITIAL);

     This could be sped up by writing it as:

         %x comment
         %%
                 int line_num = 1;

         "/*"         BEGIN(comment);

         <comment>[^*\n]*
         <comment>[^*\n]*\n      ++line_num;
         <comment>"*"+[^*/\n]*
         <comment>"*"+[^*/\n]*\n ++line_num;
         <comment>"*"+"/"        BEGIN(INITIAL);

     Now instead of each  newline  requiring  the  processing  of
     another  action,  recognizing  the newlines is "distributed"
     over the other rules to keep the matched  text  as  long  as
     possible.   Note  that  adding  rules does not slow down the
     scanner!  The speed of the scanner  is  independent  of  the
     number  of  rules or (modulo the considerations given at the
     beginning of this section) how  complicated  the  rules  are
     with regard to operators such as '*' and '|'.

     A final example in speeding up a scanner: suppose  you  want
     to  scan through a file containing identifiers and keywords,
     one per line and with no other  extraneous  characters,  and
     recognize all the keywords.  A natural first approach is:

         %%
         asm      |
         auto     |
         break    |
         ... etc ...
         volatile |
         while    /* it's a keyword */

         .|\n     /* it's not a keyword */

     To eliminate the back-tracking, introduce a catch-all rule:

         %%
         asm      |
         auto     |
         break    |
         ... etc ...
         volatile |
         while    /* it's a keyword */

         [a-z]+   |
         .|\n     /* it's not a keyword */

     Now, if it's guaranteed that there's exactly  one  word  per
     line,  then  we  can reduce the total number of matches by a
     half by merging in the recognition of newlines with that  of
     the other tokens:

         %%
         asm\n    |
         auto\n   |
         break\n  |
         ... etc ...
         volatile\n |
         while\n  /* it's a keyword */

         [a-z]+\n |
         .|\n     /* it's not a keyword */

     One has to be careful here,  as  we  have  now  reintroduced
     backtracking into the scanner.  In particular, while we know
     that there will never be any characters in the input  stream
     other  than letters or newlines, flex can't figure this out,
     and it will plan for possibly needing backtracking  when  it
     has  scanned a token like "auto" and then the next character
     is something other than a newline or a  letter.   Previously
     it  would  then  just match the "auto" rule and be done, but
     now it has no "auto" rule, only a "auto\n" rule.   To  elim-
     inate  the  possibility  of  backtracking,  we  could either
     duplicate all rules but without final newlines, or, since we
     never  expect to encounter such an input and therefore don't
     how it's classified, we can  introduce  one  more  catch-all
     rule, this one which doesn't include a newline:

         %%
         asm\n    |
         auto\n   |
         break\n  |
         ... etc ...
         volatile\n |
         while\n  /* it's a keyword */

         [a-z]+\n |
         [a-z]+   |
         .|\n     /* it's not a keyword */

     Compiled with -Cf, this is about as fast as one  can  get  a
     flex scanner to go for this particular problem.

     A final note:  flex is slow when  matching  NUL's,  particu-
     larly  when  a  token contains multiple NUL's.  It's best to
     write rules which match short amounts of text if it's  anti-
     cipated that the text will often include NUL's.


INCOMPATIBILITIES WITH LEX AND POSIX

     flex is a rewrite of the Unix lex tool (the two  implementa-
     tions  do  not share any code, though), with some extensions
     and incompatibilities, both of which are of concern to those
     who  wish to write scanners acceptable to either implementa-
     tion.  At present, the POSIX lex draft is very close to  the
     original lex implementation, so some of these incompatibili-
     ties are also in conflict with the  POSIX  draft.   But  the
     intent  is  that except as noted below, flex as it presently
     stands will ultimately be POSIX conformant (i.e., that those
     areas  of  conflict with the POSIX draft will be resolved in
     flex's favor).  Please bear in mind that  all  the  comments
     which  follow are with regard to the POSIX draft standard of
     Summer 1989, and  not  the  final  document  (or  subsequent
     drafts); they are included so flex users can be aware of the
     standardization issues and those areas where flex may in the
     near  future  undergo  changes incompatible with its current
     definition.

     flex is fully compatible with lex with the following  excep-
     tions:

     -    The undocumented lex scanner internal variable yylineno
          is  not  supported.   It  is  difficult to support this
          option efficiently, since it requires  examining  every
          character  scanned  and reexamining the characters when
          the scanner backs up.  Things get more complicated when
          the  end  of  buffer  or  file  is  reached or a NUL is
          scanned (since the scan must then be restarted with the
          proper  line  number  count),  or  the  user  uses  the
          yyless(), unput(), or REJECT actions, or  the  multiple
          input buffer functions.

          The fix is to add rules which, upon seeing  a  newline,
          increment  yylineno.   This is usually an easy process,
          though it can be a drag if some  of  the  patterns  can
          match multiple newlines along with other characters.

          yylineno is not part of the POSIX draft.

     -    The input() routine is not redefinable, though  it  may
          be  called  to  read  characters following whatever has
          been matched by a rule.  If input() encounters an  end-
          of-file  the  normal  yywrap()  processing  is done.  A
          ``real'' end-of-file is returned by input() as EOF.

          Input is instead controlled by redefining the  YY_INPUT
          macro.

          The flex restriction that input() cannot  be  redefined
          is in accordance with the POSIX draft, but YY_INPUT has
          not yet been accepted  into  the  draft  (and  probably
          won't;  it looks like the draft will simply not specify
          any way of controlling the scanner's input  other  than
          by making an initial assignment to yyin).

     -    flex scanners do not use stdio for input.   Because  of
          this,  when  writing  an  interactive  scanner one must
          explicitly call fflush() on the stream associated  with
          the terminal after writing out a prompt.  With lex such
          writes are automatically flushed since lex scanners use
          getchar() for their input.  Also, when writing interac-
          tive scanners with flex, the -I flag must be used.

     -    flex scanners are not as reentrant as lex scanners.  In
          particular,  if  you have an interactive scanner and an
          interrupt handler which long-jumps out of the  scanner,
          and  the  scanner is subsequently called again, you may
          get the following message:

              fatal flex scanner internal error--end of buffer missed

          To reenter the scanner, first use

              yyrestart( yyin );


     -    output() is not supported.  Output from the ECHO  macro
          is done to the file-pointer yyout (default stdout).

          The POSIX  draft  mentions  that  an  output()  routine
          exists  but  currently  gives  no details as to what it
          does.

     -    lex does not support exclusive start  conditions  (%x),
          though they are in the current POSIX draft.

     -    When definitions are expanded, flex  encloses  them  in
          parentheses.  With lex, the following:

              NAME    [A-Z][A-Z0-9]*
              %%
              foo{NAME}?      printf( "Found it\n" );
              %%

          will not match the string "foo" because when the  macro
          is  expanded  the rule is equivalent to "foo[A-Z][A-Z0-
          9]*?"  and the precedence is such that the '?' is asso-
          ciated  with  "[A-Z0-9]*".  With flex, the rule will be
          expanded to "foo([A-Z][A-Z0-9]*)?" and  so  the  string
          "foo" will match.  Note that because of this, the ^, $,
          <s>, /, and <<EOF>> operators cannot be used in a  flex
          definition.

          The POSIX draft interpretation is the same as flex's.

     -    To specify a character class which matches anything but
          a  left  bracket  (']'),  in lex one can use "[^]]" but
          with flex one must use "[^\]]".  The latter works  with
          lex, too.

     -    The lex %r (generate a Ratfor scanner)  option  is  not
          supported.  It is not part of the POSIX draft.

     -    If you are providing your  own  yywrap()  routine,  you
          must  include a "#undef yywrap" in the definitions sec-
          tion (section 1).  Note that the "#undef" will have  to
          be enclosed in %{}'s.

          The POSIX draft specifies that yywrap() is  a  function
          and  this is very unlikely to change; so flex users are
          warned that yywrap() is likely to be changed to a func-
          tion in the near future.

     -    After a call to unput(), yytext and  yyleng  are  unde-
          fined until the next token is matched.  This is not the
          case with lex or the present POSIX draft.

     -    The precedence of the {} (numeric  range)  operator  is
          different.   lex  interprets  "abc{1,3}" as "match one,
          two, or  three  occurrences  of  'abc'",  whereas  flex
          interprets  it  as "match 'ab' followed by one, two, or
          three occurrences of 'c'".  The latter is in  agreement
          with the current POSIX draft.

     -    The precedence of the ^  operator  is  different.   lex
          interprets  "^foo|bar"  as  "match  either 'foo' at the
          beginning of a line, or 'bar' anywhere",  whereas  flex
          interprets  it  as "match either 'foo' or 'bar' if they
          come at the beginning of a line".   The  latter  is  in
          agreement with the current POSIX draft.

     -    To refer to yytext outside of the scanner source  file,
          the  correct  definition  with  flex  is  "extern  char
          *yytext" rather than "extern char yytext[]".   This  is
          contrary  to  the  current  POSIX  draft but a point on
          which flex will not be changing, as the array represen-
          tation  entails  a  serious performance penalty.  It is
          hoped that the POSIX draft will be emended  to  support
          the  flex  variety  of declaration (as this is a fairly
          painless change to require of lex users).

     -    yyin is initialized by lex to be stdin;  flex,  on  the
          other  hand,  initializes yyin to NULL and then assigns
          it to stdin the first time the scanner is called,  pro-
          viding yyin has not already been assigned to a non-NULL
          value.  The difference is subtle, but the net effect is
          that  with  flex  scanners,  yyin does not have a valid
          value until the scanner has been called.

     -    The special table-size declarations  such  as  %a  sup-
          ported  by  lex are not required by flex scanners; flex
          ignores them.

     -    The name FLEX_SCANNER is #define'd so scanners  may  be
          written for use with either flex or lex.

     The following flex features are not included in lex  or  the
     POSIX draft standard:

         yyterminate()
         <<EOF>>
         YY_DECL
         #line directives
         %{}'s around actions
         yyrestart()
         comments beginning with '#' (deprecated)
         multiple actions on a line

     This last feature refers to the fact that with flex you  can
     put  multiple actions on the same line, separated with semi-
     colons, while with lex, the following

         foo    handle_foo(); ++num_foos_seen;

     is (rather surprisingly) truncated to

         foo    handle_foo();

     flex does not truncate the action.   Actions  that  are  not
     enclosed  in  braces are simply terminated at the end of the
     line.


DIAGNOSTICS

     reject_used_but_not_detected          undefined           or
     yymore_used_but_not_detected  undefined  -  These errors can
     occur at compile time.  They indicate that the scanner  uses
     REJECT  or yymore() but that flex failed to notice the fact,
     meaning that flex scanned the first two sections looking for
     occurrences  of  these  actions  and failed to find any, but
     somehow you snuck some in (via a #include  file,  for  exam-
     ple).  Make an explicit reference to the action in your flex
     input  file.   (Note  that  previously  flex   supported   a
     %used/%unused  mechanism for dealing with this problem; this
     feature is still supported but now deprecated, and  will  go
     away  soon unless the author hears from people who can argue
     compellingly that they need it.)

     flex scanner jammed - a scanner compiled with -s has encoun-
     tered  an  input  string  which wasn't matched by any of its
     rules.

     flex input buffer overflowed -  a  scanner  rule  matched  a
     string  long enough to overflow the scanner's internal input
     buffer (16K bytes by default - controlled by YY_BUF_SIZE  in
     "flex.skel".   Note  that  to  redefine this macro, you must
     first #undefine it).

     scanner  requires  -8  flag  -  Your  scanner  specification
     includes  recognizing  8-bit  characters  and  you  did  not
     specify the -8 flag (and your site has  not  installed  flex
     with -8 as the default).

     fatal flex scanner internal error--end of  buffer  missed  -
     This  can  occur  in  an  scanner which is reentered after a
     long-jump has jumped out (or over) the scanner's  activation
     frame.  Before reentering the scanner, use:

         yyrestart( yyin );


     too many %t classes! - You managed to put every single char-
     acter  into  its  own %t class.  flex requires that at least
     one of the classes share characters.


DEFICIENCIES / BUGS

     See flex(1).


SEE ALSO

     flex(1), lex(1), yacc(1), sed(1), awk(1).

     M. E. Lesk and E. Schmidt, LEX - Lexical Analyzer Generator


AUTHOR

     Vern Paxson, with the help of many ideas and  much  inspira-
     tion  from Van Jacobson.  Original version by Jef Poskanzer.
     The fast table representation is a partial implementation of
     a  design done by Van Jacobson.  The implementation was done
     by Kevin Gong and Vern Paxson.

     Thanks to the many flex beta-testers, feedbackers, and  con-
     tributors,  especially  Casey  Leedom, benson@odi.com, Keith
     Bostic, Frederic Brehm, Nick  Christopher,  Jason  Coughlin,
     Scott  David Daniels, Leo Eskin, Chris Faylor, Eric Goldman,
     Eric Hughes, Jeffrey R. Jones, Kevin B. Kenny,  Ronald  Lam-
     precht,  Greg  Lee, Craig Leres, Mohamed el Lozy, Jim Meyer-
     ing, Marc Nozell, Esmond Pitt, Jef Poskanzer,  Jim  Roskind,
     Dave  Tallman,  Frank Whaley, Ken Yap, and those whose names
     have slipped my marginal  mail-archiving  skills  but  whose
     contributions are appreciated all the same.

     Thanks to Keith Bostic, John Gilmore, Craig Leres, Bob  Mul-
     cahy,  Rich Salz, and Richard Stallman for help with various
     distribution headaches.

     Thanks to Esmond Pitt and Earle Horton for  8-bit  character
     support; to Benson Margulies and Fred Burke for C++ support;
     to Ove Ewerlid for the basics of support for NUL's;  and  to
     Eric Hughes for the basics of support for multiple buffers.

     Work is being done on extending flex to generate scanners in
     which  the  state  machine is directly represented in C code
     rather than tables.  These scanners  may  well  be  substan-
     tially  faster  than those generated using -f or -F.  If you
     are working in this area and  are  interested  in  comparing
     notes and seeing whether redundant work can be avoided, con-
     tact Ove Ewerlid (ewerlid@mizar.DoCS.UU.SE).

     This work was primarily done when I was  at  the  Real  Time
     Systems  Group at the Lawrence Berkeley Laboratory in Berke-
     ley, CA.  Many  thanks  to  all  there  for  the  support  I
     received.

     Send comments to:

          Vern Paxson
          Computer Science Department
          4126 Upson Hall
          Cornell University
          Ithaca, NY 14853-7501

          vern@cs.cornell.edu
          decvax!cornell!vern








Man(1) output converted with man2html
base-7.0.3.1/modules/libcom/src/flex/gen.c0000664000577000060420000007561513557101274017010 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* gen - actual generation (writing) of flex scanners */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "flexdef.h" /* declare functions that have forward references */ void gen_next_state (int); void genecs (void); void indent_put2s (char [], char []); void indent_puts (char []); static int indent_level = 0; /* each level is 4 spaces */ #define indent_up() (++indent_level) #define indent_down() (--indent_level) #define set_indent(indent_val) indent_level = indent_val /* *everything* is done in terms of arrays starting at 1, so provide * a null entry for the zero element of all C arrays */ static char C_short_decl[] = "static const short int %s[%d] =\n { 0,\n"; static char C_long_decl[] = "static const long int %s[%d] =\n { 0,\n"; static char C_state_decl[] = "static const yy_state_type %s[%d] =\n { 0,\n"; /* indent to the current level */ void do_indent(void) { int i = indent_level * 4; while ( i >= 8 ) { putchar( '\t' ); i -= 8; } while ( i > 0 ) { putchar( ' ' ); --i; } } /* generate the code to keep backtracking information */ void gen_backtracking(void) { if ( reject || num_backtracking == 0 ) return; if ( fullspd ) indent_puts( "if ( yy_current_state[-1].yy_nxt )" ); else indent_puts( "if ( yy_accept[yy_current_state] )" ); indent_up(); indent_puts( "{" ); indent_puts( "yy_last_accepting_state = yy_current_state;" ); indent_puts( "yy_last_accepting_cpos = yy_cp;" ); indent_puts( "}" ); indent_down(); } /* generate the code to perform the backtrack */ void gen_bt_action(void) { if ( reject || num_backtracking == 0 ) return; set_indent( 3 ); indent_puts( "case 0: /* must backtrack */" ); indent_puts( "/* undo the effects of YY_DO_BEFORE_ACTION */" ); indent_puts( "*yy_cp = yy_hold_char;" ); if ( fullspd || fulltbl ) indent_puts( "yy_cp = yy_last_accepting_cpos + 1;" ); else /* backtracking info for compressed tables is taken \after/ * yy_cp has been incremented for the next state */ indent_puts( "yy_cp = yy_last_accepting_cpos;" ); indent_puts( "yy_current_state = yy_last_accepting_state;" ); indent_puts( "goto yy_find_action;" ); putchar( '\n' ); set_indent( 0 ); } /* genctbl - generates full speed compressed transition table * * synopsis * genctbl(); */ void genctbl(void) { int i; int end_of_buffer_action = num_rules + 1; /* table of verify for transition and offset to next state */ printf( "static const struct yy_trans_info yy_transition[%d] =\n", tblend + numecs + 1 ); printf( " {\n" ); /* We want the transition to be represented as the offset to the * next state, not the actual state number, which is what it currently is. * The offset is base[nxt[i]] - base[chk[i]]. That's just the * difference between the starting points of the two involved states * (to - from). * * first, though, we need to find some way to put in our end-of-buffer * flags and states. We do this by making a state with absolutely no * transitions. We put it at the end of the table. */ /* at this point, we're guaranteed that there's enough room in nxt[] * and chk[] to hold tblend + numecs entries. We need just two slots. * One for the action and one for the end-of-buffer transition. We * now *assume* that we're guaranteed the only character we'll try to * index this nxt/chk pair with is EOB, i.e., 0, so we don't have to * make sure there's room for jam entries for other characters. */ base[lastdfa + 1] = tblend + 2; nxt[tblend + 1] = end_of_buffer_action; chk[tblend + 1] = numecs + 1; chk[tblend + 2] = 1; /* anything but EOB */ nxt[tblend + 2] = 0; /* so that "make test" won't show arb. differences */ /* make sure every state has a end-of-buffer transition and an action # */ for ( i = 0; i <= lastdfa; ++i ) { int anum = dfaacc[i].dfaacc_state; chk[base[i]] = EOB_POSITION; chk[base[i] - 1] = ACTION_POSITION; nxt[base[i] - 1] = anum; /* action number */ } for ( i = 0; i <= tblend; ++i ) { if ( chk[i] == EOB_POSITION ) transition_struct_out( 0, base[lastdfa + 1] - i ); else if ( chk[i] == ACTION_POSITION ) transition_struct_out( 0, nxt[i] ); else if ( chk[i] > numecs || chk[i] == 0 ) transition_struct_out( 0, 0 ); /* unused slot */ else /* verify, transition */ transition_struct_out( chk[i], base[nxt[i]] - (i - chk[i]) ); } /* here's the final, end-of-buffer state */ transition_struct_out( chk[tblend + 1], nxt[tblend + 1] ); transition_struct_out( chk[tblend + 2], nxt[tblend + 2] ); printf( " };\n" ); printf( "\n" ); /* table of pointers to start states */ printf( "static const struct yy_trans_info *yy_start_state_list[%d] =\n", lastsc * 2 + 1 ); printf( " {\n" ); for ( i = 0; i <= lastsc * 2; ++i ) printf( " &yy_transition[%d],\n", base[i] ); dataend(); if ( useecs ) genecs(); } /* generate equivalence-class tables */ void genecs(void) { int i, j; static char C_char_decl[] = "static const %s %s[%d] =\n { 0,\n"; int numrows; Char clower(); if ( numecs < csize ) printf( C_char_decl, "YY_CHAR", "yy_ec", csize ); else printf( C_char_decl, "short", "yy_ec", csize ); for ( i = 1; i < csize; ++i ) { if ( caseins && (i >= 'A') && (i <= 'Z') ) ecgroup[i] = ecgroup[clower( i )]; ecgroup[i] = abs( ecgroup[i] ); mkdata( ecgroup[i] ); } dataend(); if ( trace ) { char *readable_form(); fputs( "\n\nEquivalence Classes:\n\n", stderr ); numrows = csize / 8; for ( j = 0; j < numrows; ++j ) { for ( i = j; i < csize; i = i + numrows ) { fprintf( stderr, "%4s = %-2d", readable_form( i ), ecgroup[i] ); putc( ' ', stderr ); } putc( '\n', stderr ); } } } /* generate the code to find the action number */ void gen_find_action(void) { if ( fullspd ) indent_puts( "yy_act = yy_current_state[-1].yy_nxt;" ); else if ( fulltbl ) indent_puts( "yy_act = yy_accept[yy_current_state];" ); else if ( reject ) { indent_puts( "yy_current_state = *--yy_state_ptr;" ); indent_puts( "yy_lp = yy_accept[yy_current_state];" ); puts( "find_rule: /* we branch to this label when backtracking */" ); indent_puts( "for ( ; ; ) /* until we find what rule we matched */" ); indent_up(); indent_puts( "{" ); indent_puts( "if ( yy_lp && yy_lp < yy_accept[yy_current_state + 1] )" ); indent_up(); indent_puts( "{" ); indent_puts( "yy_act = yy_acclist[yy_lp];" ); if ( variable_trailing_context_rules ) { indent_puts( "if ( yy_act & YY_TRAILING_HEAD_MASK ||" ); indent_puts( " yy_looking_for_trail_begin )" ); indent_up(); indent_puts( "{" ); indent_puts( "if ( yy_act == yy_looking_for_trail_begin )" ); indent_up(); indent_puts( "{" ); indent_puts( "yy_looking_for_trail_begin = 0;" ); indent_puts( "yy_act &= ~YY_TRAILING_HEAD_MASK;" ); indent_puts( "break;" ); indent_puts( "}" ); indent_down(); indent_puts( "}" ); indent_down(); indent_puts( "else if ( yy_act & YY_TRAILING_MASK )" ); indent_up(); indent_puts( "{" ); indent_puts( "yy_looking_for_trail_begin = yy_act & ~YY_TRAILING_MASK;" ); indent_puts( "yy_looking_for_trail_begin |= YY_TRAILING_HEAD_MASK;" ); if ( real_reject ) { /* remember matched text in case we back up due to REJECT */ indent_puts( "yy_full_match = yy_cp;" ); indent_puts( "yy_full_state = yy_state_ptr;" ); indent_puts( "yy_full_lp = yy_lp;" ); } indent_puts( "}" ); indent_down(); indent_puts( "else" ); indent_up(); indent_puts( "{" ); indent_puts( "yy_full_match = yy_cp;" ); indent_puts( "yy_full_state = yy_state_ptr;" ); indent_puts( "yy_full_lp = yy_lp;" ); indent_puts( "break;" ); indent_puts( "}" ); indent_down(); indent_puts( "++yy_lp;" ); indent_puts( "goto find_rule;" ); } else { /* remember matched text in case we back up due to trailing context * plus REJECT */ indent_up(); indent_puts( "{" ); indent_puts( "yy_full_match = yy_cp;" ); indent_puts( "break;" ); indent_puts( "}" ); indent_down(); } indent_puts( "}" ); indent_down(); indent_puts( "--yy_cp;" ); /* we could consolidate the following two lines with those at * the beginning, but at the cost of complaints that we're * branching inside a loop */ indent_puts( "yy_current_state = *--yy_state_ptr;" ); indent_puts( "yy_lp = yy_accept[yy_current_state];" ); indent_puts( "}" ); indent_down(); } else /* compressed */ indent_puts( "yy_act = yy_accept[yy_current_state];" ); } /* genftbl - generates full transition table * * synopsis * genftbl(); */ void genftbl(void) { int i; int end_of_buffer_action = num_rules + 1; printf( C_short_decl, "yy_accept", lastdfa + 1 ); dfaacc[end_of_buffer_state].dfaacc_state = end_of_buffer_action; for ( i = 1; i <= lastdfa; ++i ) { int anum = dfaacc[i].dfaacc_state; mkdata( anum ); if ( trace && anum ) fprintf( stderr, "state # %d accepts: [%d]\n", i, anum ); } dataend(); if ( useecs ) genecs(); /* don't have to dump the actual full table entries - they were created * on-the-fly */ } /* generate the code to find the next compressed-table state */ void gen_next_compressed_state(char *char_map) { indent_put2s( "YY_CHAR yy_c = %s;", char_map ); /* save the backtracking info \before/ computing the next state * because we always compute one more state than needed - we * always proceed until we reach a jam state */ gen_backtracking(); indent_puts( "while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )" ); indent_up(); indent_puts( "{" ); indent_puts( "yy_current_state = yy_def[yy_current_state];" ); if ( usemecs ) { /* we've arrange it so that templates are never chained * to one another. This means we can afford make a * very simple test to see if we need to convert to * yy_c's meta-equivalence class without worrying * about erroneously looking up the meta-equivalence * class twice */ do_indent(); /* lastdfa + 2 is the beginning of the templates */ printf( "if ( yy_current_state >= %d )\n", lastdfa + 2 ); indent_up(); indent_puts( "yy_c = yy_meta[(int)yy_c];" ); indent_down(); } indent_puts( "}" ); indent_down(); indent_puts( "yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];" ); } /* generate the code to find the next match */ void gen_next_match(void) { /* NOTE - changes in here should be reflected in gen_next_state() and * gen_NUL_trans() */ char *char_map = useecs ? "yy_ec[(int)*yy_cp]" : "*yy_cp"; char *char_map_2 = useecs ? "yy_ec[*++yy_cp]" : "*++yy_cp"; if ( fulltbl ) { indent_put2s( "while ( (yy_current_state = yy_nxt[yy_current_state][%s]) > 0 )", char_map ); indent_up(); if ( num_backtracking > 0 ) { indent_puts( "{" ); gen_backtracking(); putchar( '\n' ); } indent_puts( "++yy_cp;" ); if ( num_backtracking > 0 ) indent_puts( "}" ); indent_down(); putchar( '\n' ); indent_puts( "yy_current_state = -yy_current_state;" ); } else if ( fullspd ) { indent_puts( "{" ); indent_puts( "const struct yy_trans_info *yy_trans_info;\n" ); indent_puts( "YY_CHAR yy_c;\n" ); indent_put2s( "for ( yy_c = %s;", char_map ); indent_puts( " (yy_trans_info = &yy_current_state[yy_c])->yy_verify == yy_c;" ); indent_put2s( " yy_c = %s )", char_map_2 ); indent_up(); if ( num_backtracking > 0 ) indent_puts( "{" ); indent_puts( "yy_current_state += yy_trans_info->yy_nxt;" ); if ( num_backtracking > 0 ) { putchar( '\n' ); gen_backtracking(); indent_puts( "}" ); } indent_down(); indent_puts( "}" ); } else { /* compressed */ indent_puts( "do" ); indent_up(); indent_puts( "{" ); gen_next_state( false ); indent_puts( "++yy_cp;" ); indent_puts( "}" ); indent_down(); do_indent(); if ( interactive ) printf( "while ( yy_base[yy_current_state] != %d );\n", jambase ); else printf( "while ( yy_current_state != %d );\n", jamstate ); if ( ! reject && ! interactive ) { /* do the guaranteed-needed backtrack to figure out the match */ indent_puts( "yy_cp = yy_last_accepting_cpos;" ); indent_puts( "yy_current_state = yy_last_accepting_state;" ); } } } /* generate the code to find the next state */ void gen_next_state(int worry_about_NULs) { /* NOTE - changes in here should be reflected in get_next_match() */ char char_map[256]; if ( worry_about_NULs && ! nultrans ) { if ( useecs ) (void) sprintf( char_map, "(*yy_cp ? yy_ec[(int)*yy_cp] : %d)", NUL_ec ); else (void) sprintf( char_map, "(*yy_cp ? *yy_cp : %d)", NUL_ec ); } else (void) strcpy( char_map, useecs ? "yy_ec[(int)*yy_cp]" : "*yy_cp" ); if ( worry_about_NULs && nultrans ) { if ( ! fulltbl && ! fullspd ) /* compressed tables backtrack *before* they match */ gen_backtracking(); indent_puts( "if ( *yy_cp )" ); indent_up(); indent_puts( "{" ); } if ( fulltbl ) indent_put2s( "yy_current_state = yy_nxt[yy_current_state][%s];", char_map ); else if ( fullspd ) indent_put2s( "yy_current_state += yy_current_state[%s].yy_nxt;", char_map ); else gen_next_compressed_state( char_map ); if ( worry_about_NULs && nultrans ) { indent_puts( "}" ); indent_down(); indent_puts( "else" ); indent_up(); indent_puts( "yy_current_state = yy_NUL_trans[yy_current_state];" ); indent_down(); } if ( fullspd || fulltbl ) gen_backtracking(); if ( reject ) indent_puts( "*yy_state_ptr++ = yy_current_state;" ); } /* generate the code to make a NUL transition */ void gen_NUL_trans(void) { /* NOTE - changes in here should be reflected in get_next_match() */ int need_backtracking = (num_backtracking > 0 && ! reject); if ( need_backtracking ) /* we'll need yy_cp lying around for the gen_backtracking() */ indent_puts( "YY_CHAR *yy_cp = yy_c_buf_p;" ); putchar( '\n' ); if ( nultrans ) { indent_puts( "yy_current_state = yy_NUL_trans[yy_current_state];" ); indent_puts( "yy_is_jam = (yy_current_state == 0);" ); } else if ( fulltbl ) { do_indent(); printf( "yy_current_state = yy_nxt[yy_current_state][%d];\n", NUL_ec ); indent_puts( "yy_is_jam = (yy_current_state <= 0);" ); } else if ( fullspd ) { do_indent(); printf( "int yy_c = %d;\n", NUL_ec ); indent_puts( "const struct yy_trans_info *yy_trans_info;\n" ); indent_puts( "yy_trans_info = &yy_current_state[yy_c];" ); indent_puts( "yy_current_state += yy_trans_info->yy_nxt;" ); indent_puts( "yy_is_jam = (yy_trans_info->yy_verify != yy_c);" ); } else { char NUL_ec_str[20]; (void) sprintf( NUL_ec_str, "%d", NUL_ec ); gen_next_compressed_state( NUL_ec_str ); if ( reject ) indent_puts( "*yy_state_ptr++ = yy_current_state;" ); do_indent(); if ( interactive ) printf( "yy_is_jam = (yy_base[yy_current_state] == %d);\n", jambase ); else printf( "yy_is_jam = (yy_current_state == %d);\n", jamstate ); } /* if we've entered an accepting state, backtrack; note that * compressed tables have *already* done such backtracking, so * we needn't bother with it again */ if ( need_backtracking && (fullspd || fulltbl) ) { putchar( '\n' ); indent_puts( "if ( ! yy_is_jam )" ); indent_up(); indent_puts( "{" ); gen_backtracking(); indent_puts( "}" ); indent_down(); } } /* generate the code to find the start state */ void gen_start_state(void) { if ( fullspd ) indent_put2s( "yy_current_state = yy_start_state_list[yy_start%s];", bol_needed ? " + (yy_bp[-1] == '\\n' ? 1 : 0)" : "" ); else { indent_puts( "yy_current_state = yy_start;" ); if ( bol_needed ) { indent_puts( "if ( yy_bp[-1] == '\\n' )" ); indent_up(); indent_puts( "++yy_current_state;" ); indent_down(); } if ( reject ) { /* set up for storing up states */ indent_puts( "yy_state_ptr = yy_state_buf;" ); indent_puts( "*yy_state_ptr++ = yy_current_state;" ); } } } /* gentabs - generate data statements for the transition tables * * synopsis * gentabs(); */ void gentabs(void) { int i, j, k, *accset, nacc, *acc_array, total_states; int end_of_buffer_action = num_rules + 1; /* *everything* is done in terms of arrays starting at 1, so provide * a null entry for the zero element of all C arrays */ static char C_char_decl[] = "static const YY_CHAR %s[%d] =\n { 0,\n"; acc_array = allocate_integer_array( current_max_dfas ); nummt = 0; /* the compressed table format jams by entering the "jam state", * losing information about the previous state in the process. * In order to recover the previous state, we effectively need * to keep backtracking information. */ ++num_backtracking; if ( reject ) { /* write out accepting list and pointer list * * first we generate the "yy_acclist" array. In the process, we compute * the indices that will go into the "yy_accept" array, and save the * indices in the dfaacc array */ int EOB_accepting_list[2]; /* set up accepting structures for the End Of Buffer state */ EOB_accepting_list[0] = 0; EOB_accepting_list[1] = end_of_buffer_action; accsiz[end_of_buffer_state] = 1; dfaacc[end_of_buffer_state].dfaacc_set = EOB_accepting_list; printf( C_short_decl, "yy_acclist", max( numas, 1 ) + 1 ); j = 1; /* index into "yy_acclist" array */ for ( i = 1; i <= lastdfa; ++i ) { acc_array[i] = j; if ( accsiz[i] != 0 ) { accset = dfaacc[i].dfaacc_set; nacc = accsiz[i]; if ( trace ) fprintf( stderr, "state # %d accepts: ", i ); for ( k = 1; k <= nacc; ++k ) { int accnum = accset[k]; ++j; if ( variable_trailing_context_rules && ! (accnum & YY_TRAILING_HEAD_MASK) && accnum > 0 && accnum <= num_rules && rule_type[accnum] == RULE_VARIABLE ) { /* special hack to flag accepting number as part * of trailing context rule */ accnum |= YY_TRAILING_MASK; } mkdata( accnum ); if ( trace ) { fprintf( stderr, "[%d]", accset[k] ); if ( k < nacc ) fputs( ", ", stderr ); else putc( '\n', stderr ); } } } } /* add accepting number for the "jam" state */ acc_array[i] = j; dataend(); } else { dfaacc[end_of_buffer_state].dfaacc_state = end_of_buffer_action; for ( i = 1; i <= lastdfa; ++i ) acc_array[i] = dfaacc[i].dfaacc_state; /* add accepting number for jam state */ acc_array[i] = 0; } /* spit out "yy_accept" array. If we're doing "reject", it'll be pointers * into the "yy_acclist" array. Otherwise it's actual accepting numbers. * In either case, we just dump the numbers. */ /* "lastdfa + 2" is the size of "yy_accept"; includes room for C arrays * beginning at 0 and for "jam" state */ k = lastdfa + 2; if ( reject ) /* we put a "cap" on the table associating lists of accepting * numbers with state numbers. This is needed because we tell * where the end of an accepting list is by looking at where * the list for the next state starts. */ ++k; printf( C_short_decl, "yy_accept", k ); for ( i = 1; i <= lastdfa; ++i ) { mkdata( acc_array[i] ); if ( ! reject && trace && acc_array[i] ) fprintf( stderr, "state # %d accepts: [%d]\n", i, acc_array[i] ); } /* add entry for "jam" state */ mkdata( acc_array[i] ); if ( reject ) /* add "cap" for the list */ mkdata( acc_array[i] ); dataend(); if ( useecs ) genecs(); if ( usemecs ) { /* write out meta-equivalence classes (used to index templates with) */ if ( trace ) fputs( "\n\nMeta-Equivalence Classes:\n", stderr ); printf( C_char_decl, "yy_meta", numecs + 1 ); for ( i = 1; i <= numecs; ++i ) { if ( trace ) fprintf( stderr, "%d = %d\n", i, abs( tecbck[i] ) ); mkdata( abs( tecbck[i] ) ); } dataend(); } total_states = lastdfa + numtemps; printf( tblend > MAX_SHORT ? C_long_decl : C_short_decl, "yy_base", total_states + 1 ); for ( i = 1; i <= lastdfa; ++i ) { int d = def[i]; if ( base[i] == JAMSTATE ) base[i] = jambase; if ( d == JAMSTATE ) def[i] = jamstate; else if ( d < 0 ) { /* template reference */ ++tmpuses; def[i] = lastdfa - d + 1; } mkdata( base[i] ); } /* generate jam state's base index */ mkdata( base[i] ); for ( ++i /* skip jam state */; i <= total_states; ++i ) { mkdata( base[i] ); def[i] = jamstate; } dataend(); printf( tblend > MAX_SHORT ? C_long_decl : C_short_decl, "yy_def", total_states + 1 ); for ( i = 1; i <= total_states; ++i ) mkdata( def[i] ); dataend(); printf( lastdfa > MAX_SHORT ? C_long_decl : C_short_decl, "yy_nxt", tblend + 1 ); for ( i = 1; i <= tblend; ++i ) { if ( nxt[i] == 0 || chk[i] == 0 ) nxt[i] = jamstate; /* new state is the JAM state */ mkdata( nxt[i] ); } dataend(); printf( lastdfa > MAX_SHORT ? C_long_decl : C_short_decl, "yy_chk", tblend + 1 ); for ( i = 1; i <= tblend; ++i ) { if ( chk[i] == 0 ) ++nummt; mkdata( chk[i] ); } dataend(); } /* write out a formatted string (with a secondary string argument) at the * current indentation level, adding a final newline */ void indent_put2s(char *fmt, char *arg) { do_indent(); printf( fmt, arg ); putchar( '\n' ); } /* write out a string at the current indentation level, adding a final * newline */ void indent_puts(char *str) { do_indent(); puts( str ); } /* make_tables - generate transition tables * * synopsis * make_tables(); * * Generates transition tables and finishes generating output file */ void make_tables(void) { int i; int did_eof_rule = false; skelout(); /* first, take care of YY_DO_BEFORE_ACTION depending on yymore being used */ set_indent( 2 ); if ( yymore_used ) { indent_puts( "yytext -= yy_more_len; \\" ); indent_puts( "yyleng = yy_cp - yytext; \\" ); } else indent_puts( "yyleng = yy_cp - yy_bp; \\" ); set_indent( 0 ); skelout(); printf( "#define YY_END_OF_BUFFER %d\n", num_rules + 1 ); if ( fullspd ) { /* need to define the transet type as a size large * enough to hold the biggest offset */ int total_table_size = tblend + numecs + 1; char *trans_offset_type = total_table_size > MAX_SHORT ? "long" : "short"; set_indent( 0 ); indent_puts( "struct yy_trans_info" ); indent_up(); indent_puts( "{" ); indent_puts( "short yy_verify;" ); /* in cases where its sister yy_verify *is* a "yes, there is a * transition", yy_nxt is the offset (in records) to the next state. * In most cases where there is no transition, the value of yy_nxt * is irrelevant. If yy_nxt is the -1th record of a state, though, * then yy_nxt is the action number for that state */ indent_put2s( "%s yy_nxt;", trans_offset_type ); indent_puts( "};" ); indent_down(); indent_puts( "typedef const struct yy_trans_info *yy_state_type;" ); } else indent_puts( "typedef int yy_state_type;" ); if ( fullspd ) genctbl(); else if ( fulltbl ) genftbl(); else gentabs(); if ( num_backtracking > 0 ) { indent_puts( "static yy_state_type yy_last_accepting_state;" ); indent_puts( "static YY_CHAR *yy_last_accepting_cpos;\n" ); } if ( nultrans ) { printf( C_state_decl, "yy_NUL_trans", lastdfa + 1 ); for ( i = 1; i <= lastdfa; ++i ) { if ( fullspd ) { if ( nultrans ) printf( " &yy_transition[%d],\n", base[i] ); else printf( " 0,\n" ); } else mkdata( nultrans[i] ); } dataend(); } if ( ddebug ) { /* spit out table mapping rules to line numbers */ indent_puts( "extern int yy_flex_debug;" ); indent_puts( "int yy_flex_debug = 1;\n" ); printf( C_short_decl, "yy_rule_linenum", num_rules ); for ( i = 1; i < num_rules; ++i ) mkdata( rule_linenum[i] ); dataend(); } if ( reject ) { /* declare state buffer variables */ puts( "static yy_state_type yy_state_buf[YY_BUF_SIZE + 2], *yy_state_ptr;" ); puts( "static YY_CHAR *yy_full_match;" ); puts( "static int yy_lp;" ); if ( variable_trailing_context_rules ) { puts( "static int yy_looking_for_trail_begin = 0;" ); puts( "static int yy_full_lp;" ); puts( "static int *yy_full_state;" ); printf( "#define YY_TRAILING_MASK 0x%x\n", YY_TRAILING_MASK ); printf( "#define YY_TRAILING_HEAD_MASK 0x%x\n", YY_TRAILING_HEAD_MASK ); } puts( "#define REJECT \\" ); puts( "{ \\" ); puts( "*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ \\" ); puts( "yy_cp = yy_full_match; /* restore poss. backed-over text */ \\" ); if ( variable_trailing_context_rules ) { puts( "yy_lp = yy_full_lp; /* restore orig. accepting pos. */ \\" ); puts( "yy_state_ptr = yy_full_state; /* restore orig. state */ \\" ); puts( "yy_current_state = *yy_state_ptr; /* restore curr. state */ \\" ); } puts( "++yy_lp; \\" ); puts( "goto find_rule; \\" ); puts( "}" ); } else { puts( "/* the intent behind this definition is that it'll catch" ); puts( " * any uses of REJECT which flex missed" ); puts( " */" ); puts( "#define REJECT reject_used_but_not_detected" ); } if ( yymore_used ) { indent_puts( "static int yy_more_flag = 0;" ); indent_puts( "static int yy_doing_yy_more = 0;" ); indent_puts( "static int yy_more_len = 0;" ); indent_puts( "#define yymore() { yy_more_flag = 1; }" ); indent_puts( "#define YY_MORE_ADJ (yy_doing_yy_more ? yy_more_len : 0)" ); } else { indent_puts( "#define yymore() yymore_used_but_not_detected" ); indent_puts( "#define YY_MORE_ADJ 0" ); } skelout(); if ( ferror( temp_action_file ) ) flexfatal( "error occurred when writing temporary action file" ); else if ( fseek( temp_action_file, 0L, SEEK_SET) != 0 ) flexfatal( "error occurred when rewinding temporary action file" ); /* copy prolog from action_file to output file */ action_out(); skelout(); set_indent( 2 ); if ( yymore_used ) { indent_puts( "yy_more_len = 0;" ); indent_puts( "yy_doing_yy_more = yy_more_flag;" ); indent_puts( "if ( yy_doing_yy_more )" ); indent_up(); indent_puts( "{" ); indent_puts( "yy_more_len = yyleng;" ); indent_puts( "yy_more_flag = 0;" ); indent_puts( "}" ); indent_down(); } skelout(); gen_start_state(); /* note, don't use any indentation */ puts( "yy_match:" ); gen_next_match(); skelout(); set_indent( 2 ); gen_find_action(); skelout(); if ( ddebug ) { indent_puts( "if ( yy_flex_debug )" ); indent_up(); indent_puts( "{" ); indent_puts( "if ( yy_act == 0 )" ); indent_up(); indent_puts( "fprintf( stderr, \"--scanner backtracking\\n\" );" ); indent_down(); do_indent(); printf( "else if ( yy_act < %d )\n", num_rules ); indent_up(); indent_puts( "fprintf( stderr, \"--accepting rule at line %d (\\\"%s\\\")\\n\"," ); indent_puts( " yy_rule_linenum[yy_act], yytext );" ); indent_down(); do_indent(); printf( "else if ( yy_act == %d )\n", num_rules ); indent_up(); indent_puts( "fprintf( stderr, \"--accepting default rule (\\\"%s\\\")\\n\"," ); indent_puts( " yytext );" ); indent_down(); do_indent(); printf( "else if ( yy_act == %d )\n", num_rules + 1 ); indent_up(); indent_puts( "fprintf( stderr, \"--(end of buffer or a NUL)\\n\" );" ); indent_down(); do_indent(); printf( "else\n" ); indent_up(); indent_puts( "fprintf( stderr, \"--EOF\\n\" );" ); indent_down(); indent_puts( "}" ); indent_down(); } /* copy actions from action_file to output file */ skelout(); indent_up(); gen_bt_action(); action_out(); /* generate cases for any missing EOF rules */ for ( i = 1; i <= lastsc; ++i ) if ( ! sceof[i] ) { do_indent(); printf( "case YY_STATE_EOF(%s):\n", scname[i] ); did_eof_rule = true; } if ( did_eof_rule ) { indent_up(); indent_puts( "yyterminate();" ); indent_down(); } /* generate code for handling NUL's, if needed */ /* first, deal with backtracking and setting up yy_cp if the scanner * finds that it should JAM on the NUL */ skelout(); set_indent( 7 ); if ( fullspd || fulltbl ) indent_puts( "yy_cp = yy_c_buf_p;" ); else { /* compressed table */ if ( ! reject && ! interactive ) { /* do the guaranteed-needed backtrack to figure out the match */ indent_puts( "yy_cp = yy_last_accepting_cpos;" ); indent_puts( "yy_current_state = yy_last_accepting_state;" ); } } /* generate code for yy_get_previous_state() */ set_indent( 1 ); skelout(); if ( bol_needed ) indent_puts( "YY_CHAR *yy_bp = yytext;\n" ); gen_start_state(); set_indent( 2 ); skelout(); gen_next_state( true ); set_indent( 1 ); skelout(); gen_NUL_trans(); skelout(); /* copy remainder of input to output */ line_directive_out( stdout ); (void) flexscan(); /* copy remainder of input to output */ } base-7.0.3.1/modules/libcom/src/flex/libmain.c0000664000577000060420000000120413557101274017631 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* libmain - flex run-time support library "main" function */ extern int yylex(); int main(int argc, char *argv[]) { return yylex(); } base-7.0.3.1/modules/libcom/src/flex/misc.c0000664000577000060420000003474013557101274017164 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* misc - miscellaneous flex routines */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include #include "flexdef.h" #include /* ANSI C does not guarantee that isascii() is defined */ #ifndef isascii #define isascii(c) ((c) <= 0177) #endif /* declare functions that have forward references */ void dataflush (void); int otoi (Char []); /* action_out - write the actions from the temporary file to lex.yy.c * * synopsis * action_out(); * * Copies the action file up to %% (or end-of-file) to lex.yy.c */ void action_out(void) { char buf[MAXLINE]; while ( fgets( buf, MAXLINE, temp_action_file ) != NULL ) if ( buf[0] == '%' && buf[1] == '%' ) break; else fputs( buf, stdout ); } /* allocate_array - allocate memory for an integer array of the given size */ void *allocate_array(int size, int element_size) { void *mem; /* on 16-bit int machines (e.g., 80286) we might be trying to * allocate more than a signed int can hold, and that won't * work. Cheap test: */ if ( element_size * size <= 0 ) flexfatal( "request for < 1 byte in allocate_array()" ); mem = (void *) malloc( (unsigned) (element_size * size) ); if ( mem == NULL ) flexfatal( "memory allocation failed in allocate_array()" ); return ( mem ); } /* all_lower - true if a string is all lower-case * * synopsis: * Char *str; * int all_lower(); * true/false = all_lower( str ); */ int all_lower(Char *str) { while ( *str ) { if ( ! isascii( (int) *str ) || ! islower( (int) *str ) ) return ( 0 ); ++str; } return ( 1 ); } /* all_upper - true if a string is all upper-case * * synopsis: * Char *str; * int all_upper(); * true/false = all_upper( str ); */ int all_upper(Char *str) { while ( *str ) { if ( ! isascii( (int) *str ) || ! isupper( (int) *str ) ) return ( 0 ); ++str; } return ( 1 ); } /* bubble - bubble sort an integer array in increasing order * * synopsis * int v[n], n; * bubble( v, n ); * * description * sorts the first n elements of array v and replaces them in * increasing order. * * passed * v - the array to be sorted * n - the number of elements of 'v' to be sorted */ void bubble(int v[], int n) { int i, j, k; for ( i = n; i > 1; --i ) for ( j = 1; j < i; ++j ) if ( v[j] > v[j + 1] ) /* compare */ { k = v[j]; /* exchange */ v[j] = v[j + 1]; v[j + 1] = k; } } /* clower - replace upper-case letter to lower-case * * synopsis: * Char clower(); * int c; * c = clower( c ); */ Char clower(int c) { return ( (isascii( c ) && isupper( c )) ? tolower( c ) : c ); } /* copy_string - returns a dynamically allocated copy of a string * * synopsis * char *str, *copy, *copy_string(); * copy = copy_string( str ); */ char *copy_string(char *str) { char *c; char *copy; /* find length */ for ( c = str; *c; ++c ) ; copy = malloc( (unsigned) ((c - str + 1) * sizeof( char )) ); if ( copy == NULL ) flexfatal( "dynamic memory failure in copy_string()" ); for ( c = copy; (*c++ = *str++); ) ; return ( copy ); } /* copy_unsigned_string - * returns a dynamically allocated copy of a (potentially) unsigned string * * synopsis * Char *str, *copy, *copy_unsigned_string(); * copy = copy_unsigned_string( str ); */ Char *copy_unsigned_string(Char *str) { Char *c; Char *copy; /* find length */ for ( c = str; *c; ++c ) ; copy = (Char *) malloc( (unsigned) ((c - str + 1) * sizeof( Char )) ); if ( copy == NULL ) flexfatal( "dynamic memory failure in copy_unsigned_string()" ); for ( c = copy; (*c++ = *str++); ) ; return ( copy ); } /* cshell - shell sort a character array in increasing order * * synopsis * * Char v[n]; * int n, special_case_0; * cshell( v, n, special_case_0 ); * * description * does a shell sort of the first n elements of array v. * If special_case_0 is true, then any element equal to 0 * is instead assumed to have infinite weight. * * passed * v - array to be sorted * n - number of elements of v to be sorted */ void cshell(Char *v, int n, int special_case_0) { int gap, i, j, jg; Char k; for ( gap = n / 2; gap > 0; gap = gap / 2 ) for ( i = gap; i < n; ++i ) for ( j = i - gap; j >= 0; j = j - gap ) { jg = j + gap; if ( special_case_0 ) { if ( v[jg] == 0 ) break; else if ( v[j] != 0 && v[j] <= v[jg] ) break; } else if ( v[j] <= v[jg] ) break; k = v[j]; v[j] = v[jg]; v[jg] = k; } } /* dataend - finish up a block of data declarations * * synopsis * dataend(); */ void dataend(void) { if ( datapos > 0 ) dataflush(); /* add terminator for initialization */ puts( " } ;\n" ); dataline = 0; datapos = 0; } /* dataflush - flush generated data statements * * synopsis * dataflush(); */ void dataflush(void) { putchar( '\n' ); if ( ++dataline >= NUMDATALINES ) { /* put out a blank line so that the table is grouped into * large blocks that enable the user to find elements easily */ putchar( '\n' ); dataline = 0; } /* reset the number of characters written on the current line */ datapos = 0; } /* flexerror - report an error message and terminate * * synopsis * char msg[]; * flexerror( msg ); */ void flexerror(char *msg) { fprintf( stderr, "%s: %s\n", program_name, msg ); flexend( 1 ); } /* flexfatal - report a fatal error message and terminate * * synopsis * char msg[]; * flexfatal( msg ); */ void flexfatal(char *msg) { fprintf( stderr, "%s: fatal internal error, %s\n", program_name, msg ); flexend( 1 ); } /* flex_gettime - return current time * * synopsis * char *flex_gettime(), *time_str; * time_str = flex_gettime(); * * note * the routine name has the "flex_" prefix because of name clashes * with Turbo-C */ /* include sys/types.h to use time_t and make lint happy */ #ifndef MS_DOS #include #endif #ifdef MS_DOS #include typedef long time_t; #endif char *flex_gettime(void) { time_t t, time(time_t *); char *result, *ctime(const time_t *), *copy_string(char *str); t = time( NULL ); result = copy_string( ctime( &t ) ); /* get rid of trailing newline */ result[24] = '\0'; return ( result ); } /* lerrif - report an error message formatted with one integer argument * * synopsis * char msg[]; * int arg; * lerrif( msg, arg ); */ void lerrif(char *msg, int arg) { char errmsg[MAXLINE]; (void) sprintf( errmsg, msg, arg ); flexerror( errmsg ); } /* lerrsf - report an error message formatted with one string argument * * synopsis * char msg[], arg[]; * lerrsf( msg, arg ); */ void lerrsf(char *msg, char *arg) { char errmsg[MAXLINE]; (void) sprintf( errmsg, msg, arg ); flexerror( errmsg ); } /* htoi - convert a hexadecimal digit string to an integer value * * synopsis: * int val, htoi(); * Char str[]; * val = htoi( str ); */ int htoi(unsigned char *str) { int result; (void) sscanf( (char *) str, "%x", &result ); return ( result ); } /* is_hex_digit - returns true if a character is a valid hex digit, false * otherwise * * synopsis: * int true_or_false, is_hex_digit(); * int ch; * val = is_hex_digit( ch ); */ int is_hex_digit(int ch) { if ( isdigit( ch ) ) return ( 1 ); switch ( clower( ch ) ) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': return ( 1 ); default: return ( 0 ); } } /* line_directive_out - spit out a "# line" statement */ void line_directive_out(FILE *output_file_name) { if ( infilename && gen_line_dirs ) fprintf( output_file_name, "# line %d \"%s\"\n", linenum, infilename ); } /* mk2data - generate a data statement for a two-dimensional array * * synopsis * int value; * mk2data( value ); * * generates a data statement initializing the current 2-D array to "value" */ void mk2data(int value) { if ( datapos >= NUMDATAITEMS ) { putchar( ',' ); dataflush(); } if ( datapos == 0 ) /* indent */ fputs( " ", stdout ); else putchar( ',' ); ++datapos; printf( "%5d", value ); } /* mkdata - generate a data statement * * synopsis * int value; * mkdata( value ); * * generates a data statement initializing the current array element to * "value" */ void mkdata(int value) { if ( datapos >= NUMDATAITEMS ) { putchar( ',' ); dataflush(); } if ( datapos == 0 ) /* indent */ fputs( " ", stdout ); else putchar( ',' ); ++datapos; printf( "%5d", value ); } /* myctoi - return the integer represented by a string of digits * * synopsis * Char array[]; * int val, myctoi(); * val = myctoi( array ); * */ int myctoi(Char *array) { int val = 0; (void) sscanf( (char *) array, "%d", &val ); return ( val ); } /* myesc - return character corresponding to escape sequence * * synopsis * Char array[], c, myesc(); * c = myesc( array ); * */ Char myesc(Char *array) { Char c, esc_char; int sptr; switch ( array[1] ) { case 'a': return ( '\a' ); case 'b': return ( '\b' ); case 'f': return ( '\f' ); case 'n': return ( '\n' ); case 'r': return ( '\r' ); case 't': return ( '\t' ); case 'v': return ( '\v' ); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { /* \ */ sptr = 1; while ( isascii( array[sptr] ) && isdigit( array[sptr] ) ) /* don't increment inside loop control because if * isdigit() is a macro it might expand into multiple * increments ... */ ++sptr; c = array[sptr]; array[sptr] = '\0'; esc_char = otoi( array + 1 ); array[sptr] = c; return ( esc_char ); } case 'x': { /* \x */ int sptr = 2; while ( isascii( array[sptr] ) && is_hex_digit( array[sptr] ) ) /* don't increment inside loop control because if * isdigit() is a macro it might expand into multiple * increments ... */ ++sptr; c = array[sptr]; array[sptr] = '\0'; esc_char = htoi( array + 2 ); array[sptr] = c; return ( esc_char ); } default: return ( array[1] ); } } /* otoi - convert an octal digit string to an integer value * * synopsis: * int val, otoi(); * Char str[]; * val = otoi( str ); */ int otoi(Char *str) { int result; (void) sscanf( (char *) str, "%o", &result ); return ( result ); } /* readable_form - return the the human-readable form of a character * * synopsis: * int c; * char *readable_form(); * = readable_form( c ); * * The returned string is in static storage. */ char *readable_form(int c) { static char rform[10]; if ( (c >= 0 && c < 32) || c >= 127 ) { switch ( c ) { case '\n': return ( "\\n" ); case '\t': return ( "\\t" ); case '\f': return ( "\\f" ); case '\r': return ( "\\r" ); case '\b': return ( "\\b" ); default: (void) sprintf( rform, "\\%.3o", c ); return ( rform ); } } else if ( c == ' ' ) return ( "' '" ); else { rform[0] = c; rform[1] = '\0'; return ( rform ); } } /* reallocate_array - increase the size of a dynamic array */ void *reallocate_array(void *array, int size, int element_size) { void *new_array; /* same worry as in allocate_array(): */ if ( size * element_size <= 0 ) flexfatal( "attempt to increase array size by less than 1 byte" ); new_array = (void *) realloc( (char *)array, (unsigned) (size * element_size )); if ( new_array == NULL ) flexfatal( "attempt to increase array size failed" ); return ( new_array ); } /* skelout - write out one section of the skeleton file * * synopsis * skelout(); * * DESCRIPTION * Copies from skelfile to stdout until a line beginning with "%%" or * EOF is found. */ void skelout(void) { char buf[MAXLINE]; while ( fgets( buf, MAXLINE, skelfile ) != NULL ) if ( buf[0] == '%' && buf[1] == '%' ) break; else fputs( buf, stdout ); } /* transition_struct_out - output a yy_trans_info structure * * synopsis * int element_v, element_n; * transition_struct_out( element_v, element_n ); * * outputs the yy_trans_info structure with the two elements, element_v and * element_n. Formats the output with spaces and carriage returns. */ void transition_struct_out(int element_v, int element_n) { printf( "%7d, %5d,", element_v, element_n ); datapos += TRANS_STRUCT_PRINT_LENGTH; if ( datapos >= 75 ) { putchar( '\n' ); if ( ++dataline % 10 == 0 ) putchar( '\n' ); datapos = 0; } } base-7.0.3.1/modules/libcom/src/flex/nfa.c0000664000577000060420000004241313557101274016771 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* nfa - NFA construction routines */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "flexdef.h" /* declare functions that have forward references */ int dupmachine (int); void mkxtion (int, int); /* add_accept - add an accepting state to a machine * * synopsis * * add_accept( mach, accepting_number ); * * accepting_number becomes mach's accepting number. */ void add_accept(int mach, int accepting_number) { /* hang the accepting number off an epsilon state. if it is associated * with a state that has a non-epsilon out-transition, then the state * will accept BEFORE it makes that transition, i.e., one character * too soon */ if ( transchar[finalst[mach]] == SYM_EPSILON ) accptnum[finalst[mach]] = accepting_number; else { int astate = mkstate( SYM_EPSILON ); accptnum[astate] = accepting_number; mach = link_machines( mach, astate ); } } /* copysingl - make a given number of copies of a singleton machine * * synopsis * * newsng = copysingl( singl, num ); * * newsng - a new singleton composed of num copies of singl * singl - a singleton machine * num - the number of copies of singl to be present in newsng */ int copysingl(int singl, int num) { int copy, i; copy = mkstate( SYM_EPSILON ); for ( i = 1; i <= num; ++i ) copy = link_machines( copy, dupmachine( singl ) ); return ( copy ); } /* dumpnfa - debugging routine to write out an nfa * * synopsis * int state1; * dumpnfa( state1 ); */ void dumpnfa(int state1) { int sym, tsp1, tsp2, anum, ns; fprintf( stderr, "\n\n********** beginning dump of nfa with start state %d\n", state1 ); /* we probably should loop starting at firstst[state1] and going to * lastst[state1], but they're not maintained properly when we "or" * all of the rules together. So we use our knowledge that the machine * starts at state 1 and ends at lastnfa. */ /* for ( ns = firstst[state1]; ns <= lastst[state1]; ++ns ) */ for ( ns = 1; ns <= lastnfa; ++ns ) { fprintf( stderr, "state # %4d\t", ns ); sym = transchar[ns]; tsp1 = trans1[ns]; tsp2 = trans2[ns]; anum = accptnum[ns]; fprintf( stderr, "%3d: %4d, %4d", sym, tsp1, tsp2 ); if ( anum != NIL ) fprintf( stderr, " [%d]", anum ); fprintf( stderr, "\n" ); } fprintf( stderr, "********** end of dump\n" ); } /* dupmachine - make a duplicate of a given machine * * synopsis * * copy = dupmachine( mach ); * * copy - holds duplicate of mach * mach - machine to be duplicated * * note that the copy of mach is NOT an exact duplicate; rather, all the * transition states values are adjusted so that the copy is self-contained, * as the original should have been. * * also note that the original MUST be contiguous, with its low and high * states accessible by the arrays firstst and lastst */ int dupmachine(int mach) { int i, init, state_offset; int state = 0; int last = lastst[mach]; for ( i = firstst[mach]; i <= last; ++i ) { state = mkstate( transchar[i] ); if ( trans1[i] != NO_TRANSITION ) { mkxtion( finalst[state], trans1[i] + state - i ); if ( transchar[i] == SYM_EPSILON && trans2[i] != NO_TRANSITION ) mkxtion( finalst[state], trans2[i] + state - i ); } accptnum[state] = accptnum[i]; } if ( state == 0 ) flexfatal( "empty machine in dupmachine()" ); state_offset = state - i + 1; init = mach + state_offset; firstst[init] = firstst[mach] + state_offset; finalst[init] = finalst[mach] + state_offset; lastst[init] = lastst[mach] + state_offset; return ( init ); } /* finish_rule - finish up the processing for a rule * * synopsis * * finish_rule( mach, variable_trail_rule, headcnt, trailcnt ); * * An accepting number is added to the given machine. If variable_trail_rule * is true then the rule has trailing context and both the head and trail * are variable size. Otherwise if headcnt or trailcnt is non-zero then * the machine recognizes a pattern with trailing context and headcnt is * the number of characters in the matched part of the pattern, or zero * if the matched part has variable length. trailcnt is the number of * trailing context characters in the pattern, or zero if the trailing * context has variable length. */ void finish_rule(int mach, int variable_trail_rule, int headcnt, int trailcnt) { add_accept( mach, num_rules ); /* we did this in new_rule(), but it often gets the wrong * number because we do it before we start parsing the current rule */ rule_linenum[num_rules] = linenum; /* if this is a continued action, then the line-number has * already been updated, giving us the wrong number */ if ( continued_action ) --rule_linenum[num_rules]; fprintf( temp_action_file, "case %d:\n", num_rules ); if ( variable_trail_rule ) { rule_type[num_rules] = RULE_VARIABLE; if ( performance_report ) fprintf( stderr, "Variable trailing context rule at line %d\n", rule_linenum[num_rules] ); variable_trailing_context_rules = true; } else { rule_type[num_rules] = RULE_NORMAL; if ( headcnt > 0 || trailcnt > 0 ) { /* do trailing context magic to not match the trailing characters */ char *scanner_cp = "yy_c_buf_p = yy_cp"; char *scanner_bp = "yy_bp"; fprintf( temp_action_file, "*yy_cp = yy_hold_char; /* undo effects of setting up yytext */\n" ); if ( headcnt > 0 ) fprintf( temp_action_file, "%s = %s + %d;\n", scanner_cp, scanner_bp, headcnt ); else fprintf( temp_action_file, "%s -= %d;\n", scanner_cp, trailcnt ); fprintf( temp_action_file, "YY_DO_BEFORE_ACTION; /* set up yytext again */\n" ); } } line_directive_out( temp_action_file ); } /* link_machines - connect two machines together * * synopsis * * new = link_machines( first, last ); * * new - a machine constructed by connecting first to last * first - the machine whose successor is to be last * last - the machine whose predecessor is to be first * * note: this routine concatenates the machine first with the machine * last to produce a machine new which will pattern-match first first * and then last, and will fail if either of the sub-patterns fails. * FIRST is set to new by the operation. last is unmolested. */ int link_machines(int first, int last) { if ( first == NIL ) return ( last ); else if ( last == NIL ) return ( first ); else { mkxtion( finalst[first], last ); finalst[first] = finalst[last]; lastst[first] = max( lastst[first], lastst[last] ); firstst[first] = min( firstst[first], firstst[last] ); return ( first ); } } /* mark_beginning_as_normal - mark each "beginning" state in a machine * as being a "normal" (i.e., not trailing context- * associated) states * * synopsis * * mark_beginning_as_normal( mach ) * * mach - machine to mark * * The "beginning" states are the epsilon closure of the first state */ void mark_beginning_as_normal(int mach) { switch ( state_type[mach] ) { case STATE_NORMAL: /* oh, we've already visited here */ return; case STATE_TRAILING_CONTEXT: state_type[mach] = STATE_NORMAL; if ( transchar[mach] == SYM_EPSILON ) { if ( trans1[mach] != NO_TRANSITION ) mark_beginning_as_normal( trans1[mach] ); if ( trans2[mach] != NO_TRANSITION ) mark_beginning_as_normal( trans2[mach] ); } break; default: flexerror( "bad state type in mark_beginning_as_normal()" ); break; } } /* mkbranch - make a machine that branches to two machines * * synopsis * * branch = mkbranch( first, second ); * * branch - a machine which matches either first's pattern or second's * first, second - machines whose patterns are to be or'ed (the | operator) * * note that first and second are NEITHER destroyed by the operation. Also, * the resulting machine CANNOT be used with any other "mk" operation except * more mkbranch's. Compare with mkor() */ int mkbranch(int first, int second) { int eps; if ( first == NO_TRANSITION ) return ( second ); else if ( second == NO_TRANSITION ) return ( first ); eps = mkstate( SYM_EPSILON ); mkxtion( eps, first ); mkxtion( eps, second ); return ( eps ); } /* mkclos - convert a machine into a closure * * synopsis * new = mkclos( state ); * * new - a new state which matches the closure of "state" */ int mkclos(int state) { return ( mkopt( mkposcl( state ) ) ); } /* mkopt - make a machine optional * * synopsis * * new = mkopt( mach ); * * new - a machine which optionally matches whatever mach matched * mach - the machine to make optional * * notes: * 1. mach must be the last machine created * 2. mach is destroyed by the call */ int mkopt(int mach) { int eps; if ( ! SUPER_FREE_EPSILON(finalst[mach]) ) { eps = mkstate( SYM_EPSILON ); mach = link_machines( mach, eps ); } /* can't skimp on the following if FREE_EPSILON(mach) is true because * some state interior to "mach" might point back to the beginning * for a closure */ eps = mkstate( SYM_EPSILON ); mach = link_machines( eps, mach ); mkxtion( mach, finalst[mach] ); return ( mach ); } /* mkor - make a machine that matches either one of two machines * * synopsis * * new = mkor( first, second ); * * new - a machine which matches either first's pattern or second's * first, second - machines whose patterns are to be or'ed (the | operator) * * note that first and second are both destroyed by the operation * the code is rather convoluted because an attempt is made to minimize * the number of epsilon states needed */ int mkor(int first, int second) { int eps, orend; if ( first == NIL ) return ( second ); else if ( second == NIL ) return ( first ); else { /* see comment in mkopt() about why we can't use the first state * of "first" or "second" if they satisfy "FREE_EPSILON" */ eps = mkstate( SYM_EPSILON ); first = link_machines( eps, first ); mkxtion( first, second ); if ( SUPER_FREE_EPSILON(finalst[first]) && accptnum[finalst[first]] == NIL ) { orend = finalst[first]; mkxtion( finalst[second], orend ); } else if ( SUPER_FREE_EPSILON(finalst[second]) && accptnum[finalst[second]] == NIL ) { orend = finalst[second]; mkxtion( finalst[first], orend ); } else { eps = mkstate( SYM_EPSILON ); first = link_machines( first, eps ); orend = finalst[first]; mkxtion( finalst[second], orend ); } } finalst[first] = orend; return ( first ); } /* mkposcl - convert a machine into a positive closure * * synopsis * new = mkposcl( state ); * * new - a machine matching the positive closure of "state" */ int mkposcl(int state) { int eps; if ( SUPER_FREE_EPSILON(finalst[state]) ) { mkxtion( finalst[state], state ); return ( state ); } else { eps = mkstate( SYM_EPSILON ); mkxtion( eps, state ); return ( link_machines( state, eps ) ); } } /* mkrep - make a replicated machine * * synopsis * new = mkrep( mach, lb, ub ); * * new - a machine that matches whatever "mach" matched from "lb" * number of times to "ub" number of times * * note * if "ub" is INFINITY then "new" matches "lb" or more occurrences of "mach" */ int mkrep(int mach, int lb, int ub) { int base_mach, tail, copy, i; base_mach = copysingl( mach, lb - 1 ); if ( ub == INFINITY ) { copy = dupmachine( mach ); mach = link_machines( mach, link_machines( base_mach, mkclos( copy ) ) ); } else { tail = mkstate( SYM_EPSILON ); for ( i = lb; i < ub; ++i ) { copy = dupmachine( mach ); tail = mkopt( link_machines( copy, tail ) ); } mach = link_machines( mach, link_machines( base_mach, tail ) ); } return ( mach ); } /* mkstate - create a state with a transition on a given symbol * * synopsis * * state = mkstate( sym ); * * state - a new state matching sym * sym - the symbol the new state is to have an out-transition on * * note that this routine makes new states in ascending order through the * state array (and increments LASTNFA accordingly). The routine DUPMACHINE * relies on machines being made in ascending order and that they are * CONTIGUOUS. Change it and you will have to rewrite DUPMACHINE (kludge * that it admittedly is) */ int mkstate(int sym) { if ( ++lastnfa >= current_mns ) { if ( (current_mns += MNS_INCREMENT) >= MAXIMUM_MNS ) lerrif( "input rules are too complicated (>= %d NFA states)", current_mns ); ++num_reallocs; firstst = reallocate_integer_array( firstst, current_mns ); lastst = reallocate_integer_array( lastst, current_mns ); finalst = reallocate_integer_array( finalst, current_mns ); transchar = reallocate_integer_array( transchar, current_mns ); trans1 = reallocate_integer_array( trans1, current_mns ); trans2 = reallocate_integer_array( trans2, current_mns ); accptnum = reallocate_integer_array( accptnum, current_mns ); assoc_rule = reallocate_integer_array( assoc_rule, current_mns ); state_type = reallocate_integer_array( state_type, current_mns ); } firstst[lastnfa] = lastnfa; finalst[lastnfa] = lastnfa; lastst[lastnfa] = lastnfa; transchar[lastnfa] = sym; trans1[lastnfa] = NO_TRANSITION; trans2[lastnfa] = NO_TRANSITION; accptnum[lastnfa] = NIL; assoc_rule[lastnfa] = num_rules; state_type[lastnfa] = current_state_type; /* fix up equivalence classes base on this transition. Note that any * character which has its own transition gets its own equivalence class. * Thus only characters which are only in character classes have a chance * at being in the same equivalence class. E.g. "a|b" puts 'a' and 'b' * into two different equivalence classes. "[ab]" puts them in the same * equivalence class (barring other differences elsewhere in the input). */ if ( sym < 0 ) { /* we don't have to update the equivalence classes since that was * already done when the ccl was created for the first time */ } else if ( sym == SYM_EPSILON ) ++numeps; else { if ( useecs ) /* map NUL's to csize */ mkechar( sym ? sym : csize, nextecm, ecgroup ); } return ( lastnfa ); } /* mkxtion - make a transition from one state to another * * synopsis * * mkxtion( statefrom, stateto ); * * statefrom - the state from which the transition is to be made * stateto - the state to which the transition is to be made */ void mkxtion(int statefrom, int stateto) { if ( trans1[statefrom] == NO_TRANSITION ) trans1[statefrom] = stateto; else if ( (transchar[statefrom] != SYM_EPSILON) || (trans2[statefrom] != NO_TRANSITION) ) flexfatal( "found too many transitions in mkxtion()" ); else { /* second out-transition for an epsilon state */ ++eps2; trans2[statefrom] = stateto; } } /* new_rule - initialize for a new rule * * synopsis * * new_rule(); * * the global num_rules is incremented and the any corresponding dynamic * arrays (such as rule_type[]) are grown as needed. */ void new_rule(void) { if ( ++num_rules >= current_max_rules ) { ++num_reallocs; current_max_rules += MAX_RULES_INCREMENT; rule_type = reallocate_integer_array( rule_type, current_max_rules ); rule_linenum = reallocate_integer_array( rule_linenum, current_max_rules ); } if ( num_rules > MAX_RULE ) lerrif( "too many rules (> %d)!", MAX_RULE ); rule_linenum[num_rules] = linenum; } base-7.0.3.1/modules/libcom/src/flex/parse.y0000664000577000060420000003410213557101274017361 0ustar anjaesctl /* parse.y - parser for flex input */ %token CHAR NUMBER SECTEND SCDECL XSCDECL WHITESPACE NAME PREVCCL EOF_OP %{ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "flexdef.h" int pat, scnum, eps, headcnt, trailcnt, anyccl, lastchar, i, actvp, rulelen; int trlcontxt, xcluflg, cclsorted, varlength, variable_trail_rule; Char clower(); static int madeany = false; /* whether we've made the '.' character class */ int previous_continued_action; /* whether the previous rule's action was '|' */ %} %% goal : initlex sect1 sect1end sect2 initforrule { /* add default rule */ int def_rule; pat = cclinit(); cclnegate( pat ); def_rule = mkstate( -pat ); finish_rule( def_rule, false, 0, 0 ); for ( i = 1; i <= lastsc; ++i ) scset[i] = mkbranch( scset[i], def_rule ); if ( spprdflt ) fputs( "YY_FATAL_ERROR( \"flex scanner jammed\" )", temp_action_file ); else fputs( "ECHO", temp_action_file ); fputs( ";\n\tYY_BREAK\n", temp_action_file ); } ; initlex : { /* initialize for processing rules */ /* create default DFA start condition */ scinstal( "INITIAL", false ); } ; sect1 : sect1 startconddecl WHITESPACE namelist1 '\n' | | error '\n' { synerr( "unknown error processing section 1" ); } ; sect1end : SECTEND ; startconddecl : SCDECL { /* these productions are separate from the s1object * rule because the semantics must be done before * we parse the remainder of an s1object */ xcluflg = false; } | XSCDECL { xcluflg = true; } ; namelist1 : namelist1 WHITESPACE NAME { scinstal( nmstr, xcluflg ); } | NAME { scinstal( nmstr, xcluflg ); } | error { synerr( "bad start condition list" ); } ; sect2 : sect2 initforrule flexrule '\n' | ; initforrule : { /* initialize for a parse of one rule */ trlcontxt = variable_trail_rule = varlength = false; trailcnt = headcnt = rulelen = 0; current_state_type = STATE_NORMAL; previous_continued_action = continued_action; new_rule(); } ; flexrule : scon '^' rule { pat = $3; finish_rule( pat, variable_trail_rule, headcnt, trailcnt ); for ( i = 1; i <= actvp; ++i ) scbol[actvsc[i]] = mkbranch( scbol[actvsc[i]], pat ); if ( ! bol_needed ) { bol_needed = true; if ( performance_report ) pinpoint_message( "'^' operator results in sub-optimal performance" ); } } | scon rule { pat = $2; finish_rule( pat, variable_trail_rule, headcnt, trailcnt ); for ( i = 1; i <= actvp; ++i ) scset[actvsc[i]] = mkbranch( scset[actvsc[i]], pat ); } | '^' rule { pat = $2; finish_rule( pat, variable_trail_rule, headcnt, trailcnt ); /* add to all non-exclusive start conditions, * including the default (0) start condition */ for ( i = 1; i <= lastsc; ++i ) if ( ! scxclu[i] ) scbol[i] = mkbranch( scbol[i], pat ); if ( ! bol_needed ) { bol_needed = true; if ( performance_report ) pinpoint_message( "'^' operator results in sub-optimal performance" ); } } | rule { pat = $1; finish_rule( pat, variable_trail_rule, headcnt, trailcnt ); for ( i = 1; i <= lastsc; ++i ) if ( ! scxclu[i] ) scset[i] = mkbranch( scset[i], pat ); } | scon EOF_OP { build_eof_action(); } | EOF_OP { /* this EOF applies to all start conditions * which don't already have EOF actions */ actvp = 0; for ( i = 1; i <= lastsc; ++i ) if ( ! sceof[i] ) actvsc[++actvp] = i; if ( actvp == 0 ) pinpoint_message( "warning - all start conditions already have <> rules" ); else build_eof_action(); } | error { synerr( "unrecognized rule" ); } ; scon : '<' namelist2 '>' ; namelist2 : namelist2 ',' NAME { if ( (scnum = sclookup( nmstr )) == 0 ) format_pinpoint_message( "undeclared start condition %s", nmstr ); else actvsc[++actvp] = scnum; } | NAME { if ( (scnum = sclookup( nmstr )) == 0 ) format_pinpoint_message( "undeclared start condition %s", nmstr ); else actvsc[actvp = 1] = scnum; } | error { synerr( "bad start condition list" ); } ; rule : re2 re { if ( transchar[lastst[$2]] != SYM_EPSILON ) /* provide final transition \now/ so it * will be marked as a trailing context * state */ $2 = link_machines( $2, mkstate( SYM_EPSILON ) ); mark_beginning_as_normal( $2 ); current_state_type = STATE_NORMAL; if ( previous_continued_action ) { /* we need to treat this as variable trailing * context so that the backup does not happen * in the action but before the action switch * statement. If the backup happens in the * action, then the rules "falling into" this * one's action will *also* do the backup, * erroneously. */ if ( ! varlength || headcnt != 0 ) { fprintf( stderr, "%s: warning - trailing context rule at line %d made variable because\n", program_name, linenum ); fprintf( stderr, " of preceding '|' action\n" ); } /* mark as variable */ varlength = true; headcnt = 0; } if ( varlength && headcnt == 0 ) { /* variable trailing context rule */ /* mark the first part of the rule as the accepting * "head" part of a trailing context rule */ /* by the way, we didn't do this at the beginning * of this production because back then * current_state_type was set up for a trail * rule, and add_accept() can create a new * state ... */ add_accept( $1, num_rules | YY_TRAILING_HEAD_MASK ); variable_trail_rule = true; } else trailcnt = rulelen; $$ = link_machines( $1, $2 ); } | re2 re '$' { synerr( "trailing context used twice" ); } | re '$' { if ( trlcontxt ) { synerr( "trailing context used twice" ); $$ = mkstate( SYM_EPSILON ); } else if ( previous_continued_action ) { /* see the comment in the rule for "re2 re" * above */ if ( ! varlength || headcnt != 0 ) { fprintf( stderr, "%s: warning - trailing context rule at line %d made variable because\n", program_name, linenum ); fprintf( stderr, " of preceding '|' action\n" ); } /* mark as variable */ varlength = true; headcnt = 0; } trlcontxt = true; if ( ! varlength ) headcnt = rulelen; ++rulelen; trailcnt = 1; eps = mkstate( SYM_EPSILON ); $$ = link_machines( $1, link_machines( eps, mkstate( '\n' ) ) ); } | re { $$ = $1; if ( trlcontxt ) { if ( varlength && headcnt == 0 ) /* both head and trail are variable-length */ variable_trail_rule = true; else trailcnt = rulelen; } } ; re : re '|' series { varlength = true; $$ = mkor( $1, $3 ); } | series { $$ = $1; } ; re2 : re '/' { /* this rule is written separately so * the reduction will occur before the trailing * series is parsed */ if ( trlcontxt ) synerr( "trailing context used twice" ); else trlcontxt = true; if ( varlength ) /* we hope the trailing context is fixed-length */ varlength = false; else headcnt = rulelen; rulelen = 0; current_state_type = STATE_TRAILING_CONTEXT; $$ = $1; } ; series : series singleton { /* this is where concatenation of adjacent patterns * gets done */ $$ = link_machines( $1, $2 ); } | singleton { $$ = $1; } ; singleton : singleton '*' { varlength = true; $$ = mkclos( $1 ); } | singleton '+' { varlength = true; $$ = mkposcl( $1 ); } | singleton '?' { varlength = true; $$ = mkopt( $1 ); } | singleton '{' NUMBER ',' NUMBER '}' { varlength = true; if ( $3 > $5 || $3 < 0 ) { synerr( "bad iteration values" ); $$ = $1; } else { if ( $3 == 0 ) $$ = mkopt( mkrep( $1, $3, $5 ) ); else $$ = mkrep( $1, $3, $5 ); } } | singleton '{' NUMBER ',' '}' { varlength = true; if ( $3 <= 0 ) { synerr( "iteration value must be positive" ); $$ = $1; } else $$ = mkrep( $1, $3, INFINITY ); } | singleton '{' NUMBER '}' { /* the singleton could be something like "(foo)", * in which case we have no idea what its length * is, so we punt here. */ varlength = true; if ( $3 <= 0 ) { synerr( "iteration value must be positive" ); $$ = $1; } else $$ = link_machines( $1, copysingl( $1, $3 - 1 ) ); } | '.' { if ( ! madeany ) { /* create the '.' character class */ anyccl = cclinit(); ccladd( anyccl, '\n' ); cclnegate( anyccl ); if ( useecs ) mkeccl( ccltbl + cclmap[anyccl], ccllen[anyccl], nextecm, ecgroup, csize, csize ); madeany = true; } ++rulelen; $$ = mkstate( -anyccl ); } | fullccl { if ( ! cclsorted ) /* sort characters for fast searching. We use a * shell sort since this list could be large. */ cshell( ccltbl + cclmap[$1], ccllen[$1], true ); if ( useecs ) mkeccl( ccltbl + cclmap[$1], ccllen[$1], nextecm, ecgroup, csize, csize ); ++rulelen; $$ = mkstate( -$1 ); } | PREVCCL { ++rulelen; $$ = mkstate( -$1 ); } | '"' string '"' { $$ = $2; } | '(' re ')' { $$ = $2; } | CHAR { ++rulelen; if ( caseins && $1 >= 'A' && $1 <= 'Z' ) $1 = clower( $1 ); $$ = mkstate( $1 ); } ; fullccl : '[' ccl ']' { $$ = $2; } | '[' '^' ccl ']' { /* *Sigh* - to be compatible Unix lex, negated ccls * match newlines */ #ifdef NOTDEF ccladd( $3, '\n' ); /* negated ccls don't match '\n' */ cclsorted = false; /* because we added the newline */ #endif cclnegate( $3 ); $$ = $3; } ; ccl : ccl CHAR '-' CHAR { if ( $2 > $4 ) synerr( "negative range in character class" ); else { if ( caseins ) { if ( $2 >= 'A' && $2 <= 'Z' ) $2 = clower( $2 ); if ( $4 >= 'A' && $4 <= 'Z' ) $4 = clower( $4 ); } for ( i = $2; i <= $4; ++i ) ccladd( $1, i ); /* keep track if this ccl is staying in alphabetical * order */ cclsorted = cclsorted && ($2 > lastchar); lastchar = $4; } $$ = $1; } | ccl CHAR { if ( caseins ) if ( $2 >= 'A' && $2 <= 'Z' ) $2 = clower( $2 ); ccladd( $1, $2 ); cclsorted = cclsorted && ($2 > lastchar); lastchar = $2; $$ = $1; } | { cclsorted = true; lastchar = 0; $$ = cclinit(); } ; string : string CHAR { if ( caseins ) if ( $2 >= 'A' && $2 <= 'Z' ) $2 = clower( $2 ); ++rulelen; $$ = link_machines( $1, mkstate( $2 ) ); } | { $$ = mkstate( SYM_EPSILON ); } ; %% /* build_eof_action - build the "<>" action for the active start * conditions */ void build_eof_action() { int i; for ( i = 1; i <= actvp; ++i ) { if ( sceof[actvsc[i]] ) format_pinpoint_message( "multiple <> rules for start condition %s", scname[actvsc[i]] ); else { sceof[actvsc[i]] = true; fprintf( temp_action_file, "case YY_STATE_EOF(%s):\n", scname[actvsc[i]] ); } } line_directive_out( temp_action_file ); } /* synerr - report a syntax error */ void synerr( str ) char str[]; { syntaxerror = true; pinpoint_message( str ); } /* format_pinpoint_message - write out a message formatted with one string, * pinpointing its location */ void format_pinpoint_message( msg, arg ) char msg[], arg[]; { char errmsg[MAXLINE]; (void) sprintf( errmsg, msg, arg ); pinpoint_message( errmsg ); } /* pinpoint_message - write out a message, pinpointing its location */ void pinpoint_message( str ) char str[]; { fprintf( stderr, "\"%s\", line %d: %s\n", infilename, linenum, str ); } /* yyerror - eat up an error message from the parser; * currently, messages are ignore */ void yyerror( msg ) char msg[]; { } #include "scan.c" #include "yylex.c" #include "flex.c" base-7.0.3.1/modules/libcom/src/flex/scan.c0000664000577000060420000021247513557101274017160 0ustar anjaesctl/* A lexical scanner generated by flex */ /* scanner skeleton */ #define FLEX_SCANNER #include /* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ #ifdef c_plusplus #ifndef __cplusplus #define __cplusplus #endif #endif #ifdef __cplusplus #include #include #else /* ! __cplusplus */ #ifdef __GNUC__ #include void *malloc( size_t ); void free( void* ); #else #include #endif /* __GNUC__ */ #endif /* ! __cplusplus */ /* amount of stuff to slurp up with each read */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* returned upon end-of-file */ #define YY_END_TOK 0 /* copy whatever the last rule matched to the standard output */ /* cast to (char *) is because for 8-bit chars, yytext is (unsigned char *) */ /* this used to be an fputs(), but since the string might contain NUL's, * we now use fwrite() */ #define ECHO (void) fwrite( (char *) yytext, yyleng, 1, yyout ) /* gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #define YY_INPUT(buf,result,max_size) \ if ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ YY_FATAL_ERROR( "read() in flex scanner failed" ); #define YY_NULL 0 /* no semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #define yyterminate() return ( YY_NULL ) /* report a fatal error */ /* The funky do-while is used to turn this macro definition into * a single C statement (which needs a semi-colon terminator). * This avoids problems with code like: * * if ( something_happens ) * YY_FATAL_ERROR( "oops, the something happened" ); * else * everything_okay(); * * Prior to using the do-while the compiler would get upset at the * "else" because it interpreted the "if" statement as being all * done when it reached the ';' after the YY_FATAL_ERROR() call. */ #define YY_FATAL_ERROR(msg) \ do \ { \ (void) fputs( msg, stderr ); \ (void) putc( '\n', stderr ); \ exit( 1 ); \ } \ while ( 0 ) /* default yywrap function - always treat EOF as an EOF */ #define yywrap() 1 /* enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN */ #define BEGIN yy_start = 1 + 2 * /* action number for EOF rule of a given start state */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* special action meaning "start processing a new file" */ #define YY_NEW_FILE \ do \ { \ yy_init_buffer( yy_current_buffer, yyin ); \ yy_load_buffer_state(); \ } \ while ( 0 ) /* default declaration of generated scanner - a define so the user can * easily add parameters */ #define YY_DECL int yylex ( void ) /* code executed at the end of each rule */ #define YY_BREAK break; #define YY_END_OF_BUFFER_CHAR 0 #ifndef YY_BUF_SIZE #define YY_BUF_SIZE (YY_READ_BUF_SIZE * 2) /* size of default input buffer */ #endif typedef struct yy_buffer_state *YY_BUFFER_STATE; #define YY_CHAR unsigned char # line 1 "scan.l" #define INITIAL 0 /* scan.l - scanner for flex input */ # line 5 "scan.l" /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #undef yywrap #define ACTION_ECHO fprintf( temp_action_file, "%s", yytext ) #define MARK_END_OF_PROLOG fprintf( temp_action_file, "%%%% end of prolog\n" ); #undef YY_DECL #define YY_DECL \ int flexscan() #define RETURNCHAR \ yylval = yytext[0]; \ return ( CHAR ); #define RETURNNAME \ (void) strcpy( nmstr, (char *) yytext ); \ return ( NAME ); #define PUT_BACK_STRING(str, start) \ for ( i = strlen( (char *) (str) ) - 1; i >= start; --i ) \ unput((str)[i]) #define CHECK_REJECT(str) \ if ( all_upper( str ) ) \ reject = true; #define CHECK_YYMORE(str) \ if ( all_lower( str ) ) \ yymore_used = true; #define SECT2 1 #define SECT2PROLOG 2 #define SECT3 3 #define CODEBLOCK 4 #define PICKUPDEF 5 #define SC 6 #define CARETISBOL 7 #define NUM 8 #define QUOTE 9 #define FIRSTCCL 10 #define CCL 11 #define ACTION 12 #define RECOVER 13 #define BRACEERROR 14 #define C_COMMENT 15 #define ACTION_COMMENT 16 #define ACTION_STRING 17 #define PERCENT_BRACE_ACTION 18 #define USED_LIST 19 #define CODEBLOCK_2 20 #define XLATION 21 # line 76 "scan.l" /* done after the current pattern has been matched and before the * corresponding action - sets up yytext */ #define YY_DO_BEFORE_ACTION \ yytext = yy_bp; \ yyleng = yy_cp - yy_bp; \ yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yy_c_buf_p = yy_cp; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 /* return all but the first 'n' matched characters back to the input stream */ #define yyless(n) \ do \ { \ /* undo effects of setting up yytext */ \ *yy_cp = yy_hold_char; \ yy_c_buf_p = yy_cp = yy_bp + n; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yytext ) struct yy_buffer_state { FILE *yy_input_file; YY_CHAR *yy_ch_buf; /* input buffer */ YY_CHAR *yy_buf_pos; /* current position in input buffer */ /* size of input buffer in bytes, not including room for EOB characters*/ int yy_buf_size; /* number of characters read into yy_ch_buf, not including EOB characters */ int yy_n_chars; int yy_eof_status; /* whether we've seen an EOF on this buffer */ #define EOF_NOT_SEEN 0 /* "pending" happens when the EOF has been seen but there's still * some text process */ #define EOF_PENDING 1 #define EOF_DONE 2 }; static YY_BUFFER_STATE yy_current_buffer; /* we provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state" */ #define YY_CURRENT_BUFFER yy_current_buffer /* yy_hold_char holds the character lost when yytext is formed */ static YY_CHAR yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif #ifndef YY_USER_INIT #define YY_USER_INIT #endif extern YY_CHAR *yytext; extern int yyleng; extern FILE *yyin, *yyout; YY_CHAR *yytext; int yyleng; FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; #define YY_END_OF_BUFFER 121 typedef int yy_state_type; static const short int yy_acclist[482] = { 0, 16444,16444, 119, 119, 121, 19, 120, 7, 19, 120, 18, 120, 19, 120, 19, 120, 16, 19, 120, 1, 7, 19, 120, 17, 18, 120, 19, 120, 19, 120, 19, 120, 19, 120, 15, 16, 19, 120, 67, 120, 59, 67, 120,16444, 8252, 68, 120, 67, 120, 53, 67, 120, 67, 120, 66, 67, 120, 51, 67, 120, 67, 120, 67, 120, 67, 120, 50, 59, 67, 120, 16444, 49, 8252, 61, 68, 120, 67, 120, 67, 120, 52, 67, 120, 120, 47, 120, 120, 119, 120, 119, 120, 119, 120, 28, 120, 29, 120, 28, 120, 28, 120, 28, 120, 28, 120, 28, 120, 31, 120, 30, 120, 32, 120, 120, 73, 120, 120, 69, 73, 120, 70, 73, 120, 72, 73, 120, 74, 120, 88, 120, 89, 120, 88, 120, 86, 88, 120, 85, 88, 120, 87, 88, 120, 75, 120, 77, 120, 120, 76, 120, 75, 120, 81, 120, 80, 81, 120, 81, 120, 81, 120, 83, 120, 83, 120, 83, 120, 84, 120, 99, 105, 120, 104, 120, 105, 120, 103, 105, 120, 105, 120, 105, 120, 100, 105, 120, 100, 105, 120, 100, 105, 120, 97, 105, 120, 98, 105, 120, 120, 33, 120, 120, 91, 120, 120, 90, 120, 22, 120, 24, 120, 120, 23, 120, 107, 110, 120, 109, 120, 110, 120, 108, 110, 120, 111, 115, 120, 113, 120, 115, 120, 114, 115, 120, 115, 120, 95, 120, 95, 120, 96, 120, 95, 120, 95, 120, 95, 120, 95, 120, 95, 120, 38, 120, 35, 120, 34, 120, 120, 38, 120, 38, 120, 44, 120, 42, 44, 120, 45, 120, 44, 120, 44, 120, 44, 120, 41, 44, 120, 41, 42, 44, 120, 41, 44, 120, 41, 44, 120, 40, 41, 44, 120, 41, 44, 120, 7, 18, 16, 1, 7, 17, 18, 2, 14, 8, 14, 12, 4, 5, 3, 15, 16, 59,16444, 8252, 8252, 68, 56, 117, 117, 117, 55, 54, 55, 50, 59,16444, 49, 8252, 61, 49, 8252, 61, 68, 63, 50, 47, 46, 119, 119, 119, 28, 29, 28, 28, 28, 28, 31, 30, 32, 71, 72, 89, 85, 77, 118, 118, 118, 78, 79, 82, 99, 104, 102, 101, 100, 100, 100, 33, 91, 22, 24, 20, 107, 109, 106, 111, 113, 112, 95, 95, 95, 96, 92, 95, 95, 95, 95, 38, 35, 34, 38, 38, 42, 45, 43, 43, 43, 42, 40, 13, 14, 8, 8, 14, 12, 4, 5, 6, 57, 58, 64, 117, 117, 55, 55, 65, 63, 28, 28, 28, 25, 118, 118, 100, 100, 21, 92, 95, 92, 95, 95, 38, 38, 39, 43, 43, 11, 4, 11, 13, 5, 117, 28, 28, 118, 100, 100, 95, 95, 38, 38, 43, 9, 28, 28, 100, 100, 95, 95, 38, 38, 26, 28, 27, 28, 93, 100, 94, 100, 93, 95, 94, 95, 36, 38, 37, 38, 10, 62 } ; static const short int yy_accept[392] = { 0, 1, 1, 1, 2, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 8, 11, 13, 15, 17, 20, 24, 27, 29, 31, 33, 35, 39, 41, 45, 48, 50, 53, 55, 58, 61, 63, 65, 67, 72, 77, 79, 81, 84, 85, 87, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 115, 117, 118, 121, 124, 127, 129, 131, 133, 135, 138, 141, 144, 146, 148, 149, 151, 153, 155, 158, 160, 162, 164, 166, 168, 170, 173, 175, 177, 180, 182, 184, 187, 190, 193, 196, 199, 200, 202, 203, 205, 206, 208, 210, 212, 213, 215, 218, 220, 222, 225, 228, 230, 232, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 257, 259, 260, 262, 264, 266, 269, 271, 273, 275, 277, 280, 284, 287, 290, 294, 297, 298, 299, 299, 299, 300, 302, 304, 304, 304, 305, 305, 306, 308, 308, 309, 310, 310, 310, 311, 311, 312, 314, 316, 317, 317, 317, 317, 319, 320, 320, 320, 320, 321, 322, 323, 324, 325, 326, 329, 332, 332, 336, 337, 338, 338, 339, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 365, 366, 366, 367, 368, 369, 370, 370, 371, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 397, 398, 399, 400, 401, 402, 402, 403, 403, 404, 405, 407, 407, 408, 409, 409, 409, 409, 410, 410, 411, 411, 412, 412, 413, 413, 413, 414, 414, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 424, 424, 425, 426, 427, 428, 428, 429, 429, 431, 432, 433, 434, 435, 436, 436, 437, 437, 438, 439, 439, 440, 440, 441, 441, 443, 443, 443, 443, 444, 444, 444, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 455, 456, 456, 456, 456, 457, 458, 459, 460, 461, 462, 463, 464, 464, 464, 466, 468, 470, 472, 474, 476, 478, 480, 481, 482, 482 } ; static const YY_CHAR yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 4, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 7, 8, 9, 10, 1, 11, 12, 12, 13, 12, 14, 15, 12, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 1, 1, 18, 1, 19, 12, 1, 25, 26, 27, 28, 29, 30, 24, 24, 24, 31, 32, 24, 33, 34, 35, 32, 24, 36, 37, 38, 39, 24, 24, 40, 41, 24, 20, 21, 22, 23, 24, 1, 25, 26, 27, 28, 29, 30, 24, 24, 24, 31, 32, 24, 33, 34, 35, 32, 24, 36, 37, 38, 39, 24, 24, 40, 41, 24, 42, 43, 44, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static const YY_CHAR yy_meta[45] = { 0, 1, 2, 3, 2, 4, 2, 5, 1, 1, 1, 6, 1, 7, 1, 8, 6, 9, 1, 1, 1, 10, 11, 1, 12, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 6, 1, 14 } ; static const short int yy_base[454] = { 0, 0, 44, 87, 129, 92, 99, 106, 107, 172, 1520, 111, 116, 216, 0, 1506, 1464, 123, 258, 141, 148, 259, 262, 266, 286, 308, 0, 120, 151, 261, 350, 155, 265, 281, 287, 351, 354, 394, 0, 437, 477, 0, 0, 517, 537, 1475, 1567, 293, 1567, 1471, 1394, 0, 360, 1567, 1416, 154, 549, 1405, 0, 1567, 590, 1567, 1402, 1567, 365, 1567, 1386, 1382, 84, 633, 676, 1567, 1396, 350, 1567, 158, 0, 161, 301, 1567, 371, 0, 1567, 1395, 0, 1365, 1352, 1341, 0, 375, 1567, 1381, 1567, 1567, 1567, 1347, 0, 1567, 1567, 1567, 1360, 1567, 1341, 1567, 1567, 1567, 1354, 1567, 427, 1567, 1567, 428, 1341, 1567, 0, 429, 1567, 0, 1567, 1343, 1567, 282, 1330, 0, 1313, 1291, 1567, 1567, 375, 1567, 379, 1567, 1326, 1567, 0, 1567, 1325, 1293, 0, 1567, 1305, 1279, 0, 1567, 1291, 1567, 0, 0, 381, 1567, 1283, 1241, 0, 1255, 1242, 0, 384, 1567, 1274, 1241, 1228, 1567, 445, 1567, 1253, 1217, 431, 1567, 448, 1239, 1203, 1219, 436, 453, 1567, 1232, 458, 0, 482, 1567, 1218, 467, 1567, 472, 487, 488, 1210, 493, 0, 498, 239, 0, 493, 1567, 0, 0, 1567, 1212, 1169, 502, 1567, 1567, 1181, 487, 489, 1567, 1185, 0, 0, 1567, 705, 0, 1567, 1198, 1567, 0, 1567, 507, 0, 511, 1567, 512, 1567, 521, 0, 1567, 0, 1143, 1140, 749, 0, 526, 1567, 1567, 0, 1567, 1155, 1567, 1567, 1129, 0, 1567, 1567, 1567, 0, 1567, 514, 1567, 1140, 1567, 0, 1108, 1099, 528, 1567, 531, 1567, 0, 1567, 541, 0, 1567, 1567, 0, 1567, 1567, 0, 546, 1087, 1567, 793, 0, 1095, 1092, 0, 547, 1567, 1062, 1059, 558, 1567, 563, 1567, 842, 0, 573, 841, 599, 1567, 854, 604, 0, 605, 552, 610, 0, 615, 817, 826, 0, 558, 1567, 567, 1567, 568, 1567, 577, 819, 1567, 605, 821, 836, 0, 0, 0, 1567, 0, 816, 809, 0, 1567, 594, 610, 824, 0, 811, 804, 620, 1567, 625, 0, 0, 767, 713, 718, 693, 685, 1567, 724, 709, 0, 626, 1567, 633, 0, 690, 1567, 680, 688, 696, 0, 693, 680, 835, 1567, 694, 682, 1567, 690, 664, 657, 596, 597, 562, 1567, 516, 1567, 518, 677, 682, 436, 437, 241, 238, 132, 136, 105, 98, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 1567, 1567, 1567, 865, 879, 893, 907, 921, 935, 949, 963, 977, 991, 1005, 1019, 1033, 1047, 1061, 1075, 1082, 1095, 1109, 1115, 1128, 1142, 1156, 1170, 1184, 1198, 1205, 1218, 1225, 1238, 1252, 1266, 1280, 1291, 1298, 1311, 1325, 1339, 1353, 1367, 1381, 1388, 1401, 1415, 1429, 693, 695, 1443, 1457, 360, 1471, 1484, 380, 1498, 700, 1512, 1519, 1525, 701, 1538, 702, 1552, 703 } ; static const short int yy_def[454] = { 0, 390, 390, 391, 391, 392, 392, 393, 393, 390, 9, 394, 394, 390, 13, 395, 395, 396, 396, 397, 397, 398, 398, 399, 399, 390, 25, 400, 400, 395, 395, 401, 401, 402, 402, 403, 403, 390, 37, 404, 404, 37, 37, 405, 406, 390, 390, 390, 390, 390, 390, 407, 390, 390, 390, 408, 409, 390, 410, 390, 390, 390, 390, 390, 390, 390, 390, 411, 412, 390, 390, 390, 390, 390, 390, 413, 414, 413, 415, 390, 415, 416, 390, 390, 417, 417, 417, 416, 418, 390, 390, 390, 390, 390, 390, 390, 419, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 412, 390, 390, 420, 421, 390, 422, 412, 390, 423, 390, 390, 390, 424, 390, 425, 425, 425, 390, 390, 426, 390, 426, 390, 390, 390, 427, 390, 390, 390, 428, 390, 390, 390, 429, 390, 390, 390, 430, 431, 431, 390, 390, 431, 432, 432, 432, 433, 390, 390, 390, 433, 433, 390, 390, 390, 390, 390, 434, 390, 390, 390, 390, 390, 434, 390, 390, 390, 390, 407, 390, 390, 390, 408, 390, 408, 390, 435, 390, 390, 436, 390, 390, 437, 438, 390, 410, 60, 390, 390, 390, 439, 390, 390, 390, 411, 411, 390, 390, 440, 441, 390, 441, 70, 390, 390, 390, 442, 390, 413, 414, 413, 390, 415, 390, 415, 416, 390, 417, 417, 417, 390, 418, 390, 390, 390, 419, 390, 390, 390, 390, 390, 443, 390, 390, 390, 423, 390, 424, 390, 424, 390, 425, 425, 425, 426, 390, 426, 390, 427, 390, 444, 428, 390, 390, 429, 390, 390, 431, 431, 431, 390, 390, 432, 432, 432, 433, 390, 390, 433, 433, 390, 390, 390, 390, 390, 445, 390, 390, 390, 390, 390, 390, 435, 435, 446, 390, 447, 446, 390, 390, 448, 438, 390, 438, 390, 439, 390, 439, 390, 390, 411, 411, 390, 449, 441, 210, 390, 442, 417, 417, 229, 390, 450, 450, 390, 451, 425, 425, 444, 390, 444, 270, 452, 432, 432, 433, 433, 390, 390, 390, 390, 453, 446, 390, 446, 447, 446, 390, 446, 390, 390, 448, 390, 411, 310, 390, 417, 417, 390, 425, 425, 432, 432, 433, 433, 390, 390, 390, 390, 411, 411, 417, 417, 425, 425, 432, 432, 433, 433, 390, 390, 417, 417, 425, 425, 432, 432, 433, 433, 390, 390, 0, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390 } ; static const short int yy_nxt[1612] = { 0, 46, 47, 48, 47, 49, 47, 46, 46, 46, 50, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 46, 46, 46, 46, 52, 53, 52, 54, 52, 46, 55, 46, 56, 46, 46, 46, 46, 46, 57, 46, 46, 46, 46, 46, 46, 46, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 46, 46, 46, 60, 61, 60, 62, 60, 63, 76, 64, 77, 389, 65, 65, 206, 76, 65, 77, 66, 388, 67, 68, 79, 79, 80, 80, 89, 90, 89, 91, 89, 89, 90, 89, 91, 89, 129, 207, 130, 99, 387, 100, 69, 65, 70, 71, 70, 72, 70, 63, 101, 64, 73, 102, 65, 65, 386, 105, 65, 106, 66, 107, 67, 68, 105, 74, 106, 129, 107, 130, 182, 135, 183, 136, 218, 108, 219, 218, 385, 219, 103, 137, 108, 384, 69, 65, 81, 81, 82, 81, 83, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 85, 84, 84, 84, 84, 86, 81, 81, 81, 92, 92, 93, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 94, 92, 92, 92, 92, 95, 92, 92, 92, 92, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 92, 92, 92, 99, 93, 100, 131, 93, 132, 383, 135, 93, 136, 93, 101, 297, 110, 102, 298, 110, 137, 382, 111, 114, 112, 111, 139, 112, 140, 115, 116, 93, 139, 93, 140, 247, 141, 173, 174, 173, 175, 173, 141, 114, 103, 248, 222, 133, 223, 115, 116, 117, 117, 118, 117, 119, 117, 120, 117, 117, 117, 121, 117, 117, 117, 117, 122, 117, 117, 117, 117, 117, 117, 117, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 124, 123, 123, 123, 123, 125, 126, 117, 127, 131, 143, 132, 144, 143, 145, 144, 215, 145, 178, 179, 178, 180, 178, 201, 201, 312, 201, 201, 146, 312, 222, 146, 223, 231, 254, 231, 255, 231, 254, 267, 255, 267, 275, 267, 275, 324, 275, 268, 216, 324, 133, 147, 148, 149, 148, 150, 148, 147, 147, 147, 151, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 153, 152, 152, 152, 152, 154, 147, 147, 147, 156, 157, 156, 158, 156, 206, 239, 206, 279, 283, 279, 285, 279, 285, 283, 285, 173, 174, 173, 175, 173, 287, 288, 287, 289, 287, 286, 381, 207, 240, 207, 182, 284, 183, 159, 380, 182, 284, 183, 160, 156, 157, 156, 158, 156, 178, 179, 178, 180, 178, 290, 292, 290, 292, 290, 292, 294, 301, 294, 302, 294, 287, 288, 287, 289, 296, 305, 309, 306, 204, 308, 218, 203, 219, 159, 218, 222, 219, 223, 160, 162, 163, 162, 164, 162, 222, 247, 223, 165, 231, 310, 231, 254, 231, 255, 254, 248, 255, 379, 166, 168, 163, 168, 169, 168, 328, 378, 329, 170, 267, 275, 267, 275, 267, 275, 171, 342, 268, 343, 172, 185, 279, 301, 279, 302, 279, 336, 337, 336, 338, 336, 301, 305, 302, 306, 186, 285, 187, 285, 186, 285, 305, 186, 306, 186, 186, 187, 188, 189, 190, 191, 286, 192, 195, 196, 195, 197, 195, 320, 377, 321, 198, 287, 288, 287, 289, 287, 290, 292, 290, 292, 290, 292, 294, 320, 294, 321, 294, 345, 346, 345, 347, 345, 352, 328, 376, 329, 204, 308, 328, 342, 329, 343, 375, 199, 208, 208, 342, 208, 343, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 209, 208, 208, 208, 208, 208, 208, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 208, 208, 208, 211, 212, 211, 213, 211, 346, 374, 343, 198, 336, 337, 336, 338, 336, 345, 346, 345, 347, 345, 368, 204, 308, 373, 204, 308, 204, 308, 295, 295, 299, 299, 340, 354, 357, 364, 340, 354, 357, 364, 372, 371, 199, 314, 370, 314, 367, 366, 365, 282, 337, 363, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 362, 361, 315, 319, 319, 320, 319, 321, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 319, 319, 319, 330, 330, 360, 330, 331, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, 330, 330, 330, 353, 359, 358, 238, 204, 308, 356, 355, 353, 353, 353, 353, 353, 353, 369, 205, 351, 349, 348, 288, 286, 339, 369, 369, 369, 369, 369, 369, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 177, 177, 335, 334, 177, 177, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 194, 194, 333, 332, 194, 194, 203, 203, 270, 326, 203, 203, 203, 203, 203, 203, 325, 203, 203, 203, 205, 205, 246, 323, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 220, 236, 318, 317, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 224, 224, 212, 311, 224, 224, 224, 224, 224, 224, 224, 307, 303, 224, 226, 226, 196, 293, 226, 226, 230, 230, 179, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 234, 234, 174, 286, 234, 234, 238, 238, 281, 280, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 241, 241, 281, 280, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 243, 243, 278, 277, 243, 243, 243, 243, 243, 243, 276, 243, 243, 243, 244, 244, 273, 272, 270, 269, 244, 244, 244, 244, 244, 246, 246, 264, 262, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 250, 250, 261, 259, 250, 250, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 257, 257, 258, 256, 257, 257, 252, 257, 257, 257, 257, 257, 257, 257, 260, 260, 251, 249, 260, 260, 245, 260, 260, 260, 260, 260, 260, 260, 263, 263, 242, 237, 236, 263, 263, 263, 263, 235, 263, 263, 263, 263, 265, 265, 233, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, 266, 266, 232, 229, 266, 266, 266, 266, 266, 266, 266, 228, 227, 266, 271, 271, 225, 214, 271, 271, 274, 204, 202, 200, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 282, 282, 193, 179, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 291, 291, 176, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 291, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 313, 313, 174, 390, 313, 313, 313, 313, 313, 313, 313, 313, 313, 316, 316, 97, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 344, 344, 97, 87, 344, 344, 350, 350, 390, 390, 350, 350, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, 331, 331, 390, 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, 45, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390 } ; static const short int yy_chk[1612] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 5, 3, 5, 379, 3, 3, 68, 6, 3, 6, 3, 378, 3, 3, 7, 8, 7, 8, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 27, 68, 27, 17, 377, 17, 3, 3, 4, 4, 4, 4, 4, 4, 17, 4, 4, 17, 4, 4, 376, 19, 4, 19, 4, 19, 4, 4, 20, 4, 20, 28, 20, 28, 55, 31, 55, 31, 75, 19, 75, 77, 375, 77, 17, 31, 20, 374, 4, 4, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 18, 21, 18, 29, 22, 29, 373, 32, 23, 32, 23, 18, 190, 21, 18, 190, 22, 32, 372, 21, 23, 21, 22, 33, 22, 33, 23, 23, 24, 34, 24, 34, 121, 33, 47, 47, 47, 47, 47, 34, 24, 18, 121, 78, 29, 78, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 30, 35, 30, 35, 36, 35, 36, 73, 36, 52, 52, 52, 52, 52, 64, 64, 440, 64, 64, 35, 440, 80, 36, 80, 89, 128, 89, 128, 89, 130, 148, 130, 148, 156, 148, 156, 443, 156, 148, 73, 443, 30, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 39, 39, 39, 39, 39, 108, 111, 115, 162, 166, 162, 168, 162, 168, 172, 168, 173, 173, 173, 173, 173, 176, 176, 176, 176, 176, 168, 371, 108, 111, 115, 181, 166, 181, 39, 370, 183, 172, 183, 39, 40, 40, 40, 40, 40, 178, 178, 178, 178, 178, 184, 185, 184, 185, 184, 185, 187, 192, 187, 192, 187, 189, 189, 189, 189, 189, 199, 204, 199, 203, 203, 217, 204, 217, 40, 219, 221, 219, 221, 40, 43, 43, 43, 43, 43, 223, 246, 223, 43, 231, 204, 231, 253, 231, 253, 255, 246, 255, 367, 43, 44, 44, 44, 44, 44, 259, 365, 259, 44, 267, 275, 267, 275, 267, 275, 44, 293, 267, 293, 44, 56, 279, 300, 279, 300, 279, 281, 281, 281, 281, 281, 302, 304, 302, 304, 56, 285, 56, 285, 56, 285, 306, 56, 306, 56, 56, 56, 56, 56, 56, 56, 285, 56, 60, 60, 60, 60, 60, 321, 363, 321, 60, 287, 287, 287, 287, 287, 290, 292, 290, 292, 290, 292, 294, 322, 294, 322, 294, 296, 296, 296, 296, 296, 309, 327, 362, 327, 309, 309, 329, 341, 329, 341, 361, 60, 69, 69, 343, 69, 343, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 70, 70, 70, 70, 70, 347, 360, 347, 70, 336, 336, 336, 336, 336, 345, 345, 345, 345, 345, 352, 368, 368, 359, 352, 352, 369, 369, 436, 436, 437, 437, 445, 449, 451, 453, 445, 449, 451, 453, 358, 356, 70, 210, 355, 210, 351, 349, 348, 339, 338, 335, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 334, 333, 210, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 270, 270, 332, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 310, 326, 325, 323, 310, 310, 318, 317, 310, 310, 310, 310, 310, 310, 353, 311, 307, 298, 297, 289, 286, 283, 353, 353, 353, 353, 353, 353, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, 393, 393, 393, 393, 393, 393, 393, 393, 393, 393, 393, 393, 393, 393, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 395, 395, 395, 395, 395, 395, 395, 395, 395, 395, 395, 395, 395, 395, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 397, 397, 397, 397, 397, 397, 397, 397, 397, 397, 397, 397, 397, 397, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 399, 399, 399, 399, 399, 399, 399, 399, 399, 399, 399, 399, 399, 399, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, 402, 402, 402, 402, 402, 402, 402, 402, 402, 402, 402, 402, 402, 402, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 407, 407, 278, 277, 407, 407, 408, 408, 408, 408, 408, 408, 408, 408, 408, 408, 408, 408, 408, 408, 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, 410, 410, 273, 272, 410, 410, 411, 411, 268, 252, 411, 411, 411, 411, 411, 411, 251, 411, 411, 411, 412, 412, 248, 239, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 414, 236, 228, 227, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 415, 415, 415, 415, 415, 415, 415, 415, 415, 415, 415, 415, 415, 415, 416, 416, 213, 206, 416, 416, 416, 416, 416, 416, 416, 202, 198, 416, 417, 417, 197, 186, 417, 417, 418, 418, 180, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 419, 419, 175, 171, 419, 419, 420, 420, 170, 169, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 421, 421, 165, 164, 421, 421, 421, 421, 421, 421, 421, 421, 421, 421, 422, 422, 160, 159, 422, 422, 422, 422, 422, 422, 158, 422, 422, 422, 423, 423, 154, 153, 151, 150, 423, 423, 423, 423, 423, 424, 424, 144, 141, 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, 425, 425, 140, 137, 425, 425, 426, 426, 426, 426, 426, 426, 426, 426, 426, 426, 426, 426, 426, 426, 427, 427, 136, 132, 427, 427, 125, 427, 427, 427, 427, 427, 427, 427, 428, 428, 124, 122, 428, 428, 119, 428, 428, 428, 428, 428, 428, 428, 429, 429, 112, 106, 102, 429, 429, 429, 429, 100, 429, 429, 429, 429, 430, 430, 95, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, 431, 431, 91, 87, 431, 431, 431, 431, 431, 431, 431, 86, 85, 431, 432, 432, 83, 72, 432, 432, 433, 67, 66, 62, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 434, 434, 57, 54, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 435, 435, 50, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 438, 438, 438, 438, 438, 438, 438, 438, 438, 438, 438, 438, 438, 438, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 441, 441, 49, 45, 441, 441, 441, 441, 441, 441, 441, 441, 441, 442, 442, 16, 442, 442, 442, 442, 442, 442, 442, 442, 442, 442, 442, 444, 444, 444, 444, 444, 444, 444, 444, 444, 444, 444, 444, 444, 444, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 447, 447, 15, 10, 447, 447, 448, 448, 0, 0, 448, 448, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 452, 452, 0, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390 } ; static yy_state_type yy_state_buf[YY_BUF_SIZE + 2], *yy_state_ptr; static YY_CHAR *yy_full_match; static int yy_lp; static int yy_looking_for_trail_begin = 0; static int yy_full_lp; static int *yy_full_state; #define YY_TRAILING_MASK 0x2000 #define YY_TRAILING_HEAD_MASK 0x4000 #define REJECT \ { \ *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ \ yy_cp = yy_full_match; /* restore poss. backed-over text */ \ yy_lp = yy_full_lp; /* restore orig. accepting pos. */ \ yy_state_ptr = yy_full_state; /* restore orig. state */ \ yy_current_state = *yy_state_ptr; /* restore curr. state */ \ ++yy_lp; \ goto find_rule; \ } #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 /* these variables are all declared out here so that section 3 code can * manipulate them */ /* points to current character in buffer */ static YY_CHAR *yy_c_buf_p = (YY_CHAR *) 0; static int yy_init = 1; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* flag which is used to allow yywrap()'s to do buffer switches * instead of setting up a fresh yyin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; static yy_state_type yy_get_previous_state ( void ); static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); static int yy_get_next_buffer ( void ); static void yyunput ( YY_CHAR c, YY_CHAR *buf_ptr ); void yyrestart ( FILE *input_file ); void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); void yy_load_buffer_state ( void ); YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); void yy_delete_buffer ( YY_BUFFER_STATE b ); void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); #define yy_new_buffer yy_create_buffer YY_DECL { yy_state_type yy_current_state; YY_CHAR *yy_cp, *yy_bp; int yy_act; static int bracelevel, didadef; int i, indented_code = false, checking_used = false, new_xlation = false; int doing_codeblock = false; Char nmdef[MAXLINE], myesc(); if ( yy_init ) { YY_USER_INIT; if ( ! yy_start ) yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( yy_current_buffer ) yy_init_buffer( yy_current_buffer, yyin ); else yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); yy_load_buffer_state(); yy_init = 0; } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = yy_c_buf_p; /* support of yytext */ *yy_cp = yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of the * current run. */ yy_bp = yy_cp; yy_current_state = yy_start; if ( yy_bp[-1] == '\n' ) ++yy_current_state; yy_state_ptr = yy_state_buf; *yy_state_ptr++ = yy_current_state; yy_match: do { YY_CHAR yy_c = yy_ec[(int)*yy_cp]; while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = yy_def[yy_current_state]; if ( yy_current_state >= 391 ) yy_c = yy_meta[(int)yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; *yy_state_ptr++ = yy_current_state; ++yy_cp; } while ( yy_current_state != 390 ); yy_find_action: yy_current_state = *--yy_state_ptr; yy_lp = yy_accept[yy_current_state]; find_rule: /* we branch to this label when backtracking */ for ( ; ; ) /* until we find what rule we matched */ { if ( yy_lp && yy_lp < yy_accept[yy_current_state + 1] ) { yy_act = yy_acclist[yy_lp]; if ( yy_act & YY_TRAILING_HEAD_MASK || yy_looking_for_trail_begin ) { if ( yy_act == yy_looking_for_trail_begin ) { yy_looking_for_trail_begin = 0; yy_act &= ~YY_TRAILING_HEAD_MASK; break; } } else if ( yy_act & YY_TRAILING_MASK ) { yy_looking_for_trail_begin = yy_act & ~YY_TRAILING_MASK; yy_looking_for_trail_begin |= YY_TRAILING_HEAD_MASK; } else { yy_full_match = yy_cp; yy_full_state = yy_state_ptr; yy_full_lp = yy_lp; break; } ++yy_lp; goto find_rule; } --yy_cp; yy_current_state = *--yy_state_ptr; yy_lp = yy_accept[yy_current_state]; } YY_DO_BEFORE_ACTION; YY_USER_ACTION; do_action: /* this label is used only to access EOF actions */ switch ( yy_act ) { case 1: # line 82 "scan.l" indented_code = true; BEGIN(CODEBLOCK); YY_BREAK case 2: # line 83 "scan.l" ++linenum; /* treat as a comment */ YY_BREAK case 3: # line 84 "scan.l" ECHO; BEGIN(C_COMMENT); YY_BREAK case 4: # line 85 "scan.l" return ( SCDECL ); YY_BREAK case 5: # line 86 "scan.l" return ( XSCDECL ); YY_BREAK case 6: # line 87 "scan.l" { ++linenum; line_directive_out( stdout ); indented_code = false; BEGIN(CODEBLOCK); } YY_BREAK case 7: # line 94 "scan.l" return ( WHITESPACE ); YY_BREAK case 8: # line 96 "scan.l" { sectnum = 2; line_directive_out( stdout ); BEGIN(SECT2PROLOG); return ( SECTEND ); } YY_BREAK case 9: # line 103 "scan.l" { pinpoint_message( "warning - %%used/%%unused have been deprecated" ); checking_used = REALLY_USED; BEGIN(USED_LIST); } YY_BREAK case 10: # line 107 "scan.l" { checking_used = REALLY_NOT_USED; BEGIN(USED_LIST); pinpoint_message( "warning - %%used/%%unused have been deprecated" ); checking_used = REALLY_NOT_USED; BEGIN(USED_LIST); } YY_BREAK case 11: # line 114 "scan.l" { #ifdef NOTDEF fprintf( stderr, "old-style lex command at line %d ignored:\n\t%s", linenum, yytext ); #endif ++linenum; } YY_BREAK case 12: # line 123 "scan.l" /* ignore old lex directive */ YY_BREAK case 13: # line 125 "scan.l" { ++linenum; xlation = (int *) malloc( sizeof( int ) * (unsigned) csize ); if ( ! xlation ) flexfatal( "dynamic memory failure building %t table" ); for ( i = 0; i < csize; ++i ) xlation[i] = 0; num_xlations = 0; BEGIN(XLATION); } YY_BREAK case 14: # line 142 "scan.l" synerr( "unrecognized '%' directive" ); YY_BREAK case 15: # line 144 "scan.l" { (void) strcpy( nmstr, (char *) yytext ); didadef = false; BEGIN(PICKUPDEF); } YY_BREAK case 16: # line 150 "scan.l" RETURNNAME; YY_BREAK case 17: # line 151 "scan.l" ++linenum; /* allows blank lines in section 1 */ YY_BREAK case 18: # line 152 "scan.l" ++linenum; return ( '\n' ); YY_BREAK case 19: # line 153 "scan.l" synerr( "illegal character" ); BEGIN(RECOVER); YY_BREAK case 20: # line 156 "scan.l" ECHO; BEGIN(INITIAL); YY_BREAK case 21: # line 157 "scan.l" ++linenum; ECHO; BEGIN(INITIAL); YY_BREAK case 22: # line 158 "scan.l" ECHO; YY_BREAK case 23: # line 159 "scan.l" ECHO; YY_BREAK case 24: # line 160 "scan.l" ++linenum; ECHO; YY_BREAK case 25: # line 163 "scan.l" ++linenum; BEGIN(INITIAL); YY_BREAK case 26: # line 164 "scan.l" ECHO; CHECK_REJECT(yytext); YY_BREAK case 27: # line 165 "scan.l" ECHO; CHECK_YYMORE(yytext); YY_BREAK case 28: # line 166 "scan.l" ECHO; YY_BREAK case 29: # line 167 "scan.l" { ++linenum; ECHO; if ( indented_code ) BEGIN(INITIAL); } YY_BREAK case 30: # line 175 "scan.l" /* separates name and definition */ YY_BREAK case 31: # line 177 "scan.l" { (void) strcpy( (char *) nmdef, (char *) yytext ); for ( i = strlen( (char *) nmdef ) - 1; i >= 0 && (nmdef[i] == ' ' || nmdef[i] == '\t'); --i ) ; nmdef[i + 1] = '\0'; ndinstal( nmstr, nmdef ); didadef = true; } YY_BREAK case 32: # line 192 "scan.l" { if ( ! didadef ) synerr( "incomplete name definition" ); BEGIN(INITIAL); ++linenum; } YY_BREAK case 33: # line 199 "scan.l" ++linenum; BEGIN(INITIAL); RETURNNAME; YY_BREAK case 34: # line 202 "scan.l" ++linenum; BEGIN(INITIAL); YY_BREAK case 35: # line 203 "scan.l" YY_BREAK case 36: # line 204 "scan.l" { if ( all_upper( yytext ) ) reject_really_used = checking_used; else synerr( "unrecognized %used/%unused construct" ); } YY_BREAK case 37: # line 210 "scan.l" { if ( all_lower( yytext ) ) yymore_really_used = checking_used; else synerr( "unrecognized %used/%unused construct" ); } YY_BREAK case 38: # line 216 "scan.l" synerr( "unrecognized %used/%unused construct" ); YY_BREAK case 39: # line 219 "scan.l" ++linenum; BEGIN(INITIAL); YY_BREAK case 40: # line 220 "scan.l" ++num_xlations; new_xlation = true; YY_BREAK case 41: # line 221 "scan.l" synerr( "bad row in translation table" ); YY_BREAK case 42: # line 222 "scan.l" /* ignore whitespace */ YY_BREAK case 43: # line 224 "scan.l" { xlation[myesc( yytext )] = (new_xlation ? num_xlations : -num_xlations); new_xlation = false; } YY_BREAK case 44: # line 229 "scan.l" { xlation[yytext[0]] = (new_xlation ? num_xlations : -num_xlations); new_xlation = false; } YY_BREAK case 45: # line 235 "scan.l" ++linenum; YY_BREAK case 46: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp -= 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ # line 238 "scan.l" { ++linenum; ACTION_ECHO; MARK_END_OF_PROLOG; BEGIN(SECT2); } YY_BREAK case 47: # line 245 "scan.l" ++linenum; ACTION_ECHO; YY_BREAK case YY_STATE_EOF(SECT2PROLOG): # line 247 "scan.l" MARK_END_OF_PROLOG; yyterminate(); YY_BREAK case 49: # line 249 "scan.l" ++linenum; /* allow blank lines in section 2 */ YY_BREAK case 50: # line 251 "scan.l" { indented_code = (yytext[0] != '%'); doing_codeblock = true; bracelevel = 1; if ( indented_code ) ACTION_ECHO; BEGIN(CODEBLOCK_2); } YY_BREAK case 51: # line 262 "scan.l" BEGIN(SC); return ( '<' ); YY_BREAK case 52: # line 263 "scan.l" return ( '^' ); YY_BREAK case 53: # line 264 "scan.l" BEGIN(QUOTE); return ( '"' ); YY_BREAK case 54: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ # line 265 "scan.l" BEGIN(NUM); return ( '{' ); YY_BREAK case 55: # line 266 "scan.l" BEGIN(BRACEERROR); YY_BREAK case 56: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ # line 267 "scan.l" return ( '$' ); YY_BREAK case 57: # line 269 "scan.l" { bracelevel = 1; BEGIN(PERCENT_BRACE_ACTION); return ( '\n' ); } YY_BREAK case 58: # line 274 "scan.l" continued_action = true; ++linenum; return ( '\n' ); YY_BREAK case 59: # line 276 "scan.l" { /* this rule is separate from the one below because * otherwise we get variable trailing context, so * we can't build the scanner using -{f,F} */ bracelevel = 0; continued_action = false; BEGIN(ACTION); return ( '\n' ); } YY_BREAK case 60: # line 287 "scan.l" { bracelevel = 0; continued_action = false; BEGIN(ACTION); return ( '\n' ); } YY_BREAK case 61: # line 294 "scan.l" ++linenum; return ( '\n' ); YY_BREAK case 62: # line 296 "scan.l" return ( EOF_OP ); YY_BREAK case 63: # line 298 "scan.l" { sectnum = 3; BEGIN(SECT3); return ( EOF ); /* to stop the parser */ } YY_BREAK case 64: # line 304 "scan.l" { int cclval; (void) strcpy( nmstr, (char *) yytext ); /* check to see if we've already encountered this ccl */ if ( (cclval = ccllookup( (Char *) nmstr )) ) { yylval = cclval; ++cclreuse; return ( PREVCCL ); } else { /* we fudge a bit. We know that this ccl will * soon be numbered as lastccl + 1 by cclinit */ cclinstal( (Char *) nmstr, lastccl + 1 ); /* push back everything but the leading bracket * so the ccl can be rescanned */ PUT_BACK_STRING((Char *) nmstr, 1); BEGIN(FIRSTCCL); return ( '[' ); } } YY_BREAK case 65: # line 333 "scan.l" { Char *nmdefptr; Char *ndlookup(); (void) strcpy( nmstr, (char *) yytext ); nmstr[yyleng - 1] = '\0'; /* chop trailing brace */ /* lookup from "nmstr + 1" to chop leading brace */ if ( ! (nmdefptr = ndlookup( nmstr + 1 )) ) synerr( "undefined {name}" ); else { /* push back name surrounded by ()'s */ unput(')'); PUT_BACK_STRING(nmdefptr, 0); unput('('); } } YY_BREAK case 66: # line 352 "scan.l" return ( yytext[0] ); YY_BREAK case 67: # line 353 "scan.l" RETURNCHAR; YY_BREAK case 68: # line 354 "scan.l" ++linenum; return ( '\n' ); YY_BREAK case 69: # line 357 "scan.l" return ( ',' ); YY_BREAK case 70: # line 358 "scan.l" BEGIN(SECT2); return ( '>' ); YY_BREAK case 71: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ # line 359 "scan.l" BEGIN(CARETISBOL); return ( '>' ); YY_BREAK case 72: # line 360 "scan.l" RETURNNAME; YY_BREAK case 73: # line 361 "scan.l" synerr( "bad start condition name" ); YY_BREAK case 74: # line 363 "scan.l" BEGIN(SECT2); return ( '^' ); YY_BREAK case 75: # line 366 "scan.l" RETURNCHAR; YY_BREAK case 76: # line 367 "scan.l" BEGIN(SECT2); return ( '"' ); YY_BREAK case 77: # line 369 "scan.l" { synerr( "missing quote" ); BEGIN(SECT2); ++linenum; return ( '"' ); } YY_BREAK case 78: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ # line 377 "scan.l" BEGIN(CCL); return ( '^' ); YY_BREAK case 79: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ # line 378 "scan.l" return ( '^' ); YY_BREAK case 80: # line 379 "scan.l" BEGIN(CCL); yylval = '-'; return ( CHAR ); YY_BREAK case 81: # line 380 "scan.l" BEGIN(CCL); RETURNCHAR; YY_BREAK case 82: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp = yy_bp + 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ # line 382 "scan.l" return ( '-' ); YY_BREAK case 83: # line 383 "scan.l" RETURNCHAR; YY_BREAK case 84: # line 384 "scan.l" BEGIN(SECT2); return ( ']' ); YY_BREAK case 85: # line 387 "scan.l" { yylval = myctoi( yytext ); return ( NUMBER ); } YY_BREAK case 86: # line 392 "scan.l" return ( ',' ); YY_BREAK case 87: # line 393 "scan.l" BEGIN(SECT2); return ( '}' ); YY_BREAK case 88: # line 395 "scan.l" { synerr( "bad character inside {}'s" ); BEGIN(SECT2); return ( '}' ); } YY_BREAK case 89: # line 401 "scan.l" { synerr( "missing }" ); BEGIN(SECT2); ++linenum; return ( '}' ); } YY_BREAK case 90: # line 409 "scan.l" synerr( "bad name in {}'s" ); BEGIN(SECT2); YY_BREAK case 91: # line 410 "scan.l" synerr( "missing }" ); ++linenum; BEGIN(SECT2); YY_BREAK case 92: # line 413 "scan.l" bracelevel = 0; YY_BREAK case 93: # line 414 "scan.l" { ACTION_ECHO; CHECK_REJECT(yytext); } YY_BREAK case 94: # line 418 "scan.l" { ACTION_ECHO; CHECK_YYMORE(yytext); } YY_BREAK case 95: # line 422 "scan.l" ACTION_ECHO; YY_BREAK case 96: # line 423 "scan.l" { ++linenum; ACTION_ECHO; if ( bracelevel == 0 || (doing_codeblock && indented_code) ) { if ( ! doing_codeblock ) fputs( "\tYY_BREAK\n", temp_action_file ); doing_codeblock = false; BEGIN(SECT2); } } YY_BREAK /* Reject and YYmore() are checked for above, in PERCENT_BRACE_ACTION */ case 97: # line 439 "scan.l" ACTION_ECHO; ++bracelevel; YY_BREAK case 98: # line 440 "scan.l" ACTION_ECHO; --bracelevel; YY_BREAK case 99: # line 441 "scan.l" ACTION_ECHO; YY_BREAK case 100: # line 442 "scan.l" ACTION_ECHO; YY_BREAK case 101: # line 443 "scan.l" ACTION_ECHO; BEGIN(ACTION_COMMENT); YY_BREAK case 102: # line 444 "scan.l" ACTION_ECHO; /* character constant */ YY_BREAK case 103: # line 445 "scan.l" ACTION_ECHO; BEGIN(ACTION_STRING); YY_BREAK case 104: # line 446 "scan.l" { ++linenum; ACTION_ECHO; if ( bracelevel == 0 ) { fputs( "\tYY_BREAK\n", temp_action_file ); BEGIN(SECT2); } } YY_BREAK case 105: # line 455 "scan.l" ACTION_ECHO; YY_BREAK case 106: # line 457 "scan.l" ACTION_ECHO; BEGIN(ACTION); YY_BREAK case 107: # line 458 "scan.l" ACTION_ECHO; YY_BREAK case 108: # line 459 "scan.l" ACTION_ECHO; YY_BREAK case 109: # line 460 "scan.l" ++linenum; ACTION_ECHO; YY_BREAK case 110: # line 461 "scan.l" ACTION_ECHO; YY_BREAK case 111: # line 463 "scan.l" ACTION_ECHO; YY_BREAK case 112: # line 464 "scan.l" ACTION_ECHO; YY_BREAK case 113: # line 465 "scan.l" ++linenum; ACTION_ECHO; YY_BREAK case 114: # line 466 "scan.l" ACTION_ECHO; BEGIN(ACTION); YY_BREAK case 115: # line 467 "scan.l" ACTION_ECHO; YY_BREAK case YY_STATE_EOF(ACTION): case YY_STATE_EOF(ACTION_COMMENT): case YY_STATE_EOF(ACTION_STRING): # line 469 "scan.l" { synerr( "EOF encountered inside an action" ); yyterminate(); } YY_BREAK case 117: # line 475 "scan.l" { yylval = myesc( yytext ); return ( CHAR ); } YY_BREAK case 118: # line 480 "scan.l" { yylval = myesc( yytext ); BEGIN(CCL); return ( CHAR ); } YY_BREAK case 119: # line 487 "scan.l" ECHO; YY_BREAK case 120: # line 488 "scan.l" ECHO; YY_BREAK case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(SECT2): case YY_STATE_EOF(SECT3): case YY_STATE_EOF(CODEBLOCK): case YY_STATE_EOF(PICKUPDEF): case YY_STATE_EOF(SC): case YY_STATE_EOF(CARETISBOL): case YY_STATE_EOF(NUM): case YY_STATE_EOF(QUOTE): case YY_STATE_EOF(FIRSTCCL): case YY_STATE_EOF(CCL): case YY_STATE_EOF(RECOVER): case YY_STATE_EOF(BRACEERROR): case YY_STATE_EOF(C_COMMENT): case YY_STATE_EOF(PERCENT_BRACE_ACTION): case YY_STATE_EOF(USED_LIST): case YY_STATE_EOF(CODEBLOCK_2): case YY_STATE_EOF(XLATION): yyterminate(); case YY_END_OF_BUFFER: { /* amount of text matched not including the EOB char */ int yy_amount_of_matched_text = yy_cp - yytext - 1; /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yy_hold_char; /* note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the end- * of-buffer state). Contrast this with the test in yyinput(). */ if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) /* this was really a NUL */ { yy_state_type yy_next_state; yy_c_buf_p = yytext + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state(); /* okay, we're now positioned to make the * NUL transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we * don't want to build jamming into it because * then it will run more slowly) */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = yytext + YY_MORE_ADJ; if ( yy_next_state ) { /* consume the NUL */ yy_cp = ++yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { goto yy_find_action; } } else switch ( yy_get_next_buffer() ) { case EOB_ACT_END_OF_FILE: { yy_did_buffer_switch_on_eof = 0; if ( yywrap() ) { /* note: because we've taken care in * yy_get_next_buffer() to have set up yytext, * we can now set up yy_c_buf_p so that if some * total hoser (like flex itself) wants * to call the scanner after we return the * YY_NULL, it'll still work - another YY_NULL * will get returned. */ yy_c_buf_p = yytext + YY_MORE_ADJ; yy_act = YY_STATE_EOF((yy_start - 1) / 2); goto do_action; } else { if ( ! yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } } break; case EOB_ACT_CONTINUE_SCAN: yy_c_buf_p = yytext + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state(); yy_cp = yy_c_buf_p; yy_bp = yytext + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yy_c_buf_p = &yy_current_buffer->yy_ch_buf[yy_n_chars]; yy_current_state = yy_get_previous_state(); yy_cp = yy_c_buf_p; yy_bp = yytext + YY_MORE_ADJ; goto yy_find_action; } break; } default: #ifdef FLEX_DEBUG printf( "action # %d\n", yy_act ); #endif YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } } } /* yy_get_next_buffer - try to read in a new buffer * * synopsis * int yy_get_next_buffer(); * * returns a code representing an action * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer() { YY_CHAR *dest = yy_current_buffer->yy_ch_buf; YY_CHAR *source = yytext - 1; /* copy prev. char, too */ int number_to_move, i; int ret_val; if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); /* try to read more data */ /* first move last chars to start of buffer */ number_to_move = yy_c_buf_p - yytext; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( yy_current_buffer->yy_eof_status != EOF_NOT_SEEN ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ yy_n_chars = 0; else { int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; else if ( num_to_read <= 0 ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); /* read in more data */ YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), yy_n_chars, num_to_read ); } if ( yy_n_chars == 0 ) { if ( number_to_move == 1 ) { ret_val = EOB_ACT_END_OF_FILE; yy_current_buffer->yy_eof_status = EOF_DONE; } else { ret_val = EOB_ACT_LAST_MATCH; yy_current_buffer->yy_eof_status = EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; yy_n_chars += number_to_move; yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; /* yytext begins at the second character in yy_ch_buf; the first * character is the one which preceded it before reading in the latest * buffer; it needs to be kept around in case it's a newline, so * yy_get_previous_state() will have with '^' rules active */ yytext = &yy_current_buffer->yy_ch_buf[1]; return ( ret_val ); } /* yy_get_previous_state - get the state just before the EOB char was reached * * synopsis * yy_state_type yy_get_previous_state(); */ static yy_state_type yy_get_previous_state() { yy_state_type yy_current_state; YY_CHAR *yy_cp; YY_CHAR *yy_bp = yytext; yy_current_state = yy_start; if ( yy_bp[-1] == '\n' ) ++yy_current_state; yy_state_ptr = yy_state_buf; *yy_state_ptr++ = yy_current_state; for ( yy_cp = yytext + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) { YY_CHAR yy_c = (*yy_cp ? yy_ec[(int)*yy_cp] : 1); while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = yy_def[yy_current_state]; if ( yy_current_state >= 391 ) yy_c = yy_meta[(int)yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; *yy_state_ptr++ = yy_current_state; } return ( yy_current_state ); } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) { int yy_is_jam; YY_CHAR yy_c = 1; while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = yy_def[yy_current_state]; if ( yy_current_state >= 391 ) yy_c = yy_meta[(int)yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; *yy_state_ptr++ = yy_current_state; yy_is_jam = (yy_current_state == 390); return ( yy_is_jam ? 0 : yy_current_state ); } static void yyunput( YY_CHAR c, YY_CHAR *yy_bp ) { YY_CHAR *yy_cp = yy_c_buf_p; /* undo effects of setting up yytext */ *yy_cp = yy_hold_char; if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) { /* need to shift things up to make room */ int number_to_move = yy_n_chars + 2; /* +2 for EOB chars */ YY_CHAR *dest = &yy_current_buffer->yy_ch_buf[yy_current_buffer->yy_buf_size + 2]; YY_CHAR *source = &yy_current_buffer->yy_ch_buf[number_to_move]; while ( source > yy_current_buffer->yy_ch_buf ) *--dest = *--source; yy_cp += dest - source; yy_bp += dest - source; yy_n_chars = yy_current_buffer->yy_buf_size; if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); } if ( yy_cp > yy_bp && yy_cp[-1] == '\n' ) yy_cp[-2] = '\n'; *--yy_cp = c; /* note: the formal parameter *must* be called "yy_bp" for this * macro to now work correctly */ YY_DO_BEFORE_ACTION; /* set up yytext again */ } void yyrestart( FILE *input_file ) { yy_init_buffer( yy_current_buffer, input_file ); yy_load_buffer_state(); } void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) { if ( yy_current_buffer == new_buffer ) return; if ( yy_current_buffer ) { /* flush out information for old buffer */ *yy_c_buf_p = yy_hold_char; yy_current_buffer->yy_buf_pos = yy_c_buf_p; yy_current_buffer->yy_n_chars = yy_n_chars; } yy_current_buffer = new_buffer; yy_load_buffer_state(); /* we don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ yy_did_buffer_switch_on_eof = 1; } void yy_load_buffer_state( void ) { yy_n_chars = yy_current_buffer->yy_n_chars; yytext = yy_c_buf_p = yy_current_buffer->yy_buf_pos; yyin = yy_current_buffer->yy_input_file; yy_hold_char = *yy_c_buf_p; } YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) malloc( sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (YY_CHAR *) malloc( (unsigned) (b->yy_buf_size + 2) ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); yy_init_buffer( b, file ); return ( b ); } void yy_delete_buffer( YY_BUFFER_STATE b ) { if ( b == yy_current_buffer ) yy_current_buffer = (YY_BUFFER_STATE) 0; free( (char *) b->yy_ch_buf ); free( (char *) b ); } void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) { b->yy_input_file = file; /* we put in the '\n' and start reading from [1] so that an * initial match-at-newline will be true. */ b->yy_ch_buf[0] = '\n'; b->yy_n_chars = 1; /* we always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[1]; b->yy_eof_status = EOF_NOT_SEEN; } # line 488 "scan.l" int yywrap() { if ( --num_input_files > 0 ) { set_input_file( *++input_files ); return ( 0 ); } else return ( 1 ); } /* set_input_file - open the given file (if NULL, stdin) for scanning */ void set_input_file( file ) char *file; { if ( file ) { infilename = file; yyin = fopen( infilename, "r" ); if ( yyin == NULL ) lerrsf( "can't open %s", file ); } else { yyin = stdin; infilename = ""; } } base-7.0.3.1/modules/libcom/src/flex/scan.l.DISTRIB0000664000577000060420000003015113557101274020255 0ustar anjaesctl /* scan.l - scanner for flex input */ %{ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #undef yywrap #define ACTION_ECHO fprintf( temp_action_file, "%s", yytext ) #define MARK_END_OF_PROLOG fprintf( temp_action_file, "%%%% end of prolog\n" ); #undef YY_DECL #define YY_DECL \ int flexscan() #define RETURNCHAR \ yylval = yytext[0]; \ return ( CHAR ); #define RETURNNAME \ (void) strcpy( nmstr, (char *) yytext ); \ return ( NAME ); #define PUT_BACK_STRING(str, start) \ for ( i = strlen( (char *) (str) ) - 1; i >= start; --i ) \ unput((str)[i]) #define CHECK_REJECT(str) \ if ( all_upper( str ) ) \ reject = true; #define CHECK_YYMORE(str) \ if ( all_lower( str ) ) \ yymore_used = true; %} %x SECT2 SECT2PROLOG SECT3 CODEBLOCK PICKUPDEF SC CARETISBOL NUM QUOTE %x FIRSTCCL CCL ACTION RECOVER BRACEERROR C_COMMENT ACTION_COMMENT %x ACTION_STRING PERCENT_BRACE_ACTION USED_LIST CODEBLOCK_2 XLATION WS [ \t\f]+ OPTWS [ \t\f]* NOT_WS [^ \t\f\r\n] NAME [a-z_][a-z_0-9-]* NOT_NAME [^a-z_\r\n]+ SCNAME {NAME} ESCSEQ \\([^\r\n]|[0-9]{1,3}|x[0-9a-f]{1,2}) %% static int bracelevel, didadef; int i, indented_code = false, checking_used = false, new_xlation = false; int doing_codeblock = false; Char nmdef[MAXLINE], myesc(); ^{WS} indented_code = true; BEGIN(CODEBLOCK); ^#.*\r?\n ++linenum; /* treat as a comment */ ^"/*" ECHO; BEGIN(C_COMMENT); ^"%s"{NAME}? return ( SCDECL ); ^"%x"{NAME}? return ( XSCDECL ); ^"%{".*\r?\n { ++linenum; line_directive_out( stdout ); indented_code = false; BEGIN(CODEBLOCK); } {WS} return ( WHITESPACE ); ^"%%".* { sectnum = 2; line_directive_out( stdout ); BEGIN(SECT2PROLOG); return ( SECTEND ); } ^"%used" { pinpoint_message( "warning - %%used/%%unused have been deprecated" ); checking_used = REALLY_USED; BEGIN(USED_LIST); } ^"%unused" { checking_used = REALLY_NOT_USED; BEGIN(USED_LIST); pinpoint_message( "warning - %%used/%%unused have been deprecated" ); checking_used = REALLY_NOT_USED; BEGIN(USED_LIST); } ^"%"[aeknopt]" ".*\r?\n { #ifdef NOTDEF fprintf( stderr, "old-style lex command at line %d ignored:\n\t%s", linenum, yytext ); #endif ++linenum; } ^"%"[cr]{OPTWS} /* ignore old lex directive */ %t{OPTWS}\r?\n { ++linenum; xlation = (int *) malloc( sizeof( int ) * (unsigned) csize ); if ( ! xlation ) flexfatal( "dynamic memory failure building %t table" ); for ( i = 0; i < csize; ++i ) xlation[i] = 0; num_xlations = 0; BEGIN(XLATION); } ^"%"[^sxanpekotcru{}]{OPTWS} synerr( "unrecognized '%' directive" ); ^{NAME} { (void) strcpy( nmstr, (char *) yytext ); didadef = false; BEGIN(PICKUPDEF); } {SCNAME} RETURNNAME; ^{OPTWS}\r?\n ++linenum; /* allows blank lines in section 1 */ {OPTWS}\r?\n ++linenum; return ( '\n' ); . synerr( "illegal character" ); BEGIN(RECOVER); "*/" ECHO; BEGIN(INITIAL); "*/".*\r?\n ++linenum; ECHO; BEGIN(INITIAL); [^*\r\n]+ ECHO; "*" ECHO; \r?\n ++linenum; ECHO; ^"%}".*\r?\n ++linenum; BEGIN(INITIAL); "reject" ECHO; CHECK_REJECT(yytext); "yymore" ECHO; CHECK_YYMORE(yytext); {NAME}|{NOT_NAME}|. ECHO; \r?\n { ++linenum; ECHO; if ( indented_code ) BEGIN(INITIAL); } {WS} /* separates name and definition */ {NOT_WS}.* { (void) strcpy( (char *) nmdef, (char *) yytext ); for ( i = strlen( (char *) nmdef ) - 1; i >= 0 && (nmdef[i] == ' ' || nmdef[i] == '\t'); --i ) ; nmdef[i + 1] = '\0'; ndinstal( nmstr, nmdef ); didadef = true; } \r?\n { if ( ! didadef ) synerr( "incomplete name definition" ); BEGIN(INITIAL); ++linenum; } .*\r?\n ++linenum; BEGIN(INITIAL); RETURNNAME; \r?\n ++linenum; BEGIN(INITIAL); {WS} "reject" { if ( all_upper( yytext ) ) reject_really_used = checking_used; else synerr( "unrecognized %used/%unused construct" ); } "yymore" { if ( all_lower( yytext ) ) yymore_really_used = checking_used; else synerr( "unrecognized %used/%unused construct" ); } {NOT_WS}+ synerr( "unrecognized %used/%unused construct" ); "%t"{OPTWS}\r?\n ++linenum; BEGIN(INITIAL); ^{OPTWS}[0-9]+ ++num_xlations; new_xlation = true; ^. synerr( "bad row in translation table" ); {WS} /* ignore whitespace */ {ESCSEQ} { xlation[myesc( yytext )] = (new_xlation ? num_xlations : -num_xlations); new_xlation = false; } . { xlation[yytext[0]] = (new_xlation ? num_xlations : -num_xlations); new_xlation = false; } \r?\n ++linenum; .*\r?\n/{NOT_WS} { ++linenum; ACTION_ECHO; MARK_END_OF_PROLOG; BEGIN(SECT2); } .*\r?\n ++linenum; ACTION_ECHO; <> MARK_END_OF_PROLOG; yyterminate(); ^{OPTWS}\r?\n ++linenum; /* allow blank lines in section 2 */ ^({WS}|"%{") { indented_code = (yytext[0] != '%'); doing_codeblock = true; bracelevel = 1; if ( indented_code ) ACTION_ECHO; BEGIN(CODEBLOCK_2); } "<" BEGIN(SC); return ( '<' ); ^"^" return ( '^' ); \" BEGIN(QUOTE); return ( '"' ); "{"/[0-9] BEGIN(NUM); return ( '{' ); "{"[^0-9\r\n][^}\r\n]* BEGIN(BRACEERROR); "$"/[ \t\r\n] return ( '$' ); {WS}"%{" { bracelevel = 1; BEGIN(PERCENT_BRACE_ACTION); return ( '\n' ); } {WS}"|".*\r?\n continued_action = true; ++linenum; return ( '\n' ); {WS} { /* this rule is separate from the one below because * otherwise we get variable trailing context, so * we can't build the scanner using -{f,F} */ bracelevel = 0; continued_action = false; BEGIN(ACTION); return ( '\n' ); } {OPTWS}/\r?\n { bracelevel = 0; continued_action = false; BEGIN(ACTION); return ( '\n' ); } ^{OPTWS}\r?\n ++linenum; return ( '\n' ); "<>" return ( EOF_OP ); ^"%%".* { sectnum = 3; BEGIN(SECT3); return ( EOF ); /* to stop the parser */ } "["([^\\\]\r\n]|{ESCSEQ})+"]" { int cclval; (void) strcpy( nmstr, (char *) yytext ); /* check to see if we've already encountered this ccl */ if ( (cclval = ccllookup( (Char *) nmstr )) ) { yylval = cclval; ++cclreuse; return ( PREVCCL ); } else { /* we fudge a bit. We know that this ccl will * soon be numbered as lastccl + 1 by cclinit */ cclinstal( (Char *) nmstr, lastccl + 1 ); /* push back everything but the leading bracket * so the ccl can be rescanned */ PUT_BACK_STRING((Char *) nmstr, 1); BEGIN(FIRSTCCL); return ( '[' ); } } "{"{NAME}"}" { register Char *nmdefptr; Char *ndlookup(); (void) strcpy( nmstr, (char *) yytext ); nmstr[yyleng - 1] = '\0'; /* chop trailing brace */ /* lookup from "nmstr + 1" to chop leading brace */ if ( ! (nmdefptr = ndlookup( nmstr + 1 )) ) synerr( "undefined {name}" ); else { /* push back name surrounded by ()'s */ unput(')'); PUT_BACK_STRING(nmdefptr, 0); unput('('); } } [/|*+?.()] return ( yytext[0] ); . RETURNCHAR; \r?\n ++linenum; return ( '\n' ); "," return ( ',' ); ">" BEGIN(SECT2); return ( '>' ); ">"/"^" BEGIN(CARETISBOL); return ( '>' ); {SCNAME} RETURNNAME; . synerr( "bad start condition name" ); "^" BEGIN(SECT2); return ( '^' ); [^"\r\n] RETURNCHAR; \" BEGIN(SECT2); return ( '"' ); \r?\n { synerr( "missing quote" ); BEGIN(SECT2); ++linenum; return ( '"' ); } "^"/[^-\r\n] BEGIN(CCL); return ( '^' ); "^"/- return ( '^' ); - BEGIN(CCL); yylval = '-'; return ( CHAR ); . BEGIN(CCL); RETURNCHAR; -/[^\]\r\n] return ( '-' ); [^\]\r\n] RETURNCHAR; "]" BEGIN(SECT2); return ( ']' ); [0-9]+ { yylval = myctoi( yytext ); return ( NUMBER ); } "," return ( ',' ); "}" BEGIN(SECT2); return ( '}' ); . { synerr( "bad character inside {}'s" ); BEGIN(SECT2); return ( '}' ); } \r?\n { synerr( "missing }" ); BEGIN(SECT2); ++linenum; return ( '}' ); } "}" synerr( "bad name in {}'s" ); BEGIN(SECT2); \r?\n synerr( "missing }" ); ++linenum; BEGIN(SECT2); {OPTWS}"%}".* bracelevel = 0; "reject" { ACTION_ECHO; CHECK_REJECT(yytext); } "yymore" { ACTION_ECHO; CHECK_YYMORE(yytext); } {NAME}|{NOT_NAME}|. ACTION_ECHO; \r?\n { ++linenum; ACTION_ECHO; if ( bracelevel == 0 || (doing_codeblock && indented_code) ) { if ( ! doing_codeblock ) fputs( "\tYY_BREAK\n", temp_action_file ); doing_codeblock = false; BEGIN(SECT2); } } /* Reject and YYmore() are checked for above, in PERCENT_BRACE_ACTION */ "{" ACTION_ECHO; ++bracelevel; "}" ACTION_ECHO; --bracelevel; [^a-z_{}"'/\r\n]+ ACTION_ECHO; {NAME} ACTION_ECHO; "/*" ACTION_ECHO; BEGIN(ACTION_COMMENT); "'"([^'\\\r\n]|\\.)*"'" ACTION_ECHO; /* character constant */ \" ACTION_ECHO; BEGIN(ACTION_STRING); \r?\n { ++linenum; ACTION_ECHO; if ( bracelevel == 0 ) { fputs( "\tYY_BREAK\n", temp_action_file ); BEGIN(SECT2); } } . ACTION_ECHO; "*/" ACTION_ECHO; BEGIN(ACTION); [^*\r\n]+ ACTION_ECHO; "*" ACTION_ECHO; \r?\n ++linenum; ACTION_ECHO; . ACTION_ECHO; [^"\\\r\n]+ ACTION_ECHO; \\. ACTION_ECHO; \r?\n ++linenum; ACTION_ECHO; \" ACTION_ECHO; BEGIN(ACTION); . ACTION_ECHO; <> { synerr( "EOF encountered inside an action" ); yyterminate(); } {ESCSEQ} { yylval = myesc( yytext ); return ( CHAR ); } {ESCSEQ} { yylval = myesc( yytext ); BEGIN(CCL); return ( CHAR ); } .*(\r?\n?) ECHO; %% int yywrap() { if ( --num_input_files > 0 ) { set_input_file( *++input_files ); return ( 0 ); } else return ( 1 ); } /* set_input_file - open the given file (if NULL, stdin) for scanning */ void set_input_file( file ) char *file; { if ( file ) { infilename = file; yyin = fopen( infilename, "r" ); if ( yyin == NULL ) lerrsf( "can't open %s", file ); } else { yyin = stdin; infilename = ""; } } base-7.0.3.1/modules/libcom/src/flex/sym.c0000664000577000060420000001674213557101274017043 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* sym - symbol table routines */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "flexdef.h" /* declare functions that have forward references */ int hashfunct (char[], int); struct hash_entry *ndtbl[NAME_TABLE_HASH_SIZE]; struct hash_entry *sctbl[START_COND_HASH_SIZE]; struct hash_entry *ccltab[CCL_HASH_SIZE]; struct hash_entry *findsym(char *sym, struct hash_entry **table, int table_size); /* addsym - add symbol and definitions to symbol table * * synopsis * char sym[], *str_def; * int int_def; * hash_table table; * int table_size; * 0 / -1 = addsym( sym, def, int_def, table, table_size ); * * -1 is returned if the symbol already exists, and the change not made. */ int addsym(char *sym, char *str_def, int int_def, struct hash_entry **table, int table_size) { int hash_val = hashfunct( sym, table_size ); struct hash_entry *sym_entry = table[hash_val]; struct hash_entry *new_entry; struct hash_entry *successor; while ( sym_entry ) { if ( ! strcmp( sym, sym_entry->name ) ) { /* entry already exists */ return ( -1 ); } sym_entry = sym_entry->next; } /* create new entry */ new_entry = (struct hash_entry *) malloc( sizeof( struct hash_entry ) ); if ( new_entry == NULL ) flexfatal( "symbol table memory allocation failed" ); if ( (successor = table[hash_val]) ) { new_entry->next = successor; successor->prev = new_entry; } else new_entry->next = NULL; new_entry->prev = NULL; new_entry->name = sym; new_entry->str_val = str_def; new_entry->int_val = int_def; table[hash_val] = new_entry; return ( 0 ); } /* cclinstal - save the text of a character class * * synopsis * Char ccltxt[]; * int cclnum; * cclinstal( ccltxt, cclnum ); */ void cclinstal(Char *ccltxt, int cclnum) { /* we don't bother checking the return status because we are not called * unless the symbol is new */ Char *copy_unsigned_string(); (void) addsym( (char *) copy_unsigned_string( ccltxt ), (char *) 0, cclnum, ccltab, CCL_HASH_SIZE ); } /* ccllookup - lookup the number associated with character class text * * synopsis * Char ccltxt[]; * int ccllookup, cclval; * cclval/0 = ccllookup( ccltxt ); */ int ccllookup(Char *ccltxt) { return ( findsym( (char *) ccltxt, ccltab, CCL_HASH_SIZE )->int_val ); } /* findsym - find symbol in symbol table * * synopsis * char sym[]; * hash_table table; * int table_size; * struct hash_entry *sym_entry, *findsym(); * sym_entry = findsym( sym, table, table_size ); */ struct hash_entry *findsym(char *sym, struct hash_entry **table, int table_size) { struct hash_entry *sym_entry = table[hashfunct( sym, table_size )]; static struct hash_entry empty_entry = { (struct hash_entry *) 0, (struct hash_entry *) 0, NULL, NULL, 0, } ; while ( sym_entry ) { if ( ! strcmp( sym, sym_entry->name ) ) return ( sym_entry ); sym_entry = sym_entry->next; } return ( &empty_entry ); } /* hashfunct - compute the hash value for "str" and hash size "hash_size" * * synopsis * char str[]; * int hash_size, hash_val; * hash_val = hashfunct( str, hash_size ); */ int hashfunct(char *str, int hash_size) { int hashval; int locstr; hashval = 0; locstr = 0; while ( str[locstr] ) hashval = ((hashval << 1) + str[locstr++]) % hash_size; return ( hashval ); } /* ndinstal - install a name definition * * synopsis * char nd[]; * Char def[]; * ndinstal( nd, def ); */ void ndinstal(char *nd, Char *def) { char *copy_string(); Char *copy_unsigned_string(); if ( addsym( copy_string( nd ), (char *) copy_unsigned_string( def ), 0, ndtbl, NAME_TABLE_HASH_SIZE ) ) synerr( "name defined twice" ); } /* ndlookup - lookup a name definition * * synopsis * char nd[], *def; * char *ndlookup(); * def/NULL = ndlookup( nd ); */ Char *ndlookup(char *nd) { return ( (Char *) findsym( nd, ndtbl, NAME_TABLE_HASH_SIZE )->str_val ); } /* scinstal - make a start condition * * synopsis * char str[]; * int xcluflg; * scinstal( str, xcluflg ); * * NOTE * the start condition is Exclusive if xcluflg is true */ void scinstal(char *str, int xcluflg) { char *copy_string(); /* bit of a hack. We know how the default start-condition is * declared, and don't put out a define for it, because it * would come out as "#define 0 1" */ /* actually, this is no longer the case. The default start-condition * is now called "INITIAL". But we keep the following for the sake * of future robustness. */ if ( strcmp( str, "0" ) ) printf( "#define %s %d\n", str, lastsc ); if ( ++lastsc >= current_max_scs ) { current_max_scs += MAX_SCS_INCREMENT; ++num_reallocs; scset = reallocate_integer_array( scset, current_max_scs ); scbol = reallocate_integer_array( scbol, current_max_scs ); scxclu = reallocate_integer_array( scxclu, current_max_scs ); sceof = reallocate_integer_array( sceof, current_max_scs ); scname = reallocate_char_ptr_array( scname, current_max_scs ); actvsc = reallocate_integer_array( actvsc, current_max_scs ); } scname[lastsc] = copy_string( str ); if ( addsym( scname[lastsc], (char *) 0, lastsc, sctbl, START_COND_HASH_SIZE ) ) format_pinpoint_message( "start condition %s declared twice", str ); scset[lastsc] = mkstate( SYM_EPSILON ); scbol[lastsc] = mkstate( SYM_EPSILON ); scxclu[lastsc] = xcluflg; sceof[lastsc] = false; } /* sclookup - lookup the number associated with a start condition * * synopsis * char str[], scnum; * int sclookup; * scnum/0 = sclookup( str ); */ int sclookup(char *str) { return ( findsym( str, sctbl, START_COND_HASH_SIZE )->int_val ); } base-7.0.3.1/modules/libcom/src/flex/tblcmp.c0000664000577000060420000006104013557101274017503 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* tblcmp - table compression routines */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "flexdef.h" /* declarations for functions that have forward references */ void mkentry (int*, int, int, int, int); void mkprot (int[], int, int); void mktemplate (int[], int, int); void mv2front (int); int tbldiff (int[], int, int[]); /* bldtbl - build table entries for dfa state * * synopsis * int state[numecs], statenum, totaltrans, comstate, comfreq; * bldtbl( state, statenum, totaltrans, comstate, comfreq ); * * State is the statenum'th dfa state. It is indexed by equivalence class and * gives the number of the state to enter for a given equivalence class. * totaltrans is the total number of transitions out of the state. Comstate * is that state which is the destination of the most transitions out of State. * Comfreq is how many transitions there are out of State to Comstate. * * A note on terminology: * "protos" are transition tables which have a high probability of * either being redundant (a state processed later will have an identical * transition table) or nearly redundant (a state processed later will have * many of the same out-transitions). A "most recently used" queue of * protos is kept around with the hope that most states will find a proto * which is similar enough to be usable, and therefore compacting the * output tables. * "templates" are a special type of proto. If a transition table is * homogeneous or nearly homogeneous (all transitions go to the same * destination) then the odds are good that future states will also go * to the same destination state on basically the same character set. * These homogeneous states are so common when dealing with large rule * sets that they merit special attention. If the transition table were * simply made into a proto, then (typically) each subsequent, similar * state will differ from the proto for two out-transitions. One of these * out-transitions will be that character on which the proto does not go * to the common destination, and one will be that character on which the * state does not go to the common destination. Templates, on the other * hand, go to the common state on EVERY transition character, and therefore * cost only one difference. */ void bldtbl(int *state, int statenum, int totaltrans, int comstate, int comfreq) { int extptr, extrct[2][CSIZE + 1]; int mindiff, minprot, i, d; int checkcom; /* If extptr is 0 then the first array of extrct holds the result of the * "best difference" to date, which is those transitions which occur in * "state" but not in the proto which, to date, has the fewest differences * between itself and "state". If extptr is 1 then the second array of * extrct hold the best difference. The two arrays are toggled * between so that the best difference to date can be kept around and * also a difference just created by checking against a candidate "best" * proto. */ extptr = 0; /* if the state has too few out-transitions, don't bother trying to * compact its tables */ if ( (totaltrans * 100) < (numecs * PROTO_SIZE_PERCENTAGE) ) mkentry( state, numecs, statenum, JAMSTATE, totaltrans ); else { /* checkcom is true if we should only check "state" against * protos which have the same "comstate" value */ checkcom = comfreq * 100 > totaltrans * CHECK_COM_PERCENTAGE; minprot = firstprot; mindiff = totaltrans; if ( checkcom ) { /* find first proto which has the same "comstate" */ for ( i = firstprot; i != NIL; i = protnext[i] ) if ( protcomst[i] == comstate ) { minprot = i; mindiff = tbldiff( state, minprot, extrct[extptr] ); break; } } else { /* since we've decided that the most common destination out * of "state" does not occur with a high enough frequency, * we set the "comstate" to zero, assuring that if this state * is entered into the proto list, it will not be considered * a template. */ comstate = 0; if ( firstprot != NIL ) { minprot = firstprot; mindiff = tbldiff( state, minprot, extrct[extptr] ); } } /* we now have the first interesting proto in "minprot". If * it matches within the tolerances set for the first proto, * we don't want to bother scanning the rest of the proto list * to see if we have any other reasonable matches. */ if ( mindiff * 100 > totaltrans * FIRST_MATCH_DIFF_PERCENTAGE ) { /* not a good enough match. Scan the rest of the protos */ for ( i = minprot; i != NIL; i = protnext[i] ) { d = tbldiff( state, i, extrct[1 - extptr] ); if ( d < mindiff ) { extptr = 1 - extptr; mindiff = d; minprot = i; } } } /* check if the proto we've decided on as our best bet is close * enough to the state we want to match to be usable */ if ( mindiff * 100 > totaltrans * ACCEPTABLE_DIFF_PERCENTAGE ) { /* no good. If the state is homogeneous enough, we make a * template out of it. Otherwise, we make a proto. */ if ( comfreq * 100 >= totaltrans * TEMPLATE_SAME_PERCENTAGE ) mktemplate( state, statenum, comstate ); else { mkprot( state, statenum, comstate ); mkentry( state, numecs, statenum, JAMSTATE, totaltrans ); } } else { /* use the proto */ mkentry( extrct[extptr], numecs, statenum, prottbl[minprot], mindiff ); /* if this state was sufficiently different from the proto * we built it from, make it, too, a proto */ if ( mindiff * 100 >= totaltrans * NEW_PROTO_DIFF_PERCENTAGE ) mkprot( state, statenum, comstate ); /* since mkprot added a new proto to the proto queue, it's possible * that "minprot" is no longer on the proto queue (if it happened * to have been the last entry, it would have been bumped off). * If it's not there, then the new proto took its physical place * (though logically the new proto is at the beginning of the * queue), so in that case the following call will do nothing. */ mv2front( minprot ); } } } /* cmptmps - compress template table entries * * synopsis * cmptmps(); * * template tables are compressed by using the 'template equivalence * classes', which are collections of transition character equivalence * classes which always appear together in templates - really meta-equivalence * classes. until this point, the tables for templates have been stored * up at the top end of the nxt array; they will now be compressed and have * table entries made for them. */ void cmptmps(void) { int tmpstorage[CSIZE + 1]; int *tmp = tmpstorage, i, j; int totaltrans, trans; peakpairs = numtemps * numecs + tblend; if ( usemecs ) { /* create equivalence classes base on data gathered on template * transitions */ nummecs = cre8ecs( tecfwd, tecbck, numecs ); } else nummecs = numecs; if ( lastdfa + numtemps + 1 >= current_max_dfas ) increase_max_dfas(); /* loop through each template */ for ( i = 1; i <= numtemps; ++i ) { totaltrans = 0; /* number of non-jam transitions out of this template */ for ( j = 1; j <= numecs; ++j ) { trans = tnxt[numecs * i + j]; if ( usemecs ) { /* the absolute value of tecbck is the meta-equivalence class * of a given equivalence class, as set up by cre8ecs */ if ( tecbck[j] > 0 ) { tmp[tecbck[j]] = trans; if ( trans > 0 ) ++totaltrans; } } else { tmp[j] = trans; if ( trans > 0 ) ++totaltrans; } } /* it is assumed (in a rather subtle way) in the skeleton that * if we're using meta-equivalence classes, the def[] entry for * all templates is the jam template, i.e., templates never default * to other non-jam table entries (e.g., another template) */ /* leave room for the jam-state after the last real state */ mkentry( tmp, nummecs, lastdfa + i + 1, JAMSTATE, totaltrans ); } } /* expand_nxt_chk - expand the next check arrays */ void expand_nxt_chk(void) { int old_max = current_max_xpairs; current_max_xpairs += MAX_XPAIRS_INCREMENT; ++num_reallocs; nxt = reallocate_integer_array( nxt, current_max_xpairs ); chk = reallocate_integer_array( chk, current_max_xpairs ); memset( (char *) (chk + old_max), 0, MAX_XPAIRS_INCREMENT * sizeof( int ) / sizeof( char ) ); } /* find_table_space - finds a space in the table for a state to be placed * * synopsis * int *state, numtrans, block_start; * int find_table_space(); * * block_start = find_table_space( state, numtrans ); * * State is the state to be added to the full speed transition table. * Numtrans is the number of out-transitions for the state. * * find_table_space() returns the position of the start of the first block (in * chk) able to accommodate the state * * In determining if a state will or will not fit, find_table_space() must take * into account the fact that an end-of-buffer state will be added at [0], * and an action number will be added in [-1]. */ int find_table_space(int *state, int numtrans) { /* firstfree is the position of the first possible occurrence of two * consecutive unused records in the chk and nxt arrays */ int i; int *state_ptr, *chk_ptr; int *ptr_to_last_entry_in_state; /* if there are too many out-transitions, put the state at the end of * nxt and chk */ if ( numtrans > MAX_XTIONS_FULL_INTERIOR_FIT ) { /* if table is empty, return the first available spot in chk/nxt, * which should be 1 */ if ( tblend < 2 ) return ( 1 ); i = tblend - numecs; /* start searching for table space near the * end of chk/nxt arrays */ } else i = firstfree; /* start searching for table space from the * beginning (skipping only the elements * which will definitely not hold the new * state) */ while ( 1 ) /* loops until a space is found */ { if ( i + numecs > current_max_xpairs ) expand_nxt_chk(); /* loops until space for end-of-buffer and action number are found */ while ( 1 ) { if ( chk[i - 1] == 0 ) /* check for action number space */ { if ( chk[i] == 0 ) /* check for end-of-buffer space */ break; else i += 2; /* since i != 0, there is no use checking to * see if (++i) - 1 == 0, because that's the * same as i == 0, so we skip a space */ } else ++i; if ( i + numecs > current_max_xpairs ) expand_nxt_chk(); } /* if we started search from the beginning, store the new firstfree for * the next call of find_table_space() */ if ( numtrans <= MAX_XTIONS_FULL_INTERIOR_FIT ) firstfree = i + 1; /* check to see if all elements in chk (and therefore nxt) that are * needed for the new state have not yet been taken */ state_ptr = &state[1]; ptr_to_last_entry_in_state = &chk[i + numecs + 1]; for ( chk_ptr = &chk[i + 1]; chk_ptr != ptr_to_last_entry_in_state; ++chk_ptr ) if ( *(state_ptr++) != 0 && *chk_ptr != 0 ) break; if ( chk_ptr == ptr_to_last_entry_in_state ) return ( i ); else ++i; } } /* inittbl - initialize transition tables * * synopsis * inittbl(); * * Initializes "firstfree" to be one beyond the end of the table. Initializes * all "chk" entries to be zero. Note that templates are built in their * own tbase/tdef tables. They are shifted down to be contiguous * with the non-template entries during table generation. */ void inittbl(void) { int i; memset( (char *) chk, 0, current_max_xpairs * sizeof( int ) / sizeof( char ) ); tblend = 0; firstfree = tblend + 1; numtemps = 0; if ( usemecs ) { /* set up doubly-linked meta-equivalence classes * these are sets of equivalence classes which all have identical * transitions out of TEMPLATES */ tecbck[1] = NIL; for ( i = 2; i <= numecs; ++i ) { tecbck[i] = i - 1; tecfwd[i - 1] = i; } tecfwd[numecs] = NIL; } } /* mkdeftbl - make the default, "jam" table entries * * synopsis * mkdeftbl(); */ void mkdeftbl(void) { int i; jamstate = lastdfa + 1; ++tblend; /* room for transition on end-of-buffer character */ if ( tblend + numecs > current_max_xpairs ) expand_nxt_chk(); /* add in default end-of-buffer transition */ nxt[tblend] = end_of_buffer_state; chk[tblend] = jamstate; for ( i = 1; i <= numecs; ++i ) { nxt[tblend + i] = 0; chk[tblend + i] = jamstate; } jambase = tblend; base[jamstate] = jambase; def[jamstate] = 0; tblend += numecs; ++numtemps; } /* mkentry - create base/def and nxt/chk entries for transition array * * synopsis * int state[numchars + 1], numchars, statenum, deflink, totaltrans; * mkentry( state, numchars, statenum, deflink, totaltrans ); * * "state" is a transition array "numchars" characters in size, "statenum" * is the offset to be used into the base/def tables, and "deflink" is the * entry to put in the "def" table entry. If "deflink" is equal to * "JAMSTATE", then no attempt will be made to fit zero entries of "state" * (i.e., jam entries) into the table. It is assumed that by linking to * "JAMSTATE" they will be taken care of. In any case, entries in "state" * marking transitions to "SAME_TRANS" are treated as though they will be * taken care of by whereever "deflink" points. "totaltrans" is the total * number of transitions out of the state. If it is below a certain threshold, * the tables are searched for an interior spot that will accommodate the * state array. */ void mkentry(int *state, int numchars, int statenum, int deflink, int totaltrans) { int minec, maxec, i, baseaddr; int tblbase, tbllast; if ( totaltrans == 0 ) { /* there are no out-transitions */ if ( deflink == JAMSTATE ) base[statenum] = JAMSTATE; else base[statenum] = 0; def[statenum] = deflink; return; } for ( minec = 1; minec <= numchars; ++minec ) { if ( state[minec] != SAME_TRANS ) if ( state[minec] != 0 || deflink != JAMSTATE ) break; } if ( totaltrans == 1 ) { /* there's only one out-transition. Save it for later to fill * in holes in the tables. */ stack1( statenum, minec, state[minec], deflink ); return; } for ( maxec = numchars; maxec > 0; --maxec ) { if ( state[maxec] != SAME_TRANS ) if ( state[maxec] != 0 || deflink != JAMSTATE ) break; } /* Whether we try to fit the state table in the middle of the table * entries we have already generated, or if we just take the state * table at the end of the nxt/chk tables, we must make sure that we * have a valid base address (i.e., non-negative). Note that not only are * negative base addresses dangerous at run-time (because indexing the * next array with one and a low-valued character might generate an * array-out-of-bounds error message), but at compile-time negative * base addresses denote TEMPLATES. */ /* find the first transition of state that we need to worry about. */ if ( totaltrans * 100 <= numchars * INTERIOR_FIT_PERCENTAGE ) { /* attempt to squeeze it into the middle of the tabls */ baseaddr = firstfree; while ( baseaddr < minec ) { /* using baseaddr would result in a negative base address below * find the next free slot */ for ( ++baseaddr; chk[baseaddr] != 0; ++baseaddr ) ; } if ( baseaddr + maxec - minec >= current_max_xpairs ) expand_nxt_chk(); for ( i = minec; i <= maxec; ++i ) if ( state[i] != SAME_TRANS ) if ( state[i] != 0 || deflink != JAMSTATE ) if ( chk[baseaddr + i - minec] != 0 ) { /* baseaddr unsuitable - find another */ for ( ++baseaddr; baseaddr < current_max_xpairs && chk[baseaddr] != 0; ++baseaddr ) ; if ( baseaddr + maxec - minec >= current_max_xpairs ) expand_nxt_chk(); /* reset the loop counter so we'll start all * over again next time it's incremented */ i = minec - 1; } } else { /* ensure that the base address we eventually generate is * non-negative */ baseaddr = max( tblend + 1, minec ); } tblbase = baseaddr - minec; tbllast = tblbase + maxec; if ( tbllast >= current_max_xpairs ) expand_nxt_chk(); base[statenum] = tblbase; def[statenum] = deflink; for ( i = minec; i <= maxec; ++i ) if ( state[i] != SAME_TRANS ) if ( state[i] != 0 || deflink != JAMSTATE ) { nxt[tblbase + i] = state[i]; chk[tblbase + i] = statenum; } if ( baseaddr == firstfree ) /* find next free slot in tables */ for ( ++firstfree; chk[firstfree] != 0; ++firstfree ) ; tblend = max( tblend, tbllast ); } /* mk1tbl - create table entries for a state (or state fragment) which * has only one out-transition * * synopsis * int state, sym, onenxt, onedef; * mk1tbl( state, sym, onenxt, onedef ); */ void mk1tbl(int state, int sym, int onenxt, int onedef) { if ( firstfree < sym ) firstfree = sym; while ( chk[firstfree] != 0 ) if ( ++firstfree >= current_max_xpairs ) expand_nxt_chk(); base[state] = firstfree - sym; def[state] = onedef; chk[firstfree] = state; nxt[firstfree] = onenxt; if ( firstfree > tblend ) { tblend = firstfree++; if ( firstfree >= current_max_xpairs ) expand_nxt_chk(); } } /* mkprot - create new proto entry * * synopsis * int state[], statenum, comstate; * mkprot( state, statenum, comstate ); */ void mkprot(int *state, int statenum, int comstate) { int i, slot, tblbase; if ( ++numprots >= MSP || numecs * numprots >= PROT_SAVE_SIZE ) { /* gotta make room for the new proto by dropping last entry in * the queue */ slot = lastprot; lastprot = protprev[lastprot]; protnext[lastprot] = NIL; } else slot = numprots; protnext[slot] = firstprot; if ( firstprot != NIL ) protprev[firstprot] = slot; firstprot = slot; prottbl[slot] = statenum; protcomst[slot] = comstate; /* copy state into save area so it can be compared with rapidly */ tblbase = numecs * (slot - 1); for ( i = 1; i <= numecs; ++i ) protsave[tblbase + i] = state[i]; } /* mktemplate - create a template entry based on a state, and connect the state * to it * * synopsis * int state[], statenum, comstate, totaltrans; * mktemplate( state, statenum, comstate, totaltrans ); */ void mktemplate(int *state, int statenum, int comstate) { int i, numdiff, tmpbase, tmp[CSIZE + 1]; Char transset[CSIZE + 1]; int tsptr; ++numtemps; tsptr = 0; /* calculate where we will temporarily store the transition table * of the template in the tnxt[] array. The final transition table * gets created by cmptmps() */ tmpbase = numtemps * numecs; if ( tmpbase + numecs >= current_max_template_xpairs ) { current_max_template_xpairs += MAX_TEMPLATE_XPAIRS_INCREMENT; ++num_reallocs; tnxt = reallocate_integer_array( tnxt, current_max_template_xpairs ); } for ( i = 1; i <= numecs; ++i ) if ( state[i] == 0 ) tnxt[tmpbase + i] = 0; else { transset[tsptr++] = i; tnxt[tmpbase + i] = comstate; } if ( usemecs ) mkeccl( transset, tsptr, tecfwd, tecbck, numecs, 0 ); mkprot( tnxt + tmpbase, -numtemps, comstate ); /* we rely on the fact that mkprot adds things to the beginning * of the proto queue */ numdiff = tbldiff( state, firstprot, tmp ); mkentry( tmp, numecs, statenum, -numtemps, numdiff ); } /* mv2front - move proto queue element to front of queue * * synopsis * int qelm; * mv2front( qelm ); */ void mv2front(int qelm) { if ( firstprot != qelm ) { if ( qelm == lastprot ) lastprot = protprev[lastprot]; protnext[protprev[qelm]] = protnext[qelm]; if ( protnext[qelm] != NIL ) protprev[protnext[qelm]] = protprev[qelm]; protprev[qelm] = NIL; protnext[qelm] = firstprot; protprev[firstprot] = qelm; firstprot = qelm; } } /* place_state - place a state into full speed transition table * * synopsis * int *state, statenum, transnum; * place_state( state, statenum, transnum ); * * State is the statenum'th state. It is indexed by equivalence class and * gives the number of the state to enter for a given equivalence class. * Transnum is the number of out-transitions for the state. */ void place_state(int *state, int statenum, int transnum) { int i; int *state_ptr; int position = find_table_space( state, transnum ); /* base is the table of start positions */ base[statenum] = position; /* put in action number marker; this non-zero number makes sure that * find_table_space() knows that this position in chk/nxt is taken * and should not be used for another accepting number in another state */ chk[position - 1] = 1; /* put in end-of-buffer marker; this is for the same purposes as above */ chk[position] = 1; /* place the state into chk and nxt */ state_ptr = &state[1]; for ( i = 1; i <= numecs; ++i, ++state_ptr ) if ( *state_ptr != 0 ) { chk[position + i] = i; nxt[position + i] = *state_ptr; } if ( position + numecs > tblend ) tblend = position + numecs; } /* stack1 - save states with only one out-transition to be processed later * * synopsis * int statenum, sym, nextstate, deflink; * stack1( statenum, sym, nextstate, deflink ); * * if there's room for another state one the "one-transition" stack, the * state is pushed onto it, to be processed later by mk1tbl. If there's * no room, we process the sucker right now. */ void stack1(int statenum, int sym, int nextstate, int deflink) { if ( onesp >= ONE_STACK_SIZE - 1 ) mk1tbl( statenum, sym, nextstate, deflink ); else { ++onesp; onestate[onesp] = statenum; onesym[onesp] = sym; onenext[onesp] = nextstate; onedef[onesp] = deflink; } } /* tbldiff - compute differences between two state tables * * synopsis * int state[], pr, ext[]; * int tbldiff, numdifferences; * numdifferences = tbldiff( state, pr, ext ) * * "state" is the state array which is to be extracted from the pr'th * proto. "pr" is both the number of the proto we are extracting from * and an index into the save area where we can find the proto's complete * state table. Each entry in "state" which differs from the corresponding * entry of "pr" will appear in "ext". * Entries which are the same in both "state" and "pr" will be marked * as transitions to "SAME_TRANS" in "ext". The total number of differences * between "state" and "pr" is returned as function value. Note that this * number is "numecs" minus the number of "SAME_TRANS" entries in "ext". */ int tbldiff(int *state, int pr, int *ext) { int i, *sp = state, *ep = ext, *protp; int numdiff = 0; protp = &protsave[numecs * (pr - 1)]; for ( i = numecs; i > 0; --i ) { if ( *++protp == *++sp ) *++ep = SAME_TRANS; else { *++ep = *sp; ++numdiff; } } return ( numdiff ); } base-7.0.3.1/modules/libcom/src/flex/yylex.c0000664000577000060420000001076113557101274017400 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* yylex - scanner front-end for flex */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Vern Paxson. * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include #include "flexdef.h" #include "parse.h" /* ANSI C does not guarantee that isascii() is defined */ #ifndef isascii #define isascii(c) ((c) <= 0177) #endif /* yylex - scan for a regular expression token * * synopsis * * token = yylex(); * * token - return token found */ int yylex(void) { int toktype; static int beglin = false; if ( eofseen ) toktype = EOF; else toktype = flexscan(); if ( toktype == EOF || toktype == 0 ) { eofseen = 1; if ( sectnum == 1 ) { synerr( "premature EOF" ); sectnum = 2; toktype = SECTEND; } else if ( sectnum == 2 ) { sectnum = 3; toktype = 0; } else toktype = 0; } if ( trace ) { if ( beglin ) { fprintf( stderr, "%d\t", num_rules + 1 ); beglin = 0; } switch ( toktype ) { case '<': case '>': case '^': case '$': case '"': case '[': case ']': case '{': case '}': case '|': case '(': case ')': case '-': case '/': case '\\': case '?': case '.': case '*': case '+': case ',': (void) putc( toktype, stderr ); break; case '\n': (void) putc( '\n', stderr ); if ( sectnum == 2 ) beglin = 1; break; case SCDECL: fputs( "%s", stderr ); break; case XSCDECL: fputs( "%x", stderr ); break; case WHITESPACE: (void) putc( ' ', stderr ); break; case SECTEND: fputs( "%%\n", stderr ); /* we set beglin to be true so we'll start * writing out numbers as we echo rules. flexscan() has * already assigned sectnum */ if ( sectnum == 2 ) beglin = 1; break; case NAME: fprintf( stderr, "'%s'", nmstr ); break; case CHAR: switch ( yylval ) { case '<': case '>': case '^': case '$': case '"': case '[': case ']': case '{': case '}': case '|': case '(': case ')': case '-': case '/': case '\\': case '?': case '.': case '*': case '+': case ',': fprintf( stderr, "\\%c", yylval ); break; default: if ( ! isascii( yylval ) || ! isprint( yylval ) ) fprintf( stderr, "\\%.3o", yylval ); else (void) putc( yylval, stderr ); break; } break; case NUMBER: fprintf( stderr, "%d", yylval ); break; case PREVCCL: fprintf( stderr, "[%d]", yylval ); break; case EOF_OP: fprintf( stderr, "<>" ); break; case 0: fprintf( stderr, "End Marker" ); break; default: fprintf( stderr, "*Something Weird* - tok: %d val: %d\n", toktype, yylval ); break; } } return ( toktype ); } base-7.0.3.1/modules/libcom/src/freeList/Makefile0000664000577000060420000000100013557101274020323 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/freeList INC += freeList.h Com_SRCS += freeListLib.c base-7.0.3.1/modules/libcom/src/freeList/freeList.h0000664000577000060420000000222213557101274020620 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* Author: Marty Kraimer Date: 04-19-94 */ #ifndef INCfreeListh #define INCfreeListh #include #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void epicsShareAPI freeListInitPvt(void **ppvt,int size,int nmalloc); epicsShareFunc void * epicsShareAPI freeListCalloc(void *pvt); epicsShareFunc void * epicsShareAPI freeListMalloc(void *pvt); epicsShareFunc void epicsShareAPI freeListFree(void *pvt,void*pmem); epicsShareFunc void epicsShareAPI freeListCleanup(void *pvt); epicsShareFunc size_t epicsShareAPI freeListItemsAvail(void *pvt); #ifdef __cplusplus } #endif #endif /*INCfreeListh*/ base-7.0.3.1/modules/libcom/src/freeList/freeListLib.c0000775000577000060420000001147413557101274021256 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* Author: Marty Kraimer Date: 04-19-94 */ #include #include #include #include "valgrind/valgrind.h" #ifndef NVALGRIND /* buffer around allocations to detect out of bounds access */ #define REDZONE sizeof(double) #else #define REDZONE 0 #endif #define epicsExportSharedSymbols #include "cantProceed.h" #include "epicsMutex.h" #include "freeList.h" #include "adjustment.h" typedef struct allocMem { struct allocMem *next; void *memory; }allocMem; typedef struct { int size; int nmalloc; void *head; allocMem *mallochead; size_t nBlocksAvailable; epicsMutexId lock; }FREELISTPVT; epicsShareFunc void epicsShareAPI freeListInitPvt(void **ppvt,int size,int nmalloc) { FREELISTPVT *pfl; pfl = callocMustSucceed(1,sizeof(FREELISTPVT), "freeListInitPvt"); pfl->size = adjustToWorstCaseAlignment(size); pfl->nmalloc = nmalloc; pfl->head = NULL; pfl->mallochead = NULL; pfl->nBlocksAvailable = 0u; pfl->lock = epicsMutexMustCreate(); *ppvt = (void *)pfl; VALGRIND_CREATE_MEMPOOL(pfl, REDZONE, 0); return; } epicsShareFunc void * epicsShareAPI freeListCalloc(void *pvt) { FREELISTPVT *pfl = pvt; # ifdef EPICS_FREELIST_DEBUG return callocMustSucceed(1,pfl->size,"freeList Debug Calloc"); # else void *ptemp; ptemp = freeListMalloc(pvt); if(ptemp) memset((char *)ptemp,0,pfl->size); return(ptemp); # endif } epicsShareFunc void * epicsShareAPI freeListMalloc(void *pvt) { FREELISTPVT *pfl = pvt; # ifdef EPICS_FREELIST_DEBUG return callocMustSucceed(1,pfl->size,"freeList Debug Malloc"); # else void *ptemp; void **ppnext; allocMem *pallocmem; int i; epicsMutexMustLock(pfl->lock); ptemp = pfl->head; if(ptemp==0) { /* layout of each block. nmalloc+1 REDZONEs for nmallocs. * The first sizeof(void*) bytes are used to store a pointer * to the next free block. * * | RED | size0 ------ | RED | size1 | ... | RED | * | | next | ----- | */ ptemp = (void *)malloc(pfl->nmalloc*(pfl->size+REDZONE)+REDZONE); if(ptemp==0) { epicsMutexUnlock(pfl->lock); return(0); } pallocmem = (allocMem *)calloc(1,sizeof(allocMem)); if(pallocmem==0) { epicsMutexUnlock(pfl->lock); free(ptemp); return(0); } pallocmem->memory = ptemp; /* real allocation */ ptemp = REDZONE + (char *) ptemp; /* skip first REDZONE */ if(pfl->mallochead) pallocmem->next = pfl->mallochead; pfl->mallochead = pallocmem; for(i=0; inmalloc; i++) { ppnext = ptemp; VALGRIND_MEMPOOL_ALLOC(pfl, ptemp, sizeof(void*)); *ppnext = pfl->head; pfl->head = ptemp; ptemp = ((char *)ptemp) + pfl->size+REDZONE; } ptemp = pfl->head; pfl->nBlocksAvailable += pfl->nmalloc; } ppnext = pfl->head; pfl->head = *ppnext; pfl->nBlocksAvailable--; epicsMutexUnlock(pfl->lock); VALGRIND_MEMPOOL_FREE(pfl, ptemp); VALGRIND_MEMPOOL_ALLOC(pfl, ptemp, pfl->size); return(ptemp); # endif } epicsShareFunc void epicsShareAPI freeListFree(void *pvt,void*pmem) { FREELISTPVT *pfl = pvt; # ifdef EPICS_FREELIST_DEBUG memset ( pmem, 0xdd, pfl->size ); free(pmem); # else void **ppnext; VALGRIND_MEMPOOL_FREE(pvt, pmem); VALGRIND_MEMPOOL_ALLOC(pvt, pmem, sizeof(void*)); epicsMutexMustLock(pfl->lock); ppnext = pmem; *ppnext = pfl->head; pfl->head = pmem; pfl->nBlocksAvailable++; epicsMutexUnlock(pfl->lock); # endif } epicsShareFunc void epicsShareAPI freeListCleanup(void *pvt) { FREELISTPVT *pfl = pvt; allocMem *phead; allocMem *pnext; VALGRIND_DESTROY_MEMPOOL(pvt); phead = pfl->mallochead; while(phead) { pnext = phead->next; free(phead->memory); free(phead); phead = pnext; } epicsMutexDestroy(pfl->lock); free(pvt); } epicsShareFunc size_t epicsShareAPI freeListItemsAvail(void *pvt) { FREELISTPVT *pfl = pvt; size_t nBlocksAvailable; epicsMutexMustLock(pfl->lock); nBlocksAvailable = pfl->nBlocksAvailable; epicsMutexUnlock(pfl->lock); return nBlocksAvailable; } base-7.0.3.1/modules/libcom/src/gpHash/Makefile0000664000577000060420000000077213557101274017777 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/gpHash INC += gpHash.h Com_SRCS += gpHashLib.c base-7.0.3.1/modules/libcom/src/gpHash/gpHash.h0000664000577000060420000000344613557101274017723 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* Author: Marty Kraimer Date: 04-07-94 */ /* gph provides a general purpose directory accessed via a hash table*/ #ifndef INC_gpHash_H #define INC_gpHash_H #include "shareLib.h" #include "ellLib.h" typedef struct{ ELLNODE node; const char *name; /*address of name placed in directory*/ void *pvtid; /*private name for subsystem user*/ void *userPvt; /*private for user*/ } GPHENTRY; struct gphPvt; #ifdef __cplusplus extern "C" { #endif /*tableSize must be power of 2 in range 256 to 65536*/ epicsShareFunc void epicsShareAPI gphInitPvt(struct gphPvt **ppvt, int tableSize); epicsShareFunc GPHENTRY * epicsShareAPI gphFind(struct gphPvt *pvt, const char *name, void *pvtid); epicsShareFunc GPHENTRY * epicsShareAPI gphFindParse(struct gphPvt *pvt, const char *name, size_t len, void *pvtid); epicsShareFunc GPHENTRY * epicsShareAPI gphAdd(struct gphPvt *pvt, const char *name, void *pvtid); epicsShareFunc void epicsShareAPI gphDelete(struct gphPvt *pvt, const char *name, void *pvtid); epicsShareFunc void epicsShareAPI gphFreeMem(struct gphPvt *pvt); epicsShareFunc void epicsShareAPI gphDump(struct gphPvt *pvt); epicsShareFunc void epicsShareAPI gphDumpFP(FILE *fp, struct gphPvt *pvt); #ifdef __cplusplus } #endif #endif /* INC_gpHash_H */ base-7.0.3.1/modules/libcom/src/gpHash/gpHashLib.c0000664000577000060420000001450013557101274020336 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Marty Kraimer Date: 04-07-94 */ #include #include #include #define epicsExportSharedSymbols #include "cantProceed.h" #include "epicsMutex.h" #include "epicsStdioRedirect.h" #include "epicsString.h" #include "dbDefs.h" #include "ellLib.h" #include "epicsPrint.h" #include "gpHash.h" typedef struct gphPvt { int size; unsigned int mask; ELLLIST **paplist; /*pointer to array of pointers to ELLLIST */ epicsMutexId lock; } gphPvt; #define MIN_SIZE 256 #define DEFAULT_SIZE 512 #define MAX_SIZE 65536 void epicsShareAPI gphInitPvt(gphPvt **ppvt, int size) { gphPvt *pgphPvt; if (size & (size - 1)) { fprintf(stderr, "gphInitPvt: %d is not a power of 2\n", size); size = DEFAULT_SIZE; } if (size < MIN_SIZE) size = MIN_SIZE; if (size > MAX_SIZE) size = MAX_SIZE; pgphPvt = callocMustSucceed(1, sizeof(gphPvt), "gphInitPvt"); pgphPvt->size = size; pgphPvt->mask = size - 1; pgphPvt->paplist = callocMustSucceed(size, sizeof(ELLLIST *), "gphInitPvt"); pgphPvt->lock = epicsMutexMustCreate(); *ppvt = pgphPvt; return; } GPHENTRY * epicsShareAPI gphFindParse(gphPvt *pgphPvt, const char *name, size_t len, void *pvtid) { ELLLIST **paplist; ELLLIST *gphlist; GPHENTRY *pgphNode; int hash; if (pgphPvt == NULL) return NULL; paplist = pgphPvt->paplist; hash = epicsMemHash((char *)&pvtid, sizeof(void *), 0); hash = epicsMemHash(name, len, hash) & pgphPvt->mask; epicsMutexMustLock(pgphPvt->lock); gphlist = paplist[hash]; if (gphlist == NULL) { pgphNode = NULL; } else { pgphNode = (GPHENTRY *) ellFirst(gphlist); } while (pgphNode) { if (pvtid == pgphNode->pvtid && strlen(pgphNode->name) == len && strncmp(name, pgphNode->name, len) == 0) break; pgphNode = (GPHENTRY *) ellNext((ELLNODE *)pgphNode); } epicsMutexUnlock(pgphPvt->lock); return pgphNode; } GPHENTRY * epicsShareAPI gphFind(gphPvt *pgphPvt, const char *name, void *pvtid) { return gphFindParse(pgphPvt, name, strlen(name), pvtid); } GPHENTRY * epicsShareAPI gphAdd(gphPvt *pgphPvt, const char *name, void *pvtid) { ELLLIST **paplist; ELLLIST *plist; GPHENTRY *pgphNode; int hash; if (pgphPvt == NULL) return NULL; paplist = pgphPvt->paplist; hash = epicsMemHash((char *)&pvtid, sizeof(void *), 0); hash = epicsStrHash(name, hash) & pgphPvt->mask; epicsMutexMustLock(pgphPvt->lock); plist = paplist[hash]; if (plist == NULL) { plist = calloc(1, sizeof(ELLLIST)); if(!plist){ epicsMutexUnlock(pgphPvt->lock); return NULL; } ellInit(plist); paplist[hash] = plist; } pgphNode = (GPHENTRY *) ellFirst(plist); while (pgphNode) { if (pvtid == pgphNode->pvtid && strcmp(name, pgphNode->name) == 0) { epicsMutexUnlock(pgphPvt->lock); return NULL; } pgphNode = (GPHENTRY *) ellNext((ELLNODE *)pgphNode); } pgphNode = calloc(1, sizeof(GPHENTRY)); if(pgphNode) { pgphNode->name = name; pgphNode->pvtid = pvtid; ellAdd(plist, (ELLNODE *)pgphNode); } epicsMutexUnlock(pgphPvt->lock); return (pgphNode); } void epicsShareAPI gphDelete(gphPvt *pgphPvt, const char *name, void *pvtid) { ELLLIST **paplist; ELLLIST *plist = NULL; GPHENTRY *pgphNode; int hash; if (pgphPvt == NULL) return; paplist = pgphPvt->paplist; hash = epicsMemHash((char *)&pvtid, sizeof(void *), 0); hash = epicsStrHash(name, hash) & pgphPvt->mask; epicsMutexMustLock(pgphPvt->lock); if (paplist[hash] == NULL) { pgphNode = NULL; } else { plist = paplist[hash]; pgphNode = (GPHENTRY *) ellFirst(plist); } while(pgphNode) { if (pvtid == pgphNode->pvtid && strcmp(name, pgphNode->name) == 0) { ellDelete(plist, (ELLNODE*)pgphNode); free((void *)pgphNode); break; } pgphNode = (GPHENTRY *) ellNext((ELLNODE*)pgphNode); } epicsMutexUnlock(pgphPvt->lock); return; } void epicsShareAPI gphFreeMem(gphPvt *pgphPvt) { ELLLIST **paplist; int h; /* Caller must ensure that no other thread is using *pvt */ if (pgphPvt == NULL) return; paplist = pgphPvt->paplist; for (h = 0; h < pgphPvt->size; h++) { ELLLIST *plist = paplist[h]; GPHENTRY *pgphNode; GPHENTRY *next; if (plist == NULL) continue; pgphNode = (GPHENTRY *) ellFirst(plist); while (pgphNode) { next = (GPHENTRY *) ellNext((ELLNODE*)pgphNode); ellDelete(plist, (ELLNODE*)pgphNode); free(pgphNode); pgphNode = next; } free(paplist[h]); } epicsMutexDestroy(pgphPvt->lock); free(paplist); free(pgphPvt); } void epicsShareAPI gphDump(gphPvt *pgphPvt) { gphDumpFP(stdout, pgphPvt); } void epicsShareAPI gphDumpFP(FILE *fp, gphPvt *pgphPvt) { unsigned int empty = 0; ELLLIST **paplist; int h; if (pgphPvt == NULL) return; fprintf(fp, "Hash table has %d buckets", pgphPvt->size); paplist = pgphPvt->paplist; for (h = 0; h < pgphPvt->size; h++) { ELLLIST *plist = paplist[h]; GPHENTRY *pgphNode; int i = 0; if (plist == NULL) { empty++; continue; } pgphNode = (GPHENTRY *) ellFirst(plist); fprintf(fp, "\n [%3d] %3d ", h, ellCount(plist)); while (pgphNode) { if (!(++i % 3)) fprintf(fp, "\n "); fprintf(fp, " %s %p", pgphNode->name, pgphNode->pvtid); pgphNode = (GPHENTRY *) ellNext((ELLNODE*)pgphNode); } } fprintf(fp, "\n%u buckets empty.\n", empty); } base-7.0.3.1/modules/libcom/src/iocsh/Makefile0000664000577000060420000000117513557101274017670 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/iocsh INC += iocsh.h INC += initHooks.h INC += registry.h INC += libComRegister.h Com_SRCS += iocsh.cpp Com_SRCS += initHooks.c Com_SRCS += registry.c Com_SRCS += libComRegister.c base-7.0.3.1/modules/libcom/src/iocsh/initHooks.c0000664000577000060420000000671513557101274020350 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Authors: Benjamin Franksen (BESY) and Marty Kraimer * Date: 06-01-91 * major Revision: 07JuL97 */ #include #include #include #define epicsExportSharedSymbols #include "dbDefs.h" #include "ellLib.h" #include "epicsMutex.h" #include "epicsThread.h" #include "initHooks.h" typedef struct initHookLink { ELLNODE node; initHookFunction func; } initHookLink; static ELLLIST functionList = ELLLIST_INIT; static epicsMutexId listLock; /* * Lazy initialization functions */ static void initHookOnce(void *arg) { listLock = epicsMutexMustCreate(); } static void initHookInit(void) { static epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT; epicsThreadOnce(&onceFlag, initHookOnce, NULL); } /* * To be called before iocInit reaches state desired. */ int initHookRegister(initHookFunction func) { initHookLink *newHook; if (!func) return 0; initHookInit(); newHook = (initHookLink *)malloc(sizeof(initHookLink)); if (!newHook) { printf("Cannot malloc a new initHookLink\n"); return -1; } newHook->func = func; epicsMutexMustLock(listLock); ellAdd(&functionList, &newHook->node); epicsMutexUnlock(listLock); return 0; } /* * Called by iocInit at various points during initialization. * This function must only be called by iocInit and relatives. */ void initHookAnnounce(initHookState state) { initHookLink *hook; initHookInit(); epicsMutexMustLock(listLock); hook = (initHookLink *)ellFirst(&functionList); epicsMutexUnlock(listLock); while (hook != NULL) { hook->func(state); epicsMutexMustLock(listLock); hook = (initHookLink *)ellNext(&hook->node); epicsMutexUnlock(listLock); } } void initHookFree(void) { initHookInit(); epicsMutexMustLock(listLock); ellFree(&functionList); epicsMutexUnlock(listLock); } /* * Call any time you want to print out a state name. */ const char *initHookName(int state) { const char *stateName[] = { "initHookAtIocBuild", "initHookAtBeginning", "initHookAfterCallbackInit", "initHookAfterCaLinkInit", "initHookAfterInitDrvSup", "initHookAfterInitRecSup", "initHookAfterInitDevSup", "initHookAfterInitDatabase", "initHookAfterFinishDevSup", "initHookAfterScanInit", "initHookAfterInitialProcess", "initHookAfterCaServerInit", "initHookAfterIocBuilt", "initHookAtIocRun", "initHookAfterDatabaseRunning", "initHookAfterCaServerRunning", "initHookAfterIocRunning", "initHookAtIocPause", "initHookAfterCaServerPaused", "initHookAfterDatabasePaused", "initHookAfterIocPaused", "initHookAfterInterruptAccept", "initHookAtEnd" }; if (state < 0 || state >= NELEMENTS(stateName)) { return "Not an initHookState"; } return stateName[state]; } base-7.0.3.1/modules/libcom/src/iocsh/initHooks.h0000664000577000060420000000455113557101274020351 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Authors: Benjamin Franksen (BESY) and Marty Kraimer * Date: 06-01-91 * major Revision: 07JuL97 */ #ifndef INC_initHooks_H #define INC_initHooks_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /* This enum must agree with the array of names defined in initHookName() */ typedef enum { initHookAtIocBuild = 0, /* Start of iocBuild/iocInit commands */ initHookAtBeginning, initHookAfterCallbackInit, initHookAfterCaLinkInit, initHookAfterInitDrvSup, initHookAfterInitRecSup, initHookAfterInitDevSup, initHookAfterInitDatabase, initHookAfterFinishDevSup, initHookAfterScanInit, initHookAfterInitialProcess, initHookAfterCaServerInit, initHookAfterIocBuilt, /* End of iocBuild command */ initHookAtIocRun, /* Start of iocRun command */ initHookAfterDatabaseRunning, initHookAfterCaServerRunning, initHookAfterIocRunning, /* End of iocRun/iocInit commands */ initHookAtIocPause, /* Start of iocPause command */ initHookAfterCaServerPaused, initHookAfterDatabasePaused, initHookAfterIocPaused, /* End of iocPause command */ /* Deprecated states, provided for backwards compatibility. * These states are announced at the same point they were before, * but will not be repeated if the IOC gets paused and restarted. */ initHookAfterInterruptAccept, /* After initHookAfterDatabaseRunning */ initHookAtEnd, /* Before initHookAfterIocRunning */ } initHookState; typedef void (*initHookFunction)(initHookState state); epicsShareFunc int initHookRegister(initHookFunction func); epicsShareFunc void initHookAnnounce(initHookState state); epicsShareFunc const char *initHookName(int state); epicsShareFunc void initHookFree(void); #ifdef __cplusplus } #endif #endif /* INC_initHooks_H */ base-7.0.3.1/modules/libcom/src/iocsh/iocsh.cpp0000664000577000060420000010747013557101274020046 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* iocsh.cpp */ /* Author: Marty Kraimer Date: 27APR2000 */ /* Heavily modified by Eric Norum Date: 03MAY2000 */ /* Adapted to C++ by Eric Norum Date: 18DEC2000 */ #include #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsMath.h" #include "errlog.h" #include "macLib.h" #include "epicsStdio.h" #include "epicsString.h" #include "epicsStdlib.h" #include "epicsThread.h" #include "epicsMutex.h" #include "envDefs.h" #include "registry.h" #include "epicsReadline.h" #include "cantProceed.h" #include "iocsh.h" extern "C" { /* * Global link to pdbbase */ epicsShareDef struct dbBase **iocshPpdbbase; /* * File-local information */ struct iocshCommand { iocshCmdDef def; struct iocshCommand *next; }; static struct iocshCommand *iocshCommandHead; static char iocshCmdID[] = "iocshCmd"; struct iocshVariable { iocshVarDef const *pVarDef; struct iocshVariable *next; }; static struct iocshVariable *iocshVariableHead; static char iocshVarID[] = "iocshVar"; extern "C" { static void varCallFunc(const iocshArgBuf *); } static epicsMutexId iocshTableMutex; static epicsThreadOnceId iocshOnceId = EPICS_THREAD_ONCE_INIT; static epicsThreadPrivateId iocshContextId; /* * I/O redirection */ #define NREDIRECTS 5 struct iocshRedirect { const char *name; const char *mode; FILE *fp; FILE *oldFp; int mustRestore; }; /* * Set up module variables */ static void iocshOnce (void *) { iocshTableMutex = epicsMutexMustCreate (); iocshContextId = epicsThreadPrivateCreate(); } static void iocshInit (void) { epicsThreadOnce (&iocshOnceId, iocshOnce, NULL); } /* * Lock the table mutex */ static void iocshTableLock (void) { iocshInit(); epicsMutexMustLock (iocshTableMutex); } /* * Unlock the table mutex */ static void iocshTableUnlock (void) { epicsMutexUnlock (iocshTableMutex); } /* * Register a command */ void epicsShareAPI iocshRegister (const iocshFuncDef *piocshFuncDef, iocshCallFunc func) { struct iocshCommand *l, *p, *n; int i; iocshTableLock (); for (l = NULL, p = iocshCommandHead ; p != NULL ; l = p, p = p->next) { i = strcmp (piocshFuncDef->name, p->def.pFuncDef->name); if (i == 0) { p->def.pFuncDef = piocshFuncDef; p->def.func = func; iocshTableUnlock (); return; } if (i < 0) break; } n = (struct iocshCommand *) callocMustSucceed (1, sizeof *n, "iocshRegister"); if (!registryAdd(iocshCmdID, piocshFuncDef->name, (void *)n)) { free (n); iocshTableUnlock (); errlogPrintf ("iocshRegister failed to add %s\n", piocshFuncDef->name); return; } if (l == NULL) { n->next = iocshCommandHead; iocshCommandHead = n; } else { n->next = l->next; l->next = n; } n->def.pFuncDef = piocshFuncDef; n->def.func = func; iocshTableUnlock (); } /* * Retrieves a previously registered function with the given name. */ const iocshCmdDef * epicsShareAPI iocshFindCommand(const char *name) { return (iocshCmdDef *) registryFind(iocshCmdID, name); } /* * Register the "var" command and any variable(s) */ static const iocshArg varCmdArg0 = { "[variable", iocshArgString}; static const iocshArg varCmdArg1 = { "[value]]", iocshArgString}; static const iocshArg *varCmdArgs[2] = {&varCmdArg0, &varCmdArg1}; static const iocshFuncDef varFuncDef = {"var", 2, varCmdArgs}; void epicsShareAPI iocshRegisterVariable (const iocshVarDef *piocshVarDef) { struct iocshVariable *l, *p, *n; int i; int found; iocshTableLock (); while ((piocshVarDef != NULL) && (piocshVarDef->name != NULL) && (*piocshVarDef->name != '\0')) { if (iocshVariableHead == NULL) iocshRegister(&varFuncDef,varCallFunc); found = 0; for (l = NULL, p = iocshVariableHead ; p != NULL ; l = p, p = p->next) { i = strcmp (piocshVarDef->name, p->pVarDef->name); if (i == 0) { if (p->pVarDef != piocshVarDef) { errlogPrintf("Warning: iocshRegisterVariable redefining %s.\n", piocshVarDef->name); p->pVarDef = piocshVarDef; } found = 1; break; } if (i < 0) break; } if (!found) { n = (struct iocshVariable *) callocMustSucceed(1, sizeof *n, "iocshRegisterVariable"); if (!registryAdd(iocshVarID, piocshVarDef->name, (void *)n)) { free(n); iocshTableUnlock(); errlogPrintf("iocshRegisterVariable failed to add %s.\n", piocshVarDef->name); return; } if (l == NULL) { n->next = iocshVariableHead; iocshVariableHead = n; } else { n->next = l->next; l->next = n; } n->pVarDef = piocshVarDef; } piocshVarDef++; } iocshTableUnlock (); } /* * Retrieves a previously registered variable with the given name. */ const iocshVarDef * epicsShareAPI iocshFindVariable(const char *name) { struct iocshVariable *temp = (iocshVariable *) registryFind(iocshVarID, name); return temp->pVarDef; } /* * Free storage created by iocshRegister/iocshRegisterVariable */ void epicsShareAPI iocshFree(void) { struct iocshCommand *pc; struct iocshVariable *pv; iocshTableLock (); for (pc = iocshCommandHead ; pc != NULL ; ) { struct iocshCommand * nc = pc->next; free (pc); pc = nc; } for (pv = iocshVariableHead ; pv != NULL ; ) { struct iocshVariable *nv = pv->next; free (pv); pv = nv; } iocshCommandHead = NULL; iocshVariableHead = NULL; iocshTableUnlock (); } /* * Report an error */ static void showError (const char *filename, int lineno, const char *msg, ...) { va_list ap; va_start (ap, msg); if (filename) fprintf(epicsGetStderr(), "%s line %d: ", filename, lineno); vfprintf (epicsGetStderr(), msg, ap); fputc ('\n', epicsGetStderr()); va_end (ap); } static int cvtArg (const char *filename, int lineno, char *arg, iocshArgBuf *argBuf, const iocshArg *piocshArg) { char *endp; switch (piocshArg->type) { case iocshArgInt: if (arg && *arg) { errno = 0; argBuf->ival = strtol (arg, &endp, 0); if (errno == ERANGE) { errno = 0; argBuf->ival = strtoul (arg, &endp, 0); if (errno == ERANGE) { showError(filename, lineno, "Integer '%s' out of range", arg); return 0; } } if (*endp) { showError(filename, lineno, "Illegal integer '%s'", arg); return 0; } } else { argBuf->ival = 0; } break; case iocshArgDouble: if (arg && *arg) { argBuf->dval = epicsStrtod (arg, &endp); if (*endp) { showError(filename, lineno, "Illegal double '%s'", arg); return 0; } } else { argBuf->dval = 0.0; } break; case iocshArgString: argBuf->sval = arg; break; case iocshArgPersistentString: argBuf->sval = (char *) malloc(strlen(arg) + 1); if (argBuf->sval == NULL) { showError(filename, lineno, "Out of memory"); return 0; } strcpy(argBuf->sval, arg); break; case iocshArgPdbbase: /* Argument must be missing or 0 or pdbbase */ if(!arg || !*arg || (*arg == '0') || (strcmp(arg, "pdbbase") == 0)) { if(!iocshPpdbbase || !*iocshPpdbbase) { showError(filename, lineno, "pdbbase not present"); return 0; } argBuf->vval = *iocshPpdbbase; break; } showError(filename, lineno, "Expecting 'pdbbase' got '%s'", arg); return 0; default: showError(filename, lineno, "Illegal argument type %d", piocshArg->type); return 0; } return 1; } /* * Open redirected I/O */ static int openRedirect(const char *filename, int lineno, struct iocshRedirect *redirect) { int i; for (i = 0 ; i < NREDIRECTS ; i++, redirect++) { if (redirect->name != NULL) { redirect->fp = fopen(redirect->name, redirect->mode); if (redirect->fp == NULL) { showError(filename, lineno, "Can't open \"%s\": %s.", redirect->name, strerror(errno)); redirect->name = NULL; while (i--) { redirect--; if (redirect->fp) { fclose(redirect->fp); redirect->fp = NULL; } redirect->name = NULL; } return -1; } redirect->mustRestore = 0; } } return 0; } /* * Start I/O redirection */ static void startRedirect(const char * /*filename*/, int /*lineno*/, struct iocshRedirect *redirect) { int i; for (i = 0 ; i < NREDIRECTS ; i++, redirect++) { if (redirect->fp != NULL) { switch(i) { case 0: redirect->oldFp = epicsGetThreadStdin(); epicsSetThreadStdin(redirect->fp); redirect->mustRestore = 1; break; case 1: redirect->oldFp = epicsGetThreadStdout(); epicsSetThreadStdout(redirect->fp); redirect->mustRestore = 1; break; case 2: redirect->oldFp = epicsGetThreadStderr(); epicsSetThreadStderr(redirect->fp); redirect->mustRestore = 1; break; } } } } /* * Finish up I/O redirection */ static void stopRedirect(const char *filename, int lineno, struct iocshRedirect *redirect) { int i; for (i = 0 ; i < NREDIRECTS ; i++, redirect++) { if (redirect->fp != NULL) { if (fclose(redirect->fp) != 0) showError(filename, lineno, "Error closing \"%s\": %s.", redirect->name, strerror(errno)); redirect->fp = NULL; if (redirect->mustRestore) { switch(i) { case 0: epicsSetThreadStdin(redirect->oldFp); break; case 1: epicsSetThreadStdout(redirect->oldFp); break; case 2: epicsSetThreadStderr(redirect->oldFp); break; } } } redirect->name = NULL; } } /* * "help" command */ static const iocshArg helpArg0 = { "[command ...]",iocshArgArgv}; static const iocshArg *helpArgs[1] = {&helpArg0}; static const iocshFuncDef helpFuncDef = {"help",1,helpArgs}; static void helpCallFunc(const iocshArgBuf *args) { int argc = args[0].aval.ac; const char * const * argv = args[0].aval.av; struct iocshFuncDef const *piocshFuncDef; struct iocshCommand *pcmd; if (argc == 1) { int l, col = 0; fprintf(epicsGetStdout(), "Type 'help ' to see the arguments of .\n"); iocshTableLock (); for (pcmd = iocshCommandHead ; pcmd != NULL ; pcmd = pcmd->next) { piocshFuncDef = pcmd->def.pFuncDef; l = strlen (piocshFuncDef->name); if ((l + col) >= 79) { fputc('\n', epicsGetStdout()); col = 0; } fputs(piocshFuncDef->name, epicsGetStdout()); col += l; if (col >= 64) { fputc('\n', epicsGetStdout()); col = 0; } else { do { fputc(' ', epicsGetStdout()); col++; } while ((col % 16) != 0); } } if (col) fputc('\n', epicsGetStdout()); iocshTableUnlock (); } else { for (int iarg = 1 ; iarg < argc ; iarg++) { for (pcmd = iocshCommandHead ; pcmd != NULL ; pcmd = pcmd->next) { piocshFuncDef = pcmd->def.pFuncDef; if (epicsStrGlobMatch(piocshFuncDef->name, argv[iarg]) != 0) { fputs(piocshFuncDef->name, epicsGetStdout()); for (int a = 0 ; a < piocshFuncDef->nargs ; a++) { const char *cp = piocshFuncDef->arg[a]->name; if ((piocshFuncDef->arg[a]->type == iocshArgArgv) || (strchr (cp, ' ') == NULL)) { fprintf(epicsGetStdout(), " %s", cp); } else { fprintf(epicsGetStdout(), " '%s'", cp); } } fprintf(epicsGetStdout(),"\n");; } } } } } typedef enum { Continue, Break, Halt } OnError; // per call to iocshBody() struct iocshScope { iocshScope *outer; OnError onerr; double timeout; bool errored; bool interactive; iocshScope() :outer(0), onerr(Continue), timeout(0.0), errored(false), interactive(false) {} }; // per thread executing iocshBody() struct iocshContext { MAC_HANDLE *handle; iocshScope *scope; }; int iocshSetError(int err) { iocshContext *ctxt; if (err && iocshContextId) { ctxt = (iocshContext *) epicsThreadPrivateGet(iocshContextId); if(ctxt && ctxt->scope) ctxt->scope->errored = 1; } return err; } /* * The body of the command interpreter */ static int iocshBody (const char *pathname, const char *commandLine, const char *macros) { FILE *fp = NULL; const char *filename = NULL; int icin, icout; char c; int quote, inword, backslash; const char *raw = NULL;; char *line = NULL; int lineno = 0; int argc; char **argv = NULL; int argvCapacity = 0; struct iocshRedirect *redirects = NULL; struct iocshRedirect *redirect = NULL; int sep; const char *prompt = NULL; const char *ifs = " \t(),\r"; iocshArgBuf *argBuf = NULL; int argBufCapacity = 0; struct iocshCommand *found; void *readlineContext = NULL; int wasOkToBlock; static const char * pairs[] = {"", "environ", NULL, NULL}; iocshScope scope; iocshContext *context; char ** defines = NULL; int ret = 0; iocshInit(); /* * See if command interpreter is interactive */ if (commandLine == NULL) { if ((pathname == NULL) || (strcmp (pathname, "") == 0)) { if ((prompt = envGetConfigParamPtr(&IOCSH_PS1)) == NULL) { prompt = "epics> "; } scope.interactive = true; } else { fp = fopen (pathname, "r"); if (fp == NULL) { fprintf(epicsGetStderr(), "Can't open %s: %s\n", pathname, strerror (errno)); return -1; } if ((filename = strrchr (pathname, '/')) == NULL) filename = pathname; else filename++; prompt = NULL; } /* * Create a command-line input context */ if ((readlineContext = epicsReadlineBegin(fp)) == NULL) { fprintf(epicsGetStderr(), "Can't allocate command-line object.\n"); if (fp) fclose(fp); return -1; } } else { // use of iocshCmd() implies "on error break" scope.onerr = Break; } /* * Set up redirection */ redirects = (struct iocshRedirect *)calloc(NREDIRECTS, sizeof *redirects); if (redirects == NULL) { fprintf(epicsGetStderr(), "Out of memory!\n"); return -1; } /* * Parse macro definitions, this check occurs before creating the * macro handle to simplify cleanup. */ if (macros) { if (macParseDefns(NULL, macros, &defines) < 0) { free(redirects); return -1; } } // Check for existing context or construct a new one. context = (iocshContext *) epicsThreadPrivateGet(iocshContextId); if (!context) { context = (iocshContext*)calloc(1, sizeof(*context)); if (!context || macCreateHandle(&context->handle, pairs)) { errlogMessage("iocsh: macCreateHandle failed."); free(redirects); free(context); return -1; } epicsThreadPrivateSet(iocshContextId, (void *) context); } MAC_HANDLE *handle = context->handle; scope.outer = context->scope; context->scope = &scope; macPushScope(handle); macInstallMacros(handle, defines); wasOkToBlock = epicsThreadIsOkToBlock(); epicsThreadSetOkToBlock(1); /* * Read commands till EOF or exit */ for (;;) { if(!scope.interactive && scope.errored) { if(scope.onerr==Continue) { /* do nothing */ } else if(scope.onerr==Break) { ret = -1; fprintf(epicsGetStderr(), "iocsh Error: Break\n" ); break; } else if(scope.onerr==Halt) { ret = -1; if(scope.timeout<=0.0 || isinf(scope.timeout)) { fprintf(epicsGetStderr(), "iocsh Error: Halt\n" ); epicsThreadSuspendSelf(); break; } else { fprintf(epicsGetStderr(), "iocsh Error: Waiting %.1f sec ...\n", scope.timeout); epicsThreadSleep(scope.timeout); } } } /* * Read a line */ if (commandLine) { if (raw != NULL) break; raw = commandLine; } else { if ((raw = epicsReadline(prompt, readlineContext)) == NULL) break; } lineno++; /* * Skip leading white-space */ icin = 0; while ((c = raw[icin]) && isspace(c)) { icin++; } /* * Ignore comment lines other than to echo * them if they came from a script (disable echoing * with '#-'). This avoids macLib errors from comments. */ if (c == '#') { if ((prompt == NULL) && (commandLine == NULL)) if (raw[icin + 1] != '-') puts(raw); continue; } /* * Expand macros */ free(line); if ((line = macDefExpand(raw, handle)) == NULL) { scope.errored = true; continue; } /* * Skip leading white-space coming from a macro */ while ((c = line[icin]) && isspace(c)) { icin++; } /* * Echo non-empty lines read from a script. * Comments delineated with '#-' aren't echoed. */ if ((prompt == NULL) && *line && (commandLine == NULL)) { if ((c != '#') || (line[icin + 1] != '-')) { puts(line); } } /* * Ignore lines that became a comment or empty after macro expansion */ if (!c || c == '#') continue; /* * Break line into words */ icout = 0; inword = 0; argc = 0; quote = EOF; backslash = 0; redirect = NULL; for (;;) { if (argc >= argvCapacity) { int newCapacity = argvCapacity + 20; char **newv = (char **)realloc (argv, newCapacity * sizeof *argv); if (newv == NULL) { fprintf (epicsGetStderr(), "Out of memory!\n"); argc = -1; scope.errored = true; break; } argv = newv; argvCapacity = newCapacity; } c = line[icin++]; if (c == '\0') break; if ((quote == EOF) && !backslash && (strchr (ifs, c))) sep = 1; else sep = 0; if ((quote == EOF) && !backslash) { int redirectFd = 1; if (c == '\\') { backslash = 1; continue; } if (c == '<') { if (redirect != NULL) { break; } redirect = &redirects[0]; sep = 1; redirect->mode = "r"; } if ((c >= '1') && (c <= '9') && (line[icin] == '>')) { redirectFd = c - '0'; c = '>'; icin++; } if (c == '>') { if (redirect != NULL) break; if (redirectFd >= NREDIRECTS) { redirect = &redirects[1]; break; } redirect = &redirects[redirectFd]; sep = 1; if (line[icin] == '>') { icin++; redirect->mode = "a"; } else { redirect->mode = "w"; } } } if (inword) { if (c == quote) { quote = EOF; } else { if ((quote == EOF) && !backslash) { if (sep) { inword = 0; line[icout++] = '\0'; } else if ((c == '"') || (c == '\'')) { quote = c; } else { line[icout++] = c; } } else { line[icout++] = c; } } } else { if (!sep) { if (((c == '"') || (c == '\'')) && !backslash) { quote = c; } if (redirect != NULL) { if (redirect->name != NULL) { argc = -1; break; } redirect->name = line + icout; redirect = NULL; } else { argv[argc++] = line + icout; } if (quote == EOF) line[icout++] = c; inword = 1; } } backslash = 0; } if (redirect != NULL) { showError(filename, lineno, "Illegal redirection."); scope.errored = true; continue; } if (argc < 0) { break; } if (quote != EOF) { showError(filename, lineno, "Unbalanced quote."); scope.errored = true; continue; } if (backslash) { showError(filename, lineno, "Trailing backslash."); scope.errored = true; continue; } if (inword) line[icout++] = '\0'; argv[argc] = NULL; /* * Special case -- Redirected input but no command * Treat as if 'iocsh filename'. */ if ((argc == 0) && (redirects[0].name != NULL)) { const char *commandFile = redirects[0].name; redirects[0].name = NULL; if (openRedirect(filename, lineno, redirects) < 0) continue; startRedirect(filename, lineno, redirects); if(iocshBody(commandFile, NULL, macros)) scope.errored = true; stopRedirect(filename, lineno, redirects); continue; } /* * Special command? */ if ((argc > 0) && (strcmp(argv[0], "exit") == 0)) break; /* * Set up redirection */ if ((openRedirect(filename, lineno, redirects) == 0) && (argc > 0)) { // error unless a function is actually called. // handles command not-found and arg parsing errors. scope.errored = true; /* * Look up command */ found = (iocshCommand *)registryFind (iocshCmdID, argv[0]); if (found) { /* * Process arguments and call function */ struct iocshFuncDef const *piocshFuncDef = found->def.pFuncDef; for (int iarg = 0 ; ; ) { if (iarg == piocshFuncDef->nargs) { startRedirect(filename, lineno, redirects); /* execute */ scope.errored = false; try { (*found->def.func)(argBuf); } catch(std::exception& e){ fprintf(epicsGetStderr(), "c++ error: %s\n", e.what()); scope.errored = true; } catch(...) { fprintf(epicsGetStderr(), "c++ error unknown\n"); scope.errored = true; } break; } if (iarg >= argBufCapacity) { int newCapacity = argBufCapacity + 20; void *newBuf = realloc(argBuf, newCapacity * sizeof *argBuf); if (newBuf == NULL) { fprintf (epicsGetStderr(), "Out of memory!\n"); break; } argBuf = (iocshArgBuf *) newBuf; argBufCapacity = newCapacity; } if (piocshFuncDef->arg[iarg]->type == iocshArgArgv) { argBuf[iarg].aval.ac = argc-iarg; argBuf[iarg].aval.av = argv+iarg; iarg = piocshFuncDef->nargs; } else { if (!cvtArg (filename, lineno, ((iarg < argc) ? argv[iarg+1] : NULL), &argBuf[iarg], piocshFuncDef->arg[iarg])) break; iarg++; } } if ((prompt != NULL) && (strcmp(argv[0], "epicsEnvSet") == 0)) { const char *newPrompt; if ((newPrompt = envGetConfigParamPtr(&IOCSH_PS1)) != NULL) prompt = newPrompt; } } else { showError(filename, lineno, "Command %s not found.", argv[0]); } } stopRedirect(filename, lineno, redirects); } macPopScope(handle); if (!scope.outer) { macDeleteHandle(handle); free(context); epicsThreadPrivateSet(iocshContextId, NULL); } else { context->scope = scope.outer; } if (fp && (fp != stdin)) fclose (fp); if (redirects != NULL) { stopRedirect(filename, lineno, redirects); free (redirects); } free(line); free (argv); free (argBuf); errlogFlush(); if (readlineContext) epicsReadlineEnd(readlineContext); epicsThreadSetOkToBlock(wasOkToBlock); return ret; } /* * External access to the command interpreter */ int epicsShareAPI iocsh (const char *pathname) { return iocshLoad(pathname, NULL); } int epicsShareAPI iocshCmd (const char *cmd) { return iocshRun(cmd, NULL); } int epicsShareAPI iocshLoad(const char *pathname, const char *macros) { if (pathname) epicsEnvSet("IOCSH_STARTUP_SCRIPT", pathname); return iocshBody(pathname, NULL, macros); } int epicsShareAPI iocshRun(const char *cmd, const char *macros) { if (cmd == NULL) return 0; return iocshBody(NULL, cmd, macros); } /* * Needed to work around the necessary limitations of macLib and * environment variables. In every other case of macro expansion * it is the expected outcome that defined macros override any * environment variables. * * iocshLoad/Run turn this on its head as it is very likely that * an epicsEnvSet command may be run within the context of their * calls. Thus, it would be expected that the new value would be * returned in any future macro expansion. * * To do so, the epicsEnvSet command needs to be able to access * and update the shared MAC_HANDLE that the iocsh uses. Which is * what this function is provided for. */ void epicsShareAPI iocshEnvClear(const char *name) { iocshContext *context; if (iocshContextId) { context = (iocshContext *) epicsThreadPrivateGet(iocshContextId); if (context != NULL) { macPutValue(context->handle, name, NULL); } } } /* * Internal commands */ static void varHandler(const iocshVarDef *v, const char *setString) { switch(v->type) { default: fprintf(epicsGetStderr(), "Can't handle variable %s of type %d.\n", v->name, v->type); return; case iocshArgInt: break; case iocshArgDouble: break; } if(setString == NULL) { switch(v->type) { default: break; case iocshArgInt: fprintf(epicsGetStdout(), "%s = %d\n", v->name, *(int *)v->pval); break; case iocshArgDouble: fprintf(epicsGetStdout(), "%s = %g\n", v->name, *(double *)v->pval); break; } } else { switch(v->type) { default: break; case iocshArgInt: { char *endp; long ltmp = strtol(setString, &endp, 0); if((*setString != '\0') && (*endp == '\0')) *(int *)v->pval = ltmp; else fprintf(epicsGetStderr(), "Invalid integer value. Var %s not changed.\n", v->name); break; } case iocshArgDouble: { char *endp; double dtmp = epicsStrtod(setString, &endp); if((*setString != '\0') && (*endp == '\0')) *(double *)v->pval = dtmp; else fprintf(epicsGetStderr(), "Invalid double value. Var %s not changed.\n", v->name); break; } } } } static void varCallFunc(const iocshArgBuf *args) { struct iocshVariable *v; if(args[0].sval == NULL) { for (v = iocshVariableHead ; v != NULL ; v = v->next) varHandler(v->pVarDef, args[1].sval); } else { v = (iocshVariable *)registryFind(iocshVarID, args[0].sval); if (v == NULL) { fprintf(epicsGetStderr(), "Var %s not found.\n", args[0].sval); } else { varHandler(v->pVarDef, args[1].sval); } } } /* iocshCmd */ static const iocshArg iocshCmdArg0 = { "command",iocshArgString}; static const iocshArg *iocshCmdArgs[1] = {&iocshCmdArg0}; static const iocshFuncDef iocshCmdFuncDef = {"iocshCmd",1,iocshCmdArgs}; static void iocshCmdCallFunc(const iocshArgBuf *args) { iocshCmd(args[0].sval); } /* iocshLoad */ static const iocshArg iocshLoadArg0 = { "pathname",iocshArgString}; static const iocshArg iocshLoadArg1 = { "macros", iocshArgString}; static const iocshArg *iocshLoadArgs[2] = {&iocshLoadArg0, &iocshLoadArg1}; static const iocshFuncDef iocshLoadFuncDef = {"iocshLoad",2,iocshLoadArgs}; static void iocshLoadCallFunc(const iocshArgBuf *args) { iocshLoad(args[0].sval, args[1].sval); } /* iocshRun */ static const iocshArg iocshRunArg0 = { "command",iocshArgString}; static const iocshArg iocshRunArg1 = { "macros", iocshArgString}; static const iocshArg *iocshRunArgs[2] = {&iocshRunArg0, &iocshRunArg1}; static const iocshFuncDef iocshRunFuncDef = {"iocshRun",2,iocshRunArgs}; static void iocshRunCallFunc(const iocshArgBuf *args) { iocshRun(args[0].sval, args[1].sval); } /* on */ static const iocshArg onArg0 = { "'error' 'continue' | 'break' | 'wait' [value] | 'halt'", iocshArgArgv }; static const iocshArg *onArgs[1] = {&onArg0}; static const iocshFuncDef onFuncDef = {"on", 1, onArgs}; static void onCallFunc(const iocshArgBuf *args) { iocshContext *context = (iocshContext *) epicsThreadPrivateGet(iocshContextId); #define USAGE() fprintf(epicsGetStderr(), "Usage: on error [continue | break | halt | wait ]\n") if(!context || !context->scope) { // we are not called through iocshBody()... } else if(args->aval.ac<3 || strcmp(args->aval.av[1], "error")!=0) { USAGE(); } else if(context->scope->interactive) { fprintf(epicsGetStderr(), "Interactive shell ignores on error ...\n"); } else { // don't fault on previous, ignored, errors context->scope->errored = false; if(strcmp(args->aval.av[2], "continue")==0) { context->scope->onerr = Continue; } else if(strcmp(args->aval.av[2], "break")==0) { context->scope->onerr = Break; } else if(strcmp(args->aval.av[2], "halt")==0) { context->scope->onerr = Halt; context->scope->timeout = 0.0; } else if(strcmp(args->aval.av[2], "wait")==0) { context->scope->onerr = Halt; if(args->aval.ac<=3) { USAGE(); } else if(epicsParseDouble(args->aval.av[3], &context->scope->timeout, NULL)) { context->scope->timeout = 5.0; } else { USAGE(); fprintf(epicsGetStderr(), "Unable to parse 'on error wait' time %s\n", args->aval.av[3]); } } else { fprintf(epicsGetStderr(), "Usage: on error [continue | break | halt | wait ]\n"); context->scope->errored = true; } } #undef USAGE } /* * Dummy internal commands -- register and install in command table * so they show up in the help display */ /* comment */ static const iocshArg commentArg0 = { "newline-terminated comment", iocshArgArgv}; static const iocshArg *commentArgs[1] = {&commentArg0}; static const iocshFuncDef commentFuncDef = {"#",1,commentArgs}; static void commentCallFunc(const iocshArgBuf *) { } /* exit */ static const iocshFuncDef exitFuncDef = {"exit",0,0}; static void exitCallFunc(const iocshArgBuf *) { } static void localRegister (void) { iocshRegister(&commentFuncDef,commentCallFunc); iocshRegister(&exitFuncDef,exitCallFunc); iocshRegister(&helpFuncDef,helpCallFunc); iocshRegister(&iocshCmdFuncDef,iocshCmdCallFunc); iocshRegister(&iocshLoadFuncDef,iocshLoadCallFunc); iocshRegister(&iocshRunFuncDef,iocshRunCallFunc); iocshRegister(&onFuncDef, onCallFunc); } } /* extern "C" */ /* * Register local commands on application startup */ class IocshRegister { public: IocshRegister() { localRegister(); } }; static IocshRegister iocshRegisterObj; base-7.0.3.1/modules/libcom/src/iocsh/iocsh.h0000664000577000060420000000707213557101274017510 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* iocsh.h ioc: call registered function*/ /* Author: Marty Kraimer Date: 27APR2000 */ #ifndef INCiocshH #define INCiocshH #include #include "compilerDependencies.h" #include "shareLib.h" #if defined(vxWorks) || defined(__rtems__) #define IOCSH_STATIC_FUNC #else #define IOCSH_STATIC_FUNC static EPICS_ALWAYS_INLINE #endif #ifdef __cplusplus extern "C" { #endif typedef enum { iocshArgInt, iocshArgDouble, iocshArgString, iocshArgPdbbase, iocshArgArgv, iocshArgPersistentString }iocshArgType; typedef union iocshArgBuf { int ival; double dval; char *sval; void *vval; struct { int ac; char **av; } aval; }iocshArgBuf; typedef struct iocshVarDef { const char *name; iocshArgType type; void * pval; }iocshVarDef; typedef struct iocshArg { const char *name; iocshArgType type; }iocshArg; typedef struct iocshFuncDef { const char *name; int nargs; const iocshArg * const *arg; }iocshFuncDef; typedef void (*iocshCallFunc)(const iocshArgBuf *argBuf); typedef struct iocshCmdDef { iocshFuncDef const *pFuncDef; iocshCallFunc func; }iocshCmdDef; epicsShareFunc void epicsShareAPI iocshRegister( const iocshFuncDef *piocshFuncDef, iocshCallFunc func); epicsShareFunc void epicsShareAPI iocshRegisterVariable ( const iocshVarDef *piocshVarDef); epicsShareFunc const iocshCmdDef * epicsShareAPI iocshFindCommand( const char* name) EPICS_DEPRECATED; epicsShareFunc const iocshVarDef * epicsShareAPI iocshFindVariable( const char* name); /* iocshFree frees storage used by iocshRegister*/ /* This should only be called when iocsh is no longer needed*/ epicsShareFunc void epicsShareAPI iocshFree(void); /** shorthand for @code iocshLoad(pathname, NULL) @endcode */ epicsShareFunc int epicsShareAPI iocsh(const char *pathname); /** shorthand for @code iocshRun(cmd, NULL) @endcode */ epicsShareFunc int epicsShareAPI iocshCmd(const char *cmd); /** Read and evaluate IOC shell commands from the given file. * @param pathname Path to script file * @param macros NULL or a comma seperated list of macro definitions. eg. "VAR1=x,VAR2=y" * @return 0 on success, non-zero on error */ epicsShareFunc int epicsShareAPI iocshLoad(const char *pathname, const char* macros); /** Evaluate a single IOC shell command * @param cmd Command string. eg. "echo \"something or other\"" * @param macros NULL or a comma seperated list of macro definitions. eg. "VAR1=x,VAR2=y" * @return 0 on success, non-zero on error */ epicsShareFunc int epicsShareAPI iocshRun(const char *cmd, const char* macros); /** @brief Signal error from an IOC shell function. * * @param err 0 - success (no op), !=0 - error * @return The err argument value. */ epicsShareFunc int iocshSetError(int err); /* Makes macros that shadow environment variables work correctly with epicsEnvSet */ epicsShareFunc void epicsShareAPI iocshEnvClear(const char *name); /* 'weak' link to pdbbase */ epicsShareExtern struct dbBase **iocshPpdbbase; #ifdef __cplusplus } #endif #endif /*INCiocshH*/ base-7.0.3.1/modules/libcom/src/iocsh/libComRegister.c0000664000577000060420000003363413557101274021313 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The University of Saskatchewan * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #define epicsExportSharedSymbols #include "iocsh.h" #include "asLib.h" #include "epicsStdioRedirect.h" #include "epicsString.h" #include "epicsTime.h" #include "epicsThread.h" #include "epicsMutex.h" #include "envDefs.h" #include "osiUnistd.h" #include "logClient.h" #include "errlog.h" #include "taskwd.h" #include "registry.h" #include "epicsGeneralTime.h" #include "libComRegister.h" /* date */ void date(const char *format) { epicsTimeStamp now; char nowText[80] = {'\0'}; if (epicsTimeGetCurrent(&now)) { puts("Current time not available."); return; } if (format == NULL || format[0] == '\0') format = "%Y/%m/%d %H:%M:%S.%06f"; epicsTimeToStrftime(nowText, sizeof(nowText), format, &now); puts(nowText); } static const iocshArg dateArg0 = { "format",iocshArgString}; static const iocshArg * const dateArgs[] = {&dateArg0}; static const iocshFuncDef dateFuncDef = {"date", 1, dateArgs}; static void dateCallFunc (const iocshArgBuf *args) { date(args[0].sval); } /* echo */ IOCSH_STATIC_FUNC void echo(char* str) { if (str) dbTranslateEscape(str, str); /* in-place is safe */ else str = ""; printf("%s\n", str); } static const iocshArg echoArg0 = { "string",iocshArgString}; static const iocshArg * const echoArgs[1] = {&echoArg0}; static const iocshFuncDef echoFuncDef = {"echo",1,echoArgs}; static void echoCallFunc(const iocshArgBuf *args) { echo(args[0].sval); } /* chdir */ static const iocshArg chdirArg0 = { "directory name",iocshArgString}; static const iocshArg * const chdirArgs[1] = {&chdirArg0}; static const iocshFuncDef chdirFuncDef = {"cd",1,chdirArgs}; static void chdirCallFunc(const iocshArgBuf *args) { if (args[0].sval == NULL || iocshSetError(chdir(args[0].sval))) { fprintf(stderr, "Invalid directory path, ignored\n"); } } /* print current working directory */ static const iocshFuncDef pwdFuncDef = { "pwd", 0, 0 }; static void pwdCallFunc (const iocshArgBuf *args) { char buf[256]; char *pwd = getcwd ( buf, sizeof(buf) - 1 ); if ( pwd ) { printf ( "%s\n", pwd ); } } /* epicsEnvSet */ static const iocshArg epicsEnvSetArg0 = { "name",iocshArgString}; static const iocshArg epicsEnvSetArg1 = { "value",iocshArgString}; static const iocshArg * const epicsEnvSetArgs[2] = {&epicsEnvSetArg0,&epicsEnvSetArg1}; static const iocshFuncDef epicsEnvSetFuncDef = {"epicsEnvSet",2,epicsEnvSetArgs}; static void epicsEnvSetCallFunc(const iocshArgBuf *args) { char *name = args[0].sval; char *value = args[1].sval; if (name == NULL) { fprintf(stderr, "Missing environment variable name argument.\n"); return; } if (value == NULL) { fprintf(stderr, "Missing environment variable value argument.\n"); return; } epicsEnvSet (name, value); } /* epicsEnvUnset */ static const iocshArg epicsEnvUnsetArg0 = { "name",iocshArgString}; static const iocshArg * const epicsEnvUnsetArgs[1] = {&epicsEnvUnsetArg0}; static const iocshFuncDef epicsEnvUnsetFuncDef = {"epicsEnvUnset",1,epicsEnvUnsetArgs}; static void epicsEnvUnsetCallFunc(const iocshArgBuf *args) { char *name = args[0].sval; if (name == NULL) { fprintf(stderr, "Missing environment variable name argument.\n"); return; } epicsEnvUnset (name); } /* epicsParamShow */ IOCSH_STATIC_FUNC void epicsParamShow() { epicsPrtEnvParams (); } static const iocshFuncDef epicsParamShowFuncDef = {"epicsParamShow",0,NULL}; static void epicsParamShowCallFunc(const iocshArgBuf *args) { epicsParamShow (); } /* epicsPrtEnvParams */ static const iocshFuncDef epicsPrtEnvParamsFuncDef = {"epicsPrtEnvParams",0,0}; static void epicsPrtEnvParamsCallFunc(const iocshArgBuf *args) { epicsPrtEnvParams (); } /* epicsEnvShow */ static const iocshArg epicsEnvShowArg0 = { "[name]",iocshArgString}; static const iocshArg * const epicsEnvShowArgs[1] = {&epicsEnvShowArg0}; static const iocshFuncDef epicsEnvShowFuncDef = {"epicsEnvShow",1,epicsEnvShowArgs}; static void epicsEnvShowCallFunc(const iocshArgBuf *args) { epicsEnvShow (args[0].sval); } /* registryDump */ static const iocshFuncDef registryDumpFuncDef = {"registryDump",0,NULL}; static void registryDumpCallFunc(const iocshArgBuf *args) { registryDump (); } /* iocLogInit */ static const iocshFuncDef iocLogInitFuncDef = {"iocLogInit",0}; static void iocLogInitCallFunc(const iocshArgBuf *args) { iocLogInit (); } /* iocLogDisable */ IOCSH_STATIC_FUNC void setIocLogDisable(int val) { iocLogDisable = val; } static const iocshArg iocLogDisableArg0 = {"(0,1)=>(false,true)",iocshArgInt}; static const iocshArg * const iocLogDisableArgs[1] = {&iocLogDisableArg0}; static const iocshFuncDef iocLogDisableFuncDef = {"setIocLogDisable",1,iocLogDisableArgs}; static void iocLogDisableCallFunc(const iocshArgBuf *args) { setIocLogDisable(args[0].ival); } /* iocLogShow */ static const iocshArg iocLogShowArg0 = {"level",iocshArgInt}; static const iocshArg * const iocLogShowArgs[1] = {&iocLogShowArg0}; static const iocshFuncDef iocLogShowFuncDef = {"iocLogShow",1,iocLogShowArgs}; static void iocLogShowCallFunc(const iocshArgBuf *args) { iocLogShow (args[0].ival); } /* eltc */ static const iocshArg eltcArg0 = {"(0,1)=>(false,true)",iocshArgInt}; static const iocshArg * const eltcArgs[1] = {&eltcArg0}; static const iocshFuncDef eltcFuncDef = {"eltc",1,eltcArgs}; static void eltcCallFunc(const iocshArgBuf *args) { eltc(args[0].ival); } /* errlogInit */ static const iocshArg errlogInitArg0 = { "bufsize",iocshArgInt}; static const iocshArg * const errlogInitArgs[1] = {&errlogInitArg0}; static const iocshFuncDef errlogInitFuncDef = {"errlogInit",1,errlogInitArgs}; static void errlogInitCallFunc(const iocshArgBuf *args) { errlogInit(args[0].ival); } /* errlogInit2 */ static const iocshArg errlogInit2Arg0 = { "bufSize",iocshArgInt}; static const iocshArg errlogInit2Arg1 = { "maxMsgSize",iocshArgInt}; static const iocshArg * const errlogInit2Args[] = {&errlogInit2Arg0, &errlogInit2Arg1}; static const iocshFuncDef errlogInit2FuncDef = {"errlogInit2", 2, errlogInit2Args}; static void errlogInit2CallFunc(const iocshArgBuf *args) { errlogInit2(args[0].ival, args[1].ival); } /* errlog */ IOCSH_STATIC_FUNC void errlog(const char *message) { errlogPrintfNoConsole("%s\n", message); } static const iocshArg errlogArg0 = { "message",iocshArgString}; static const iocshArg * const errlogArgs[1] = {&errlogArg0}; static const iocshFuncDef errlogFuncDef = {"errlog",1,errlogArgs}; static void errlogCallFunc(const iocshArgBuf *args) { errlog(args[0].sval); errlogFlush(); } /* iocLogPrefix */ static const iocshArg iocLogPrefixArg0 = { "prefix",iocshArgString}; static const iocshArg * const iocLogPrefixArgs[1] = {&iocLogPrefixArg0}; static const iocshFuncDef iocLogPrefixFuncDef = {"iocLogPrefix",1,iocLogPrefixArgs}; static void iocLogPrefixCallFunc(const iocshArgBuf *args) { iocLogPrefix(args[0].sval); } /* epicsThreadShowAll */ static const iocshArg epicsThreadShowAllArg0 = { "level",iocshArgInt}; static const iocshArg * const epicsThreadShowAllArgs[1] = {&epicsThreadShowAllArg0}; static const iocshFuncDef epicsThreadShowAllFuncDef = {"epicsThreadShowAll",1,epicsThreadShowAllArgs}; static void epicsThreadShowAllCallFunc(const iocshArgBuf *args) { epicsThreadShowAll(args[0].ival); } /* epicsThreadShow */ static const iocshArg threadArg0 = { "[-level] [thread ...]", iocshArgArgv}; static const iocshArg * const threadArgs[1] = { &threadArg0 }; static const iocshFuncDef threadFuncDef = {"epicsThreadShow",1,threadArgs}; static void threadCallFunc(const iocshArgBuf *args) { int i = 1; int first = 1; int level = 0; const char *cp; epicsThreadId tid; unsigned long ltmp; int argc = args[0].aval.ac; char **argv = args[0].aval.av; char *endp; if ((i < argc) && (*(cp = argv[i]) == '-')) { level = atoi (cp + 1); i++; } if (i >= argc) { epicsThreadShowAll (level); return; } for ( ; i < argc ; i++) { cp = argv[i]; ltmp = strtoul (cp, &endp, 0); if (*endp) { tid = epicsThreadGetId (cp); if (!tid) { fprintf(stderr, "\t'%s' is not a known thread name\n", cp); continue; } } else { tid = (epicsThreadId)ltmp; } if (first) { epicsThreadShow (0, level); first = 0; } epicsThreadShow (tid, level); } } /* taskwdShow */ static const iocshArg taskwdShowArg0 = { "level",iocshArgInt}; static const iocshArg * const taskwdShowArgs[1] = {&taskwdShowArg0}; static const iocshFuncDef taskwdShowFuncDef = {"taskwdShow",1,taskwdShowArgs}; static void taskwdShowCallFunc(const iocshArgBuf *args) { taskwdShow(args[0].ival); } /* epicsMutexShowAll */ static const iocshArg epicsMutexShowAllArg0 = { "onlyLocked",iocshArgInt}; static const iocshArg epicsMutexShowAllArg1 = { "level",iocshArgInt}; static const iocshArg * const epicsMutexShowAllArgs[2] = {&epicsMutexShowAllArg0,&epicsMutexShowAllArg1}; static const iocshFuncDef epicsMutexShowAllFuncDef = {"epicsMutexShowAll",2,epicsMutexShowAllArgs}; static void epicsMutexShowAllCallFunc(const iocshArgBuf *args) { epicsMutexShowAll(args[0].ival,args[1].ival); } /* epicsThreadSleep */ static const iocshArg epicsThreadSleepArg0 = { "seconds",iocshArgDouble}; static const iocshArg * const epicsThreadSleepArgs[1] = {&epicsThreadSleepArg0}; static const iocshFuncDef epicsThreadSleepFuncDef = {"epicsThreadSleep",1,epicsThreadSleepArgs}; static void epicsThreadSleepCallFunc(const iocshArgBuf *args) { epicsThreadSleep(args[0].dval); } /* epicsThreadResume */ static const iocshArg epicsThreadResumeArg0 = { "[thread ...]", iocshArgArgv}; static const iocshArg * const epicsThreadResumeArgs[1] = { &epicsThreadResumeArg0 }; static const iocshFuncDef epicsThreadResumeFuncDef = {"epicsThreadResume",1,epicsThreadResumeArgs}; static void epicsThreadResumeCallFunc(const iocshArgBuf *args) { int i; const char *cp; epicsThreadId tid; unsigned long ltmp; char nameBuf[64]; int argc = args[0].aval.ac; char **argv = args[0].aval.av; char *endp; for (i = 1 ; i < argc ; i++) { cp = argv[i]; ltmp = strtoul(cp, &endp, 0); if (*endp) { tid = epicsThreadGetId(cp); if (!tid) { fprintf(stderr, "'%s' is not a valid thread name\n", cp); continue; } } else { tid =(epicsThreadId)ltmp; epicsThreadGetName(tid, nameBuf, sizeof nameBuf); if (nameBuf[0] == '\0') { fprintf(stderr, "'%s' is not a valid thread id\n", cp); continue; } } if (!epicsThreadIsSuspended(tid)) { fprintf(stderr, "Thread %s is not suspended\n", cp); continue; } epicsThreadResume(tid); } } /* generalTimeReport */ static const iocshArg generalTimeReportArg0 = { "interest_level", iocshArgArgv}; static const iocshArg * const generalTimeReportArgs[1] = { &generalTimeReportArg0 }; static const iocshFuncDef generalTimeReportFuncDef = {"generalTimeReport",1,generalTimeReportArgs}; static void generalTimeReportCallFunc(const iocshArgBuf *args) { generalTimeReport(args[0].ival); } /* installLastResortEventProvider */ static const iocshFuncDef installLastResortEventProviderFuncDef = {"installLastResortEventProvider", 0, NULL}; static void installLastResortEventProviderCallFunc(const iocshArgBuf *args) { installLastResortEventProvider(); } static iocshVarDef asCheckClientIPDef[] = { { "asCheckClientIP", iocshArgInt, 0 }, { NULL, iocshArgInt, NULL } }; void epicsShareAPI libComRegister(void) { iocshRegister(&dateFuncDef, dateCallFunc); iocshRegister(&echoFuncDef, echoCallFunc); iocshRegister(&chdirFuncDef, chdirCallFunc); iocshRegister(&pwdFuncDef, pwdCallFunc); iocshRegister(&epicsEnvSetFuncDef, epicsEnvSetCallFunc); iocshRegister(&epicsEnvUnsetFuncDef, epicsEnvUnsetCallFunc); iocshRegister(&epicsParamShowFuncDef, epicsParamShowCallFunc); iocshRegister(&epicsPrtEnvParamsFuncDef, epicsPrtEnvParamsCallFunc); iocshRegister(&epicsEnvShowFuncDef, epicsEnvShowCallFunc); iocshRegister(®istryDumpFuncDef, registryDumpCallFunc); iocshRegister(&iocLogInitFuncDef, iocLogInitCallFunc); iocshRegister(&iocLogDisableFuncDef, iocLogDisableCallFunc); iocshRegister(&iocLogShowFuncDef, iocLogShowCallFunc); iocshRegister(&eltcFuncDef, eltcCallFunc); iocshRegister(&errlogInitFuncDef,errlogInitCallFunc); iocshRegister(&errlogInit2FuncDef,errlogInit2CallFunc); iocshRegister(&errlogFuncDef, errlogCallFunc); iocshRegister(&iocLogPrefixFuncDef, iocLogPrefixCallFunc); iocshRegister(&epicsThreadShowAllFuncDef,epicsThreadShowAllCallFunc); iocshRegister(&threadFuncDef, threadCallFunc); iocshRegister(&taskwdShowFuncDef,taskwdShowCallFunc); iocshRegister(&epicsMutexShowAllFuncDef,epicsMutexShowAllCallFunc); iocshRegister(&epicsThreadSleepFuncDef,epicsThreadSleepCallFunc); iocshRegister(&epicsThreadResumeFuncDef,epicsThreadResumeCallFunc); iocshRegister(&generalTimeReportFuncDef,generalTimeReportCallFunc); iocshRegister(&installLastResortEventProviderFuncDef, installLastResortEventProviderCallFunc); asCheckClientIPDef[0].pval = &asCheckClientIP; iocshRegisterVariable(asCheckClientIPDef); } base-7.0.3.1/modules/libcom/src/iocsh/libComRegister.h0000664000577000060420000000115613557101274021312 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_libComRegister_H #define INC_libComRegister_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void epicsShareAPI libComRegister(void); #ifdef __cplusplus } #endif #endif /* INC_libComRegister_H */ base-7.0.3.1/modules/libcom/src/iocsh/registry.c0000664000577000060420000000456413557101274020251 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /*registry.c */ /* Author: Marty Kraimer Date: 08JUN99 */ #include #include #include #include #define epicsExportSharedSymbols #include "dbDefs.h" #include "cantProceed.h" #include "epicsFindSymbol.h" #include "gpHash.h" #include "registry.h" static struct gphPvt *gphPvt = 0; static void registryInit(int tableSize) { if(tableSize==0) tableSize = DEFAULT_TABLE_SIZE; gphInitPvt(&gphPvt,tableSize); if(!gphPvt) cantProceed("registry why did gphInitPvt fail\n"); } epicsShareFunc int epicsShareAPI registrySetTableSize(int size) { if(gphPvt) { printf("registryInit already called\n"); return(-1); } registryInit(size); return(0); } epicsShareFunc int epicsShareAPI registryAdd( void *registryID,const char *name,void *data) { GPHENTRY *pentry; if(!gphPvt) registryInit(0); pentry = gphAdd(gphPvt,name,registryID); if(!pentry) return(FALSE); pentry->userPvt = data; return(TRUE); } epicsShareFunc int epicsShareAPI registryChange( void *registryID,const char *name,void *data) { GPHENTRY *pentry; if(!gphPvt) registryInit(0); pentry = gphFind(gphPvt,(char *)name,registryID); if(!pentry) return(FALSE); pentry->userPvt = data; return(TRUE); } epicsShareFunc void * epicsShareAPI registryFind( void *registryID,const char *name) { GPHENTRY *pentry; if(name==0) return(0); if(registryID==0) return(epicsFindSymbol(name)); if(!gphPvt) registryInit(0); pentry = gphFind(gphPvt,(char *)name,registryID); if(!pentry) return(0); return(pentry->userPvt); } epicsShareFunc void epicsShareAPI registryFree(void) { if(!gphPvt) return; gphFreeMem(gphPvt); gphPvt = 0; } epicsShareFunc int epicsShareAPI registryDump(void) { if(!gphPvt) return(0); gphDump(gphPvt); return(0); } base-7.0.3.1/modules/libcom/src/iocsh/registry.h0000664000577000060420000000224213557101274020245 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #ifndef INCregistryh #define INCregistryh #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif #define DEFAULT_TABLE_SIZE 1024 epicsShareFunc int epicsShareAPI registryAdd( void *registryID,const char *name,void *data); epicsShareFunc void *epicsShareAPI registryFind( void *registryID,const char *name); epicsShareFunc int epicsShareAPI registryChange( void *registryID,const char *name,void *data); epicsShareFunc int epicsShareAPI registrySetTableSize(int size); epicsShareFunc void epicsShareAPI registryFree(void); epicsShareFunc int epicsShareAPI registryDump(void); #ifdef __cplusplus } #endif #endif /* INCregistryh */ base-7.0.3.1/modules/libcom/src/libComVersion.h0000664000577000060420000000166013557101274020046 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef LIBCOMVERSION_H #define LIBCOMVERSION_H #include #include #ifndef VERSION_INT # define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P)) #endif /* include generated headers with: * EPICS_LIBCOM_MAJOR_VERSION * EPICS_LIBCOM_MINOR_VERSION * EPICS_LIBCOM_MAINTENANCE_VERSION * EPICS_LIBCOM_DEVELOPMENT_FLAG */ #include "libComVersionNum.h" #define LIBCOM_VERSION_INT VERSION_INT(EPICS_LIBCOM_MAJOR_VERSION, EPICS_LIBCOM_MINOR_VERSION, EPICS_LIBCOM_MAINTENANCE_VERSION, 0) #endif // LIBCOMVERSION_H base-7.0.3.1/modules/libcom/src/libComVersionNum.h@0000664000577000060420000000054313557101274020625 0ustar anjaesctl#ifndef LIBCOMVERSION_H # error include libComVersion.h, not this header #endif #define EPICS_LIBCOM_MAJOR_VERSION @EPICS_LIBCOM_MAJOR_VERSION@ #define EPICS_LIBCOM_MINOR_VERSION @EPICS_LIBCOM_MINOR_VERSION@ #define EPICS_LIBCOM_MAINTENANCE_VERSION @EPICS_LIBCOM_MAINTENANCE_VERSION@ #define EPICS_LIBCOM_DEVELOPMENT_FLAG @EPICS_LIBCOM_DEVELOPMENT_FLAG@ base-7.0.3.1/modules/libcom/src/log/Makefile0000664000577000060420000000147713557101274017351 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/log INC += iocLog.h INC += logClient.h Com_SRCS += iocLog.c Com_SRCS += logClient.c PROD_HOST += iocLogServer iocLogServer_SRCS = iocLogServer.c iocLogServer_LIBS = Com iocLogServer_SYS_LIBS_solaris += socket iocLogServer_SYS_LIBS_WIN32 += user32 ws2_32 dbghelp SCRIPTS_HOST = S99logServer EXPAND += S99logServer@ EXPAND_VARS = INSTALL_BIN=$(abspath $(INSTALL_BIN)) base-7.0.3.1/modules/libcom/src/log/S99logServer@0000664000577000060420000000144413557101274020203 0ustar anjaesctl#!/bin/sh # # System-V init script for the EPICS IOC Log Server. # INSTALL_BIN=@INSTALL_BIN@ # To change the default values for the EPICS Environment parameters, # uncomment and modify the relevant lines below. # EPICS_IOC_LOG_PORT="6500" export EPICS_IOC_LOG_PORT # EPICS_IOC_LOG_FILE_NAME="/path/to/iocLog" export EPICS_IOC_LOG_FILE_NAME # EPICS_IOC_LOG_FILE_LIMIT="1000000" export EPICS_IOC_LOG_FILE_LIMIT if [ $1 = "start" ]; then if [ -x $INSTALL_BIN/iocLogServer ]; then echo "Starting EPICS Log Server " $INSTALL_BIN/iocLogServer & fi else if [ $1 = "stop" ]; then pid=`ps -e | sed -ne '/iocLogSe/s/^ *\([1-9][0-9]*\).*$/\1/p'` if [ "${pid}" != "" ]; then echo "Stopping EPICS Log Server " kill ${pid} fi fi fi base-7.0.3.1/modules/libcom/src/log/iocLog.c0000664000577000060420000000700713557101274017264 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* logClient.c,v 1.25.2.6 2004/10/07 13:37:34 mrk Exp */ /* * Author: Jeffrey O. Hill * Date: 080791 */ #include #include #define epicsExportSharedSymbols #include "envDefs.h" #include "errlog.h" #include "logClient.h" #include "iocLog.h" #include "epicsExit.h" int iocLogDisable = 0; static const int iocLogSuccess = 0; static const int iocLogError = -1; static logClientId iocLogClient; /* * getConfig() * Get Server Configuration */ static int getConfig (struct in_addr *pserver_addr, unsigned short *pserver_port) { long status; long epics_port; status = envGetLongConfigParam (&EPICS_IOC_LOG_PORT, &epics_port); if (status<0) { fprintf (stderr, "iocLog: EPICS environment variable \"%s\" undefined\n", EPICS_IOC_LOG_PORT.name); return iocLogError; } if (epics_port<0 || epics_port>USHRT_MAX) { fprintf (stderr, "iocLog: EPICS environment variable \"%s\" out of range\n", EPICS_IOC_LOG_PORT.name); return iocLogError; } *pserver_port = (unsigned short) epics_port; status = envGetInetAddrConfigParam (&EPICS_IOC_LOG_INET, pserver_addr); if (status<0) { fprintf (stderr, "iocLog: EPICS environment variable \"%s\" undefined\n", EPICS_IOC_LOG_INET.name); return iocLogError; } return iocLogSuccess; } /* * iocLogFlush () */ void epicsShareAPI epicsShareAPI iocLogFlush (void) { if (iocLogClient!=NULL) { logClientFlush (iocLogClient); } } /* * logClientSendMessage () */ static void logClientSendMessage ( logClientId id, const char * message ) { if ( !iocLogDisable ) { logClientSend (id, message); } } /* * iocLogClientDestroy() */ static void iocLogClientDestroy (logClientId id) { errlogRemoveListeners (logClientSendMessage, id); } /* * iocLogClientInit() */ static logClientId iocLogClientInit (void) { int status; logClientId id; struct in_addr addr; unsigned short port; status = getConfig (&addr, &port); if (status) { return NULL; } id = logClientCreate (addr, port); if (id != NULL) { errlogAddListener (logClientSendMessage, id); epicsAtExit (iocLogClientDestroy, id); } return id; } /* * iocLogInit() */ int epicsShareAPI iocLogInit (void) { /* * check for global disable */ if (iocLogDisable) { return iocLogSuccess; } /* * dont init twice */ if (iocLogClient!=NULL) { return iocLogSuccess; } iocLogClient = iocLogClientInit (); if (iocLogClient) { return iocLogSuccess; } else { return iocLogError; } } /* * iocLogShow () */ void epicsShareAPI iocLogShow (unsigned level) { if (iocLogClient!=NULL) { logClientShow (iocLogClient, level); } } /* * logClientInit(); deprecated */ logClientId epicsShareAPI logClientInit (void) { return iocLogClientInit (); } base-7.0.3.1/modules/libcom/src/log/iocLog.h0000664000577000060420000000205713557101274017271 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* logClient.h,v 1.5.2.1 2003/07/08 00:08:06 jhill Exp */ /* * * Author: Jeffrey O. Hill * Date: 080791 */ #ifndef INCiocLogh #define INCiocLogh 1 #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /* * ioc log client interface */ epicsShareExtern int iocLogDisable; epicsShareFunc int epicsShareAPI iocLogInit (void); epicsShareFunc void epicsShareAPI iocLogShow (unsigned level); epicsShareFunc void epicsShareAPI iocLogFlush (void); #ifdef __cplusplus } #endif #endif /*INCiocLogh*/ base-7.0.3.1/modules/libcom/src/log/iocLogServer.c0000664000577000060420000005660313557101274020461 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* iocLogServer.c */ /* * archive logMsg() from several IOC's to a common rotating file * * * Author: Jeffrey O. Hill * Date: 080791 */ #include #include #include #include #include #include #ifdef UNIX #include #include #endif #include "dbDefs.h" #include "epicsAssert.h" #include "fdmgr.h" #include "envDefs.h" #include "osiSock.h" #include "epicsStdio.h" static unsigned short ioc_log_port; static long ioc_log_file_limit; static char ioc_log_file_name[512]; static char ioc_log_file_command[256]; struct iocLogClient { int insock; struct ioc_log_server *pserver; size_t nChar; char recvbuf[1024]; char name[32]; char ascii_time[32]; }; struct ioc_log_server { char outfile[256]; long filePos; FILE *poutfile; void *pfdctx; SOCKET sock; long max_file_size; }; #define IOCLS_ERROR (-1) #define IOCLS_OK 0 static void acceptNewClient (void *pParam); static void readFromClient(void *pParam); static void logTime (struct iocLogClient *pclient); static int getConfig(void); static int openLogFile(struct ioc_log_server *pserver); static void handleLogFileError(void); static void envFailureNotify(const ENV_PARAM *pparam); static void freeLogClient(struct iocLogClient *pclient); static void writeMessagesToLog (struct iocLogClient *pclient); #ifdef UNIX static int setupSIGHUP(struct ioc_log_server *); static void sighupHandler(int); static void serviceSighupRequest(void *pParam); static int getDirectory(void); static int sighupPipe[2]; #endif /* * * main() * */ int main(void) { struct sockaddr_in serverAddr; /* server's address */ struct timeval timeout; int status; struct ioc_log_server *pserver; osiSockIoctl_t optval; status = getConfig(); if (status<0) { fprintf(stderr, "iocLogServer: EPICS environment underspecified\n"); fprintf(stderr, "iocLogServer: failed to initialize\n"); return IOCLS_ERROR; } pserver = (struct ioc_log_server *) calloc(1, sizeof *pserver); if (!pserver) { fprintf(stderr, "iocLogServer: %s\n", strerror(errno)); return IOCLS_ERROR; } pserver->pfdctx = (void *) fdmgr_init(); if (!pserver->pfdctx) { fprintf(stderr, "iocLogServer: %s\n", strerror(errno)); return IOCLS_ERROR; } /* * Open the socket. Use ARPA Internet address format and stream * sockets. Format described in . */ pserver->sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); if (pserver->sock == INVALID_SOCKET) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf(stderr, "iocLogServer: sock create err: %s\n", sockErrBuf); free(pserver); return IOCLS_ERROR; } epicsSocketEnableAddressReuseDuringTimeWaitState ( pserver->sock ); /* Zero the sock_addr structure */ memset((void *)&serverAddr, 0, sizeof serverAddr); serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(ioc_log_port); /* get server's Internet address */ status = bind ( pserver->sock, (struct sockaddr *)&serverAddr, sizeof (serverAddr) ); if (status < 0) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf(stderr, "iocLogServer: bind err: %s\n", sockErrBuf ); fprintf (stderr, "iocLogServer: a server is already installed on port %u?\n", (unsigned)ioc_log_port); return IOCLS_ERROR; } /* listen and accept new connections */ status = listen(pserver->sock, 10); if (status < 0) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf(stderr, "iocLogServer: listen err %s\n", sockErrBuf); return IOCLS_ERROR; } /* * Set non blocking IO * to prevent dead locks */ optval = TRUE; status = socket_ioctl( pserver->sock, FIONBIO, &optval); if (status < 0){ char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf(stderr, "iocLogServer: ioctl FIONBIO err %s\n", sockErrBuf); return IOCLS_ERROR; } # ifdef UNIX status = setupSIGHUP(pserver); if (status < 0) { return IOCLS_ERROR; } # endif status = openLogFile(pserver); if (status < 0) { fprintf(stderr, "File access problems to `%s' because `%s'\n", ioc_log_file_name, strerror(errno)); return IOCLS_ERROR; } status = fdmgr_add_callback( pserver->pfdctx, pserver->sock, fdi_read, acceptNewClient, pserver); if (status < 0) { fprintf(stderr, "iocLogServer: failed to add read callback\n"); return IOCLS_ERROR; } while (TRUE) { timeout.tv_sec = 60; /* 1 min */ timeout.tv_usec = 0; fdmgr_pend_event(pserver->pfdctx, &timeout); fflush(pserver->poutfile); } } /* * seekLatestLine (struct ioc_log_server *pserver) */ static int seekLatestLine (struct ioc_log_server *pserver) { static const time_t invalidTime = (time_t) -1; time_t theLatestTime = invalidTime; long latestFilePos = -1; int status; /* * start at the beginning */ rewind (pserver->poutfile); while (1) { struct tm theDate; int convertStatus; char month[16]; /* * find the line in the file with the latest date * * this assumes ctime() produces dates of the form: * DayName MonthName dayNum 24hourHourNum:minNum:secNum yearNum */ convertStatus = fscanf ( pserver->poutfile, " %*s %*s %15s %d %d:%d:%d %d %*[^\n] ", month, &theDate.tm_mday, &theDate.tm_hour, &theDate.tm_min, &theDate.tm_sec, &theDate.tm_year); if (convertStatus==6) { static const char *pMonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; static const unsigned nMonths = sizeof(pMonths)/sizeof(pMonths[0]); time_t lineTime = (time_t) -1; unsigned iMonth; for (iMonth=0; iMonthtm_epoch_year) { theDate.tm_year -= tm_epoch_year; theDate.tm_isdst = -1; /* dont know */ lineTime = mktime (&theDate); if ( lineTime != invalidTime ) { if (theLatestTime == invalidTime || difftime(lineTime, theLatestTime)>=0) { latestFilePos = ftell (pserver->poutfile); theLatestTime = lineTime; } } else { char date[128]; size_t nChar; nChar = strftime (date, sizeof(date), "%a %b %d %H:%M:%S %Y\n", &theDate); if (nChar>0) { fprintf (stderr, "iocLogServer: strange date in log file: %s\n", date); } else { fprintf (stderr, "iocLogServer: strange date in log file\n"); } } } else { fprintf (stderr, "iocLogServer: strange year in log file: %d\n", theDate.tm_year); } } else { fprintf (stderr, "iocLogServer: strange month in log file: %s\n", month); } } else { int c = fgetc (pserver->poutfile); /* * bypass the line if it does not match the expected format */ while ( c!=EOF && c!='\n' ) { c = fgetc (pserver->poutfile); } if (c==EOF) { break; } } } /* * move to the proper location in the file */ if (latestFilePos != -1) { status = fseek (pserver->poutfile, latestFilePos, SEEK_SET); if (status!=IOCLS_OK) { fclose (pserver->poutfile); pserver->poutfile = stderr; return IOCLS_ERROR; } } else { status = fseek (pserver->poutfile, 0L, SEEK_END); if (status!=IOCLS_OK) { fclose (pserver->poutfile); pserver->poutfile = stderr; return IOCLS_ERROR; } } pserver->filePos = ftell (pserver->poutfile); if (theLatestTime==invalidTime) { if (pserver->filePos!=0) { fprintf (stderr, "iocLogServer: **** Warning ****\n"); fprintf (stderr, "iocLogServer: no recognizable dates in \"%s\"\n", ioc_log_file_name); fprintf (stderr, "iocLogServer: logging at end of file\n"); } } return IOCLS_OK; } /* * openLogFile() * */ static int openLogFile (struct ioc_log_server *pserver) { enum TF_RETURN ret; if (pserver->poutfile && pserver->poutfile != stderr){ fclose (pserver->poutfile); pserver->poutfile = NULL; } pserver->poutfile = fopen(ioc_log_file_name, "r+"); if (pserver->poutfile) { fclose (pserver->poutfile); pserver->poutfile = NULL; ret = truncateFile (ioc_log_file_name, ioc_log_file_limit); if (ret==TF_ERROR) { return IOCLS_ERROR; } pserver->poutfile = fopen(ioc_log_file_name, "r+"); } else { pserver->poutfile = fopen(ioc_log_file_name, "w"); } if (!pserver->poutfile) { pserver->poutfile = stderr; return IOCLS_ERROR; } strcpy (pserver->outfile, ioc_log_file_name); pserver->max_file_size = ioc_log_file_limit; return seekLatestLine (pserver); } /* * handleLogFileError() * */ static void handleLogFileError(void) { fprintf(stderr, "iocLogServer: log file access problem (errno=%s)\n", strerror(errno)); exit(IOCLS_ERROR); } /* * acceptNewClient() * */ static void acceptNewClient ( void *pParam ) { struct ioc_log_server *pserver = (struct ioc_log_server *) pParam; struct iocLogClient *pclient; osiSocklen_t addrSize; struct sockaddr_in addr; int status; osiSockIoctl_t optval; pclient = ( struct iocLogClient * ) malloc ( sizeof ( *pclient ) ); if ( ! pclient ) { return; } addrSize = sizeof ( addr ); pclient->insock = epicsSocketAccept ( pserver->sock, (struct sockaddr *)&addr, &addrSize ); if ( pclient->insock==INVALID_SOCKET || addrSize < sizeof (addr) ) { static unsigned acceptErrCount; static int lastErrno; int thisErrno; free ( pclient ); if ( SOCKERRNO == SOCK_EWOULDBLOCK || SOCKERRNO == SOCK_EINTR ) { return; } thisErrno = SOCKERRNO; if ( acceptErrCount % 1000 || lastErrno != thisErrno ) { fprintf ( stderr, "Accept Error %d\n", SOCKERRNO ); } acceptErrCount++; lastErrno = thisErrno; return; } /* * Set non blocking IO * to prevent dead locks */ optval = TRUE; status = socket_ioctl( pclient->insock, FIONBIO, &optval); if(status<0){ char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf(stderr, "%s:%d ioctl FBIO client er %s\n", __FILE__, __LINE__, sockErrBuf); epicsSocketDestroy ( pclient->insock ); free(pclient); return; } pclient->pserver = pserver; pclient->nChar = 0u; ipAddrToA (&addr, pclient->name, sizeof(pclient->name)); logTime(pclient); #if 0 status = fprintf( pclient->pserver->poutfile, "%s %s ----- Client Connect -----\n", pclient->name, pclient->ascii_time); if(status<0){ handleLogFileError(); } #endif /* * turn on KEEPALIVE so if the client crashes * this task will find out and exit */ { long true = 1; status = setsockopt( pclient->insock, SOL_SOCKET, SO_KEEPALIVE, (char *)&true, sizeof(true) ); if(status<0){ fprintf(stderr, "Keepalive option set failed\n"); } } status = shutdown(pclient->insock, SHUT_WR); if(status<0){ char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf (stderr, "%s:%d shutdown err %s\n", __FILE__, __LINE__, sockErrBuf); epicsSocketDestroy ( pclient->insock ); free(pclient); return; } status = fdmgr_add_callback( pserver->pfdctx, pclient->insock, fdi_read, readFromClient, pclient); if (status<0) { epicsSocketDestroy ( pclient->insock ); free(pclient); fprintf(stderr, "%s:%d client fdmgr_add_callback() failed\n", __FILE__, __LINE__); return; } } /* * readFromClient() * */ #define NITEMS 1 static void readFromClient(void *pParam) { struct iocLogClient *pclient = (struct iocLogClient *)pParam; int recvLength; int size; logTime(pclient); size = (int) (sizeof(pclient->recvbuf) - pclient->nChar); recvLength = recv(pclient->insock, &pclient->recvbuf[pclient->nChar], size, 0); if (recvLength <= 0) { if (recvLength<0) { int errnoCpy = SOCKERRNO; if (errnoCpy==SOCK_EWOULDBLOCK || errnoCpy==SOCK_EINTR) { return; } if (errnoCpy != SOCK_ECONNRESET && errnoCpy != SOCK_ECONNABORTED && errnoCpy != SOCK_EPIPE && errnoCpy != SOCK_ETIMEDOUT ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf(stderr, "%s:%d socket=%d size=%d read error=%s\n", __FILE__, __LINE__, pclient->insock, size, sockErrBuf); } } /* * disconnect */ freeLogClient (pclient); return; } pclient->nChar += (size_t) recvLength; writeMessagesToLog (pclient); } /* * writeMessagesToLog() */ static void writeMessagesToLog (struct iocLogClient *pclient) { int status; size_t lineIndex = 0; while (TRUE) { size_t nchar; size_t nTotChar; size_t crIndex; int ntci; if ( lineIndex >= pclient->nChar ) { pclient->nChar = 0u; break; } /* * find the first carrage return and create * an entry in the log for the message associated * with it. If a carrage return does not exist and * the buffer isnt full then move the partial message * to the front of the buffer and wait for a carrage * return to arrive. If the buffer is full and there * is no carrage return then force the message out and * insert an artificial carrage return. */ nchar = pclient->nChar - lineIndex; for ( crIndex = lineIndex; crIndex < pclient->nChar; crIndex++ ) { if ( pclient->recvbuf[crIndex] == '\n' ) { break; } } if ( crIndex < pclient->nChar ) { nchar = crIndex - lineIndex; } else { nchar = pclient->nChar - lineIndex; if ( nchar < sizeof ( pclient->recvbuf ) ) { if ( lineIndex != 0 ) { pclient->nChar = nchar; memmove ( pclient->recvbuf, & pclient->recvbuf[lineIndex], nchar); } break; } } /* * reset the file pointer if we hit the end of the file */ nTotChar = strlen(pclient->name) + strlen(pclient->ascii_time) + nchar + 3u; assert (nTotChar <= INT_MAX); ntci = (int) nTotChar; if ( pclient->pserver->max_file_size && pclient->pserver->filePos+ntci >= pclient->pserver->max_file_size ) { if ( pclient->pserver->max_file_size >= pclient->pserver->filePos ) { unsigned nPadChar; /* * this gets rid of leftover junk at the end of the file */ nPadChar = pclient->pserver->max_file_size - pclient->pserver->filePos; while (nPadChar--) { status = putc ( ' ', pclient->pserver->poutfile ); if ( status == EOF ) { handleLogFileError(); } } } # ifdef DEBUG fprintf ( stderr, "ioc log server: resetting the file pointer\n" ); # endif fflush ( pclient->pserver->poutfile ); rewind ( pclient->pserver->poutfile ); pclient->pserver->filePos = ftell ( pclient->pserver->poutfile ); } /* * NOTE: !! change format string here then must * change nTotChar calc above !! */ assert (ncharpserver->poutfile, "%s %s %.*s\n", pclient->name, pclient->ascii_time, (int) nchar, &pclient->recvbuf[lineIndex]); if (status<0) { handleLogFileError(); } else { if (status != ntci) { fprintf(stderr, "iocLogServer: didnt calculate number of characters correctly?\n"); } pclient->pserver->filePos += status; } lineIndex += nchar+1u; } } /* * freeLogClient () */ static void freeLogClient(struct iocLogClient *pclient) { int status; # ifdef DEBUG if(length == 0){ fprintf(stderr, "iocLogServer: nil message disconnect\n"); } # endif /* * flush any left overs */ if (pclient->nChar) { /* * this forces a flush */ if (pclient->nCharrecvbuf)) { pclient->recvbuf[pclient->nChar] = '\n'; } writeMessagesToLog (pclient); } status = fdmgr_clear_callback( pclient->pserver->pfdctx, pclient->insock, fdi_read); if (status!=IOCLS_OK) { fprintf(stderr, "%s:%d fdmgr_clear_callback() failed\n", __FILE__, __LINE__); } epicsSocketDestroy ( pclient->insock ); free (pclient); return; } /* * * logTime() * */ static void logTime(struct iocLogClient *pclient) { time_t sec; char *pcr; char *pTimeString; sec = time (NULL); pTimeString = ctime (&sec); strncpy (pclient->ascii_time, pTimeString, sizeof (pclient->ascii_time) ); pclient->ascii_time[sizeof(pclient->ascii_time)-1] = '\0'; pcr = strchr(pclient->ascii_time, '\n'); if (pcr) { *pcr = '\0'; } } /* * * getConfig() * Get Server Configuration * * */ static int getConfig(void) { int status; char *pstring; long param; status = envGetLongConfigParam( &EPICS_IOC_LOG_PORT, ¶m); if(status>=0){ ioc_log_port = (unsigned short) param; } else { ioc_log_port = 7004U; } status = envGetLongConfigParam( &EPICS_IOC_LOG_FILE_LIMIT, &ioc_log_file_limit); if(status>=0){ if (ioc_log_file_limit < 0) { envFailureNotify (&EPICS_IOC_LOG_FILE_LIMIT); return IOCLS_ERROR; } } else { ioc_log_file_limit = 10000; } pstring = envGetConfigParam( &EPICS_IOC_LOG_FILE_NAME, sizeof ioc_log_file_name, ioc_log_file_name); if(pstring == NULL){ envFailureNotify(&EPICS_IOC_LOG_FILE_NAME); return IOCLS_ERROR; } /* * its ok to not specify the IOC_LOG_FILE_COMMAND */ pstring = envGetConfigParam( &EPICS_IOC_LOG_FILE_COMMAND, sizeof ioc_log_file_command, ioc_log_file_command); return IOCLS_OK; } /* * * failureNotify() * * */ static void envFailureNotify(const ENV_PARAM *pparam) { fprintf(stderr, "iocLogServer: EPICS environment variable `%s' undefined\n", pparam->name); } #ifdef UNIX static int setupSIGHUP(struct ioc_log_server *pserver) { int status; struct sigaction sigact; status = getDirectory(); if (status<0){ fprintf(stderr, "iocLogServer: failed to determine log file " "directory\n"); return IOCLS_ERROR; } /* * Set up SIGHUP handler. SIGHUP will cause the log file to be * closed and re-opened, possibly with a different name. */ sigact.sa_handler = sighupHandler; sigemptyset (&sigact.sa_mask); sigact.sa_flags = 0; if (sigaction(SIGHUP, &sigact, NULL)){ fprintf(stderr, "iocLogServer: %s\n", strerror(errno)); return IOCLS_ERROR; } status = pipe(sighupPipe); if(status<0){ fprintf(stderr, "iocLogServer: failed to create pipe because `%s'\n", strerror(errno)); return IOCLS_ERROR; } status = fdmgr_add_callback( pserver->pfdctx, sighupPipe[0], fdi_read, serviceSighupRequest, pserver); if(status<0){ fprintf(stderr, "iocLogServer: failed to add SIGHUP callback\n"); return IOCLS_ERROR; } return IOCLS_OK; } /* * * sighupHandler() * * */ static void sighupHandler(int signo) { const char msg[] = "SIGHUP\n"; const ssize_t bytesWritten = write(sighupPipe[1], msg, sizeof(msg)); if (bytesWritten != sizeof(msg)) { fprintf(stderr, "iocLogServer: failed to write to SIGHUP pipe because " "`%s'\n", strerror(errno)); } } /* * serviceSighupRequest() * */ static void serviceSighupRequest(void *pParam) { struct ioc_log_server *pserver = (struct ioc_log_server *)pParam; char buff[256]; int status; /* * Read and discard message from pipe. */ if (read(sighupPipe[0], buff, sizeof buff) <= 0) { fprintf(stderr, "iocLogServer: failed to read from SIGHUP pipe because " "`%s'\n", strerror(errno)); }; /* * Determine new log file name. */ status = getDirectory(); if (status<0){ fprintf(stderr, "iocLogServer: failed to determine new log " "file name\n"); return; } /* * Try (re)opening the file. */ status = openLogFile(pserver); if(status<0){ fprintf(stderr, "File access problems to `%s' because `%s'\n", ioc_log_file_name, strerror(errno)); /* Revert to old filename */ strcpy(ioc_log_file_name, pserver->outfile); status = openLogFile(pserver); if(status<0){ fprintf(stderr, "File access problems to `%s' because `%s'\n", ioc_log_file_name, strerror(errno)); return; } else { fprintf(stderr, "iocLogServer: re-opened old log file %s\n", ioc_log_file_name); } } else { fprintf(stderr, "iocLogServer: opened new log file %s\n", ioc_log_file_name); } } /* * * getDirectory() * * */ static int getDirectory(void) { FILE *pipe; char dir[256]; int i; if (ioc_log_file_command[0] != '\0') { /* * Use popen() to execute command and grab output. */ pipe = popen(ioc_log_file_command, "r"); if (pipe == NULL) { fprintf(stderr, "Problem executing `%s' because `%s'\n", ioc_log_file_command, strerror(errno)); return IOCLS_ERROR; } if (fgets(dir, sizeof(dir), pipe) == NULL) { fprintf(stderr, "Problem reading o/p from `%s' because `%s'\n", ioc_log_file_command, strerror(errno)); (void) pclose(pipe); return IOCLS_ERROR; } (void) pclose(pipe); /* * Terminate output at first newline and discard trailing * slash character if present.. */ for (i=0; dir[i] != '\n' && dir[i] != '\0'; i++) ; dir[i] = '\0'; i = strlen(dir); if (i > 1 && dir[i-1] == '/') dir[i-1] = '\0'; /* * Use output as directory part of file name. */ if (dir[0] != '\0') { char *name = ioc_log_file_name; char *slash = strrchr(ioc_log_file_name, '/'); char temp[256]; if (slash != NULL) name = slash + 1; strcpy(temp,name); sprintf(ioc_log_file_name,"%s/%s",dir,temp); } } return IOCLS_OK; } #endif base-7.0.3.1/modules/libcom/src/log/logClient.c0000664000577000060420000004037113557101274017771 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* logClient.c,v 1.25.2.6 2004/10/07 13:37:34 mrk Exp */ /* * Author: Jeffrey O. Hill * Date: 080791 */ /* * ANSI C */ #include #include #include #include #define EPICS_PRIVATE_API #define epicsExportSharedSymbols #include "dbDefs.h" #include "epicsEvent.h" #include "iocLog.h" #include "epicsMutex.h" #include "epicsThread.h" #include "epicsTime.h" #include "osiSock.h" #include "epicsAssert.h" #include "epicsExit.h" #include "epicsSignal.h" #include "epicsExport.h" #include "logClient.h" int logClientDebug = 0; epicsExportAddress (int, logClientDebug); typedef struct { char msgBuf[0x4000]; struct sockaddr_in addr; char name[64]; epicsMutexId mutex; SOCKET sock; epicsThreadId restartThreadId; epicsEventId stateChangeNotify; epicsEventId shutdownNotify; unsigned connectCount; unsigned nextMsgIndex; unsigned backlog; unsigned connected; unsigned shutdown; unsigned shutdownConfirm; int connFailStatus; } logClient; static const double LOG_RESTART_DELAY = 5.0; /* sec */ static const double LOG_SERVER_SHUTDOWN_TIMEOUT = 30.0; /* sec */ /* * If set using iocLogPrefix() this string is prepended to all log messages: */ static char* logClientPrefix = NULL; /* * logClientClose () */ static void logClientClose ( logClient *pClient ) { if (logClientDebug) { fprintf (stderr, "log client: lingering for connection close..."); fflush (stderr); } /* * mutex on */ epicsMutexMustLock (pClient->mutex); /* * close any preexisting connection to the log server */ if ( pClient->sock != INVALID_SOCKET ) { epicsSocketDestroy ( pClient->sock ); pClient->sock = INVALID_SOCKET; } pClient->connected = 0u; /* * mutex off */ epicsMutexUnlock (pClient->mutex); if (logClientDebug) fprintf (stderr, "done\n"); } /* * logClientDestroy */ static void logClientDestroy (logClientId id) { enum epicsSocketSystemCallInterruptMechanismQueryInfo interruptInfo; logClient *pClient = (logClient *) id; epicsTimeStamp begin, current; double diff; /* command log client thread to shutdown - taking mutex here */ /* forces cache flush on SMP machines */ epicsMutexMustLock ( pClient->mutex ); pClient->shutdown = 1u; epicsMutexUnlock ( pClient->mutex ); epicsEventSignal ( pClient->shutdownNotify ); /* unblock log client thread blocking in send() or connect() */ interruptInfo = epicsSocketSystemCallInterruptMechanismQuery (); switch ( interruptInfo ) { case esscimqi_socketCloseRequired: logClientClose ( pClient ); break; case esscimqi_socketBothShutdownRequired: shutdown ( pClient->sock, SHUT_WR ); break; case esscimqi_socketSigAlarmRequired: epicsSignalRaiseSigAlarm ( pClient->restartThreadId ); break; default: break; }; /* wait for confirmation that the thread exited - taking */ /* mutex here forces cache update on SMP machines */ epicsTimeGetCurrent ( & begin ); epicsMutexMustLock ( pClient->mutex ); do { epicsMutexUnlock ( pClient->mutex ); epicsEventWaitWithTimeout ( pClient->stateChangeNotify, LOG_SERVER_SHUTDOWN_TIMEOUT / 10.0 ); epicsTimeGetCurrent ( & current ); diff = epicsTimeDiffInSeconds ( & current, & begin ); epicsMutexMustLock ( pClient->mutex ); } while ( ! pClient->shutdownConfirm && diff < LOG_SERVER_SHUTDOWN_TIMEOUT ); epicsMutexUnlock ( pClient->mutex ); if ( ! pClient->shutdownConfirm ) { fprintf ( stderr, "log client shutdown: timed out stopping" " reconnect thread for \"%s\" after %.1f seconds - cleanup aborted\n", pClient->name, LOG_SERVER_SHUTDOWN_TIMEOUT ); return; } logClientClose ( pClient ); epicsMutexDestroy ( pClient->mutex ); epicsEventDestroy ( pClient->stateChangeNotify ); epicsEventDestroy ( pClient->shutdownNotify ); free ( pClient ); } /* * This method requires the pClient->mutex be owned already. */ static void sendMessageChunk(logClient * pClient, const char * message) { unsigned strSize; strSize = strlen ( message ); while ( strSize ) { unsigned msgBufBytesLeft = sizeof ( pClient->msgBuf ) - pClient->nextMsgIndex; if ( msgBufBytesLeft < strSize && pClient->nextMsgIndex != 0u && pClient->connected) { /* buffer is full, thus flush it */ logClientFlush ( pClient ); msgBufBytesLeft = sizeof ( pClient->msgBuf ) - pClient->nextMsgIndex; } if ( msgBufBytesLeft == 0u ) { fprintf ( stderr, "log client: messages to \"%s\" are lost\n", pClient->name ); break; } if ( msgBufBytesLeft > strSize) msgBufBytesLeft = strSize; memcpy ( & pClient->msgBuf[pClient->nextMsgIndex], message, msgBufBytesLeft ); pClient->nextMsgIndex += msgBufBytesLeft; strSize -= msgBufBytesLeft; message += msgBufBytesLeft; } } /* * logClientSend () */ void epicsShareAPI logClientSend ( logClientId id, const char * message ) { logClient * pClient = ( logClient * ) id; if ( ! pClient || ! message ) { return; } epicsMutexMustLock ( pClient->mutex ); if (logClientPrefix) { sendMessageChunk(pClient, logClientPrefix); } sendMessageChunk(pClient, message); epicsMutexUnlock (pClient->mutex); } void epicsShareAPI logClientFlush ( logClientId id ) { unsigned nSent; int status = 0; logClient * pClient = ( logClient * ) id; if ( ! pClient || ! pClient->connected ) { return; } epicsMutexMustLock ( pClient->mutex ); nSent = pClient->backlog; while ( nSent < pClient->nextMsgIndex && pClient->connected ) { status = send ( pClient->sock, pClient->msgBuf + nSent, pClient->nextMsgIndex - nSent, 0 ); if ( status < 0 ) break; nSent += status; } if ( pClient->backlog > 0 && status >= 0 ) { /* On Linux send 0 bytes can detect EPIPE */ /* NOOP on Windows, fails on vxWorks */ errno = 0; status = send ( pClient->sock, NULL, 0, 0 ); if (!(errno == ECONNRESET || errno == EPIPE)) status = 0; } if ( status < 0 ) { if ( ! pClient->shutdown ) { char sockErrBuf[128]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf ( stderr, "log client: lost contact with log server at \"%s\" because \"%s\"\n", pClient->name, sockErrBuf ); } pClient->backlog = 0; logClientClose ( pClient ); } else if ( nSent > 0 && pClient->nextMsgIndex > 0 ) { int backlog = epicsSocketUnsentCount ( pClient->sock ); if (backlog >= 0) { pClient->backlog = backlog; nSent -= backlog; } pClient->nextMsgIndex -= nSent; if ( nSent > 0 && pClient->nextMsgIndex > 0 ) { memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], pClient->nextMsgIndex ); } } epicsMutexUnlock ( pClient->mutex ); } /* * logClientMakeSock () */ static void logClientMakeSock (logClient *pClient) { if (logClientDebug) { fprintf (stderr, "log client: creating socket..."); fflush (stderr); } epicsMutexMustLock (pClient->mutex); /* * allocate a socket */ pClient->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, 0 ); if ( pClient->sock == INVALID_SOCKET ) { char sockErrBuf[128]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf ( stderr, "log client: no socket error %s\n", sockErrBuf ); } epicsMutexUnlock (pClient->mutex); if (logClientDebug) fprintf (stderr, "done\n"); } /* * logClientConnect() */ static void logClientConnect (logClient *pClient) { osiSockIoctl_t optval; int errnoCpy; int status; if ( pClient->sock == INVALID_SOCKET ) { logClientMakeSock ( pClient ); if ( pClient->sock == INVALID_SOCKET ) { return; } } while ( 1 ) { status = connect (pClient->sock, (struct sockaddr *)&pClient->addr, sizeof(pClient->addr)); if ( status >= 0 ) { break; } else { errnoCpy = SOCKERRNO; if ( errnoCpy == SOCK_EINTR ) { continue; } else if ( errnoCpy==SOCK_EINPROGRESS || errnoCpy==SOCK_EWOULDBLOCK ) { return; } else if ( errnoCpy==SOCK_EALREADY ) { break; } else { if ( pClient->connFailStatus != errnoCpy && ! pClient->shutdown ) { char sockErrBuf[128]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf (stderr, "log client: failed to connect to \"%s\" because %d=\"%s\"\n", pClient->name, errnoCpy, sockErrBuf); pClient->connFailStatus = errnoCpy; } logClientClose ( pClient ); return; } } } epicsMutexMustLock (pClient->mutex); pClient->connected = 1u; pClient->connFailStatus = 0; /* * discover that the connection has expired * (after a long delay) */ optval = TRUE; status = setsockopt (pClient->sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, sizeof(optval)); if (status<0) { char sockErrBuf[128]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf (stderr, "log client: unable to enable keepalive option because \"%s\"\n", sockErrBuf); } /* * we don't need full-duplex (clients only write), so we shutdown * the read end of our socket */ status = shutdown (pClient->sock, SHUT_RD); if (status < 0) { char sockErrBuf[128]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf (stderr, "%s:%d shutdown(sock,SHUT_RD) error was \"%s\"\n", __FILE__, __LINE__, sockErrBuf); /* not fatal (although it shouldn't happen) */ } /* * set how long we will wait for the TCP state machine * to clean up when we issue a close(). This * guarantees that messages are serialized when we * switch connections. */ { struct linger lingerval; lingerval.l_onoff = TRUE; lingerval.l_linger = 60*5; status = setsockopt (pClient->sock, SOL_SOCKET, SO_LINGER, (char *) &lingerval, sizeof(lingerval)); if (status<0) { char sockErrBuf[128]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf (stderr, "log client: unable to set linger options because \"%s\"\n", sockErrBuf); } } pClient->connectCount++; epicsMutexUnlock ( pClient->mutex ); epicsEventSignal ( pClient->stateChangeNotify ); fprintf ( stderr, "log client: connected to log server at \"%s\"\n", pClient->name ); } /* * logClientRestart () */ static void logClientRestart ( logClientId id ) { logClient *pClient = (logClient *)id; /* SMP safe state inspection */ epicsMutexMustLock ( pClient->mutex ); while ( ! pClient->shutdown ) { unsigned isConn; isConn = pClient->connected; epicsMutexUnlock ( pClient->mutex ); if ( ! isConn ) logClientConnect ( pClient ); logClientFlush ( pClient ); epicsEventWaitWithTimeout ( pClient->shutdownNotify, LOG_RESTART_DELAY); epicsMutexMustLock ( pClient->mutex ); } epicsMutexUnlock ( pClient->mutex ); pClient->shutdownConfirm = 1u; epicsEventSignal ( pClient->stateChangeNotify ); } /* * logClientCreate() */ logClientId epicsShareAPI logClientCreate ( struct in_addr server_addr, unsigned short server_port) { logClient *pClient; pClient = calloc (1, sizeof (*pClient)); if (pClient==NULL) { return NULL; } pClient->addr.sin_family = AF_INET; pClient->addr.sin_addr = server_addr; pClient->addr.sin_port = htons(server_port); ipAddrToDottedIP (&pClient->addr, pClient->name, sizeof(pClient->name)); pClient->mutex = epicsMutexCreate (); if ( ! pClient->mutex ) { free ( pClient ); return NULL; } pClient->sock = INVALID_SOCKET; pClient->connected = 0u; pClient->connFailStatus = 0; pClient->shutdown = 0; pClient->shutdownConfirm = 0; epicsAtExit (logClientDestroy, (void*) pClient); pClient->stateChangeNotify = epicsEventCreate (epicsEventEmpty); if ( ! pClient->stateChangeNotify ) { epicsMutexDestroy ( pClient->mutex ); free ( pClient ); return NULL; } pClient->shutdownNotify = epicsEventCreate (epicsEventEmpty); if ( ! pClient->shutdownNotify ) { epicsMutexDestroy ( pClient->mutex ); epicsEventDestroy ( pClient->stateChangeNotify ); free ( pClient ); return NULL; } pClient->restartThreadId = epicsThreadCreate ( "logRestart", epicsThreadPriorityLow, epicsThreadGetStackSize(epicsThreadStackSmall), logClientRestart, pClient ); if ( pClient->restartThreadId == NULL ) { epicsMutexDestroy ( pClient->mutex ); epicsEventDestroy ( pClient->stateChangeNotify ); epicsEventDestroy ( pClient->shutdownNotify ); free (pClient); fprintf(stderr, "log client: unable to start log client connection watch dog thread\n"); return NULL; } return (void *) pClient; } /* * logClientShow () */ void epicsShareAPI logClientShow (logClientId id, unsigned level) { logClient *pClient = (logClient *) id; if ( pClient->connected ) { printf ("log client: connected to log server at \"%s\"\n", pClient->name); } else { printf ("log client: disconnected from log server at \"%s\"\n", pClient->name); } if (logClientPrefix) { printf ("log client: prefix is \"%s\"\n", logClientPrefix); } if (level>0) { printf ("log client: sock %s, connect cycles = %u\n", pClient->sock==INVALID_SOCKET?"INVALID":"OK", pClient->connectCount); } if (level>1) { printf ("log client: %u bytes in buffer\n", pClient->nextMsgIndex); if (pClient->nextMsgIndex) printf("-------------------------\n" "%.*s-------------------------\n", (int)(pClient->nextMsgIndex), pClient->msgBuf); } } /* * iocLogPrefix() */ void epicsShareAPI iocLogPrefix(const char * prefix) { /* If we have already established a log prefix, don't let the user change * it. The iocLogPrefix command is expected to be run from the IOC startup * script during initialization; the prefix can't be changed once it has * been set. */ if (logClientPrefix) { printf ("iocLogPrefix: The prefix was already set to \"%s\" " "and can't be changed.\n", logClientPrefix); return; } if (prefix) { unsigned prefixLen = strlen(prefix); if (prefixLen > 0) { char * localCopy = malloc(prefixLen+1); strcpy(localCopy, prefix); logClientPrefix = localCopy; } } } base-7.0.3.1/modules/libcom/src/log/logClient.h0000664000577000060420000000312313557101274017770 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* logClient.h,v 1.5.2.1 2003/07/08 00:08:06 jhill Exp */ /* * * Author: Jeffrey O. Hill * Date: 080791 */ #ifndef INClogClienth #define INClogClienth 1 #include "shareLib.h" #include "osiSock.h" /* for 'struct in_addr' */ /* include default log client interface for backward compatibility */ #include "iocLog.h" #ifdef __cplusplus extern "C" { #endif typedef void *logClientId; epicsShareFunc logClientId epicsShareAPI logClientCreate ( struct in_addr server_addr, unsigned short server_port); epicsShareFunc void epicsShareAPI logClientSend (logClientId id, const char *message); epicsShareFunc void epicsShareAPI logClientShow (logClientId id, unsigned level); epicsShareFunc void epicsShareAPI logClientFlush (logClientId id); epicsShareFunc void epicsShareAPI iocLogPrefix(const char* prefix); /* deprecated interface; retained for backward compatibility */ /* note: implementations are in iocLog.c, not logClient.c */ epicsShareFunc logClientId epicsShareAPI logClientInit (void); #ifdef __cplusplus } #endif #endif /*INClogClienth*/ base-7.0.3.1/modules/libcom/src/macLib/EPICS/macLib.pm0000664000577000060420000001730413557101274020703 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* package EPICS::macLib::entry; sub new ($$) { my $class = shift; my $this = { name => shift, type => shift, raw => '', val => '', visited => 0, error => 0, }; bless $this, $class; return $this; } sub report ($) { my ($this) = @_; return unless defined $this->{raw}; printf "%1s %-16s %-16s %s\n", ($this->{error} ? '*' : ' '), $this->{name}, $this->{raw}, $this->{val}; } package EPICS::macLib; use Carp; sub new ($@) { my $proto = shift; my $class = ref($proto) || $proto; my $this = { dirty => 0, noWarn => 0, macros => [{}], # [0] is current scope, [1] is parent etc. }; bless $this, $class; $this->installList(@_); return $this; } sub installList ($@) { # Argument is a list of strings which are arguments to installMacros my $this = shift; while (@_) { $this->installMacros(shift); } } sub installMacros ($$) { # Argument is a string: a=1,b="2",c,d='hello' my $this = shift; $_ = shift; until (defined pos($_) and pos($_) == length($_)) { m/\G \s* /xgc; # Skip whitespace if (m/\G ( [A-Za-z0-9_-]+ ) \s* /xgc) { my ($name, $val) = ($1); if (m/\G = \s* /xgc) { # The value follows, handle quotes and escapes until (pos($_) == length($_)) { if (m/\G , /xgc) { last; } elsif (m/\G ' ( ( [^'] | \\ ' )* ) ' /xgc) { $val .= $1; } elsif (m/\G " ( ( [^"] | \\ " )* ) " /xgc) { $val .= $1; } elsif (m/\G \\ ( . ) /xgc) { $val .= $1; } elsif (m/\G ( . ) /xgc) { $val .= $1; } else { die "How did I get here?"; } } $this->putValue($name, $val); } elsif (m/\G , /xgc or (pos($_) == length($_))) { $this->putValue($name, undef); } else { warn "How did I get here?"; } } elsif (m/\G ( .* )/xgc) { croak "Can't find a macro definition in '$1'"; } else { last; } } } sub putValue ($$$) { my ($this, $name, $raw) = @_; if (exists $this->{macros}[0]{$name}) { if (!defined $raw) { delete $this->{macros}[0]{$name}; } else { $this->{macros}[0]{$name}{raw} = $raw; } } else { my $entry = EPICS::macLib::entry->new($name, 'macro'); $entry->{raw} = $raw; $this->{macros}[0]{$name} = $entry; } $this->{dirty} = 1; } sub pushScope ($) { my ($this) = @_; unshift @{$this->{macros}}, {}; } sub popScope ($) { my ($this) = @_; shift @{$this->{macros}}; } sub suppressWarning($$) { my ($this, $suppress) = @_; $this->{noWarn} = $suppress; } sub expandString($$) { my ($this, $src) = @_; $this->_expand; (my $name = $src) =~ s/^ (.{20}) .* $/$1.../xs; my $entry = EPICS::macLib::entry->new($name, 'string'); my $result = $this->_translate($entry, 0, $src); return $result unless $entry->{error}; return $this->{noWarn} ? $result : undef; } sub reportMacros ($) { my ($this) = @_; $this->_expand; print "Macro report\n============\n"; foreach my $scope (@{$this->{macros}}) { foreach my $name (keys %{$scope}) { my $entry = $scope->{$name}; $entry->report; } } continue { print " -- scope ends --\n"; } } # Private routines, not intended for public use sub _expand ($) { my ($this) = @_; return unless $this->{dirty}; foreach my $scope (@{$this->{macros}}) { foreach my $name (keys %{$scope}) { my $entry = $scope->{$name}; $entry->{val} = $this->_translate($entry, 1, $entry->{raw}); } } $this->{dirty} = 0; } sub _lookup ($$$$$) { my ($this, $name) = @_; foreach my $scope (@{$this->{macros}}) { if (exists $scope->{$name}) { return undef # Macro marked as deleted unless defined $scope->{$name}{raw}; return $scope->{$name}; } } return undef; } sub _translate ($$$$) { my ($this, $entry, $level, $str) = @_; return $this->_trans($entry, $level, '', \$str); } sub _trans ($$$$$) { my ($this, $entry, $level, $term, $R) = @_; return $$R if (!defined $$R or $$R =~ m/\A [^\$]* \Z/x); # Short-circuit if no macros my $quote = 0; my $val; until (defined pos($$R) and pos($$R) == length($$R)) { if ($term and ($$R =~ m/\G (?= [$term] ) /xgc)) { last; } if ($$R =~ m/\G \$ ( [({] ) /xgc) { my $macEnd = $1; $macEnd =~ tr/({/)}/; my $name2 = $this->_trans($entry, $level+1, "=$macEnd", $R); my $entry2 = $this->_lookup($name2); if (!defined $entry2) { # Macro not found if ($$R =~ m/\G = /xgc) { # Use default value given $val .= $this->_trans($entry, $level+1, $macEnd, $R); } else { unless ($this->{noWarn}) { $entry->{error} = 1; printf STDERR "macLib: macro '%s' is undefined (expanding %s '%s')\n", $name2, $entry->{type}, $entry->{name}; } $val .= "\$($name2)"; } $$R =~ m/\G [$macEnd] /xgc; # Discard close bracket } else { # Macro found if ($entry2->{visited}) { $entry->{error} = 1; printf STDERR "macLib: %s '%s' is recursive (expanding %s '%s')\n", $entry->{type}, $entry->{name}, $entry2->{type}, $entry2->{name}; $val .= "\$($name)"; } else { if ($$R =~ m/\G = /xgc) { # Discard default value local $this->{noWarn} = 1; # Temporarily kill warnings $this->_trans($entry, $level+1, $macEnd, $R); } $$R =~ m/\G [$macEnd] /xgc; # Discard close bracket if ($this->{dirty}) { # Translate raw value $entry2->{visited} = 1; $val .= $this->_trans($entry, $level+1, '', \$entry2->{raw}); $entry2->{visited} = 0; } else { $val .= $entry2->{val}; # Here's one I made earlier... } } } } elsif ($level > 0) { # Discard quotes and escapes if ($quote and $$R =~ m/\G $quote /xgc) { $quote = 0; } elsif ($$R =~ m/\G ( ['"] ) /xgc) { $quote = $1; } elsif ($$R =~ m/\G \\? ( . ) /xgc) { $val .= $1; } else { warn "How did I get here? level=$level"; } } else { # Level 0 if ($$R =~ m/\G \\ ( . ) /xgc) { $val .= "\\$1"; } elsif ($$R =~ m/\G ( [^\\\$'")}]* ) /xgc) { $val .= $1; } elsif ($$R =~ m/\G ( . ) /xgc) { $val .= $1; } else { warn "How did I get here? level=$level"; } } } return $val; } 1; base-7.0.3.1/modules/libcom/src/macLib/Makefile0000664000577000060420000000110213557101274017740 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/macLib INC += macLib.h Com_SRCS += macCore.c Com_SRCS += macEnv.c Com_SRCS += macUtil.c PERL_MODULES += EPICS/macLib.pm base-7.0.3.1/modules/libcom/src/macLib/macCore.c0000664000577000060420000007016113557101274020030 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Implementation of core macro substitution library (macLib) * * The implementation is fairly unsophisticated and linked lists are * used to store macro values. Typically there will will be only a * small number of macros and performance won't be a problem. Special * measures are taken to avoid unnecessary expansion of macros whose * definitions reference other macros. Whenever a macro is created, * modified or deleted, a "dirty" flag is set; this causes a full * expansion of all macros the next time a macro value is read * * Original Author: William Lupton, W. M. Keck Observatory */ #include #include #include #include #define epicsExportSharedSymbols #include "dbDefs.h" #include "errlog.h" #include "dbmf.h" #include "macLib.h" /*** Local structure definitions ***/ /* * Entry in linked list of macro definitions */ typedef struct mac_entry { ELLNODE node; /* prev and next pointers */ char *name; /* entry name */ char *type; /* entry type */ char *rawval; /* raw (unexpanded) value */ char *value; /* expanded macro value */ size_t length; /* length of value */ int error; /* error expanding value? */ int visited; /* ever been visited? */ int special; /* special (internal) entry? */ int level; /* scoping level */ } MAC_ENTRY; /*** Local function prototypes ***/ /* * These static functions peform low-level operations on macro entries */ static MAC_ENTRY *first ( MAC_HANDLE *handle ); static MAC_ENTRY *last ( MAC_HANDLE *handle ); static MAC_ENTRY *next ( MAC_ENTRY *entry ); static MAC_ENTRY *previous( MAC_ENTRY *entry ); static MAC_ENTRY *create( MAC_HANDLE *handle, const char *name, int special ); static MAC_ENTRY *lookup( MAC_HANDLE *handle, const char *name, int special ); static char *rawval( MAC_HANDLE *handle, MAC_ENTRY *entry, const char *value ); static void delete( MAC_HANDLE *handle, MAC_ENTRY *entry ); static long expand( MAC_HANDLE *handle ); static void trans ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level, const char *term, const char **rawval, char **value, char *valend ); static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level, const char **rawval, char **value, char *valend ); static void cpy2val( const char *src, char **value, char *valend ); static char *Strdup( const char *string ); /*** Constants ***/ /* * Magic number for validating context. */ #define MAC_MAGIC 0xbadcafe /* ...sells sub-standard coffee? */ /* * Flag bits */ #define FLAG_SUPPRESS_WARNINGS 0x1 #define FLAG_USE_ENVIRONMENT 0x80 /*** Library routines ***/ /* * Create a new macro substitution context and return an opaque handle * associated with the new context. Also optionally load an initial set * of macro definitions */ long /* 0 = OK; <0 = ERROR */ epicsShareAPI macCreateHandle( MAC_HANDLE **pHandle, /* address of variable to receive pointer */ /* to new macro substitution context */ const char * pairs[] ) /* pointer to NULL-terminated array of */ /* {name,value} pair strings; a NULL */ /* value implies undefined; a NULL */ /* argument implies no macros */ { MAC_HANDLE *handle; /* pointer to macro substitution context */ /* ensure NULL handle pointer is returned on error */ *pHandle = NULL; /* allocate macro substitution context */ handle = ( MAC_HANDLE * ) dbmfMalloc( sizeof( MAC_HANDLE ) ); if ( handle == NULL ) { errlogPrintf( "macCreateHandle: failed to allocate context\n" ); return -1; } /* initialize context */ handle->magic = MAC_MAGIC; handle->dirty = FALSE; handle->level = 0; handle->debug = 0; handle->flags = 0; ellInit( &handle->list ); /* use environment variables if so specified */ if (pairs && pairs[0] && !strcmp(pairs[0],"") && pairs[1] && !strcmp(pairs[1],"environ") && !pairs[3]) { handle->flags |= FLAG_USE_ENVIRONMENT; } else { /* if supplied, load macro definitions */ for ( ; pairs && pairs[0]; pairs += 2 ) { if ( macPutValue( handle, pairs[0], pairs[1] ) < 0 ) { dbmfFree( handle ); return -1; } } } /* set returned handle pointer */ *pHandle = handle; return 0; } /* * Turn on/off suppression of printed warnings from macLib calls * for the given handle */ void epicsShareAPI macSuppressWarning( MAC_HANDLE *handle, /* opaque handle */ int suppress /* 0 means issue, 1 means suppress */ ) { if ( handle && handle->magic == MAC_MAGIC ) { handle->flags = (handle->flags & ~FLAG_SUPPRESS_WARNINGS) | (suppress ? FLAG_SUPPRESS_WARNINGS : 0); } } /* * Expand a string that may contain macro references and return the * expanded string * * This is a very basic and powerful routine. It's basically a wrapper * around the the translation "engine" trans() */ long /* strlen(dest), <0 if any macros are */ /* undefined */ epicsShareAPI macExpandString( MAC_HANDLE *handle, /* opaque handle */ const char *src, /* source string */ char *dest, /* destination string */ long capacity ) /* capacity of destination buffer (dest) */ { MAC_ENTRY entry; const char *s; char *d; long length; /* check handle */ if ( handle == NULL || handle->magic != MAC_MAGIC ) { errlogPrintf( "macExpandString: NULL or invalid handle\n" ); return -1; } /* debug output */ if ( handle->debug & 1 ) printf( "macExpandString( %s, capacity = %ld )\n", src, capacity ); /* Check size */ if (capacity <= 1) return -1; /* expand raw values if necessary */ if ( expand( handle ) < 0 ) errlogPrintf( "macExpandString: failed to expand raw values\n" ); /* fill in necessary fields in fake macro entry structure */ entry.name = (char *) src; entry.type = "string"; entry.error = FALSE; /* expand the string */ s = src; d = dest; *d = '\0'; trans( handle, &entry, 0, "", &s, &d, d + capacity - 1 ); /* return +/- #chars copied depending on successful expansion */ length = d - dest; length = ( entry.error ) ? -length : length; /* debug output */ if ( handle->debug & 1 ) printf( "macExpandString() -> %ld\n", length ); return length; } /* * Define the value of a macro. A NULL value deletes the macro if it * already existed */ long /* strlen(value) */ epicsShareAPI macPutValue( MAC_HANDLE *handle, /* opaque handle */ const char *name, /* macro name */ const char *value ) /* macro value */ { MAC_ENTRY *entry; /* pointer to this macro's entry structure */ /* check handle */ if ( handle == NULL || handle->magic != MAC_MAGIC ) { errlogPrintf( "macPutValue: NULL or invalid handle\n" ); return -1; } if ( handle->debug & 1 ) printf( "macPutValue( %s, %s )\n", name, value ? value : "NULL" ); /* handle NULL value case: if name was found, delete entry (may be several entries at different scoping levels) */ if ( value == NULL ) { /* * FIXME: shouldn't be able to delete entries from lower scopes * NOTE: when this is changed, this functionality of removing * a macro from all scopes will still be needed by iocshEnvClear */ while ( ( entry = lookup( handle, name, FALSE ) ) != NULL ) { int done = strcmp(entry->type, "environment variable") == 0; delete( handle, entry ); if (done) break; } return 0; } /* look up macro name */ entry = lookup( handle, name, FALSE ); /* new entry must be created if macro doesn't exist or if it only exists at a lower scoping level */ if ( entry == NULL || entry->level < handle->level ) { entry = create( handle, name, FALSE ); if ( entry == NULL ) { errlogPrintf( "macPutValue: failed to create macro %s = %s\n", name, value ); return -1; } else { entry->type = "macro"; } } /* copy raw value */ if ( rawval( handle, entry, value ) == NULL ) { errlogPrintf( "macPutValue: failed to copy macro %s = %s\n", name, value ) ; return -1; } /* return length of value */ return strlen( value ); } /* * Return the value of a macro */ long /* strlen(value), <0 if undefined */ epicsShareAPI macGetValue( MAC_HANDLE *handle, /* opaque handle */ const char *name, /* macro name or reference */ char *value, /* string to receive macro value or name */ /* argument if macro is undefined */ long capacity ) /* capacity of destination buffer (value) */ { MAC_ENTRY *entry; /* pointer to this macro's entry structure */ long length; /* number of characters returned */ /* check handle */ if ( handle == NULL || handle->magic != MAC_MAGIC ) { errlogPrintf( "macGetValue: NULL or invalid handle\n" ); return -1; } /* debug output */ if ( handle->debug & 1 ) printf( "macGetValue( %s )\n", name ); /* look up macro name */ entry = lookup( handle, name, FALSE ); /* if capacity <= 1 or VALUE == NULL just return -1 / 0 for undefined / defined macro */ if ( capacity <= 1 || value == NULL ) { return ( entry == NULL ) ? -1 : 0; } /* if not found, copy name to value and return minus #chars copied */ if ( entry == NULL ) { strncpy( value, name, capacity ); return ( value[capacity-1] == '\0' ) ? - (long) strlen( name ) : -capacity; } /* expand raw values if necessary; if fail (can only fail because of memory allocation failure), return same as if not found */ if ( expand( handle ) < 0 ) { errlogPrintf( "macGetValue: failed to expand raw values\n" ); strncpy( value, name, capacity ); return ( value[capacity-1] == '\0' ) ? - (long) strlen( name ) : -capacity; } /* copy value and return +/- #chars copied depending on successful expansion */ strncpy( value, entry->value, capacity ); length = ( value[capacity-1] == '\0' ) ? entry->length : capacity; return ( entry->error ) ? -length : length; } /* * Free up all storage associated with and delete a macro substitution * context */ long /* 0 = OK; <0 = ERROR */ epicsShareAPI macDeleteHandle( MAC_HANDLE *handle ) /* opaque handle */ { MAC_ENTRY *entry, *nextEntry; /* check handle */ if ( handle == NULL || handle->magic != MAC_MAGIC ) { errlogPrintf( "macDeleteHandle: NULL or invalid handle\n" ); return -1; } /* debug output */ if ( handle->debug & 1 ) printf( "macDeleteHandle()\n" ); /* delete all entries */ for ( entry = first( handle ); entry != NULL; entry = nextEntry ) { nextEntry = next( entry ); delete( handle, entry ); } /* clear magic field and free context structure */ handle->magic = 0; dbmfFree( handle ); return 0; } /* * Mark the start of a new scoping level */ long /* 0 = OK; <0 = ERROR */ epicsShareAPI macPushScope( MAC_HANDLE *handle ) /* opaque handle */ { MAC_ENTRY *entry; /* check handle */ if ( handle == NULL || handle->magic != MAC_MAGIC ) { errlogPrintf( "macPushScope: NULL or invalid handle\n" ); return -1; } /* debug output */ if ( handle->debug & 1 ) printf( "macPushScope()\n" ); /* increment scoping level */ handle->level++; /* create new "special" entry of name "" */ entry = create( handle, "", TRUE ); if ( entry == NULL ) { handle->level--; errlogPrintf( "macPushScope: failed to push scope\n" ); return -1; } else { entry->type = "scope marker"; } return 0; } /* * Pop all macros defined since the last call to macPushScope() */ long /* 0 = OK; <0 = ERROR */ epicsShareAPI macPopScope( MAC_HANDLE *handle ) /* opaque handle */ { MAC_ENTRY *entry, *nextEntry; /* check handle */ if ( handle == NULL || handle->magic != MAC_MAGIC ) { errlogPrintf( "macPopScope: NULL or invalid handle\n" ); return -1; } /* debug output */ if ( handle->debug & 1 ) printf( "macPopScope()\n" ); /* check scoping level isn't already zero */ if ( handle->level == 0 ) { errlogPrintf( "macPopScope: no scope to pop\n" ); return -1; } /* look up most recent scope entry */ entry = lookup( handle, "", TRUE ); if ( entry == NULL ) { errlogPrintf( "macPopScope: no scope to pop\n" ); return -1; } /* delete scope entry and all macros defined since it */ for ( ; entry != NULL; entry = nextEntry ) { nextEntry = next( entry ); delete( handle, entry ); } /* decrement scoping level */ handle->level--; return 0; } /* * Report macro details to standard output */ long /* 0 = OK; <0 = ERROR */ epicsShareAPI macReportMacros( MAC_HANDLE *handle ) /* opaque handle */ { const char *format = "%-1s %-16s %-16s %s\n"; MAC_ENTRY *entry; /* check handle */ if ( handle == NULL || handle->magic != MAC_MAGIC ) { errlogPrintf( "macReportMacros: NULL or invalid handle\n" ); return -1; } /* expand raw values if necessary; report but ignore failure */ if ( expand( handle ) < 0 ) errlogPrintf( "macGetValue: failed to expand raw values\n" ); /* loop through macros, reporting names and values */ printf( format, "e", "name", "rawval", "value" ); printf( format, "-", "----", "------", "-----" ); for ( entry = first( handle ); entry != NULL; entry = next( entry ) ) { /* differentiate between "special" (scope marker) and ordinary entries */ if ( entry->special ) printf( format, "s", "----", "------", "-----" ); else printf( format, entry->error ? "*" : " ", entry->name, entry->rawval ? entry->rawval : "", entry->value ? entry->value : ""); } return 0; } /******************** beginning of static functions ********************/ /* * Return pointer to first macro entry (could be preprocessor macro) */ static MAC_ENTRY *first( MAC_HANDLE *handle ) { return ( MAC_ENTRY * ) ellFirst( &handle->list ); } /* * Return pointer to last macro entry (could be preprocessor macro) */ static MAC_ENTRY *last( MAC_HANDLE *handle ) { return ( MAC_ENTRY * ) ellLast( &handle->list ); } /* * Return pointer to next macro entry (could be preprocessor macro) */ static MAC_ENTRY *next( MAC_ENTRY *entry ) { return ( MAC_ENTRY * ) ellNext( ( ELLNODE * ) entry ); } /* * Return pointer to previous macro entry (could be preprocessor macro) */ static MAC_ENTRY *previous( MAC_ENTRY *entry ) { return ( MAC_ENTRY * ) ellPrevious( ( ELLNODE * ) entry ); } /* * Create new macro entry (can assume it doesn't exist) */ static MAC_ENTRY *create( MAC_HANDLE *handle, const char *name, int special ) { ELLLIST *list = &handle->list; MAC_ENTRY *entry = ( MAC_ENTRY * ) dbmfMalloc( sizeof( MAC_ENTRY ) ); if ( entry != NULL ) { entry->name = Strdup( name ); if ( entry->name == NULL ) { dbmfFree( entry ); entry = NULL; } else { entry->type = ""; entry->rawval = NULL; entry->value = NULL; entry->length = 0; entry->error = FALSE; entry->visited = FALSE; entry->special = special; entry->level = handle->level; ellAdd( list, ( ELLNODE * ) entry ); } } return entry; } /* * Look up macro entry with matching "special" attribute by name */ static MAC_ENTRY *lookup( MAC_HANDLE *handle, const char *name, int special ) { MAC_ENTRY *entry; if ( handle->debug & 2 ) printf( "lookup-> level = %d, name = %s, special = %d\n", handle->level, name, special ); /* search backwards so scoping works */ for ( entry = last( handle ); entry != NULL; entry = previous( entry ) ) { if ( entry->special != special ) continue; if ( strcmp( name, entry->name ) == 0 ) break; } if ( (special == FALSE) && (entry == NULL) && (handle->flags & FLAG_USE_ENVIRONMENT) ) { char *value = getenv(name); if (value) { entry = create( handle, name, FALSE ); if ( entry ) { entry->type = "environment variable"; if ( rawval( handle, entry, value ) == NULL ) entry = NULL; } } } if ( handle->debug & 2 ) printf( "<-lookup level = %d, name = %s, result = %p\n", handle->level, name, entry ); return entry; } /* * Copy raw value to macro entry */ static char *rawval( MAC_HANDLE *handle, MAC_ENTRY *entry, const char *value ) { if ( entry->rawval != NULL ) dbmfFree( entry->rawval ); entry->rawval = Strdup( value ); handle->dirty = TRUE; return entry->rawval; } /* * Delete a macro entry; requires re-expansion of macro values since this * macro may be referenced by another one */ static void delete( MAC_HANDLE *handle, MAC_ENTRY *entry ) { ELLLIST *list = &handle->list; ellDelete( list, ( ELLNODE * ) entry ); dbmfFree( entry->name ); if ( entry->rawval != NULL ) dbmfFree( entry->rawval ); if ( entry->value != NULL ) free( entry->value ); dbmfFree( entry ); handle->dirty = TRUE; } /* * Expand macro definitions (expensive but done very infrequently) */ static long expand( MAC_HANDLE *handle ) { MAC_ENTRY *entry; const char *rawval; char *value; if ( !handle->dirty ) return 0; for ( entry = first( handle ); entry != NULL; entry = next( entry ) ) { if ( handle->debug & 2 ) printf( "\nexpand %s = %s\n", entry->name, entry->rawval ? entry->rawval : "" ); if ( entry->value == NULL ) { if ( ( entry->value = malloc( MAC_SIZE + 1 ) ) == NULL ) { return -1; } } /* start at level 1 so quotes and escapes will be removed from expanded value */ rawval = entry->rawval; value = entry->value; *value = '\0'; entry->error = FALSE; trans( handle, entry, 1, "", &rawval, &value, entry->value + MAC_SIZE ); entry->length = value - entry->value; entry->value[MAC_SIZE] = '\0'; } handle->dirty = FALSE; return 0; } /* * Translate raw macro value (recursive). This is by far the most complicated * of the macro routines and calls itself recursively both to translate any * macros referenced in the name and to translate the resulting name */ static void trans( MAC_HANDLE *handle, MAC_ENTRY *entry, int level, const char *term, const char **rawval, char **value, char *valend ) { char quote; const char *r; char *v; int discard; int macRef; /* return immediately if raw value is NULL */ if ( *rawval == NULL ) return; /* discard quotes and escapes if level is > 0 (i.e. if these aren't the user's quotes and escapes) */ discard = ( level > 0 ); /* debug output */ if ( handle->debug & 2 ) printf( "trans-> entry = %p, level = %d, capacity = %u, discard = %s, " "rawval = %s\n", entry, level, (unsigned int)(valend - *value), discard ? "T" : "F", *rawval ); /* initially not in quotes */ quote = 0; /* scan characters until hit terminator or end of string */ for ( r = *rawval, v = *value; strchr( term, *r ) == NULL; r++ ) { /* handle quoted characters (quotes are discarded if in name) */ if ( quote ) { if ( *r == quote ) { quote = 0; if ( discard ) continue; } } else if ( *r == '"' || *r == '\'' ) { quote = *r; if ( discard ) continue; } /* macro reference if '$' followed by '(' or '{' */ macRef = ( *r == '$' && *( r + 1 ) != '\0' && strchr( "({", *( r + 1 ) ) != NULL ); /* macros are not expanded in single quotes */ if ( macRef && quote != '\'' ) { /* Handle macro reference */ refer ( handle, entry, level, &r, &v, valend ); } else { /* handle escaped characters (escape is discarded if in name) */ if ( *r == '\\' && *( r + 1 ) != '\0' ) { if ( v < valend && !discard ) *v++ = '\\'; if ( v < valend ) *v++ = *++r; } /* copy character to output */ else { if ( v < valend ) *v++ = *r; } /* ensure string remains properly terminated */ if ( v <= valend ) *v = '\0'; } } /* debug output */ if ( handle->debug & 2 ) printf( "<-trans level = %d, length = %4u, value = %s\n", level, (unsigned int)(v - *value), *value ); /* update pointers to next characters to scan in raw value and to fill in in output value (if at end of input, step back so terminator is still there to be seen) */ *rawval = ( *r == '\0' ) ? r - 1 : r; *value = v; return; } /* * Expand a macro reference, handling default values and defining scoped * macros as encountered. This code used to part of trans(), but was * pulled out for ease of understanding. */ static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level, const char **rawval, char **value, char *valend ) { const char *r = *rawval; char *v = *value; char refname[MAC_SIZE + 1] = {'\0'}; char *rn = refname; MAC_ENTRY *refentry; const char *defval = NULL; const char *macEnd; const char *errval = NULL; int pop = FALSE; /* debug output */ if ( handle->debug & 2 ) printf( "refer-> entry = %p, level = %d, capacity = %u, rawval = %s\n", entry, level, (unsigned int)(valend - *value), *rawval ); /* step over '$(' or '${' */ r++; macEnd = ( *r == '(' ) ? "=,)" : "=,}"; r++; /* translate name (may contain macro references); truncated quietly if too long but always guaranteed zero-terminated */ trans( handle, entry, level + 1, macEnd, &r, &rn, rn + MAC_SIZE ); refname[MAC_SIZE] = '\0'; /* Is there a default value? */ if ( *r == '=' ) { MAC_ENTRY dflt; int flags = handle->flags; handle->flags |= FLAG_SUPPRESS_WARNINGS; /* store its location in case we need it */ defval = ++r; /* Find the end, discarding its value */ dflt.name = refname; dflt.type = "default value"; dflt.error = FALSE; trans( handle, &dflt, level + 1, macEnd+1, &r, &v, v); handle->flags = flags; } /* extract and set values for any scoped macros */ if ( *r == ',' ) { MAC_ENTRY subs; int flags = handle->flags; handle->flags |= FLAG_SUPPRESS_WARNINGS; subs.type = "scoped macro"; subs.error = FALSE; macPushScope( handle ); pop = TRUE; while ( *r == ',' ) { char subname[MAC_SIZE + 1] = {'\0'}; char subval[MAC_SIZE + 1] = {'\0'}; char *sn = subname; char *sv = subval; /* translate the macro name */ ++r; subs.name = refname; trans( handle, &subs, level + 1, macEnd, &r, &sn, sn + MAC_SIZE ); subname[MAC_SIZE] = '\0'; /* If it has a value, translate that and assign it */ if ( *r == '=' ) { ++r; subs.name = subname; trans( handle, &subs, level + 1, macEnd+1, &r, &sv, sv + MAC_SIZE); subval[MAC_SIZE] = '\0'; macPutValue( handle, subname, subval ); handle->dirty = TRUE; /* re-expand with new macro values */ } } handle->flags = flags; } /* Now we can look up the translated name */ refentry = lookup( handle, refname, FALSE ); if ( refentry ) { if ( !refentry->visited ) { /* reference is good, use it */ if ( !handle->dirty ) { /* copy the already-expanded value, merge any error status */ cpy2val( refentry->value, &v, valend ); entry->error = entry->error || refentry->error; } else { /* translate raw value */ const char *rv = refentry->rawval; refentry->visited = TRUE; trans( handle, entry, level + 1, "", &rv, &v, valend ); refentry->visited = FALSE; } goto cleanup; } /* reference is recursive */ entry->error = TRUE; errval = ",recursive)"; if ( (handle->flags & FLAG_SUPPRESS_WARNINGS) == 0 ) { errlogPrintf( "macLib: %s %s is recursive (expanding %s %s)\n", entry->type, entry->name, refentry->type, refentry->name ); } } else { /* no macro found by this name */ if ( defval ) { /* there was a default value, translate that instead */ trans( handle, entry, level + 1, macEnd+1, &defval, &v, valend ); goto cleanup; } entry->error = TRUE; errval = ",undefined)"; if ( (handle->flags & FLAG_SUPPRESS_WARNINGS) == 0 ) { errlogPrintf( "macLib: macro %s is undefined (expanding %s %s)\n", refname, entry->type, entry->name ); } } /* Bad reference, insert either $(name,) or $(name) */ if ( v < valend ) *v++ = '$'; if ( v < valend ) *v++ = '('; cpy2val( refname, &v, valend ); if (handle->flags & FLAG_SUPPRESS_WARNINGS) { if ( v < valend ) *v++ = ')'; *v = '\0'; } else cpy2val( errval, &v, valend ); cleanup: if (pop) { macPopScope( handle ); } /* debug output */ if ( handle->debug & 2 ) printf( "<-refer level = %d, length = %4u, value = %s\n", level, (unsigned int)(v - *value), *value ); *rawval = r; *value = v; return; } /* * Copy a string, honoring the 'end of destination string' pointer * Returns with **value pointing to the '\0' terminator */ static void cpy2val(const char *src, char **value, char *valend) { char *v = *value; while ((v < valend) && (*v = *src++)) { v++; } *v = '\0'; *value = v; } /* * strdup() implementation which uses our own memory allocator */ static char *Strdup(const char *string ) { char *copy = dbmfMalloc( strlen( string ) + 1 ); if ( copy != NULL ) strcpy( copy, string ); return copy; } base-7.0.3.1/modules/libcom/src/macLib/macEnv.c0000664000577000060420000000347713557101274017676 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Macro expansion of environment variables */ #include #include #include #define epicsExportSharedSymbols #include "errlog.h" #include "epicsString.h" #include "macLib.h" char * epicsShareAPI macEnvExpand(const char *str) { return macDefExpand(str, NULL); } char * epicsShareAPI macDefExpand(const char *str, MAC_HANDLE *macros) { MAC_HANDLE *handle; static const char * pairs[] = { "", "environ", NULL, NULL }; long destCapacity = 128; char *dest = NULL; int n; if (macros) { handle = macros; } else { if (macCreateHandle(&handle, pairs)){ errlogMessage("macDefExpand: macCreateHandle failed."); return NULL; } } do { destCapacity *= 2; /* * Use free/malloc rather than realloc since there's no need to * keep the original contents. */ free(dest); dest = malloc(destCapacity); if(!dest) goto done; n = macExpandString(handle, str, dest, destCapacity); } while (n >= (destCapacity - 1)); if (n < 0) { free(dest); dest = NULL; } else { size_t unused = destCapacity - ++n; if (unused >= 20) dest = realloc(dest, n); } done: if (macros == NULL) { if (macDeleteHandle(handle)) { errlogMessage("macDefExpand: macDeleteHandle failed."); } } return dest; } base-7.0.3.1/modules/libcom/src/macLib/macLib.h0000664000577000060420000001255213557101274017653 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Definitions for macro substitution library (macLib) * * William Lupton, W. M. Keck Observatory */ #ifndef INCmacLibH #define INCmacLibH /* * EPICS include files needed by this file */ #include "ellLib.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /* * Maximum size of macro name or value string (simpler to make fixed) */ #define MAC_SIZE 256 /* * Macro substitution context. One of these contexts is allocated each time * macCreateHandle() is called */ typedef struct { long magic; /* magic number (used for authentication) */ int dirty; /* values need expanding from raw values? */ int level; /* scoping level */ int debug; /* debugging level */ ELLLIST list; /* macro name / value list */ int flags; /* operating mode flags */ } MAC_HANDLE; /* * Function prototypes (core library) */ epicsShareFunc long /* 0 = OK; <0 = ERROR */ epicsShareAPI macCreateHandle( MAC_HANDLE **handle, /* address of variable to receive pointer */ /* to new macro substitution context */ const char * pairs[] /* pointer to NULL-terminated array of */ /* {name,value} pair strings; a NULL */ /* value implies undefined; a NULL */ /* argument implies no macros */ ); epicsShareFunc void epicsShareAPI macSuppressWarning( MAC_HANDLE *handle, /* opaque handle */ int falseTrue /*0 means issue, 1 means suppress*/ ); epicsShareFunc long /* strlen(dest), <0 if any macros are */ /* undefined */ epicsShareAPI macExpandString( MAC_HANDLE *handle, /* opaque handle */ const char *src, /* source string */ char *dest, /* destination string */ long capacity /* capacity of destination buffer (dest) */ ); epicsShareFunc long /* strlen(value) */ epicsShareAPI macPutValue( MAC_HANDLE *handle, /* opaque handle */ const char *name, /* macro name */ const char *value /* macro value */ ); epicsShareFunc long /* strlen(value), <0 if undefined */ epicsShareAPI macGetValue( MAC_HANDLE *handle, /* opaque handle */ const char *name, /* macro name or reference */ char *value, /* string to receive macro value or name */ /* argument if macro is undefined */ long capacity /* capacity of destination buffer (value) */ ); epicsShareFunc long /* 0 = OK; <0 = ERROR */ epicsShareAPI macDeleteHandle( MAC_HANDLE *handle /* opaque handle */ ); epicsShareFunc long /* 0 = OK; <0 = ERROR */ epicsShareAPI macPushScope( MAC_HANDLE *handle /* opaque handle */ ); epicsShareFunc long /* 0 = OK; <0 = ERROR */ epicsShareAPI macPopScope( MAC_HANDLE *handle /* opaque handle */ ); epicsShareFunc long /* 0 = OK; <0 = ERROR */ epicsShareAPI macReportMacros( MAC_HANDLE *handle /* opaque handle */ ); /* * Function prototypes (utility library) */ epicsShareFunc long /* #defns encountered; <0 = ERROR */ epicsShareAPI macParseDefns( MAC_HANDLE *handle, /* opaque handle; can be NULL if default */ /* special characters are to be used */ const char *defns, /* macro definitions in "a=xxx,b=yyy" */ /* format */ char **pairs[] /* address of variable to receive pointer */ /* to NULL-terminated array of {name, */ /* value} pair strings; all storage is */ /* allocated contiguously */ ); epicsShareFunc long /* #macros defined; <0 = ERROR */ epicsShareAPI macInstallMacros( MAC_HANDLE *handle, /* opaque handle */ char *pairs[] /* pointer to NULL-terminated array of */ /* {name,value} pair strings; a NULL */ /* value implies undefined; a NULL */ /* argument implies no macros */ ); epicsShareFunc char * /* expanded string; NULL if any undefined macros */ epicsShareAPI macEnvExpand( const char *str /* string to be expanded */ ); epicsShareFunc char * /* expanded string; NULL if any undefined macros */ epicsShareAPI macDefExpand( const char *str, /* string to be expanded */ MAC_HANDLE *macros /* opaque handle; can be NULL if default */ /* special characters are to be used */ ); #ifdef __cplusplus } #endif #endif /*INCmacLibH*/ base-7.0.3.1/modules/libcom/src/macLib/macLibNOTES0000664000577000060420000001166313557101274020240 0ustar anjaesctl# Test input file for macTest filter, doubling as notes on usage of the # macro library. Some special strings at start of line are supported: # # 1. '#' indicates a comment and is ignored # # 2. '%set' is followed by "a=b, c=d" style macro definitions which are # passed through macParseDefs() and macInstallMacros() # # 3. '%push' pushes a scoping level by calling macPushScope() # # 4. '%pop' pops a scoping level by calling macPopScope() # # 5. '%report' reports macro definitions by calling macReportMacros() # # 6. all other lines are expanded by callins macExpandString() # introduction ------------ See the README file for the library specification and see the header comments of macCore.c (the core library) for notes on what has been implemented. This file contains tutorial information and examples. It's the best documentation that there is. simple examples --------------- To define the a, b, c and d macros to be 1, 2, 3 and 4 (note optional white space is ignored around '=' and ',' characters): %set a=1, b=2, c = 3 , d = 4 These macros can be dereferenced using '$(xxx)' or '${xxx}' notation: a = $(a), b = $(b), c = ${c}, d = ${d}. Macro values can reference other macros in an arbitrarily complex way. The only current restrictions are that a macro name or value cannot exceed 256 characters in length. This restriction could fairly easily be lifted. Here's an example: %set x = ${$(y$(z))} If this is expanded now: $(x), it won't work because the other macros aren't defined yet. So: %set cash=lucre, ywork=cash, z=work Now expansion yields "$(x)" (work -> ywork -> cash -> lucre). One can inadvertently set up circular references. For example: %set mac1=$(mac2), mac2=$(mac3), mac3=$(mac1) An attempt to dereference mac1 gives $(mac1). When a macro expansion fails, the translation that failed is replaced with the text that could not be expanded. You can get a report of current macro definitions by doing %report The '*' character in the first column indicates a problem expanding that macro. We'll get rid of these problem macros: %set mac1, mac2, mac3 You can also push a new scoping level by doing %push and pop back to it by doing %pop For example: %set level = 0 %push %set level = 1 %push %set level = 2 %push %set level = 3 %report Level is $(level) %pop Level is $(level) %pop Level is $(level) %pop Level is $(level) %pop (That last error was deliberate) quote and escape handling ------------------------- Both single and double quotes are supported, as are escapes. Some of the implications are quite subtle and some of the design choices made may not meet with universal approval. The basic idea is that a string in which macro references have been expanded should look like the source string apart from the where macros have been expanded. This implies that quote and escape characters should not be removed from the string. Single and double quotes are different in that (as in most shells), macros are substituted within double quotes but not within single quotes. Back quotes are not special characters. Missing quotes at the end of a string are automatically and quietly supplied. We've already seen some examples but what happens here: $(x)? Should not that have been expanded? Why wasn't it? The answer is given later on. As a clue, this is: $(x). This isn't: $(x)! Characters may be escaped by preceding them with a \ (back slash). Escapes work even within quotes, which is more like C than most shells. Thus, '\'' works, and it doesn't with the C shell. Quotes and escapes can also be used in "a=b, c=d" style assignments but they are not part of macro names. For example: %set \= = equal, \' = quote defines macros called "=" and "'", not "\=" and "\'". To reference these macros, "$(=)" ('$(=)') works because "=" is not special in this context. However the quote must be escaped because quotes are always special, as in "$(\')" ('$(\')'). miscellaneous notes ------------------- In the "a=b, c=d" strings that are parsed by macParseDefns(), a macro can be set to an empty value by putting nothing after the equals sign, as in %set empty = resulting in empty = "$(empty)". Omitting the equal sign gives the macro a NULL rather than empty value (with the result that the macro is deleted since this is how macPutValue() interprets a NULL value), so %set empty deletes the empty macro, as can be seen when we try to de-reference it: $(empty). The only special characters in these "a=b, c=d" strings are "=", "," and white space surrounding these characters (plus quotes and escapes of course). This means that %set fred 2 actually sets a macro called "fred 2" to a NULL value (i.e. it deletes it if it existed). This is probably wrong and the space should be taken as an implicit '='. However, to do this and still to support ignored white space around '=' and ',' characters is a bigger change than I am prepared to make at present. What was the problem expanding "$(x)" before? It was single quotes from words like "wasn't"! Is this a problem or a feature? base-7.0.3.1/modules/libcom/src/macLib/macLibREADME0000664000577000060420000001643413557101274020306 0ustar anjaesctl1. Macro substitution library ----------------------------- This library could be used directly by applications which need to support macro substitution. It will be implemented on all platforms. 1.1 Core library ---------------- The core library provides a minimal set of basic operations. Some utility routines, described later, use core routines to provide a more convenient interface for some purposes. a) long macCreateHandle( MAC_HANDLE **handle, char *pairs[] ); void macSuppressWarning(MAC_HANDLE *handle,int falseTrue); Creates a new macro substitution context and returns an opaque handle to that context. An application can, if it desires, have several active contexts, although most will not. If desired, an initial set of macro definitions may be loaded ("pairs" is set to NULL to avoid this). The definitions are in the "standard" pairs format, as described under macParseDefns(). Note that MAC_HANDLE is a typedef for the context structure. The opaque handle is of type "MAC_HANDLE *", which is a pointer to the context structure. The memory for this context is allocated by this routine. macSuppressWarning can be called to suppress the marning message when macExpandString cant expand a macro. A non zero value will suppress the messages. c) long macGetValue ( MAC_HANDLE *handle, char *name, char *value, long maxlen ); Returns up to maxlen characters of the value of macro "name". "value" will be zero-terminated if the length of the value is less than maxlen. The function value will be the number of characters copied to "value" (see below for behavior if the macro is undefined). If maxlen is zero, no characters will be copied to "value" (which can be NULL) and the call can be used to check whether the macro is defined. Note that truncation of the value is therefore not reported. Is this a problem? If the macro is undefined, the macro reference will be returned in the value string (if permitted by maxlen) and the function value will be _minus_ the number of characters copied. Note that treatment of maxlen is intended to be consistent with what people are used to with strncpy(). If the value contains macro references then the references will be expanded recursively. This expansion will detect a direct or indirect self reference. Macro references begin with a "$" immediately followed by either a "(" or a "{" character. The macro name comes next, and may optionally be succeeded by an "=" and a default value, which will be returned if the named macro is undefined at the moment of expansion. The reference is terminated by the matching ")" or "}" character. d) long macPutValue( MAC_HANDLE *handle, char *name, char *value ); Sets the value of the macro "name". If "value" is NULL, it undefines all instances of "name" at all scoping levels (it's not an error if "name" is not defined in this case). Macros referenced by "value" need not be defined at this point. The function returns the length of the value string. e) long macDeleteHandle( MAC_HANDLE *handle ); Marks a handle invalid, and frees all storage associated with it. Note that this does not free any strings into which macro values have been returned. Macro values are always returned into strings which were pre-allocated by the caller. f) long macPushScope( MAC_HANDLE *handle ); Marks the start of a new scoping level such that all definitions made up until the next macPopScope() call will be lost on macPopScope() and those current at macPushScope() will be re-instated. g) long macPopScope( MAC_HANDLE *handle ); See macPushScope. h) Error handling These routines conform to 0 (=OK) for success, -1 (=ERROR) for failure, and small positive values for extra info. I contravened this for macGetValue() and macExpandString() because I felt that it was worth returning information both on success / failure and on value length. Errors and warnings are reported using errlogPrintf(). 1.2 Utility library ------------------- These are convenience functions. If other useful functions emerge during implementation, the list may grow. a) macParseDefns( MAC_HANDLE *handle, char *defns, char **pairs[] ); This takes a set of macro definitions in "a=xxx,b=yyy" format and converts them into an array of pointers to character strings which are, in order, "first name", "first value", "second name", "second value" etc. The array is terminated with two NULL pointers and all storage is allocated contiguously so that it can be freed with a single call to free(). This routine is independent of any handle and provides a generally useful service which may be used elsewhere. Any macro references in values are not expanded at this point since the referenced macros may be defined or redefined before the macro actually has to be translated. Shell-style escapes and quotes are supported, as are things like "A=B,B=$(C$(A)),CA=CA,CB=CB" (sets B to "CB"). White space is significant within values but ignored elsewhere (i.e. surrounding "=" and "," characters). Probably noone will ever want to, but the special meanings of "$", "{", "}", "(", ")", "=" and "," can all be changed via macPutXxxx() calls. This routine does not have a handle argument, so they must be changed globally for it to use the new definitions. Should it have a handle argument? This makes it more of a pain to use but guarantees that there will be no conflicts. I think it should really. The function value is the number of definitions encountered, or -1 if the supplied string is invalid. b) long macInstallMacros( MAC_HANDLE *handle, char *pairs[] ); This takes an array of pairs as defined above and installs them as definitions by calling macPutValue(). The pairs array is terminated by a NULL pointer. The function value is the number of macros defined. c) long macExpandString( MAC_HANDLE *handle, char *src, char *dest, long maxlen ); This operates on a string which may contain macro references. It parses the string looking for such references and passes them to macGetValue() for translation. It returns the expanded string in the supplied argument. The function value is similar to that of macGetValue(). Its absolute value is the number of characters copied. If negative, at least one undefined macro has been left unexpanded. d) char *macEnvExpand( char *src ); This operates on a string which may contain macros defined by environment variables. It parses the string looking for such references and passes them to macGetValue() for translation. It uses malloc() to allocate space for the expanded string and returns a pointer to this null-terminated string. It returns NULL if the source string contains any undefined references. e) char *macDefExpand( char *src, MAC_HANDLE *macros ); This operates in the same manner as macEnvExpand, but takes an optional macro handle that can contain a set of macro definitions. These macros are appended to the set of macros from environment variables when expanding the string. f) long macReportMacros( MAC_HANDLE *handle ); This reports details of current definitions to standard output, and is intended purely for debugging purposes. base-7.0.3.1/modules/libcom/src/macLib/macUtil.c0000664000577000060420000001761613557101274020063 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Implementation of utility macro substitution library (macLib) * * William Lupton, W. M. Keck Observatory */ #include #include #include #include #define epicsExportSharedSymbols #include "dbDefs.h" #include "errlog.h" #include "macLib.h" /* * Parse macros definitions in "a=xxx,b=yyy" format and convert them to * a contiguously allocated array pointers to names and values, and the * name and value strings, terminated with two NULL pointers. Quotes * and escapes are honored but only removed from macro names (not * values) */ long /* #defns encountered; <0 = ERROR */ epicsShareAPI macParseDefns( MAC_HANDLE *handle, /* opaque handle; can be NULL if default */ /* special characters are to be used */ const char *defns, /* macro definitions in "a=xxx,b=yyy" */ /* format */ char **pairs[] ) /* address of variable to receive pointer */ /* to NULL-terminated array of {name, */ /* value} pair strings; all storage is */ /* allocated contiguously */ { static const size_t altNumMax = 4; size_t numMax; int i; int num; int quote; int escape; size_t nbytes; const char **ptr; const char **end; int *del; char *memCp, **memCpp; const char *c; char *s, *d, **p; enum { preName, inName, preValue, inValue } state; /* debug output */ if ( handle && (handle->debug & 1) ) printf( "macParseDefns( %s )\n", defns ); /* allocate temporary pointer arrays; in worst case they need to have as many entries as the length of the defns string */ numMax = strlen( defns ); if ( numMax < altNumMax ) numMax = altNumMax; ptr = (const char **) calloc( numMax, sizeof( char * ) ); end = (const char **) calloc( numMax, sizeof( char * ) ); del = (int *) calloc( numMax, sizeof( int ) ); if ( ptr == NULL || end == NULL || del == NULL ) goto error; /* go through definitions, noting pointers to starts and ends of macro names and values; honor quotes and escapes; ignore white space around assignment and separator characters */ num = 0; del[0] = FALSE; quote = 0; state = preName; for ( c = (const char *) defns; *c != '\0'; c++ ) { /* handle quotes */ if ( quote ) quote = ( *c == quote ) ? 0 : quote; else if ( *c == '\'' || *c == '"' ) quote = *c; /* handle escapes (pointer incremented below) */ escape = ( *c == '\\' && *( c + 1 ) != '\0' ); switch ( state ) { case preName: if ( !quote && !escape && ( isspace( (int) *c ) || *c == ',' ) ) break; ptr[num] = c; state = inName; /* fall through (may be empty name) */ case inName: if ( quote || escape || ( *c != '=' && *c != ',' ) ) break; end[num] = c; while ( end[num] > ptr[num] && isspace( (int) *( end[num] - 1 ) ) ) end[num]--; num++; del[num] = FALSE; state = preValue; if ( *c != ',' ) break; del[num] = TRUE; /* fall through (','; will delete) */ case preValue: if ( !quote && !escape && isspace( (int) *c ) ) break; ptr[num] = c; state = inValue; /* fall through (may be empty value) */ case inValue: if ( quote || escape || *c != ',' ) break; end[num] = c; while ( end[num] > ptr[num] && isspace( (int) *( end[num] - 1 ) ) ) end[num]--; num++; del[num] = FALSE; state = preName; break; } /* if this was escape, increment pointer now (couldn't do before because could have ignored escape at start of name or value) */ if ( escape ) c++; } /* tidy up from state at end of string */ switch ( state ) { case preName: break; case inName: end[num] = c; while ( end[num] > ptr[num] && isspace( (int) *( end[num] - 1 ) ) ) end[num]--; num++; del[num] = TRUE; case preValue: ptr[num] = c; case inValue: end[num] = c; while ( end[num] > ptr[num] && isspace( (int) *( end[num] - 1 ) ) ) end[num]--; num++; del[num] = FALSE; } /* debug output */ if ( handle != NULL && handle->debug & 4 ) for ( i = 0; i < num; i += 2 ) printf( "[%ld] %.*s = [%ld] %.*s (%s) (%s)\n", (long) (end[i+0] - ptr[i+0]), (int) (end[i+0] - ptr[i+0]), ptr[i+0], (long) (end[i+1] - ptr[i+1]), (int) (end[i+1] - ptr[i+1]), ptr[i+1], del[i+0] ? "del" : "nodel", del[i+1] ? "del" : "nodel" ); /* calculate how much memory to allocate: pointers followed by strings */ nbytes = ( num + 2 ) * sizeof( char * ); for ( i = 0; i < num; i++ ) nbytes += end[i] - ptr[i] + 1; /* allocate memory and set returned pairs pointer */ memCp = malloc( nbytes ); if ( memCp == NULL ) goto error; memCpp = ( char ** ) memCp; *pairs = memCpp; /* copy pointers and strings (memCpp accesses the pointer section and memCp accesses the string section) */ memCp += ( num + 2 ) * sizeof( char * ); for ( i = 0; i < num; i++ ) { /* if no '=' followed the name, macro will be deleted */ if ( del[i] ) *memCpp++ = NULL; else *memCpp++ = memCp; /* copy value regardless of the above */ strncpy( memCp, (const char *) ptr[i], end[i] - ptr[i] ); memCp += end[i] - ptr[i]; *memCp++ = '\0'; } /* add two NULL pointers */ *memCpp++ = NULL; *memCpp++ = NULL; /* remove quotes and escapes from names in place (unlike values, they will not be re-parsed) */ for ( p = *pairs; *p != NULL; p += 2 ) { quote = 0; for ( s = d = *p; *s != '\0'; s++ ) { /* quotes are not copied */ if ( quote ) { if ( *s == quote ) { quote = 0; continue; } } else if ( *s == '\'' || *s == '"' ) { quote = *s; continue; } /* escapes are not copied but next character is */ if ( *s == '\\' && *( s + 1 ) != '\0' ) s++; /* others are copied */ *d++ = *s; } /* need to terminate destination */ *d++ = '\0'; } /* free workspace */ free( ( void * ) ptr ); free( ( void * ) end ); free( ( char * ) del ); /* debug output */ if ( handle != NULL && handle->debug & 1 ) printf( "macParseDefns() -> %d\n", num / 2 ); /* success exit; return number of definitions */ return num / 2; /* error exit */ error: errlogPrintf( "macParseDefns: failed to allocate memory\n" ); if ( ptr != NULL ) free( ( void * ) ptr ); if ( end != NULL ) free( ( void * ) end ); if ( del != NULL ) free( ( char * ) del ); *pairs = NULL; return -1; } /* * Install an array of name / value pairs as macro definitions. The * array should have an even number of elements followed by at least * one (preferably two) NULL pointers */ long /* #macros defined; <0 = ERROR */ epicsShareAPI macInstallMacros( MAC_HANDLE *handle, /* opaque handle */ char *pairs[] ) /* pointer to NULL-terminated array of */ /* {name,value} pair strings; a NULL */ /* value implies undefined; a NULL */ /* argument implies no macros */ { int n; char **p; /* debug output */ if ( handle->debug & 1 ) printf( "macInstallMacros( %s, %s, ... )\n", pairs && pairs[0] ? pairs[0] : "NULL", pairs && pairs[1] ? pairs[1] : "NULL" ); /* go through array defining macros */ for ( n = 0, p = pairs; p != NULL && p[0] != NULL; n++, p += 2 ) { if ( macPutValue( handle, p[0], p[1] ) < 0 ) return -1; } /* debug output */ if ( handle->debug & 1 ) printf( "macInstallMacros() -> %d\n", n ); /* return number of macros defined */ return n; } base-7.0.3.1/modules/libcom/src/misc/Makefile0000664000577000060420000000230713557101274017514 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/misc INC += alarm.h INC += alarmString.h INC += adjustment.h INC += cantProceed.h INC += dbDefs.h INC += epicsConvert.h INC += epicsExit.h INC += epicsStdlib.h INC += epicsString.h INC += epicsTypes.h INC += shareLib.h INC += epicsExport.h INC += unixFileName.h INC += locationException.h INC += ipAddrToAsciiAsynchronous.h INC += compilerDependencies.h INC += epicsUnitTest.h INC += testMain.h # epicsVersion.h is created by this Makefile INC += epicsVersion.h Com_SRCS += alarmString.c Com_SRCS += aToIPAddr.c Com_SRCS += adjustment.c Com_SRCS += cantProceed.c Com_SRCS += epicsConvert.c Com_SRCS += epicsExit.c Com_SRCS += epicsStdlib.c Com_SRCS += epicsString.c Com_SRCS += truncateFile.c Com_SRCS += ipAddrToAsciiAsynchronous.cpp Com_SRCS += epicsUnitTest.c base-7.0.3.1/modules/libcom/src/misc/RULES0000664000577000060420000000144413557101274016672 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. MAKEVERSION = $(LIBCOM)/misc/makeEpicsVersion.pl MAKEVERSION_FLAGS = -o $(notdir $@) $(QUIET_FLAG) MAKEVERSION_FLAGS += $(if $(EPICS_SITE_VERSION),-v "$(EPICS_SITE_VERSION)") $(COMMON_DIR)/epicsVersion.h: $(CONFIG)/CONFIG_BASE_VERSION \ $(CONFIG)/CONFIG_SITE $(MAKEVERSION) @$(RM) $(notdir $@) $(PERL) $(MAKEVERSION) $(MAKEVERSION_FLAGS) $< @$(MV) $(notdir $@) $@ base-7.0.3.1/modules/libcom/src/misc/aToIPAddr.c0000664000577000060420000001177013557101274017773 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 LANS LLC, as Operator of * Los Alamos National Laboratory. * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * rational replacement for inet_addr() * * author: Jeff Hill */ #include #include #define epicsExportSharedSymbols #include "epicsTypes.h" #include "osiSock.h" #ifndef NELEMENTS #define NELEMENTS(A) (sizeof(A)/sizeof(A[0])) #endif /*NELEMENTS*/ /* * addrArrayToUL () */ static int addrArrayToUL ( const unsigned *pAddr, unsigned nElements, struct in_addr *pIpAddr ) { unsigned i; epicsUInt32 addr = 0ul; for ( i=0u; i < nElements; i++ ) { if ( pAddr[i] > 0xff ) { return -1; } addr <<= 8; addr |= ( epicsUInt32 ) pAddr[i]; } pIpAddr->s_addr = htonl ( addr ); return 0; } /* * initIPAddr() * !! ipAddr should be passed in in network byte order !! * !! port is passed in in host byte order !! */ static int initIPAddr ( struct in_addr ipAddr, unsigned port, struct sockaddr_in *pIP ) { if ( port > 0xffff ) { return -1; } { epicsUInt16 port_16 = (epicsUInt16) port; memset (pIP, '\0', sizeof(*pIP)); pIP->sin_family = AF_INET; pIP->sin_port = htons(port_16); pIP->sin_addr = ipAddr; } return 0; } /* * rational replacement for inet_addr() * which allows the limited broadcast address * 255.255.255.255, allows the user * to specify a port number, and allows also a * named host to be specified. * * Sets the port number to "defaultPort" only if * "pAddrString" does not contain an address of the form * "n.n.n.n:p or host:p" */ epicsShareFunc int epicsShareAPI aToIPAddr( const char *pAddrString, unsigned short defaultPort, struct sockaddr_in *pIP ) { int status; unsigned addr[4]; unsigned long rawAddr; /* * !! change n elements here requires change in format below !! */ char hostName[512]; char dummy[8]; unsigned port; struct in_addr ina; /* * dotted ip addresses */ status = sscanf ( pAddrString, " %u . %u . %u . %u %7s ", addr, addr+1u, addr+2u, addr+3u, dummy ); if ( status == 4 ) { if ( addrArrayToUL ( addr, NELEMENTS ( addr ), & ina ) < 0 ) { return -1; } port = defaultPort; return initIPAddr ( ina, port, pIP ); } /* * dotted ip addresses and port */ status = sscanf ( pAddrString, " %u . %u . %u . %u : %u %7s", addr, addr+1u, addr+2u, addr+3u, &port, dummy ); if ( status >= 5 ) { if ( status > 5 ) { /* * valid at the start but detritus on the end */ return -1; } if ( addrArrayToUL ( addr, NELEMENTS ( addr ), &ina ) < 0 ) { return -1; } return initIPAddr ( ina, port, pIP ); } /* * IP address as a raw number */ status = sscanf ( pAddrString, " %lu %7s ", &rawAddr, dummy ); if ( status == 1 ) { if ( rawAddr > 0xffffffff ) { return -1; } port = defaultPort; { epicsUInt32 rawAddr_32 = ( epicsUInt32 ) rawAddr; ina.s_addr = htonl ( rawAddr_32 ); return initIPAddr ( ina, port, pIP ); } } /* * IP address as a raw number, and port */ status = sscanf ( pAddrString, " %lu : %u %7s ", &rawAddr, &port, dummy ); if ( status >= 2 ) { if ( status > 2 ) { /* * valid at the start but detritus on the end */ return -1; } if ( rawAddr > 0xffffffff ) { return -1; } { epicsUInt32 rawAddr_32 = ( epicsUInt32 ) rawAddr; ina.s_addr = htonl ( rawAddr_32 ); return initIPAddr ( ina, port, pIP ); } } /* * host name string */ status = sscanf ( pAddrString, " %511[^:] %s ", hostName, dummy ); if ( status == 1 ) { port = defaultPort; status = hostToIPAddr ( hostName, &ina ); if ( status == 0 ) { return initIPAddr ( ina, port, pIP ); } } /* * host name string, and port */ status = sscanf ( pAddrString, " %511[^:] : %u %s ", hostName, &port, dummy ); if ( status >= 2 ) { if ( status > 2 ) { /* * valid at the start but detritus on the end */ return -1; } status = hostToIPAddr ( hostName, &ina ); if ( status == 0 ) { return initIPAddr ( ina, port, pIP ); } } return -1; } base-7.0.3.1/modules/libcom/src/misc/adjustment.c0000664000577000060420000000366713557101274020410 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* src/libCom/adjustment.c */ /* Author: Peregrine McGehee */ #include #include #include /* Up to now epicsShareThings have been declared as imports * (Appropriate for other stuff) * After setting the following they will be declared as exports * (Appropriate for what we implenment) */ #define epicsExportSharedSymbols #include "adjustment.h" epicsShareFunc size_t adjustToWorstCaseAlignment(size_t size) { int align_size, adjust; struct test_long_word { char c; long lw; }; struct test_double { char c; double d; }; struct test_ptr { char c; void *p; }; int test_long_size = sizeof(struct test_long_word) - sizeof(long); int test_double_size = sizeof(struct test_double) - sizeof(double); int test_ptr_size = sizeof(struct test_ptr) - sizeof(void *); size_t adjusted_size = size; /* * Use Jeff's alignment tests to determine worst case of long, * double or pointer alignment requirements. */ align_size = test_long_size > test_ptr_size ? test_long_size : test_ptr_size; align_size = align_size > test_double_size ? align_size : test_double_size; /* * Increase the size to fit worst case alignment if not already * properly aligned. */ adjust = align_size - size%align_size; if (adjust != align_size) adjusted_size += adjust; return (adjusted_size); } base-7.0.3.1/modules/libcom/src/misc/adjustment.h0000664000577000060420000000143213557101274020401 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* src/libCom/adjustment.h */ #ifndef INCadjustmenth #define INCadjustmenth #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc size_t adjustToWorstCaseAlignment(size_t size); #ifdef __cplusplus } #endif #endif /*INCadjustmenth*/ base-7.0.3.1/modules/libcom/src/misc/alarm.h0000664000577000060420000000572513557101274017330 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Alarm definitions, must match menuAlarmSevr.dbd and menuAlarmStat.dbd */ /* * Authors: Bob Dalesio and Marty Kraimer * Date: 11-7-90 */ #ifndef INC_alarm_H #define INC_alarm_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif #define NO_ALARM 0 /* ALARM SEVERITIES - must match menuAlarmSevr.dbd */ typedef enum { epicsSevNone = NO_ALARM, epicsSevMinor, epicsSevMajor, epicsSevInvalid, ALARM_NSEV } epicsAlarmSeverity; #define firstEpicsAlarmSev epicsSevNone #define MINOR_ALARM epicsSevMinor #define MAJOR_ALARM epicsSevMajor #define INVALID_ALARM epicsSevInvalid #define lastEpicsAlarmSev epicsSevInvalid /* ALARM STATUS - must match menuAlarmStat.dbd */ typedef enum { epicsAlarmNone = NO_ALARM, epicsAlarmRead, epicsAlarmWrite, epicsAlarmHiHi, epicsAlarmHigh, epicsAlarmLoLo, epicsAlarmLow, epicsAlarmState, epicsAlarmCos, epicsAlarmComm, epicsAlarmTimeout, epicsAlarmHwLimit, epicsAlarmCalc, epicsAlarmScan, epicsAlarmLink, epicsAlarmSoft, epicsAlarmBadSub, epicsAlarmUDF, epicsAlarmDisable, epicsAlarmSimm, epicsAlarmReadAccess, epicsAlarmWriteAccess, ALARM_NSTATUS } epicsAlarmCondition; #define firstEpicsAlarmCond epicsAlarmNone #define READ_ALARM epicsAlarmRead #define WRITE_ALARM epicsAlarmWrite #define HIHI_ALARM epicsAlarmHiHi #define HIGH_ALARM epicsAlarmHigh #define LOLO_ALARM epicsAlarmLoLo #define LOW_ALARM epicsAlarmLow #define STATE_ALARM epicsAlarmState #define COS_ALARM epicsAlarmCos #define COMM_ALARM epicsAlarmComm #define TIMEOUT_ALARM epicsAlarmTimeout #define HW_LIMIT_ALARM epicsAlarmHwLimit #define CALC_ALARM epicsAlarmCalc #define SCAN_ALARM epicsAlarmScan #define LINK_ALARM epicsAlarmLink #define SOFT_ALARM epicsAlarmSoft #define BAD_SUB_ALARM epicsAlarmBadSub #define UDF_ALARM epicsAlarmUDF #define DISABLE_ALARM epicsAlarmDisable #define SIMM_ALARM epicsAlarmSimm #define READ_ACCESS_ALARM epicsAlarmReadAccess #define WRITE_ACCESS_ALARM epicsAlarmWriteAccess #define lastEpicsAlarmCond epicsAlarmWriteAccess /* Name string arrays */ epicsShareExtern const char *epicsAlarmSeverityStrings [ALARM_NSEV]; epicsShareExtern const char *epicsAlarmConditionStrings [ALARM_NSTATUS]; #ifdef __cplusplus } #endif #endif /* INC_alarm_H */ base-7.0.3.1/modules/libcom/src/misc/alarmString.c0000664000577000060420000000232613557101274020504 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* String names for alarm status and severity values */ #define epicsExportSharedSymbols #include "alarm.h" /* ALARM SEVERITIES - must match menuAlarmSevr.dbd and alarm.h */ epicsShareDef const char * epicsAlarmSeverityStrings[ALARM_NSEV] = { "NO_ALARM", "MINOR", "MAJOR", "INVALID" }; /* ALARM STATUS - must match menuAlarmStat.dbd and alarm.h */ epicsShareDef const char * epicsAlarmConditionStrings[ALARM_NSTATUS] = { "NO_ALARM", "READ", "WRITE", "HIHI", "HIGH", "LOLO", "LOW", "STATE", "COS", "COMM", "TIMEOUT", "HWLIMIT", "CALC", "SCAN", "LINK", "SOFT", "BAD_SUB", "UDF", "DISABLE", "SIMM", "READ_ACCESS", "WRITE_ACCESS" }; base-7.0.3.1/modules/libcom/src/misc/alarmString.h0000664000577000060420000000164413557101274020513 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * This file is deprecated, use alarm.h instead. * * Old string names for alarm status and severity values */ #ifndef INC_alarmString_H #define INC_alarmString_H #include "alarm.h" #ifdef __cplusplus extern "C" { #endif /* Old versions of alarmString.h defined these names: */ #define alarmSeverityString epicsAlarmSeverityStrings #define alarmStatusString epicsAlarmConditionStrings #ifdef __cplusplus } #endif #endif base-7.0.3.1/modules/libcom/src/misc/cantProceed.c0000664000577000060420000000437213557101274020453 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* Author: Marty Kraimer Date: 04JAN99 */ #include #include #include #define epicsExportSharedSymbols #include "errlog.h" #include "cantProceed.h" #include "epicsThread.h" #include "epicsStackTrace.h" epicsShareFunc 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", msg, (unsigned long)count, (unsigned long)size); errlogPrintf("Thread %s (%p) suspending.\n", epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); errlogFlush(); epicsThreadSuspendSelf(); } } return mem; } epicsShareFunc 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", msg, (unsigned long)size); errlogPrintf("Thread %s (%p) suspending.\n", epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); errlogFlush(); epicsThreadSuspendSelf(); } } return mem; } epicsShareFunc void cantProceed(const char *msg, ...) { va_list pvar; va_start(pvar, msg); if (msg) errlogVprintf(msg, pvar); va_end(pvar); errlogPrintf("Thread %s (%p) can't proceed, suspending.\n", epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); epicsStackTrace(); errlogFlush(); epicsThreadSleep(1.0); while (1) epicsThreadSuspendSelf(); } base-7.0.3.1/modules/libcom/src/misc/cantProceed.h0000664000577000060420000000172713557101274020461 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INCcantProceedh #define INCcantProceedh #include #include "compilerDependencies.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void cantProceed(const char *errorMessage, ...) EPICS_PRINTF_STYLE(1,2); epicsShareFunc void * callocMustSucceed(size_t count, size_t size, const char *errorMessage); epicsShareFunc void * mallocMustSucceed(size_t size, const char *errorMessage); #ifdef __cplusplus } #endif #endif /* cantProceedh */ base-7.0.3.1/modules/libcom/src/misc/dbDefs.h0000664000577000060420000000360713557101274017420 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Marty Kraimer * Date: 6-1-90 */ #ifndef INC_dbDefs_H #define INC_dbDefs_H #include #ifdef TRUE # undef TRUE #endif #define TRUE 1 #ifdef FALSE # undef FALSE #endif #define FALSE 0 /* deprecated, use static */ #ifndef LOCAL # define LOCAL static #endif /* number of elements in an array */ #ifndef NELEMENTS # define NELEMENTS(array) (sizeof (array) / sizeof ((array) [0])) #endif /* byte offset of member in structure - deprecated, use offsetof */ #ifndef OFFSET # define OFFSET(structure, member) offsetof(structure, member) #endif /* Subtract member byte offset, returning pointer to parent object */ #ifndef CONTAINER # ifdef __GNUC__ # define CONTAINER(ptr, structure, member) ({ \ const __typeof(((structure*)0)->member) *_ptr = (ptr); \ (structure*)((char*)_ptr - offsetof(structure, member)); \ }) # else # define CONTAINER(ptr, structure, member) \ ((structure*)((char*)(ptr) - offsetof(structure, member))) # endif #endif /*Process Variable Name Size */ /* PVNAME_STRINGSZ includes the nil terminator */ #define PVNAME_STRINGSZ 61 #define PVNAME_SZ (PVNAME_STRINGSZ - 1) /* Buffer size for the string representation of a DBF_*LINK field */ #define PVLINK_STRINGSZ 1024 /* dbAccess enums/menus can have up to this many choices */ #define DB_MAX_CHOICES 30 #endif /* INC_dbDefs_H */ base-7.0.3.1/modules/libcom/src/misc/epicsConvert.c0000664000577000060420000000176713557101274020675 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /*epicsConvert.c*/ #include #include #define epicsExportSharedSymbols #include "epicsMath.h" #include "epicsConvert.h" #include "cantProceed.h" epicsShareFunc float epicsConvertDoubleToFloat(double value) { double abs; if (value == 0 || !finite(value)) return (float) value; abs = fabs(value); if (abs >= FLT_MAX) return (value > 0) ? FLT_MAX : -FLT_MAX; if (abs <= FLT_MIN) return (value > 0) ? FLT_MIN : -FLT_MIN; return (float) value; } base-7.0.3.1/modules/libcom/src/misc/epicsConvert.h0000664000577000060420000000137413557101274020674 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /*epicsConvert.h*/ #ifndef INC_epicsConvert_H #define INC_epicsConvert_H #include #ifdef __cplusplus extern "C" { #endif epicsShareFunc float epicsConvertDoubleToFloat(double value); #ifdef __cplusplus } #endif #endif /* INC_epicsConvert_H */ base-7.0.3.1/modules/libcom/src/misc/epicsExit.c0000664000577000060420000001267013557101274020161 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /*epicsExit.c*/ /* * Author: Marty Kraimer * Date: 23AUG2004 * Thread exit revisions: Jeff Hill * Date: 06Dec2006 * * Note that epicsExitCallAtThreadExits is currently called directly from the * thread entry wrapper in OS dependent code. That approach might not work * correctly if the thread exits indirectly instead of just returning from * the function specified to epicsThreadCreate. For example the thread might * exit via the exit() call. There might be OS dependent solutions for that * weakness. * */ #include #include #include #include #define epicsExportSharedSymbols #include "ellLib.h" #include "errlog.h" #include "epicsThread.h" #include "epicsMutex.h" #include "cantProceed.h" #include "epicsExit.h" void epicsMutexCleanup(void); typedef struct exitNode { ELLNODE node; epicsExitFunc func; void *arg; char name[1]; }exitNode; typedef struct exitPvt { ELLLIST list; } exitPvt; int atExitDebug = 0; static epicsThreadOnceId exitPvtOnce = EPICS_THREAD_ONCE_INIT; static epicsThreadOnceId exitLaterOnce = EPICS_THREAD_ONCE_INIT; static exitPvt * pExitPvtPerProcess = 0; static epicsMutexId exitPvtLock = 0; static epicsThreadPrivateId exitPvtPerThread = 0; static int exitLaterStatus; static void destroyExitPvt(exitPvt * pep) { ellFree ( &pep->list ); free ( pep ); } static exitPvt * createExitPvt(void) { exitPvt * pep = calloc ( 1, sizeof ( * pep ) ); if ( pep ) { ellInit ( &pep->list ); } return pep; } static void exitPvtOnceFunc(void *pParm) { exitPvtPerThread = epicsThreadPrivateCreate (); assert ( exitPvtPerThread ); exitPvtLock = epicsMutexMustCreate (); } static void epicsExitInit(void) { epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); } static void epicsExitCallAtExitsPvt(exitPvt *pep) { exitNode *pexitNode; while ( ( pexitNode = (exitNode *) ellLast ( & pep->list ) ) ) { if (atExitDebug && pexitNode->name[0]) fprintf(stderr, "atExit %s(%p)\n", pexitNode->name, pexitNode->arg); else if(atExitDebug) fprintf(stderr, "atExit %p(%p)\n", pexitNode->func, pexitNode->arg); pexitNode->func ( pexitNode->arg ); ellDelete ( & pep->list, & pexitNode->node ); free ( pexitNode ); } } epicsShareFunc void epicsExitCallAtExits(void) { exitPvt * pep = 0; epicsExitInit (); epicsMutexMustLock ( exitPvtLock ); if ( pExitPvtPerProcess ) { pep = pExitPvtPerProcess; pExitPvtPerProcess = 0; } epicsMutexUnlock ( exitPvtLock ); if ( pep ) { epicsExitCallAtExitsPvt ( pep ); destroyExitPvt ( pep ); } /* Handle specially to avoid circular reference */ epicsMutexCleanup(); } epicsShareFunc void epicsExitCallAtThreadExits(void) { exitPvt * pep; epicsExitInit (); pep = epicsThreadPrivateGet ( exitPvtPerThread ); if ( pep ) { epicsExitCallAtExitsPvt ( pep ); destroyExitPvt ( pep ); epicsThreadPrivateSet ( exitPvtPerThread, 0 ); } } static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg, const char *name) { int status = -1; exitNode * pExitNode = calloc ( 1, sizeof( *pExitNode ) + (name?strlen(name):0) ); if ( pExitNode ) { pExitNode->func = func; pExitNode->arg = arg; if(name) strcpy(pExitNode->name, name); ellAdd ( & pep->list, & pExitNode->node ); status = 0; } return status; } epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg) { exitPvt * pep; epicsExitInit (); pep = epicsThreadPrivateGet ( exitPvtPerThread ); if ( ! pep ) { pep = createExitPvt (); if ( ! pep ) { return -1; } epicsThreadPrivateSet ( exitPvtPerThread, pep ); } return epicsAtExitPvt ( pep, func, arg, NULL ); } epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name) { int status = -1; epicsExitInit (); epicsMutexMustLock ( exitPvtLock ); if ( !pExitPvtPerProcess ) { pExitPvtPerProcess = createExitPvt (); } if ( pExitPvtPerProcess ) { status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg, name ); } epicsMutexUnlock ( exitPvtLock ); return status; } epicsShareFunc void epicsExit(int status) { epicsExitCallAtExits(); epicsThreadSleep(0.1); exit(status); } static void exitNow(void *junk) { epicsExit(exitLaterStatus); } static void exitLaterOnceFunc(void *raw) { int *status = raw; exitLaterStatus = *status; epicsThreadMustCreate("exitLater", epicsThreadPriorityLow, epicsThreadGetStackSize(epicsThreadStackSmall), &exitNow, NULL); } epicsShareFunc void epicsExitLater(int status) { epicsThreadOnce(&exitLaterOnce, &exitLaterOnceFunc, &status); } #include "epicsExport.h" epicsExportAddress(int,atExitDebug); base-7.0.3.1/modules/libcom/src/misc/epicsExit.h0000664000577000060420000000212213557101274020155 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /*epicsExit.h*/ #ifndef epicsExith #define epicsExith #include #ifdef __cplusplus extern "C" { #endif typedef void (*epicsExitFunc)(void *arg); epicsShareFunc void epicsExit(int status); epicsShareFunc void epicsExitLater(int status); epicsShareFunc void epicsExitCallAtExits(void); epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name); #define epicsAtExit(F,A) epicsAtExit3(F,A,#F) epicsShareFunc void epicsExitCallAtThreadExits(void); epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg); #ifdef __cplusplus } #endif #endif /*epicsExith*/ base-7.0.3.1/modules/libcom/src/misc/epicsExport.h0000664000577000060420000000251613557101274020534 0ustar anjaesctl/*epicsExport.h */ /*************************************************************************\ * 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. * 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. \*************************************************************************/ #ifndef INCepicsExporth #define INCepicsExporth #ifdef __cplusplus extern "C" { #endif #define epicsExportSharedSymbols #include typedef void (*REGISTRAR)(void); #define EPICS_EXPORT_POBJ(typ,obj) pvar_ ## typ ## _ ## obj #define EPICS_EXPORT_PFUNC(obj) pvar_func_ ## obj #define epicsExportAddress(typ,obj) \ epicsShareExtern typ *EPICS_EXPORT_POBJ(typ,obj); \ epicsShareDef typ *EPICS_EXPORT_POBJ(typ,obj) = (typ *)(char *)&obj #define epicsExportRegistrar(func) \ epicsShareFunc REGISTRAR EPICS_EXPORT_PFUNC(func) = (REGISTRAR)(void*)&func #define epicsRegisterFunction(func) \ static void register_func_ ## func(void) { \ registryFunctionAdd(#func,(REGISTRYFUNCTION)func);} \ epicsExportRegistrar(register_func_ ## func) #ifdef __cplusplus } #endif #endif /* epicsExporth */ base-7.0.3.1/modules/libcom/src/misc/epicsStdlib.c0000664000577000060420000002106213557101274020464 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonna LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Authors: Eric Norum & Andrew Johnson */ #include #include #include #include #define epicsExportSharedSymbols #include "epicsMath.h" #include "epicsStdlib.h" #include "epicsString.h" #include "epicsConvert.h" /* These are the conversion primitives */ epicsShareFunc int epicsParseLong(const char *str, long *to, int base, char **units) { int c; char *endp; long value; while ((c = *str) && isspace(c)) ++str; errno = 0; value = strtol(str, &endp, base); if (endp == str) return S_stdlib_noConversion; if (errno == EINVAL) /* Not universally supported */ return S_stdlib_badBase; if (errno == ERANGE) return S_stdlib_overflow; while ((c = *endp) && isspace(c)) ++endp; if (c && !units) return S_stdlib_extraneous; *to = value; if (units) *units = endp; return 0; } epicsShareFunc int epicsParseULong(const char *str, unsigned long *to, int base, char **units) { int c; char *endp; unsigned long value; while ((c = *str) && isspace(c)) ++str; errno = 0; value = strtoul(str, &endp, base); if (endp == str) return S_stdlib_noConversion; if (errno == EINVAL) /* Not universally supported */ return S_stdlib_badBase; if (errno == ERANGE) return S_stdlib_overflow; while ((c = *endp) && isspace(c)) ++endp; if (c && !units) return S_stdlib_extraneous; *to = value; if (units) *units = endp; return 0; } epicsShareFunc int epicsParseLLong(const char *str, long long *to, int base, char **units) { int c; char *endp; long long value; while ((c = *str) && isspace(c)) ++str; errno = 0; value = strtoll(str, &endp, base); if (endp == str) return S_stdlib_noConversion; if (errno == EINVAL) /* Not universally supported */ return S_stdlib_badBase; if (errno == ERANGE) return S_stdlib_overflow; while ((c = *endp) && isspace(c)) ++endp; if (c && !units) return S_stdlib_extraneous; *to = value; if (units) *units = endp; return 0; } epicsShareFunc int epicsParseULLong(const char *str, unsigned long long *to, int base, char **units) { int c; char *endp; unsigned long long value; while ((c = *str) && isspace(c)) ++str; errno = 0; value = strtoull(str, &endp, base); if (endp == str) return S_stdlib_noConversion; if (errno == EINVAL) /* Not universally supported */ return S_stdlib_badBase; if (errno == ERANGE) return S_stdlib_overflow; while ((c = *endp) && isspace(c)) ++endp; if (c && !units) return S_stdlib_extraneous; *to = value; if (units) *units = endp; return 0; } epicsShareFunc int epicsParseDouble(const char *str, double *to, char **units) { int c; char *endp; double value; while ((c = *str) && isspace(c)) ++str; errno = 0; value = epicsStrtod(str, &endp); if (endp == str) return S_stdlib_noConversion; if (errno == ERANGE) return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow; while ((c = *endp) && isspace(c)) ++endp; if (c && !units) return S_stdlib_extraneous; *to = value; if (units) *units = endp; return 0; } /* These call the primitives */ epicsShareFunc int epicsParseInt8(const char *str, epicsInt8 *to, int base, char **units) { long value; int status = epicsParseLong(str, &value, base, units); if (status) return status; if (value < -0x80 || value > 0x7f) return S_stdlib_overflow; *to = (epicsInt8) value; return 0; } epicsShareFunc int epicsParseUInt8(const char *str, epicsUInt8 *to, int base, char **units) { unsigned long value; int status = epicsParseULong(str, &value, base, units); if (status) return status; if (value > 0xff && value <= ~0xffUL) return S_stdlib_overflow; *to = (epicsUInt8) value; return 0; } epicsShareFunc int epicsParseInt16(const char *str, epicsInt16 *to, int base, char **units) { long value; int status = epicsParseLong(str, &value, base, units); if (status) return status; if (value < -0x8000 || value > 0x7fff) return S_stdlib_overflow; *to = (epicsInt16) value; return 0; } epicsShareFunc int epicsParseUInt16(const char *str, epicsUInt16 *to, int base, char **units) { unsigned long value; int status = epicsParseULong(str, &value, base, units); if (status) return status; if (value > 0xffff && value <= ~0xffffUL) return S_stdlib_overflow; *to = (epicsUInt16) value; return 0; } epicsShareFunc int epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units) { long value; int status = epicsParseLong(str, &value, base, units); if (status) return status; #if (LONG_MAX > 0x7fffffffLL) if (value < -0x80000000L || value > 0x7fffffffL) return S_stdlib_overflow; #endif *to = (epicsInt32) value; return 0; } epicsShareFunc int epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units) { unsigned long value; int status = epicsParseULong(str, &value, base, units); if (status) return status; #if (ULONG_MAX > 0xffffffffULL) if (value > 0xffffffffUL && value <= ~0xffffffffUL) return S_stdlib_overflow; #endif *to = (epicsUInt32) value; return 0; } epicsShareFunc int epicsParseInt64(const char *str, epicsInt64 *to, int base, char **units) { #if (LONG_MAX == 0x7fffffffffffffffLL) long value; int status = epicsParseLong(str, &value, base, units); #else long long value; int status = epicsParseLLong(str, &value, base, units); #endif if (status) return status; *to = (epicsInt64) value; return 0; } epicsShareFunc int epicsParseUInt64(const char *str, epicsUInt64 *to, int base, char **units) { #if (ULONG_MAX == 0xffffffffffffffffULL) unsigned long value; int status = epicsParseULong(str, &value, base, units); #else unsigned long long value; int status = epicsParseULLong(str, &value, base, units); #endif if (status) return status; *to = (epicsUInt64) value; return 0; } epicsShareFunc int epicsParseFloat(const char *str, float *to, char **units) { double value, abs; int status = epicsParseDouble(str, &value, units); if (status) return status; abs = fabs(value); if (value > 0 && abs <= FLT_MIN) return S_stdlib_underflow; if (finite(value) && abs >= FLT_MAX) return S_stdlib_overflow; *to = (float) value; return 0; } /* If strtod() works properly, the OS-specific osdStrtod.h does: * #define epicsStrtod strtod * * If strtod() is broken, osdStrtod.h defines this prototype: * epicsShareFunc double epicsStrtod(const char *str, char **endp); */ #ifndef epicsStrtod epicsShareFunc double epicsStrtod(const char *str, char **endp) { const char *cp = str; int negative = 0; double res; while (isspace((int)*cp)) cp++; if (*cp == '+') { cp++; } else if (*cp == '-') { negative = 1; cp++; } if (epicsStrnCaseCmp("0x", cp, 2) == 0) { if (negative) return strtol(str, endp, 16); else return strtoul(str, endp, 16); } if (!isalpha((int)*cp)) { res = strtod(str, endp); if (isinf(res)) errno = ERANGE; return res; } if (epicsStrnCaseCmp("NAN", cp, 3) == 0) { res = epicsNAN; cp += 3; if (*cp == '(') { cp++; while (*cp && (*cp++ != ')')) continue; } } else if (epicsStrnCaseCmp("INF", cp, 3) == 0) { res = negative ? -epicsINF : epicsINF; cp += 3; if (epicsStrnCaseCmp("INITY", cp, 5) == 0) { cp += 5; } } else { cp = str; res = 0; } if (endp) *endp = (char *)cp; return res; } #endif base-7.0.3.1/modules/libcom/src/misc/epicsStdlib.h0000664000577000060420000000626513557101274020501 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsStdlib.h */ /* Author: Eric Norum */ #ifndef INC_epicsStdlib_H #define INC_epicsStdlib_H #include #include #include "shareLib.h" #include "osdStrtod.h" #include "epicsTypes.h" #include "errMdef.h" #ifdef __cplusplus extern "C" { #endif #define S_stdlib_noConversion (M_stdlib | 1) /* No digits to convert */ #define S_stdlib_extraneous (M_stdlib | 2) /* Extraneous characters */ #define S_stdlib_underflow (M_stdlib | 3) /* Too small to represent */ #define S_stdlib_overflow (M_stdlib | 4) /* Too large to represent */ #define S_stdlib_badBase (M_stdlib | 5) /* Number base not supported */ epicsShareFunc int epicsParseLong(const char *str, long *to, int base, char **units); epicsShareFunc int epicsParseULong(const char *str, unsigned long *to, int base, char **units); epicsShareFunc int epicsParseLLong(const char *str, long long *to, int base, char **units); epicsShareFunc int epicsParseULLong(const char *str, unsigned long long *to, int base, char **units); epicsShareFunc int epicsParseDouble(const char *str, double *to, char **units); epicsShareFunc int epicsParseFloat(const char *str, float *to, char **units); epicsShareFunc int epicsParseInt8(const char *str, epicsInt8 *to, int base, char **units); epicsShareFunc int epicsParseUInt8(const char *str, epicsUInt8 *to, int base, char **units); epicsShareFunc int epicsParseInt16(const char *str, epicsInt16 *to, int base, char **units); epicsShareFunc int epicsParseUInt16(const char *str, epicsUInt16 *to, int base, char **units); epicsShareFunc int epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units); epicsShareFunc int epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units); epicsShareFunc int epicsParseInt64(const char *str, epicsInt64 *to, int base, char **units); epicsShareFunc int epicsParseUInt64(const char *str, epicsUInt64 *to, int base, char **units); #define epicsParseFloat32(str, to, units) epicsParseFloat(str, to, units) #define epicsParseFloat64(str, to, units) epicsParseDouble(str, to, units) /* These macros return 1 if successful, 0 on failure. * This is analagous to the return value from sscanf() */ #define epicsScanLong(str, to, base) (!epicsParseLong(str, to, base, NULL)) #define epicsScanULong(str, to, base) (!epicsParseULong(str, to, base, NULL)) #define epicsScanLLong(str, to, base) (!epicsParseLLong(str, to, base, NULL)) #define epicsScanULLong(str, to, base) (!epicsParseULLong(str, to, base, NULL)) #define epicsScanFloat(str, to) (!epicsParseFloat(str, to, NULL)) #define epicsScanDouble(str, to) (!epicsParseDouble(str, to, NULL)) #ifdef __cplusplus } #endif #endif /* INC_epicsStdlib_H */ base-7.0.3.1/modules/libcom/src/misc/epicsString.c0000664000577000060420000002254713557101274020522 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Authors: Jun-ichi Odagiri, Marty Kraimer, Eric Norum, * Mark Rivers, Andrew Johnson, Ralph Lange * * Routines in this file should have corresponding test code in * libCom/test/epicsStringTest.c */ #include #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsStdio.h" #include "cantProceed.h" #include "epicsString.h" /* Deprecated, use epicsStrnRawFromEscaped() instead */ int dbTranslateEscape(char *dst, const char *src) { size_t big_enough = strlen(src) + 1; return epicsStrnRawFromEscaped(dst, big_enough, src, big_enough); } int epicsStrnRawFromEscaped(char *dst, size_t dstlen, const char *src, size_t srclen) { int rem = dstlen; int ndst = 0; while (srclen--) { int c = *src++; #define OUT(chr) if (--rem > 0) ndst++, *dst++ = chr if (!c) break; input: if (c != '\\') { OUT(c); continue; } if (!srclen-- || !(c = *src++)) break; switch (c) { case 'a': OUT('\a'); break; case 'b': OUT('\b'); break; case 'f': OUT('\f'); break; case 'n': OUT('\n'); break; case 'r': OUT('\r'); break; case 't': OUT('\t'); break; case 'v': OUT('\v'); break; case '\\': OUT('\\'); break; case '\'': OUT('\''); break; case '\"': OUT('\"'); break; case '0' :case '1' :case '2' :case '3' : case '4' :case '5' :case '6' :case '7' : { /* \ooo */ unsigned int u = c - '0'; if (!srclen-- || !(c = *src++)) { OUT(u); goto done; } if (c < '0' || c > '7') { OUT(u); goto input; } u = u << 3 | (c - '0'); if (!srclen-- || !(c = *src++)) { OUT(u); goto done; } if (c < '0' || c > '7') { OUT(u); goto input; } u = u << 3 | (c - '0'); if (u > 0377) { /* Undefined behaviour! */ } OUT(u); } break; case 'x' : { /* \xXXX... */ unsigned int u = 0; if (!srclen-- || !(c = *src++ & 0xff)) goto done; while (isxdigit(c)) { u = u << 4 | ((c > '9') ? toupper(c) - 'A' + 10 : c - '0'); if (u > 0xff) { /* Undefined behaviour! */ } if (!srclen-- || !(c = *src++ & 0xff)) { OUT(u); goto done; } } OUT(u); goto input; } default: OUT(c); } #undef OUT } done: if (dstlen) *dst = '\0'; return ndst; } int epicsStrnEscapedFromRaw(char *dst, size_t dstlen, const char *src, size_t srclen) { int rem = dstlen; int ndst = 0; if (dst == src) return -1; while (srclen--) { int c = *src++; #define OUT(chr) ndst++; if (--rem > 0) *dst++ = chr switch (c) { case '\a': OUT('\\'); OUT('a'); break; case '\b': OUT('\\'); OUT('b'); break; case '\f': OUT('\\'); OUT('f'); break; case '\n': OUT('\\'); OUT('n'); break; case '\r': OUT('\\'); OUT('r'); break; case '\t': OUT('\\'); OUT('t'); break; case '\v': OUT('\\'); OUT('v'); break; case '\\': OUT('\\'); OUT('\\'); break; case '\'': OUT('\\'); OUT('\''); break; case '\"': OUT('\\'); OUT('\"'); break; default: if (isprint(c & 0xff)) { OUT(c); break; } OUT('\\'); OUT('0' + ((c & 0300) >> 6)); OUT('0' + ((c & 0070) >> 3)); OUT('0' + (c & 0007)); } #undef OUT } if (dstlen) *dst = '\0'; return ndst; } size_t epicsStrnEscapedFromRawSize(const char *src, size_t srclen) { size_t ndst = srclen; while (srclen--) { int c = *src++; switch (c) { case '\a': case '\b': case '\f': case '\n': case '\r': case '\t': case '\v': case '\\': case '\'': case '\"': ndst++; break; default: if (!isprint(c & 0xff)) ndst += 3; } } return ndst; } int epicsStrCaseCmp(const char *s1, const char *s2) { while (1) { int ch1 = toupper((int) *s1); int ch2 = toupper((int) *s2); if (ch2 == 0) return (ch1 != 0); if (ch1 == 0) return -1; if (ch1 < ch2) return -1; if (ch1 > ch2) return 1; s1++; s2++; } } int epicsStrnCaseCmp(const char *s1, const char *s2, size_t len) { size_t i = 0; while (i++ < len) { int ch1 = toupper((int) *s1); int ch2 = toupper((int) *s2); if (ch2 == 0) return (ch1 != 0); if (ch1 == 0) return -1; if (ch1 < ch2) return -1; if (ch1 > ch2) return 1; s1++; s2++; } return 0; } char * epicsStrnDup(const char *s, size_t len) { char *buf = mallocMustSucceed(len + 1, "epicsStrnDup"); strncpy(buf, s, len); buf[len] = '\0'; return buf; } char * epicsStrDup(const char *s) { return strcpy(mallocMustSucceed(strlen(s)+1, "epicsStrDup"), s); } int epicsStrPrintEscaped(FILE *fp, const char *s, size_t len) { int nout = 0; while (len--) { char c = *s++; switch (c) { case '\a': nout += fprintf(fp, "\\a"); break; case '\b': nout += fprintf(fp, "\\b"); break; case '\f': nout += fprintf(fp, "\\f"); break; case '\n': nout += fprintf(fp, "\\n"); break; case '\r': nout += fprintf(fp, "\\r"); break; case '\t': nout += fprintf(fp, "\\t"); break; case '\v': nout += fprintf(fp, "\\v"); break; case '\\': nout += fprintf(fp, "\\\\"); break; case '\'': nout += fprintf(fp, "\\'"); break; case '\"': nout += fprintf(fp, "\\\""); break; default: if (isprint(0xff & (int)c)) nout += fprintf(fp, "%c", c); else nout += fprintf(fp, "\\%03o", (unsigned char)c); break; } } return nout; } /* Until Base requires POSIX 2008 we must provide our own implementation */ size_t epicsStrnLen(const char *s, size_t maxlen) { size_t i; for (i=0; i> 5)); if (!(c = *str++)) break; hash ^= (hash << 7) ^ c ^ (hash >> 3); } return hash; } unsigned int epicsMemHash(const char *str, size_t length, unsigned int seed) { unsigned int hash = seed; while (length--) { hash ^= ~((hash << 11) ^ *str++ ^ (hash >> 5)); if (!length--) break; hash ^= (hash << 7) ^ *str++ ^ (hash >> 3); } return hash; } base-7.0.3.1/modules/libcom/src/misc/epicsString.h0000664000577000060420000000433113557101274020516 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Authors: Jun-ichi Odagiri, Marty Kraimer, Eric Norum, * Mark Rivers, Andrew Johnson, Ralph Lange */ #ifndef INC_epicsString_H #define INC_epicsString_H #include #include "epicsTypes.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc int epicsStrnRawFromEscaped(char *outbuf, size_t outsize, const char *inbuf, size_t inlen); epicsShareFunc int epicsStrnEscapedFromRaw(char *outbuf, size_t outsize, const char *inbuf, size_t inlen); epicsShareFunc size_t epicsStrnEscapedFromRawSize(const char *buf, size_t len); epicsShareFunc int epicsStrCaseCmp(const char *s1, const char *s2); epicsShareFunc int epicsStrnCaseCmp(const char *s1, const char *s2, size_t len); epicsShareFunc char * epicsStrDup(const char *s); epicsShareFunc char * epicsStrnDup(const char *s, size_t len); epicsShareFunc int epicsStrPrintEscaped(FILE *fp, const char *s, size_t n); #define epicsStrSnPrintEscaped epicsStrnEscapedFromRaw epicsShareFunc size_t epicsStrnLen(const char *s, size_t maxlen); epicsShareFunc int epicsStrGlobMatch(const char *str, const char *pattern); epicsShareFunc char * epicsStrtok_r(char *s, const char *delim, char **lasts); epicsShareFunc unsigned int epicsStrHash(const char *str, unsigned int seed); epicsShareFunc unsigned int epicsMemHash(const char *str, size_t length, unsigned int seed); /* dbTranslateEscape is deprecated, use epicsStrnRawFromEscaped instead */ epicsShareFunc int dbTranslateEscape(char *s, const char *ct); #ifdef __cplusplus } #endif #endif /* INC_epicsString_H */ base-7.0.3.1/modules/libcom/src/misc/epicsTypes.h0000664000577000060420000001277413557101274020366 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeff Hill * Date: 5-95 */ #ifndef INC_epicsTypes_H #define INC_epicsTypes_H #include "shareLib.h" #include "compilerDependencies.h" #ifndef stringOf # if defined (__STDC__ ) || defined (__cplusplus) # define stringOf(TOKEN) #TOKEN # else # define stringOf(TOKEN) "TOKEN" # endif #endif typedef enum { epicsFalse = 0, epicsTrue = 1 } epicsBoolean EPICS_DEPRECATED; /* * Architecture Independent Data Types * These are sufficient for all our current archs */ typedef char epicsInt8; typedef unsigned char epicsUInt8; typedef short epicsInt16; typedef unsigned short epicsUInt16; typedef int epicsInt32; typedef unsigned int epicsUInt32; typedef long long epicsInt64; typedef unsigned long long epicsUInt64; typedef epicsUInt16 epicsEnum16; typedef float epicsFloat32; typedef double epicsFloat64; typedef epicsInt32 epicsStatus; typedef struct { unsigned length; char *pString; } epicsString; /* * !! Dont use this - it may vanish in the future !! * * Provided only for backwards compatibility with * db_access.h * */ #define MAX_STRING_SIZE 40 typedef char epicsOldString[MAX_STRING_SIZE]; /* * union of all types * * Strings included here as pointers only so that we support * large string types. * * Arrays included here as pointers because large arrays will * not fit in this union. */ typedef union epics_any { epicsInt8 int8; epicsUInt8 uInt8; epicsInt16 int16; epicsUInt16 uInt16; epicsEnum16 enum16; epicsInt32 int32; epicsUInt32 uInt32; epicsInt64 int64; epicsUInt64 uInt64; epicsFloat32 float32; epicsFloat64 float64; epicsString string; } epicsAny; /* * Corresponding Type Codes * (this enum must start at zero) * * !! Update epicsTypeToDBR_XXXX[] and DBR_XXXXToEpicsType * in db_access.h if you edit this enum !! */ typedef enum { epicsInt8T, epicsUInt8T, epicsInt16T, epicsUInt16T, epicsEnum16T, epicsInt32T, epicsUInt32T, epicsFloat32T, epicsFloat64T, epicsStringT, epicsOldStringT } epicsType; #define firstEpicsType epicsInt8T #define lastEpicsType epicsOldStringT #define validEpicsType(x) ((x>=firstEpicsType) && (x<=lastEpicsType)) #define invalidEpicsType(x) ((xlastEpicsType)) /* * The enumeration "epicsType" is an index to this array * of type name strings. */ #ifdef epicsTypesGLOBAL epicsShareDef const char *epicsTypeNames [lastEpicsType+1] = { "epicsInt8", "epicsUInt8", "epicsInt16", "epicsUInt16", "epicsEnum16", "epicsInt32", "epicsUInt32", "epicsFloat32", "epicsFloat64", "epicsString", "epicsOldString", }; #else /* epicsTypesGLOBAL */ epicsShareExtern const char *epicsTypeNames [lastEpicsType+1]; #endif /* epicsTypesGLOBAL */ /* * The enumeration "epicsType" is an index to this array * of type code name strings. */ #ifdef epicsTypesGLOBAL epicsShareDef const char *epicsTypeCodeNames [lastEpicsType+1] = { "epicsInt8T", "epicsUInt8T", "epicsInt16T", "epicsUInt16T", "epicsEnum16T", "epicsInt32T", "epicsUInt32T", "epicsFloat32T", "epicsFloat64T", "epicsStringT", "epicsOldStringT", }; #else /* epicsTypesGLOBAL */ epicsShareExtern const char *epicsTypeCodeNames [lastEpicsType+1]; #endif /* epicsTypesGLOBAL */ #ifdef epicsTypesGLOBAL epicsShareDef const unsigned epicsTypeSizes [lastEpicsType+1] = { sizeof (epicsInt8), sizeof (epicsUInt8), sizeof (epicsInt16), sizeof (epicsUInt16), sizeof (epicsEnum16), sizeof (epicsInt32), sizeof (epicsUInt32), sizeof (epicsFloat32), sizeof (epicsFloat64), sizeof (epicsString), sizeof (epicsOldString), }; #else /* epicsTypesGLOBAL */ epicsShareExtern const unsigned epicsTypeSizes [lastEpicsType+1]; #endif /* epicsTypesGLOBAL */ /* * The enumeration "epicsType" is an index to this array * of type class identifiers. */ typedef enum { epicsIntC, epicsUIntC, epicsEnumC, epicsFloatC, epicsStringC, epicsOldStringC } epicsTypeClass; #ifdef epicsTypesGLOBAL epicsShareDef const epicsTypeClass epicsTypeClasses [lastEpicsType+1] = { epicsIntC, epicsUIntC, epicsIntC, epicsUIntC, epicsEnumC, epicsIntC, epicsUIntC, epicsFloatC, epicsFloatC, epicsStringC, epicsOldStringC }; #else /* epicsTypesGLOBAL */ epicsShareExtern const epicsTypeClass epicsTypeClasses [lastEpicsType+1]; #endif /* epicsTypesGLOBAL */ #ifdef epicsTypesGLOBAL epicsShareDef const char *epicsTypeAnyFieldName [lastEpicsType+1] = { "int8", "uInt8", "int16", "uInt16", "enum16", "int32", "uInt32", "float32", "float64", "string", "", /* Old Style Strings will not be in epicsAny type */ }; #else /* epicsTypesGLOBAL */ epicsShareExtern const char *epicsTypeAnyFieldName [lastEpicsType+1]; #endif /* epicsTypesGLOBAL */ #endif /* INC_epicsTypes_H */ base-7.0.3.1/modules/libcom/src/misc/epicsUnitTest.c0000664000577000060420000001635413557101274021032 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Andrew Johnson * * Unit test module which generates output in the Test Anything Protocol * format. See perldoc Test::Harness for details of output format. * */ #include #include #ifdef _WIN32 # include #endif #define epicsExportSharedSymbols #include "epicsThread.h" #include "epicsMutex.h" #include "epicsUnitTest.h" #include "epicsExit.h" #include "epicsTime.h" #include "ellLib.h" #include "errlog.h" #include "cantProceed.h" #include "epicsStackTrace.h" typedef struct { ELLNODE node; const char *name; int tests; int failures; int skips; } testFailure; static epicsMutexId testLock = 0; static int perlHarness; static int planned; static int tested; static int passed; static int failed; static int skipped; static int bonus; static const char *todo; epicsTimeStamp started; static int Harness; static int Programs; static int Tests; ELLLIST faults; const char *testing = NULL; static epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT; #ifdef _WIN32 /* * if we return FALSE, _CrtDbgReport is called to print to file etc * if we return TRUE, we are the only function called */ static int testReportHook(int reportType, char *message, int *returnValue) { int nRet = 0; switch (reportType) { case _CRT_ASSERT: case _CRT_ERROR: epicsStackTrace(); break; default: break; } if (returnValue) { *returnValue = 0; } return nRet; } #endif static void testOnce(void *dummy) { testLock = epicsMutexMustCreate(); perlHarness = (getenv("HARNESS_ACTIVE") != NULL); #ifdef _WIN32 _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE |_CRTDBG_MODE_DEBUG ); _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR ); _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE |_CRTDBG_MODE_DEBUG ); _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR ); _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE |_CRTDBG_MODE_DEBUG ); _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR ); _CrtSetReportHook2( _CRT_RPTHOOK_INSTALL, testReportHook ); #endif } void testPlan(int plan) { epicsThreadOnce(&onceFlag, testOnce, NULL); epicsMutexMustLock(testLock); planned = plan; tested = passed = failed = skipped = bonus = 0; todo = NULL; if (plan) printf("1..%d\n", plan); epicsMutexUnlock(testLock); } int testOkV(int pass, const char *fmt, va_list pvar) { const char *result = "not ok"; epicsMutexMustLock(testLock); tested++; if (pass) { result += 4; /* skip "not " */ passed++; if (todo) bonus++; } else { if (todo) passed++; else failed++; } printf("%s %2d - ", result, tested); vprintf(fmt, pvar); if (todo) printf(" # TODO %s", todo); putchar('\n'); fflush(stdout); epicsMutexUnlock(testLock); return pass; } int testOk(int pass, const char *fmt, ...) { va_list pvar; va_start(pvar, fmt); testOkV(pass, fmt, pvar); va_end(pvar); return pass; } void testPass(const char *fmt, ...) { va_list pvar; va_start(pvar, fmt); testOkV(1, fmt, pvar); va_end(pvar); } void testFail(const char *fmt, ...) { va_list pvar; va_start(pvar, fmt); testOkV(0, fmt, pvar); va_end(pvar); } void testSkip(int skip, const char *why) { epicsMutexMustLock(testLock); while (skip-- > 0) { tested++; passed++; skipped++; printf("ok %2d # SKIP %s\n", tested, why); } fflush(stdout); epicsMutexUnlock(testLock); } void testTodoBegin(const char *why) { epicsMutexMustLock(testLock); todo = why; epicsMutexUnlock(testLock); } void testTodoEnd(void) { todo = NULL; } int testDiag(const char *fmt, ...) { va_list pvar; va_start(pvar, fmt); epicsMutexMustLock(testLock); printf("# "); vprintf(fmt, pvar); putchar('\n'); fflush(stdout); epicsMutexUnlock(testLock); va_end(pvar); return 0; } void testAbort(const char *fmt, ...) { va_list pvar; va_start(pvar, fmt); printf("Bail out! "); vprintf(fmt, pvar); putchar('\n'); fflush(stdout); va_end(pvar); abort(); } static void testResult(const char *result, int count) { printf("%12.12s: %3d = %5.2f%%\n", result, count, 100.0 * count / tested); } int testDone(void) { int status = 0; epicsMutexMustLock(testLock); if (perlHarness) { if (!planned) printf("1..%d\n", tested); } else { if (planned && tested > planned) { printf("\nRan %d tests but only planned for %d!\n", tested, planned); status = 2; } else if (planned && tested < planned) { printf("\nPlanned %d tests but only ran %d\n", planned, tested); status = 2; } printf("\n Results\n =======\n Tests: %-3d\n", tested); if (tested) { testResult("Passed", passed); if (bonus) testResult("Todo Passes", bonus); if (failed) { testResult("Failed", failed); status = 1; } if (skipped) testResult("Skipped", skipped); } } if (Harness) { if (failed) { testFailure *fault = callocMustSucceed(1, sizeof(testFailure), "testDone calloc"); fault->name = testing; fault->tests = tested; fault->failures = failed; fault->skips = skipped; ellAdd(&faults, &fault->node); } Programs++; Tests += tested; } epicsMutexUnlock(testLock); return (status); } /* Our test harness, for RTEMS and vxWorks */ void testHarnessExit(void *dummy) { epicsTimeStamp ended; int Faulty; if (!Harness) return; epicsTimeGetCurrent(&ended); printf("\n\n EPICS Test Harness Results" "\n ==========================\n\n"); Faulty = ellCount(&faults); if (!Faulty) printf("All tests successful.\n"); else { int Failures = 0; testFailure *f; printf("Failing Program Tests Faults\n" "---------------------------------------\n"); while ((f = (testFailure *)ellGet(&faults))) { Failures += f->failures; printf("%-25s %5d %5d\n", f->name, f->tests, f->failures); if (f->skips) printf("%d subtests skipped\n", f->skips); free(f); } printf("\nFailed %d/%d test programs. %d/%d subtests failed.\n", Faulty, Programs, Failures, Tests); } printf("Programs=%d, Tests=%d, %.0f wallclock secs\n\n", Programs, Tests, epicsTimeDiffInSeconds(&ended, &started)); } void testHarness(void) { epicsThreadOnce(&onceFlag, testOnce, NULL); epicsAtExit(testHarnessExit, NULL); Harness = 1; Programs = 0; Tests = 0; ellInit(&faults); epicsTimeGetCurrent(&started); } void runTestFunc(const char *name, TESTFUNC func) { printf("\n***** %s *****\n", name); testing = name; func(); /* May not return */ epicsThreadSleep(1.0); } base-7.0.3.1/modules/libcom/src/misc/epicsUnitTest.h0000664000577000060420000000324413557101274021031 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Andrew Johnson */ #ifndef INC_epicsUnitTest_H #define INC_epicsUnitTest_H #include #include "compilerDependencies.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc void testPlan(int tests); epicsShareFunc int testOkV(int pass, const char *fmt, va_list pvar); epicsShareFunc int testOk(int pass, const char *fmt, ...) EPICS_PRINTF_STYLE(2, 3); epicsShareFunc void testPass(const char *fmt, ...) EPICS_PRINTF_STYLE(1, 2); epicsShareFunc void testFail(const char *fmt, ...) EPICS_PRINTF_STYLE(1, 2); epicsShareFunc void testSkip(int skip, const char *why); epicsShareFunc void testTodoBegin(const char *why); epicsShareFunc void testTodoEnd(void); epicsShareFunc int testDiag(const char *fmt, ...) EPICS_PRINTF_STYLE(1, 2); epicsShareFunc void testAbort(const char *fmt, ...) EPICS_PRINTF_STYLE(1, 2); epicsShareFunc int testDone(void); #define testOk1(cond) testOk(cond, "%s", #cond) typedef int (*TESTFUNC)(void); epicsShareFunc void testHarness(void); epicsShareFunc void testHarnessExit(void *dummy); epicsShareFunc void runTestFunc(const char *name, TESTFUNC func); #define runTest(func) runTestFunc(#func, func) #define testHarnessDone() testHarnessExit(0) #ifdef __cplusplus } #endif #endif /* INC_epicsUnitTest_H */ base-7.0.3.1/modules/libcom/src/misc/ipAddrToAsciiAsynchronous.cpp0000664000577000060420000003643213557101274023661 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #include #include #include #include //#define EPICS_FREELIST_DEBUG #define EPICS_PRIVATE_API #define epicsExportSharedSymbols #include "ipAddrToAsciiAsynchronous.h" #include "epicsThread.h" #include "epicsMutex.h" #include "epicsEvent.h" #include "epicsGuard.h" #include "epicsExit.h" #include "tsDLList.h" #include "tsFreeList.h" #include "errlog.h" // - this class implements the asynchronous DNS query // - it completes early with the host name in dotted IP address form // if the ipAddrToAsciiEngine is destroyed before IO completion // or if there are too many items already in the engine's queue. class ipAddrToAsciiTransactionPrivate : public ipAddrToAsciiTransaction, public tsDLNode < ipAddrToAsciiTransactionPrivate > { public: ipAddrToAsciiTransactionPrivate ( class ipAddrToAsciiEnginePrivate & engineIn ); virtual ~ipAddrToAsciiTransactionPrivate (); osiSockAddr address () const; void show ( unsigned level ) const; void * operator new ( size_t size, tsFreeList < ipAddrToAsciiTransactionPrivate, 0x80 > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < ipAddrToAsciiTransactionPrivate, 0x80 > & )) osiSockAddr addr; ipAddrToAsciiEnginePrivate & engine; ipAddrToAsciiCallBack * pCB; bool pending; void ipAddrToAscii ( const osiSockAddr &, ipAddrToAsciiCallBack & ); void release (); void operator delete ( void * ); private: ipAddrToAsciiTransactionPrivate & operator = ( const ipAddrToAsciiTransactionPrivate & ); ipAddrToAsciiTransactionPrivate ( const ipAddrToAsciiTransactionPrivate & ); }; #ifdef _MSC_VER # pragma warning ( push ) # pragma warning ( disable:4660 ) #endif template class tsFreeList < ipAddrToAsciiTransactionPrivate, 0x80 >; #ifdef _MSC_VER # pragma warning ( pop ) #endif extern "C" { static void ipAddrToAsciiEngineGlobalMutexConstruct ( void * ); } namespace { struct ipAddrToAsciiGlobal : public epicsThreadRunable { ipAddrToAsciiGlobal(); virtual ~ipAddrToAsciiGlobal() {} virtual void run (); char nameTmp [1024]; tsFreeList < ipAddrToAsciiTransactionPrivate, 0x80 > transactionFreeList; tsDLList < ipAddrToAsciiTransactionPrivate > labor; mutable epicsMutex mutex; epicsEvent laborEvent; epicsEvent destructorBlockEvent; epicsThread thread; // pCurrent may be changed by any thread (worker or other) ipAddrToAsciiTransactionPrivate * pCurrent; // pActive may only be changed by the worker ipAddrToAsciiTransactionPrivate * pActive; unsigned cancelPendingCount; bool exitFlag; bool callbackInProgress; }; } // - this class executes the synchronous DNS query // - it creates one thread class ipAddrToAsciiEnginePrivate : public ipAddrToAsciiEngine { public: ipAddrToAsciiEnginePrivate() :refcount(1u), released(false) {} virtual ~ipAddrToAsciiEnginePrivate () {} void show ( unsigned level ) const; unsigned refcount; bool released; static ipAddrToAsciiGlobal * pEngine; ipAddrToAsciiTransaction & createTransaction (); void release (); private: ipAddrToAsciiEnginePrivate ( const ipAddrToAsciiEngine & ); ipAddrToAsciiEnginePrivate & operator = ( const ipAddrToAsciiEngine & ); }; ipAddrToAsciiGlobal * ipAddrToAsciiEnginePrivate :: pEngine = 0; static epicsThreadOnceId ipAddrToAsciiEngineGlobalMutexOnceFlag = EPICS_THREAD_ONCE_INIT; // the users are not required to supply a show routine // for there transaction callback class void ipAddrToAsciiCallBack::show ( unsigned /* level */ ) const {} // some noop pure virtual destructures ipAddrToAsciiCallBack::~ipAddrToAsciiCallBack () {} ipAddrToAsciiTransaction::~ipAddrToAsciiTransaction () {} ipAddrToAsciiEngine::~ipAddrToAsciiEngine () {} static void ipAddrToAsciiEngineGlobalMutexConstruct ( void * ) { try { ipAddrToAsciiEnginePrivate::pEngine = new ipAddrToAsciiGlobal (); } catch (std::exception& e) { errlogPrintf("ipAddrToAsciiEnginePrivate ctor fails with: %s\n", e.what()); } } void ipAddrToAsciiEngine::cleanup() { { epicsGuard G(ipAddrToAsciiEnginePrivate::pEngine->mutex); ipAddrToAsciiEnginePrivate::pEngine->exitFlag = true; } ipAddrToAsciiEnginePrivate::pEngine->laborEvent.signal(); ipAddrToAsciiEnginePrivate::pEngine->thread.exitWait(); delete ipAddrToAsciiEnginePrivate::pEngine; ipAddrToAsciiEnginePrivate::pEngine = 0; } // for now its probably sufficent to allocate one // DNS transaction thread for all codes sharing // the same process that need DNS services but we // leave our options open for the future ipAddrToAsciiEngine & ipAddrToAsciiEngine::allocate () { epicsThreadOnce ( & ipAddrToAsciiEngineGlobalMutexOnceFlag, ipAddrToAsciiEngineGlobalMutexConstruct, 0 ); if(!ipAddrToAsciiEnginePrivate::pEngine) throw std::runtime_error("ipAddrToAsciiEngine::allocate fails"); return * new ipAddrToAsciiEnginePrivate(); } ipAddrToAsciiGlobal::ipAddrToAsciiGlobal () : mutex(__FILE__, __LINE__), thread ( *this, "ipToAsciiProxy", epicsThreadGetStackSize(epicsThreadStackBig), epicsThreadPriorityLow ), pCurrent ( 0 ), pActive ( 0 ), cancelPendingCount ( 0u ), exitFlag ( false ), callbackInProgress ( false ) { this->thread.start (); // start the thread } void ipAddrToAsciiEnginePrivate::release () { bool last; { epicsGuard < epicsMutex > guard ( this->pEngine->mutex ); if(released) throw std::logic_error("Engine release() called again!"); // released==true prevents new transactions released = true; { // cancel any pending transactions tsDLIter < ipAddrToAsciiTransactionPrivate > it(pEngine->labor.firstIter()); while(it.valid()) { ipAddrToAsciiTransactionPrivate *trn = it.pointer(); ++it; if(this==&trn->engine) { trn->pending = false; pEngine->labor.remove(*trn); } } // cancel transaction in lookup or callback if (pEngine->pCurrent && this==&pEngine->pCurrent->engine) { pEngine->pCurrent->pending = false; pEngine->pCurrent = 0; } // wait for completion of in-progress callback pEngine->cancelPendingCount++; while(pEngine->pActive && this==&pEngine->pActive->engine && ! pEngine->thread.isCurrentThread()) { epicsGuardRelease < epicsMutex > unguard ( guard ); pEngine->destructorBlockEvent.wait(); } pEngine->cancelPendingCount--; if(pEngine->cancelPendingCount) pEngine->destructorBlockEvent.signal(); } assert(refcount>0); last = 0==--refcount; } if(last) { delete this; } } void ipAddrToAsciiEnginePrivate::show ( unsigned level ) const { epicsGuard < epicsMutex > guard ( this->pEngine->mutex ); printf ( "ipAddrToAsciiEngine at %p with %u requests pending\n", static_cast (this), this->pEngine->labor.count () ); if ( level > 0u ) { tsDLIter < ipAddrToAsciiTransactionPrivate > pItem = this->pEngine->labor.firstIter (); while ( pItem.valid () ) { pItem->show ( level - 1u ); pItem++; } } if ( level > 1u ) { printf ( "mutex:\n" ); this->pEngine->mutex.show ( level - 2u ); printf ( "laborEvent:\n" ); this->pEngine->laborEvent.show ( level - 2u ); printf ( "exitFlag boolean = %u\n", this->pEngine->exitFlag ); printf ( "exit event:\n" ); } } inline void * ipAddrToAsciiTransactionPrivate::operator new ( size_t size, tsFreeList < ipAddrToAsciiTransactionPrivate, 0x80 > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void ipAddrToAsciiTransactionPrivate::operator delete ( void * pTrans, tsFreeList < ipAddrToAsciiTransactionPrivate, 0x80 > & freeList ) { freeList.release ( pTrans ); } #endif void ipAddrToAsciiTransactionPrivate::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } ipAddrToAsciiTransaction & ipAddrToAsciiEnginePrivate::createTransaction () { epicsGuard G(this->pEngine->mutex); if(this->released) throw std::logic_error("createTransaction() on release()'d ipAddrToAsciiEngine"); assert(this->refcount>0); ipAddrToAsciiTransactionPrivate *ret = new ( this->pEngine->transactionFreeList ) ipAddrToAsciiTransactionPrivate ( *this ); this->refcount++; return * ret; } void ipAddrToAsciiGlobal::run () { epicsGuard < epicsMutex > guard ( this->mutex ); while ( ! this->exitFlag ) { { epicsGuardRelease < epicsMutex > unguard ( guard ); this->laborEvent.wait (); } while ( true ) { ipAddrToAsciiTransactionPrivate * pItem = this->labor.get (); if ( ! pItem ) { break; } osiSockAddr addr = pItem->addr; this->pCurrent = pItem; if ( this->exitFlag ) { sockAddrToDottedIP ( & addr.sa, this->nameTmp, sizeof ( this->nameTmp ) ); } else { epicsGuardRelease < epicsMutex > unguard ( guard ); // depending on DNS configuration, this could take a very long time // so we release the lock sockAddrToA ( &addr.sa, this->nameTmp, sizeof ( this->nameTmp ) ); } // the ipAddrToAsciiTransactionPrivate destructor is allowed to // set pCurrent to nill and avoid blocking on a slow DNS // operation if ( ! this->pCurrent ) { continue; } // fix for lp:1580623 // a destructing cac sets pCurrent to NULL, so // make local copy to avoid race when releasing the guard ipAddrToAsciiTransactionPrivate *pCur = pActive = pCurrent; this->callbackInProgress = true; { epicsGuardRelease < epicsMutex > unguard ( guard ); // dont call callback with lock applied pCur->pCB->transactionComplete ( this->nameTmp ); } this->callbackInProgress = false; pActive = 0; if ( this->pCurrent ) { this->pCurrent->pending = false; this->pCurrent = 0; } if ( this->cancelPendingCount ) { this->destructorBlockEvent.signal (); } } } } ipAddrToAsciiTransactionPrivate::ipAddrToAsciiTransactionPrivate ( ipAddrToAsciiEnginePrivate & engineIn ) : engine ( engineIn ), pCB ( 0 ), pending ( false ) { memset ( & this->addr, '\0', sizeof ( this->addr ) ); this->addr.sa.sa_family = AF_UNSPEC; } void ipAddrToAsciiTransactionPrivate::release () { this->~ipAddrToAsciiTransactionPrivate (); this->engine.pEngine->transactionFreeList.release ( this ); } ipAddrToAsciiTransactionPrivate::~ipAddrToAsciiTransactionPrivate () { ipAddrToAsciiGlobal *pGlobal = this->engine.pEngine; bool last; { epicsGuard < epicsMutex > guard ( pGlobal->mutex ); while ( this->pending ) { if ( pGlobal->pCurrent == this && pGlobal->callbackInProgress && ! pGlobal->thread.isCurrentThread() ) { // cancel from another thread while callback in progress // waits for callback to complete assert ( pGlobal->cancelPendingCount < UINT_MAX ); pGlobal->cancelPendingCount++; { epicsGuardRelease < epicsMutex > unguard ( guard ); pGlobal->destructorBlockEvent.wait (); } assert ( pGlobal->cancelPendingCount > 0u ); pGlobal->cancelPendingCount--; if ( ! this->pending ) { if ( pGlobal->cancelPendingCount ) { pGlobal->destructorBlockEvent.signal (); } break; } } else { if ( pGlobal->pCurrent == this ) { // cancel from callback, or while lookup in progress pGlobal->pCurrent = 0; } else { // cancel before lookup starts pGlobal->labor.remove ( *this ); } this->pending = false; } } assert(this->engine.refcount>0); last = 0==--this->engine.refcount; } if(last) { delete &this->engine; } } void ipAddrToAsciiTransactionPrivate::ipAddrToAscii ( const osiSockAddr & addrIn, ipAddrToAsciiCallBack & cbIn ) { bool success; ipAddrToAsciiGlobal *pGlobal = this->engine.pEngine; { epicsGuard < epicsMutex > guard ( pGlobal->mutex ); if (this->engine.released) { errlogPrintf("Warning: ipAddrToAscii on transaction with release()'d ipAddrToAsciiEngine"); success = false; } else if ( !this->pending && pGlobal->labor.count () < 16u ) { // put some reasonable limit on queue expansion this->addr = addrIn; this->pCB = & cbIn; this->pending = true; pGlobal->labor.add ( *this ); success = true; } else { success = false; } } if ( success ) { pGlobal->laborEvent.signal (); } else { char autoNameTmp[256]; sockAddrToDottedIP ( & addrIn.sa, autoNameTmp, sizeof ( autoNameTmp ) ); cbIn.transactionComplete ( autoNameTmp ); } } osiSockAddr ipAddrToAsciiTransactionPrivate::address () const { return this->addr; } void ipAddrToAsciiTransactionPrivate::show ( unsigned level ) const { epicsGuard < epicsMutex > guard ( this->engine.pEngine->mutex ); char ipAddr [64]; sockAddrToDottedIP ( &this->addr.sa, ipAddr, sizeof ( ipAddr ) ); printf ( "ipAddrToAsciiTransactionPrivate for address %s\n", ipAddr ); if ( level > 0u ) { printf ( "\tengine %p\n", static_cast ( & this->engine ) ); this->pCB->show ( level - 1u ); } } base-7.0.3.1/modules/libcom/src/misc/ipAddrToAsciiAsynchronous.h0000664000577000060420000000321713557101274023321 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef ipAddrToAsciiAsynchronous_h #define ipAddrToAsciiAsynchronous_h #include "osiSock.h" #include "shareLib.h" class epicsShareClass ipAddrToAsciiCallBack { public: virtual void transactionComplete ( const char * pHostName ) = 0; virtual void show ( unsigned level ) const; virtual ~ipAddrToAsciiCallBack () = 0; }; class epicsShareClass ipAddrToAsciiTransaction { public: virtual void release () = 0; virtual void ipAddrToAscii ( const osiSockAddr &, ipAddrToAsciiCallBack & ) = 0; virtual osiSockAddr address () const = 0; virtual void show ( unsigned level ) const = 0; protected: virtual ~ipAddrToAsciiTransaction () = 0; }; class epicsShareClass ipAddrToAsciiEngine { public: virtual void release () = 0; virtual ipAddrToAsciiTransaction & createTransaction () = 0; virtual void show ( unsigned level ) const = 0; static ipAddrToAsciiEngine & allocate (); protected: virtual ~ipAddrToAsciiEngine () = 0; public: #ifdef EPICS_PRIVATE_API static void cleanup(); #endif }; #endif // ifdef ipAddrToAsciiAsynchronous_h base-7.0.3.1/modules/libcom/src/misc/locationException.h0000664000577000060420000000431113557101274021711 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ // // Author: Jeff Hill // #ifndef locationException_h #define locationException_h #include #include "cantProceed.h" #include "errlog.h" template class sourceFileLocation : public T { public: sourceFileLocation (const T &parm, const char *fileName, unsigned lineNumber); sourceFileLocation ( const sourceFileLocation & ); sourceFileLocation & operator = ( const sourceFileLocation & ); const char *fileName () const; unsigned lineNumber () const; private: const char *pFileName; unsigned lineNumberCopy; }; template inline sourceFileLocation::sourceFileLocation (const T &parm, const char *fileName, unsigned lineNumber) : T ( parm ), pFileName ( fileName ) , lineNumberCopy ( lineNumber ) {} template inline sourceFileLocation::sourceFileLocation ( const sourceFileLocation &in ) : T ( in ), pFileName ( in.pFileName ), lineNumberCopy ( in.lineNumberCopy ) { } template < class T > inline sourceFileLocation & sourceFileLocation::operator = ( const sourceFileLocation &in ) { this->pFileName = in.pFileName; this->lineNumberCopy = in.lineNumberCopy; return *this; } template < class T > inline unsigned sourceFileLocation::lineNumber () const { return this->lineNumberCopy; } template inline const char * sourceFileLocation::fileName () const { return this->pFileName; } #define throwWithLocation(parm) throwExceptionWithLocation (parm, __FILE__, __LINE__); template inline void throwExceptionWithLocation (const T &parm, const char *pFileName, unsigned lineNo) { throw sourceFileLocation (parm, pFileName, lineNo); } #endif // ifdef locationException_h base-7.0.3.1/modules/libcom/src/misc/makeEpicsVersion.pl0000664000577000060420000000547113557101274021665 0ustar anjaesctl#!/usr/bin/env perl #************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* use strict; use Getopt::Std; use File::Basename; my $tool = basename($0); our ($opt_h, $opt_q, $opt_v); our $opt_o = 'epicsVersion.h'; $Getopt::Std::OUTPUT_HELP_VERSION = 1; HELP_MESSAGE() unless getopts('ho:qv:') && @ARGV == 1; HELP_MESSAGE() if $opt_h; my ($infile) = @ARGV; print "Building $opt_o from $infile\n" unless $opt_q; open my $VARS, '<', $infile or die "$tool: Can't open $infile: $!\n"; my ($ver, $rev, $mod, $patch, $snapshot); while (<$VARS>) { chomp; next if m/^\s*#/; # Skip comments if (m/^EPICS_VERSION\s*=\s*(\d+)/) { $ver = $1; } if (m/^EPICS_REVISION\s*=\s*(\d+)/) { $rev = $1; } if (m/^EPICS_MODIFICATION\s*=\s*(\d+)/) { $mod = $1; } if (m/^EPICS_PATCH_LEVEL\s*=\s*(\d+)/) { $patch = $1; } if (m/^EPICS_DEV_SNAPSHOT\s*=\s*([-\w]*)/) { $snapshot = $1; } } close $VARS; map { die "$tool: Variable missing from $infile" unless defined $_; } $ver, $rev, $mod, $patch, $snapshot; my $ver_str = "$ver.$rev.$mod"; $ver_str .= ".$patch" if $patch > 0; my $ver_short = $ver_str; $ver_str .= $snapshot if $snapshot ne ''; $ver_str .= "-$opt_v" if $opt_v; print "Found EPICS Version $ver_str\n" unless $opt_q; open my $OUT, '>', $opt_o or die "$tool: Can't create $opt_o: $!\n"; my $obase = basename($opt_o, '.h'); print $OUT <<"END"; /* Generated file $opt_o */ #ifndef INC_${obase}_H #define INC_${obase}_H #define EPICS_VERSION $ver #define EPICS_REVISION $rev #define EPICS_MODIFICATION $mod #define EPICS_PATCH_LEVEL $patch #define EPICS_DEV_SNAPSHOT "$snapshot" #define EPICS_SITE_VERSION "$opt_v" #define EPICS_VERSION_SHORT "$ver_short" #define EPICS_VERSION_FULL "$ver_str" #define EPICS_VERSION_STRING "EPICS $ver_str" #define epicsReleaseVersion "EPICS R$ver_str" #ifndef VERSION_INT # define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P)) #endif #define EPICS_VERSION_INT VERSION_INT($ver, $rev, $mod, $patch) #endif /* INC_${obase}_H */ END close $OUT; sub HELP_MESSAGE { print STDERR "Usage: $tool [options] CONFIG_BASE_VERSION\n", " -h Help: Print this message\n", " -q Quiet: Only print errors\n", " -o file Output filename, default is $opt_o\n", " -v vers Site-specific version string\n", "\n"; exit 1; } base-7.0.3.1/modules/libcom/src/misc/shareLib.h0000664000577000060420000001760613557101274017766 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Compiler specific key words to set up external symbols and entry points * * USAGE: * There are two distinct classes of keywords in this file: * * 1) epicsShareAPI - specifies a multi-language calling mechanism. On windows * this is the pascal calling convention which is used by visual basic and other * high level tools. This is only necessary if a C/C++ function needs to be called * from other languages or from high level tools. The epicsShareAPI keyword * must be present between the function's returned data type and the function's * name. All compilers targeting windows accept the __stdcall keyword in this * location. Functions with variable argument lists should not use the epicsShareAPI * keyword because __stdcall (pascal) calling convention cannot support variable * length ed argument lists. * * int epicsShareAPI myExtFunc ( int arg ); * int epicsShareAPI myExtFunc ( int arg ) {} * * ** NOTE ** The epicsShareAPI attribute is deprecated and has been removed * from all IOC-specific APIs. Most libCom APIs still use it, but * it may get removed from these at some point in the future. * * 2) epicsShare{Func,Class,Extern,Def} - specifies shareable library (DLL) * export/import related information in the source code. On windows these keywords * allow faster dispatching of calls to DLLs because more is known at compile time. * It is also not necessary to maintain a linker input files specifying the DLL * entry points. This maintenance can be more laborious with C++ decorated symbol * names. These keywords are only necessary if the address of a function or data * internal to a shareable library (DLL) needs to be visible outside of this shareable * library (DLL). All compilers targeting windows accept the __declspec(dllexport) * and __declspec(dllimport) keywords. For GCC version 4 and above the first three * keywords specify a visibility attribute of "default", which marks the symbol as * exported even when gcc is given the option -fvisibility=hidden. Doing this can * significantly reduce the number of symbols exported to a shared library. See the * URL below for more information. * * In header files declare references to externally visible variables, classes and * functions like this: * * #include "shareLib.h" * epicsShareFunc int myExtFunc ( int arg ); * epicsShareExtern int myExtVar; * class epicsShareClass myClass { int func ( void ); }; * * In the implementation file, however, you write: * * #include * #define epicsExportSharedSymbols * #include * * epicsShareDef int myExtVar = 4; * int myExtFunc ( int arg ) {} * int myClass::func ( void ) {} * * By default shareLib.h sets the DLL import / export keywords to import from * a DLL so that, for DLL consumers (users), nothing special must be done. However, * DLL implementors must set epicsExportSharedSymbols as above to specify * which functions are exported from the DLL and which of them are imported * from other DLLs. * * You must first #include what you import and then define epicsExportSharedSymbols * only right before you #include the prototypes for what you implement! You must * include shareLib.h again each time that the state of the import/ export keywords * changes, but this usually occurs as a side effect of including the shareable * libraries header file(s). * * Frequently a header file for a shareable library exported interface will * have some preprocessor switches like this if this header file must also * include header files describing interfaces to other shareable libraries. * * #ifdef epicsExportSharedSymbols * # define interfacePDQ_epicsExportSharedSymbols * # undef epicsExportSharedSymbols * #endif * * #include "epicsTypes.h" * #include "epicsTime.h" * * #ifdef interfacePDQ_epicsExportSharedSymbols * # define epicsExportSharedSymbols * # include "shareLib.h" * #endif * * epicsShareFunc int myExtFunc ( int arg ); * epicsShareExtern int myExtVar; * class epicsShareClass myClass {}; * * Fortunately, the above is only the concern of library authors and will have no * impact on persons using functions and or external data from a library. */ #undef epicsShareExtern #undef epicsShareDef #undef epicsShareClass #undef epicsShareFunc #undef epicsShareAPI #undef READONLY #if defined(_WIN32) || defined(__CYGWIN__) /* * Check if EPICS_BUILD_DLL or EPICS_CALL_DLL defined and use the dllimport/ * dllexport keywords if this is a shared library build of base under WIN32. */ # if defined(epicsExportSharedSymbols) # if defined(EPICS_BUILD_DLL) # define epicsShareExtern __declspec(dllexport) extern # define epicsShareClass __declspec(dllexport) # define epicsShareFunc __declspec(dllexport) # else # define epicsShareExtern extern # define epicsShareClass # define epicsShareFunc # endif # else # if defined(EPICS_CALL_DLL) # define epicsShareExtern __declspec(dllimport) extern # define epicsShareClass __declspec(dllimport) # define epicsShareFunc __declspec(dllimport) # else # define epicsShareExtern extern # define epicsShareClass # define epicsShareFunc # endif # endif # define epicsShareDef # define epicsShareAPI __stdcall /* function removes arguments */ # define READONLY const #elif __GNUC__ >= 4 /* * See http://gcc.gnu.org/wiki/Visibility * For these to work, gcc must be given the flag * -fvisibility=hidden * and g++ the flags * -fvisibility=hidden -fvisibility-inlines-hidden */ # define epicsShareExtern __attribute__ ((visibility("default"))) extern # define epicsShareClass __attribute__ ((visibility("default"))) # define epicsShareFunc __attribute__ ((visibility("default"))) # define epicsShareDef # define epicsShareAPI # if defined(__STDC__) || defined (__cplusplus) # define READONLY const # else # define READONLY # endif /* * if its the old VAX C Compiler (not DEC C) */ #elif defined(VAXC) /* * VAXC creates FORTRAN common blocks when * we use "extern int fred"/"int fred=4". Therefore, * the initialization is not loaded unless we * call a function in that object module. * * DEC CXX does not have this problem. * We suspect (but do not know) that DEC C * also does not have this problem. */ # define epicsShareExtern globalref # define epicsShareDef globaldef # define READONLY const # define epicsShareClass # define epicsShareFunc # define epicsShareAPI #else /* else => no import/export specifiers */ # define epicsShareExtern extern # define epicsShareAPI # define epicsShareClass # define epicsShareDef # define epicsShareFunc # if defined(__STDC__) || defined (__cplusplus) # define READONLY const # else # define READONLY # endif #endif #ifndef INLINE_defs_EPICS #define INLINE_defs_EPICS # ifndef __cplusplus # if defined (__GNUC__) # define INLINE static __inline__ # elif defined (_MSC_VER) # define INLINE __inline # elif defined (_SUNPRO_C) # pragma error_messages (off, E_EXTERN_PRIOR_REDECL_STATIC) # define INLINE static # else # define INLINE static # endif # endif /* ifndef __cplusplus */ #endif /* ifdef INLINE_defs_EPICS */ base-7.0.3.1/modules/libcom/src/misc/testMain.h0000664000577000060420000000327513557101274020016 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_testMain_H #define INC_testMain_H /* This header defines a convenience macro for use by pure test programs. * A pure test program cannot take any arguments since it must be fully * automatable. If your program needs to use argv/argc, it may be doing * measurements not unit and/or regression testing. On Host architectures * these programs needs to be named main and take dummy argc/argv args, * but on vxWorks and RTEMS they must be named as the test program. * * Use this macro as follows: * * #include "testMain.h" * #include "epicsUnitTest.h" * * MAIN(myProgTest) { * testPlan(...); * testOk(...) * return testDone(); * } */ #if defined(__rtems__) #ifdef __cplusplus #define MAIN(prog) extern "C" int prog(void); extern "C" int main() __attribute__((weak, alias(#prog))); extern "C" int prog(void) #else #define MAIN(prog) int prog(); int main() __attribute__((weak, alias(#prog))); int prog() #endif #elif defined(vxWorks) #ifdef __cplusplus #define MAIN(prog) extern "C" int prog(void) #else #define MAIN(prog) int prog() #endif #else #ifdef __cplusplus #define MAIN(prog) int main(int /*argc*/, char * /*argv*/ [] ) #else #define MAIN(prog) int main(int argc, char *argv[] ) #endif #endif #endif /* INC_testMain_H */ base-7.0.3.1/modules/libcom/src/misc/truncateFile.c0000664000577000060420000000562313557101274020651 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsStdio.h" #ifndef SEEK_END #define SEEK_END 2 #endif /* * truncate to specified size (we dont use truncate() * because it is not portable) */ epicsShareFunc enum TF_RETURN truncateFile (const char *pFileName, unsigned long size) { long filePos; FILE *pFile; FILE *ptmp; int status; int c; unsigned charNo; /* * see cast of size to long below */ if (size>LONG_MAX) { return TF_ERROR; } pFile = fopen(pFileName, "r"); if (!pFile) { fprintf (stderr, "File access problems to `%s' because `%s'\n", pFileName, strerror(errno)); return TF_ERROR; } /* * This is not required under UNIX but does appear * to be required under WIN32. */ status = fseek (pFile, 0L, SEEK_END); if (status!=TF_OK) { fclose (pFile); return TF_ERROR; } filePos = ftell(pFile); if (filePos <= (long) size) { fclose (pFile); return TF_OK; } ptmp = epicsTempFile(); if (!ptmp) { fprintf (stderr, "File access problems to temp file because `%s'\n", strerror(errno)); fclose (pFile); return TF_ERROR; } rewind (pFile); charNo = 0u; while (charNo #ifdef __cplusplus extern "C" { #endif #define OSI_PATH_LIST_SEPARATOR ":" #define OSI_PATH_SEPARATOR "/" /** Return the absolute path of the current executable. @returns NULL or the path. Caller must free() */ epicsShareFunc char *epicsGetExecName(void); /** Return the absolute path of the directory containing the current executable. @returns NULL or the path. Caller must free() */ epicsShareFunc char *epicsGetExecDir(void); #ifdef __cplusplus } #endif #endif /* unixFileNameH */ base-7.0.3.1/modules/libcom/src/osi/Makefile0000664000577000060420000000763713557101274017366 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/osi INC += compilerDependencies.h INC += compilerSpecific.h INC += osiFileName.h INC += osiSock.h INC += osdSock.h INC += epicsInterrupt.h INC += osdInterrupt.h INC += epicsMutex.h INC += osdMutex.h INC += epicsSpin.h INC += epicsEvent.h INC += osdEvent.h INC += epicsMath.h INC += osdMessageQueue.h INC += osdStrtod.h INC += epicsAssert.h INC += epicsFindSymbol.h INC += osiPoolStatus.h INC += osdPoolStatus.h INC += osdThread.h INC += epicsThread.h INC += epicsTime.h INC += epicsGeneralTime.h INC += osdTime.h INC += generalTimeSup.h INC += osiClockTime.h INC += epicsSignal.h INC += osiProcess.h INC += osiUnistd.h INC += osiWireFormat.h INC += osdWireFormat.h INC += osdWireConfig.h INC += epicsAtomic.h INC += epicsAtomicDefault.h INC += epicsAtomicOSD.h INC += epicsAtomicCD.h INC += epicsEndian.h INC += epicsReadline.h INC += epicsMessageQueue.h INC += epicsStdio.h INC += epicsStdioRedirect.h INC += epicsTempFile.h INC += epicsGetopt.h INC += epicsStackTrace.h INC += devLib.h INC += devLibVME.h INC += devLibVMEImpl.h INC += osdVME.h INC += epicsMMIO.h INC += epicsMMIODef.h Com_SRCS += epicsThread.cpp Com_SRCS += epicsMutex.cpp Com_SRCS += epicsEvent.cpp Com_SRCS += epicsTime.cpp Com_SRCS += epicsMessageQueue.cpp Com_SRCS += epicsMath.cpp Com_SRCS += epicsAtomicOSD.cpp Com_SRCS += epicsGeneralTime.c # Time providers Com_SRCS += osiClockTime.c Com_SRCS_vxWorks += osiNTPTime.c tz2timezone.c Com_SRCS_RTEMS += osiNTPTime.c ifeq ($(OS_CLASS),vxWorks) osdTime.o: USR_CXXFLAGS += -DBUILD_TIME=$(shell perl -e 'print time') endif Com_SRCS += osdSock.c Com_SRCS += osdSockAddrReuse.cpp Com_SRCS += osdSockUnsentCount.c Com_SRCS += osiSock.c Com_SRCS += systemCallIntMech.cpp Com_SRCS += epicsSocketConvertErrnoToString.cpp Com_SRCS += osdAssert.c Com_SRCS += osdFindSymbol.c Com_SRCS += osdInterrupt.c Com_SRCS += osdPoolStatus.c Com_SRCS += osdSignal.cpp Com_SRCS += osdEnv.c # Command-line input support epicsReadline_CFLAGS += -DEPICS_COMMANDLINE_LIBRARY=EPICS_COMMANDLINE_LIBRARY_$(COMMANDLINE_LIBRARY) epicsReadline_INCLUDES += $(INCLUDES_$(COMMANDLINE_LIBRARY)) Com_SRCS += epicsReadline.c Com_SRCS += epicsTempFile.cpp Com_SRCS += epicsStdio.c Com_SRCS += osdStdio.c #POSIX thread priority scheduling flag THREAD_CPPFLAGS_NO += -DDONT_USE_POSIX_THREAD_PRIORITY_SCHEDULING osdThread_CPPFLAGS += $(THREAD_CPPFLAGS_$(USE_POSIX_THREAD_PRIORITY_SCHEDULING)) osdSpin_CPPFLAGS += $(THREAD_CPPFLAGS_$(USE_POSIX_THREAD_PRIORITY_SCHEDULING)) Com_SRCS += osdThread.c Com_SRCS += osdThreadExtra.c Com_SRCS += osdThreadHooks.c Com_SRCS += osdMutex.c Com_SRCS += osdSpin.c Com_SRCS += osdEvent.c Com_SRCS += osdTime.cpp Com_SRCS += osdMonotonic.c Com_SRCS += osdProcess.c Com_SRCS += osdNetIntf.c Com_SRCS += osdMessageQueue.c Com_SRCS += osdgetexec.c Com_SRCS += devLibVME.c Com_SRCS += devLibVMEOSD.c Com_SRCS_vxWorks += atReboot.cpp # For old vxWorks applications INC_vxWorks += camacLib.h INC_vxWorks += epicsDynLink.h INC_vxWorks += module_types.h INC_vxWorks += task_params.h Com_SRCS_vxWorks += epicsDynLink.c Com_SRCS_vxWorks += veclist.c Com_SRCS_vxWorks += logMsgToErrlog.cpp Com_SRCS_vxWorks += strtoll.c Com_SRCS_vxWorks += strtoull.c #This forces the vxWorks compatibility stuff to be loaded OBJS_vxWorks = vxComLibrary INC_WIN32 += epicsAtomicMS.h Com_SRCS_WIN32 += epicsGetopt.c Com_SRCS_WIN32 += setThreadName.cpp #Com_SRCS_WIN32 += dllmain.cpp Com_SRCS_WIN32 += forceBadAllocException.cpp #Stack trace support Com_SRCS += epicsStackTrace.c Com_SRCS += osdBackTrace.cpp Com_SRCS += osdFindAddr.c base-7.0.3.1/modules/libcom/src/osi/RULES0000664000577000060420000000122413557101274016525 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. # Ensure epicsVersion.h gets built first osdAssert$(DEP): $(COMMON_DIR)/epicsVersion.h epicsTime$(DEP): $(COMMON_DIR)/epicsVersion.h osdNetIntf$(DEP): $(COMMON_DIR)/epicsVersion.h osdSock$(DEP): $(COMMON_DIR)/epicsVersion.h base-7.0.3.1/modules/libcom/src/osi/TODOfuture0000664000577000060420000000074313557101274017640 0ustar anjaesctlepicsThreadClockGetResolution Consider adding a routine double epicsThreadClockGetResolution(); It would return the clock tick rate where by clock we mean the thing that is like the vxWorks sysClockRateGet(). The implementation should be: Provide a default that determines the rate emperically at int time by calling epicsThreadSleep for amaller and smaller delays until the actual delay no longer decreases. Each os/xxx can override the default if desired. base-7.0.3.1/modules/libcom/src/osi/compiler/clang/compilerSpecific.h0000664000577000060420000000317313557101274024244 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: * Jeffrey O. Hill * johill@lanl.gov */ #ifndef compilerSpecific_h #define compilerSpecific_h #ifndef __clang__ # error compiler/clang/compilerSpecific.h is only for use with the clang compiler #endif #if __has_attribute(always_inline) #define EPICS_ALWAYS_INLINE __inline__ __attribute__((always_inline)) #else #define EPICS_ALWAYS_INLINE __inline__ #endif /* Expands to a 'const char*' which describes the name of the current function scope */ #define EPICS_FUNCTION __PRETTY_FUNCTION__ #ifdef __cplusplus /* * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification */ #define CXX_PLACEMENT_DELETE #define CXX_THROW_SPECIFICATION #endif /* __cplusplus */ /* * __has_attribute() is not supported on all versions of clang yet */ /* * Enable format-string checking */ #define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__printf__,f,a))) /* * Deprecation marker */ #define EPICS_DEPRECATED __attribute__((deprecated)) /* * Unused marker */ #define EPICS_UNUSED __attribute__((unused)) #endif /* ifndef compilerSpecific_h */ base-7.0.3.1/modules/libcom/src/osi/compiler/clang/epicsAtomicCD.h0000664000577000060420000000123413557101274023427 0ustar anjaesctl /*************************************************************************\ * Copyright (c) 2011 LANS LLC, as Operator of * Los Alamos National Laboratory. * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef epicsAtomicCD_h #define epicsAtomicCD_h #define EPICS_ATOMIC_CMPLR_NAME "CLANG" #include "epicsAtomicOSD.h" #endif /* epicsAtomicCD_h */ base-7.0.3.1/modules/libcom/src/osi/compiler/default/compilerSpecific.h0000664000577000060420000000233113557101274024577 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: * Jeffrey O. Hill * johill@lanl.gov */ #ifndef compilerSpecific_h #define compilerSpecific_h /* The 'inline' key work, possibily with compiler * dependent flags to force inlineing where it would * otherwise not be done. * * Warning: Second guessing the compiler may result in larger code size */ #define EPICS_ALWAYS_INLINE inline #ifdef __cplusplus /* * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification * * (our default guess is that the compiler implements the C++ 97 standard) */ #define CXX_THROW_SPECIFICATION #define CXX_PLACEMENT_DELETE #endif /* __cplusplus */ #endif /* ifndef compilerSpecific_h */ base-7.0.3.1/modules/libcom/src/osi/compiler/default/epicsAtomicCD.h0000664000577000060420000000123613557101274023771 0ustar anjaesctl /*************************************************************************\ * Copyright (c) 2011 LANS LLC, as Operator of * Los Alamos National Laboratory. * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef epicsAtomicCD_h #define epicsAtomicCD_h #define EPICS_ATOMIC_CMPLR_NAME "DEFAULT" #include "epicsAtomicOSD.h" #endif /* epicsAtomicCD_h */ base-7.0.3.1/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h0000664000577000060420000000404713557101274023715 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: * Jeffrey O. Hill * johill@lanl.gov */ #ifndef compilerSpecific_h #define compilerSpecific_h #ifndef __GNUC__ # error compiler/gcc/compilerSpecific.h is only for use with the gnu compiler #endif #ifdef __clang__ # error compiler/gcc/compilerSpecific.h is not for use with the clang compiler #endif #if __GNUC__ > 2 # define EPICS_ALWAYS_INLINE __inline__ __attribute__((always_inline)) #else # define EPICS_ALWAYS_INLINE __inline__ #endif /* Expands to a 'const char*' which describes the name of the current function scope */ #define EPICS_FUNCTION __PRETTY_FUNCTION__ #ifdef __cplusplus /* * in general we dont like ifdefs but they do allow us to check the * compiler version and make the optimistic assumption that * standards incompliance issues will be fixed by future compiler * releases */ /* * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification */ #if __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 95 ) # define CXX_THROW_SPECIFICATION #endif #if __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 96 ) # define CXX_PLACEMENT_DELETE #endif #endif /* __cplusplus */ /* * Enable format-string checking if possible */ #define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__printf__,f,a))) /* * Deprecation marker if possible */ #if (__GNUC__ > 2) # define EPICS_DEPRECATED __attribute__((deprecated)) #endif /* * Unused marker */ #define EPICS_UNUSED __attribute__((unused)) #endif /* ifndef compilerSpecific_h */ base-7.0.3.1/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h0000664000577000060420000001254213557101274023103 0ustar anjaesctl /*************************************************************************\ * Copyright (c) 2011 LANS LLC, as Operator of * Los Alamos National Laboratory. * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef epicsAtomicCD_h #define epicsAtomicCD_h #ifndef __GNUC__ # error this header is only for use with the gnu compiler #endif #define EPICS_ATOMIC_CMPLR_NAME "GCC" #define GCC_ATOMIC_CONCAT( A, B ) GCC_ATOMIC_CONCATR(A,B) #define GCC_ATOMIC_CONCATR( A, B ) ( A ## B ) #define GCC_ATOMIC_INTRINSICS_AVAIL_INT_T \ GCC_ATOMIC_CONCAT ( \ __GCC_HAVE_SYNC_COMPARE_AND_SWAP_, \ __SIZEOF_INT__ ) #define GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T \ GCC_ATOMIC_CONCAT ( \ __GCC_HAVE_SYNC_COMPARE_AND_SWAP_, \ __SIZEOF_SIZE_T__ ) #define GCC_ATOMIC_INTRINSICS_MIN_X86 \ ( defined ( __i486 ) || defined ( __pentium ) || \ defined ( __pentiumpro ) || defined ( __MMX__ ) ) #define GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER \ ( ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 401 ) #define GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER \ ( GCC_ATOMIC_INTRINSICS_MIN_X86 && \ GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER ) #ifdef __cplusplus extern "C" { #endif /* * We are optimistic that __sync_synchronize is implemented * in all version four gcc invariant of target. The gnu doc * seems to say that when not supported by architecture a call * to an external function is generated but in practice * this isn`t the case for some of the atomic intrinsics, and * so there is an undefined symbol. So far we have not seen * that with __sync_synchronize, but we can only guess based * on experimental evidence. * * For example we know that when generating object code for * 386 most of the atomic intrinsics are not present and * we see undefined symbols with mingw, but we don`t have * troubles with __sync_synchronize. */ #if GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER #ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER #define EPICS_ATOMIC_READ_MEMORY_BARRIER EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) { __sync_synchronize (); } #endif #ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER #define EPICS_ATOMIC_WRITE_MEMORY_BARRIER EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) { __sync_synchronize (); } #endif #else #ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER #if GCC_ATOMIC_INTRINSICS_MIN_X86 #define EPICS_ATOMIC_READ_MEMORY_BARRIER EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) { asm("mfence;"); } #endif #endif #ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER #if GCC_ATOMIC_INTRINSICS_MIN_X86 #define EPICS_ATOMIC_WRITE_MEMORY_BARRIER EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) { asm("mfence;"); } #endif #endif #endif /* if GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER */ #if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T \ || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER #define EPICS_ATOMIC_INCR_INTT EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) { return __sync_add_and_fetch ( pTarget, 1 ); } #define EPICS_ATOMIC_DECR_INTT EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ) { return __sync_sub_and_fetch ( pTarget, 1 ); } #define EPICS_ATOMIC_ADD_INTT EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ) { return __sync_add_and_fetch ( pTarget, delta ); } #define EPICS_ATOMIC_CAS_INTT EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, int oldVal, int newVal ) { return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); } #endif /* if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T */ #if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T \ || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER #define EPICS_ATOMIC_INCR_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) { return __sync_add_and_fetch ( pTarget, 1u ); } #define EPICS_ATOMIC_DECR_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) { return __sync_sub_and_fetch ( pTarget, 1u ); } #define EPICS_ATOMIC_ADD_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta ) { return __sync_add_and_fetch ( pTarget, delta ); } #define EPICS_ATOMIC_SUB_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ) { return __sync_sub_and_fetch ( pTarget, delta ); } #define EPICS_ATOMIC_CAS_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, size_t oldVal, size_t newVal ) { return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); } #define EPICS_ATOMIC_CAS_PTRT EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) { return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); } #endif /* if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T */ #ifdef __cplusplus } /* end of extern "C" */ #endif /* * if currently unavailable as gcc intrinsics we * will try for an os specific inline solution */ #include "epicsAtomicOSD.h" #endif /* epicsAtomicCD_h */ base-7.0.3.1/modules/libcom/src/osi/compiler/msvc/compilerSpecific.h0000664000577000060420000000314313557101274024125 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: * Jeffrey O. Hill * johill@lanl.gov */ #ifndef compilerSpecific_h #define compilerSpecific_h #ifndef _MSC_VER # error compiler/msvc/compilerSpecific.h is only for use with the Microsoft compiler #endif #if _MSC_VER >= 1200 #define EPICS_ALWAYS_INLINE __forceinline #else #define EPICS_ALWAYS_INLINE __inline #endif /* Expands to a 'const char*' which describes the name of the current function scope */ #define EPICS_FUNCTION __FUNCTION__ #ifdef __cplusplus /* * in general we dont like ifdefs but they do allow us to check the * compiler version and make the optimistic assumption that * standards incompliance issues will be fixed by future compiler * releases */ /* * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification */ #if _MSC_VER >= 1200 /* visual studio 6.0 or later */ # define CXX_PLACEMENT_DELETE #endif #if _MSC_VER > 1300 /* some release after visual studio 7 we hope */ # define CXX_THROW_SPECIFICATION #endif #endif /* __cplusplus */ #endif /* ifndef compilerSpecific_h */ base-7.0.3.1/modules/libcom/src/osi/compiler/msvc/epicsAtomicCD.h0000664000577000060420000000651713557101274023324 0ustar anjaesctl /*************************************************************************\ * Copyright (c) 2011 LANS LLC, as Operator of * Los Alamos National Laboratory. * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef epicsAtomicCD_h #define epicsAtomicCD_h #include "epicsAssert.h" #ifndef _MSC_VER # error this header file is only for use with with the Microsoft Compiler #endif #ifdef _MSC_EXTENSIONS #define EPICS_ATOMIC_CMPLR_NAME "MSVC-INTRINSIC" #include #if defined ( _M_IX86 ) # pragma warning( push ) # pragma warning( disable : 4793 ) EPICS_ATOMIC_INLINE void epicsAtomicMemoryBarrier (void) { long fence; __asm { xchg fence, eax } } # pragma warning( pop ) #elif defined ( _M_X64 ) # define MS_ATOMIC_64 # pragma intrinsic ( __faststorefence ) EPICS_ATOMIC_INLINE void epicsAtomicMemoryBarrier (void) { __faststorefence (); } #elif defined ( _M_IA64 ) # define MS_ATOMIC_64 # pragma intrinsic ( __mf ) EPICS_ATOMIC_INLINE void epicsAtomicMemoryBarrier (void) { __mf (); } #else # error unexpected target architecture, msvc version of epicsAtomicCD.h #endif /* * The windows doc appears to recommend defining InterlockedExchange * to be _InterlockedExchange to cause it to be an intrinsic, but that * creates issues when later, in a windows os specific header, we include * windows.h. Therefore, we except some code duplication between the msvc * csAtomic.h and win32 osdAtomic.h to avoid problems, and to keep the * os specific windows.h header file out of the msvc cdAtomic.h */ #define MS_LONG long #define MS_InterlockedExchange _InterlockedExchange #define MS_InterlockedCompareExchange _InterlockedCompareExchange #define MS_InterlockedIncrement _InterlockedIncrement #define MS_InterlockedDecrement _InterlockedDecrement #define MS_InterlockedExchange _InterlockedExchange #define MS_InterlockedExchangeAdd _InterlockedExchangeAdd #if defined ( MS_ATOMIC_64 ) # define MS_LONGLONG long long # define MS_InterlockedIncrement64 _InterlockedIncrement64 # define MS_InterlockedDecrement64 _InterlockedDecrement64 # define MS_InterlockedExchange64 _InterlockedExchange64 # define MS_InterlockedExchangeAdd64 _InterlockedExchangeAdd64 # define MS_InterlockedCompareExchange64 _InterlockedCompareExchange64 #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #define EPICS_ATOMIC_READ_MEMORY_BARRIER EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) { epicsAtomicMemoryBarrier (); } #define EPICS_ATOMIC_WRITE_MEMORY_BARRIER EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) { epicsAtomicMemoryBarrier (); } #ifdef __cplusplus } /* end of extern "C" */ #endif /* __cplusplus */ #include "epicsAtomicMS.h" #include "epicsAtomicDefault.h" #else /* ifdef _MSC_EXTENSIONS */ #define EPICS_ATOMIC_CMPLR_NAME "MSVC-DIRECT" /* * if unavailable as an intrinsic we will try * for os specific inline solution */ #include "epicsAtomicOSD.h" #endif /* ifdef _MSC_EXTENSIONS */ #endif /* epicsAtomicCD_h */ base-7.0.3.1/modules/libcom/src/osi/compiler/solStudio/compilerSpecific.h0000664000577000060420000000243113557101274025141 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: * Jeffrey O. Hill * johill@lanl.gov */ #ifndef compilerSpecific_h #define compilerSpecific_h #if !defined(__SUNPRO_C) && !defined (__SUNPRO_CC) # error Not Solaris Studio #endif #if (defined(__SUNPRO_C) && __SUNPRO_C < 0x590) || \ (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) # define EPICS_ALWAYS_INLINE inline #else # define EPICS_ALWAYS_INLINE inline __attribute__((always_inline)) #endif #ifdef __cplusplus /* * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification * * (our default guess is that the compiler implements the C++ 97 standard) */ #define CXX_THROW_SPECIFICATION #define CXX_PLACEMENT_DELETE #endif /* __cplusplus */ #endif /* ifndef compilerSpecific_h */ base-7.0.3.1/modules/libcom/src/osi/compiler/solStudio/epicsAtomicCD.h0000664000577000060420000000124013557101274024325 0ustar anjaesctl /*************************************************************************\ * Copyright (c) 2011 LANS LLC, as Operator of * Los Alamos National Laboratory. * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef epicsAtomicCD_h #define epicsAtomicCD_h #define EPICS_ATOMIC_CMPLR_NAME "SOLSTUDIO" #include "epicsAtomicOSD.h" #endif /* epicsAtomicCD_h */ base-7.0.3.1/modules/libcom/src/osi/compilerDependencies.h0000664000577000060420000000354313557101274022210 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: * Jeffrey O. Hill * johill@lanl.gov */ #ifndef compilerDependencies_h #define compilerDependencies_h #include "compilerSpecific.h" #ifdef __cplusplus /* * usage: void func () epicsThrows (( std::bad_alloc, std::logic_error )) * * Note: now a widely accepted concensus (ref Meyers and C++ faq) is that * one should avoid using throw specifications in C++ code */ #if defined ( CXX_THROW_SPECIFICATION ) # define epicsThrows(X) throw X #else # define epicsThrows(X) #endif /* * usage: epicsPlacementDeleteOperator (( void *, myMemoryManager & )) */ #if defined ( CXX_PLACEMENT_DELETE ) # define epicsPlacementDeleteOperator(X) void operator delete X; #else # define epicsPlacementDeleteOperator(X) #endif #endif /* __cplusplus */ #ifndef EPICS_PRINTF_STYLE /* * No format-string checking */ # define EPICS_PRINTF_STYLE(f,a) #endif #ifndef EPICS_DEPRECATED /* * No deprecation markers */ #define EPICS_DEPRECATED #endif #ifndef EPICS_UNUSED # define EPICS_UNUSED #endif #ifndef EPICS_FUNCTION #if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)) || (defined(__cplusplus) && __cplusplus>=201103L) # define EPICS_FUNCTION __func__ #else /* Expands to a 'const char*' which describes the name of the current function scope */ # define EPICS_FUNCTION ("") #endif #endif #endif /* ifndef compilerDependencies_h */ base-7.0.3.1/modules/libcom/src/osi/devLib.h0000664000577000060420000001054013557101274017267 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devLib.h */ /* * Original Author: Marty Kraimer * Author: Jeff Hill * Date: 03-10-93 */ #ifndef EPICSDEVLIB_H #define EPICSDEVLIB_H /* * Support macros */ /* * Normalize a digital value and convert it to type TYPE * * Ex: * float f; * int d; * f = devNormalizeDigital(d,12) * */ #define devCreateMask(NBITS) ((1<<(NBITS))-1) #define devDigToNml(DIGITAL,NBITS) \ (((double)(DIGITAL))/devCreateMask(NBITS)) #define devNmlToDig(NORMAL,NBITS) \ (((long)(NORMAL)) * devCreateMask(NBITS)) /* * * Alignment mask * (for use when testing to see if the proper number of least * significant bits are zero) * */ #define devCreateAlignmentMask(CTYPE)\ (sizeof(CTYPE)>sizeof(double)?sizeof(double)-1:sizeof(CTYPE)-1) /* * pointer aligned test * (returns true if the pointer is on the worst case alignemnt * boundary for its type) */ #define devPtrAlignTest(PTR) (!(devCreateAlignmentMask(*PTR)&(long)(PTR))) /* * error codes (and messages) associated with devLib.c */ #define S_dev_success 0 #define S_dev_vectorInUse (M_devLib| 1) /*interrupt vector in use*/ #define S_dev_vecInstlFail (M_devLib| 2) /*interrupt vector install failed*/ #define S_dev_uknIntType (M_devLib| 3) /*Unrecognized interrupt type*/ #define S_dev_vectorNotInUse (M_devLib| 4) /*Interrupt vector not in use by caller*/ #define S_dev_badA16 (M_devLib| 5) /*Invalid VME A16 address*/ #define S_dev_badA24 (M_devLib| 6) /*Invalid VME A24 address*/ #define S_dev_badA32 (M_devLib| 7) /*Invalid VME A32 address*/ #define S_dev_uknAddrType (M_devLib| 8) /*Unrecognized address space type*/ #define S_dev_addressOverlap (M_devLib| 9) /*Specified device address overlaps another device*/ #define S_dev_identifyOverlap (M_devLib| 10) /*This device already owns the address range*/ #define S_dev_addrMapFail (M_devLib| 11) /*unable to map address*/ #define S_dev_intDisconnect (M_devLib| 12) /*Interrupt at vector disconnected from an EPICS device*/ #define S_dev_internal (M_devLib| 13) /*Internal failure*/ #define S_dev_intEnFail (M_devLib| 14) /*unable to enable interrupt level*/ #define S_dev_intDissFail (M_devLib| 15) /*unable to disable interrupt level*/ #define S_dev_noMemory (M_devLib| 16) /*Memory allocation failed*/ #define S_dev_addressNotFound (M_devLib| 17) /*Specified device address unregistered*/ #define S_dev_noDevice (M_devLib| 18) /*No device at specified address*/ #define S_dev_wrongDevice (M_devLib| 19) /*Wrong device type found at specified address*/ #define S_dev_badSignalNumber (M_devLib| 20) /*Signal number (offset) to large*/ #define S_dev_badSignalCount (M_devLib| 21) /*Signal count to large*/ #define S_dev_badRequest (M_devLib| 22) /*Device does not support requested operation*/ #define S_dev_highValue (M_devLib| 23) /*Parameter to high*/ #define S_dev_lowValue (M_devLib| 24) /*Parameter to low*/ #define S_dev_multDevice (M_devLib| 25) /*Specified address is ambiguous (more than one device responds)*/ #define S_dev_badSelfTest (M_devLib| 26) /*Device self test failed*/ #define S_dev_badInit (M_devLib| 27) /*Device failed during initialization*/ #define S_dev_hdwLimit (M_devLib| 28) /*Input exceeds Hardware Limit*/ #define S_dev_deviceDoesNotFit (M_devLib| 29) /*Unable to locate address space for device*/ #define S_dev_deviceTMO (M_devLib| 30) /*device timed out*/ #define S_dev_badFunction (M_devLib| 31) /*bad function pointer*/ #define S_dev_badVector (M_devLib| 32) /*bad interrupt vector*/ #define S_dev_badArgument (M_devLib| 33) /*bad function argument*/ #define S_dev_badISA (M_devLib| 34) /*Invalid ISA address*/ #define S_dev_badCRCSR (M_devLib| 35) /*Invalid VME CR/CSR address*/ #define S_dev_vxWorksIntEnFail S_dev_intEnFail #endif /* EPICSDEVLIB_H */ /* * Retain compatibility by including VME by default */ #ifndef NO_DEVLIB_COMPAT # include "devLibVME.h" #endif base-7.0.3.1/modules/libcom/src/osi/devLibVME.c0000664000577000060420000006206413557101274017642 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devLib.c - support for allocation of common device resources */ /* * Original Author: Marty Kraimer * Author: Jeff Hill * Date: 03-10-93 * * NOTES: * .01 06-14-93 joh needs devAllocInterruptVector() routine */ #include #include #include #define epicsExportSharedSymbols #include "dbDefs.h" #include "epicsMutex.h" #include "errlog.h" #include "ellLib.h" #define NO_DEVLIB_COMPAT #include "devLibVME.h" #include "devLibVMEImpl.h" static ELLLIST addrAlloc[atLast]; static ELLLIST addrFree[atLast]; static size_t addrLast[atLast] = { 0xffff, 0xffffff, 0xffffffff, 0xffffff, 0xffffff, }; static unsigned addrHexDig[atLast] = { 4, 6, 8, 6, 6 }; static long addrFail[atLast] = { S_dev_badA16, S_dev_badA24, S_dev_badA32, S_dev_badISA, S_dev_badCRCSR }; static epicsMutexId addrListLock; static char devLibInitFlag; const char *epicsAddressTypeName[] = { "VME A16", "VME A24", "VME A32", "ISA", "VME CR/CSR" }; typedef struct{ ELLNODE node; const char *pOwnerName; volatile void *pPhysical; /* * first, last is used here instead of base, size * so that we can store a block that is the maximum size * available in type size_t */ size_t begin; size_t end; }rangeItem; /* * These routines are not exported */ static long devLibInit(void); static long addrVerify( epicsAddressType addrType, size_t base, size_t size); static long blockFind ( epicsAddressType addrType, const rangeItem *pRange, /* size needed */ size_t requestSize, /* n ls bits zero in base addr */ unsigned alignment, /* base address found */ size_t *pFirst); static void report_conflict( epicsAddressType addrType, size_t base, size_t size, const char *pOwnerName); static void report_conflict_device( epicsAddressType addrType, const rangeItem *pRange); static void devInsertAddress( ELLLIST *pRangeList, rangeItem *pNewRange); static long devListAddressMap( ELLLIST *pRangeList); static long devCombineAdjacentBlocks( ELLLIST *pRangeList, rangeItem *pRange); static long devInstallAddr( rangeItem *pRange, /* item on the free list to be split */ const char *pOwnerName, epicsAddressType addrType, size_t base, size_t size, volatile void **ppPhysicalAddress); #define SUCCESS 0 /* * devBusToLocalAddr() */ long devBusToLocalAddr( epicsAddressType addrType, size_t busAddr, volatile void **ppLocalAddress) { long status; volatile void *localAddress; /* * Make sure that devLib has been intialized */ if (!devLibInitFlag) { status = devLibInit(); if(status){ return status; } } /* * Make sure we have a valid bus address */ status = addrVerify (addrType, busAddr, 4); if (status) { return status; } /* * Call the virtual os routine to map the bus address to a CPU address */ status = (*pdevLibVME->pDevMapAddr) (addrType, 0, busAddr, 4, &localAddress); if (status) { errPrintf (status, __FILE__, __LINE__, "%s bus address =0X%X\n", epicsAddressTypeName[addrType], (unsigned int)busAddr); return status; } /* * Return the local CPU address if the pointer is supplied */ if (ppLocalAddress) { *ppLocalAddress = localAddress; } return SUCCESS; }/*end devBusToLocalAddr()*/ /* * devRegisterAddress() */ long devRegisterAddress( const char *pOwnerName, epicsAddressType addrType, size_t base, size_t size, volatile void **ppPhysicalAddress) { rangeItem *pRange; long s; if (!devLibInitFlag) { s = devLibInit(); if(s){ return s; } } s = addrVerify (addrType, base, size); if (s) { return s; } if (size == 0) { return S_dev_lowValue; } #ifdef DEBUG printf ("Req Addr 0X%X Size 0X%X\n", base, size); #endif epicsMutexMustLock(addrListLock); pRange = (rangeItem *) ellFirst(&addrFree[addrType]); while (TRUE) { if (pRange->begin > base) { pRange = NULL; # ifdef DEBUG printf ("Unable to locate a free block\n"); devListAddressMap (&addrFree[addrType]); # endif break; } else if (base + (size - 1) <= pRange->end) { # ifdef DEBUG printf ("Found free block Begin 0X%X End 0X%X\n", pRange->begin, pRange->end); # endif break; } pRange = (rangeItem *) ellNext (&pRange->node); } epicsMutexUnlock(addrListLock); if (pRange==NULL) { report_conflict (addrType, base, size, pOwnerName); return S_dev_addressOverlap; } s = devInstallAddr( pRange, /* item on the free list to be split */ pOwnerName, addrType, base, size, ppPhysicalAddress); return s; } /* * devReadProbe() * * a bus error safe "wordSize" read at the specified address which returns * unsuccessful status if the device isnt present */ long devReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } return (*pdevLibVME->pDevReadProbe) (wordSize, ptr, pValue); } /* * devWriteProbe * * a bus error safe "wordSize" write at the specified address which returns * unsuccessful status if the device isnt present */ long devWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } return (*pdevLibVME->pDevWriteProbe) (wordSize, ptr, pValue); } /* * devInstallAddr() */ static long devInstallAddr ( rangeItem *pRange, /* item on the free list to be split */ const char *pOwnerName, epicsAddressType addrType, size_t base, size_t size, volatile void **ppPhysicalAddress) { volatile void *pPhysicalAddress; rangeItem *pNewRange; size_t reqEnd = base + (size-1); long status; /* * does it start below the specified block */ if (base < pRange->begin) { return S_dev_badArgument; } /* * does it end above the specified block */ if (reqEnd > pRange->end) { return S_dev_badArgument; } /* * always map through the virtual os in case the memory * management is set up there */ status = (*pdevLibVME->pDevMapAddr) (addrType, 0, base, size, &pPhysicalAddress); if (status) { errPrintf (status, __FILE__, __LINE__, "%s base=0X%X size = 0X%X", epicsAddressTypeName[addrType], (unsigned int)base, (unsigned int)size); return status; } /* * set the callers variable if the pointer is supplied */ if (ppPhysicalAddress) { *ppPhysicalAddress = pPhysicalAddress; } /* * does it start at the beginning of the block */ if (pRange->begin == base) { if (pRange->end == reqEnd) { epicsMutexMustLock(addrListLock); ellDelete(&addrFree[addrType], &pRange->node); epicsMutexUnlock(addrListLock); free ((void *)pRange); } else { pRange->begin = base + size; } } /* * does it end at the end of the block */ else if (pRange->end == reqEnd) { pRange->end = base-1; } /* * otherwise split the item on the free list */ else { pNewRange = (rangeItem *) calloc (1, sizeof(*pRange)); if(!pNewRange){ return S_dev_noMemory; } pNewRange->begin = base + size; pNewRange->end = pRange->end; pNewRange->pOwnerName = ""; pNewRange->pPhysical = NULL; pRange->end = base - 1; /* * add the node after the old item on the free list * (blocks end up ordered by address) */ epicsMutexMustLock(addrListLock); ellInsert(&addrFree[addrType], &pRange->node, &pNewRange->node); epicsMutexUnlock(addrListLock); } /* * allocate a new address range entry and add it to * the list */ pNewRange = (rangeItem *)calloc (1, sizeof(*pRange)); if (!pNewRange) { return S_dev_noMemory; } pNewRange->begin = base; pNewRange->end = reqEnd; pNewRange->pOwnerName = pOwnerName; pNewRange->pPhysical = pPhysicalAddress; devInsertAddress (&addrAlloc[addrType], pNewRange); return SUCCESS; } /* * report_conflict() */ static void report_conflict ( epicsAddressType addrType, size_t base, size_t size, const char *pOwnerName ) { const rangeItem *pRange; errPrintf ( S_dev_addressOverlap, __FILE__, __LINE__, "%10s 0X%08X - OX%08X Requested by %s", epicsAddressTypeName[addrType], (unsigned int)base, (unsigned int)(base+size-1), pOwnerName); pRange = (rangeItem *) ellFirst(&addrAlloc[addrType]); while (pRange) { if (pRange->begin <= base + (size-1) && pRange->end >= base) { report_conflict_device (addrType, pRange); } pRange = (rangeItem *) pRange->node.next; } } /* * report_conflict_device() */ static void report_conflict_device(epicsAddressType addrType, const rangeItem *pRange) { errPrintf ( S_dev_identifyOverlap, __FILE__, __LINE__, "%10s 0X%08X - 0X%08X Owned by %s", epicsAddressTypeName[addrType], (unsigned int)pRange->begin, (unsigned int)pRange->end, pRange->pOwnerName); } /* * devUnregisterAddress() */ long devUnregisterAddress( epicsAddressType addrType, size_t baseAddress, const char *pOwnerName) { rangeItem *pRange; int s; if (!devLibInitFlag) { s = devLibInit(); if(s) { return s; } } s = addrVerify (addrType, baseAddress, 1); if (s != SUCCESS) { return s; } epicsMutexMustLock(addrListLock); pRange = (rangeItem *) ellFirst(&addrAlloc[addrType]); while (pRange) { if (pRange->begin == baseAddress) { break; } if (pRange->begin > baseAddress) { pRange = NULL; break; } pRange = (rangeItem *) ellNext(&pRange->node); } epicsMutexUnlock(addrListLock); if (!pRange) { return S_dev_addressNotFound; } if (strcmp(pOwnerName,pRange->pOwnerName)) { s = S_dev_addressOverlap; errPrintf ( s, __FILE__, __LINE__, "unregister address for %s at 0X%X failed because %s owns it", pOwnerName, (unsigned int)baseAddress, pRange->pOwnerName); return s; } epicsMutexMustLock(addrListLock); ellDelete (&addrAlloc[addrType], &pRange->node); epicsMutexUnlock(addrListLock); pRange->pOwnerName = ""; devInsertAddress (&addrFree[addrType], pRange); s = devCombineAdjacentBlocks (&addrFree[addrType], pRange); if(s){ errMessage (s, "devCombineAdjacentBlocks error"); return s; } return SUCCESS; } /* * devCombineAdjacentBlocks() */ static long devCombineAdjacentBlocks( ELLLIST *pRangeList, rangeItem *pRange) { rangeItem *pBefore; rangeItem *pAfter; pBefore = (rangeItem *) ellPrevious (&pRange->node); pAfter = (rangeItem *) ellNext (&pRange->node); /* * combine adjacent blocks */ if (pBefore) { if (pBefore->end == pRange->begin-1) { epicsMutexMustLock(addrListLock); pRange->begin = pBefore->begin; ellDelete (pRangeList, &pBefore->node); epicsMutexUnlock(addrListLock); free ((void *)pBefore); } } if (pAfter) { if (pAfter->begin == pRange->end+1) { epicsMutexMustLock(addrListLock); pRange->end = pAfter->end; ellDelete (pRangeList, &pAfter->node); epicsMutexUnlock(addrListLock); free((void *)pAfter); } } return SUCCESS; } /* * devInsertAddress() */ static void devInsertAddress( ELLLIST *pRangeList, rangeItem *pNewRange) { rangeItem *pBefore; rangeItem *pAfter; epicsMutexMustLock(addrListLock); pAfter = (rangeItem *) ellFirst (pRangeList); while (pAfter) { if (pNewRange->end < pAfter->begin) { break; } pAfter = (rangeItem *) ellNext (&pAfter->node); } if (pAfter) { pBefore = (rangeItem *) ellPrevious (&pAfter->node); ellInsert (pRangeList, &pBefore->node, &pNewRange->node); } else { ellAdd (pRangeList, &pNewRange->node); } epicsMutexUnlock(addrListLock); } /* * devAllocAddress() */ long devAllocAddress( const char *pOwnerName, epicsAddressType addrType, size_t size, unsigned alignment, /* n ls bits zero in base addr*/ volatile void ** pLocalAddress ) { int s; rangeItem *pRange; size_t base = 0; if (!devLibInitFlag) { s = devLibInit(); if(s){ return s; } } s = addrVerify (addrType, 0, size); if(s){ return s; } if (size == 0) { return S_dev_lowValue; } epicsMutexMustLock(addrListLock); pRange = (rangeItem *) ellFirst (&addrFree[addrType]); while (pRange) { if ((pRange->end - pRange->begin) + 1 >= size){ s = blockFind ( addrType, pRange, size, alignment, &base); if (s==SUCCESS) { break; } } pRange = (rangeItem *) pRange->node.next; } epicsMutexUnlock(addrListLock); if(!pRange){ s = S_dev_deviceDoesNotFit; errMessage(s, epicsAddressTypeName[addrType]); return s; } s = devInstallAddr (pRange, pOwnerName, addrType, base, size, pLocalAddress); return s; } /* * addrVerify() * * care has been taken here not to overflow type size_t */ static long addrVerify(epicsAddressType addrType, size_t base, size_t size) { if (addrType>=atLast) { return S_dev_uknAddrType; } if (size == 0) { return addrFail[addrType]; } if (size-1 > addrLast[addrType]) { return addrFail[addrType]; } if (base > addrLast[addrType]) { return addrFail[addrType]; } if (size - 1 > addrLast[addrType] - base) { return addrFail[addrType]; } return SUCCESS; } /* * devLibInit() */ static long devLibInit (void) { rangeItem *pRange; int i; if(devLibInitFlag) return(SUCCESS); if(!pdevLibVME) { epicsPrintf ("pdevLibVME is NULL\n"); return S_dev_internal; } if (NELEMENTS(addrAlloc) != NELEMENTS(addrFree)) { return S_dev_internal; } addrListLock = epicsMutexMustCreate(); epicsMutexMustLock(addrListLock); for (i=0; ipOwnerName = ""; pRange->pPhysical = NULL; pRange->begin = 0; pRange->end = addrLast[i]; ellAdd (&addrFree[i], &pRange->node); } epicsMutexUnlock(addrListLock); devLibInitFlag = TRUE; return pdevLibVME->pDevInit(); } /* * devAddressMap() */ long devAddressMap(void) { return devListAddressMap(addrAlloc); } /* * devListAddressMap() */ static long devListAddressMap(ELLLIST *pRangeList) { rangeItem *pri; int i; long s; if (!devLibInitFlag) { s = devLibInit (); if (s) { return s; } } epicsMutexMustLock(addrListLock); for (i=0; ibegin, addrHexDig[i], (unsigned long) pri->end, pri->pPhysical, pri->pOwnerName); pri = (rangeItem *) ellNext (&pri->node); } } epicsMutexUnlock(addrListLock); return SUCCESS; } /* * * blockFind() * * Find unoccupied block in a large block * */ static long blockFind ( epicsAddressType addrType, const rangeItem *pRange, /* size needed */ size_t requestSize, /* n ls bits zero in base addr */ unsigned alignment, /* base address found */ size_t *pBase) { int s = SUCCESS; size_t bb; size_t mask; size_t newBase; size_t newSize; /* * align the block base */ mask = devCreateMask (alignment); newBase = pRange->begin; if ( mask & newBase ) { newBase |= mask; newBase++; } if ( requestSize == 0) { return S_dev_badRequest; } /* * align size of block */ newSize = requestSize; if (mask & newSize) { newSize |= mask; newSize++; } if (pRange->end - pRange->begin + 1 < newSize) { return S_dev_badRequest; } bb = pRange->begin; while (bb <= (pRange->end + 1) - newSize) { s = devNoResponseProbe (addrType, bb, newSize); if (s==SUCCESS) { *pBase = bb; return SUCCESS; } bb += newSize; } return s; } /* * devNoResponseProbe() */ long devNoResponseProbe (epicsAddressType addrType, size_t base, size_t size) { volatile void *pPhysical; size_t probe; size_t byteNo; unsigned wordSize; union { char charWord; short shortWord; int intWord; long longWord; }allWordSizes; long s; if (!devLibInitFlag) { s = devLibInit(); if (s) { return s; } } byteNo = 0; while (byteNo < size) { probe = base + byteNo; /* * for all word sizes */ for (wordSize=1; wordSize<=sizeof(allWordSizes); wordSize <<= 1) { /* * only check naturally aligned words */ if ( (probe&(wordSize-1)) != 0 ) { break; } if (byteNo+wordSize>size) { break; } /* * every byte in the block must * map to a physical address */ s = (*pdevLibVME->pDevMapAddr) (addrType, 0, probe, wordSize, &pPhysical); if (s!=SUCCESS) { return s; } /* * verify that no device is present */ s = (*pdevLibVME->pDevReadProbe)(wordSize, pPhysical, &allWordSizes); if (s==SUCCESS) { return S_dev_addressOverlap; } } byteNo++; } return SUCCESS; } long devConnectInterruptVME( unsigned vectorNumber, void (*pFunction)(void *), void *parameter ) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } return (*pdevLibVME->pDevConnectInterruptVME) (vectorNumber, pFunction, parameter); } long devDisconnectInterruptVME( unsigned vectorNumber, void (*pFunction)(void *) ) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } return (*pdevLibVME->pDevDisconnectInterruptVME) (vectorNumber, pFunction); } int devInterruptInUseVME (unsigned level) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } return (*pdevLibVME->pDevInterruptInUseVME) (level); } long devEnableInterruptLevelVME (unsigned level) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } return (*pdevLibVME->pDevEnableInterruptLevelVME) (level); } long devDisableInterruptLevelVME (unsigned level) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } return (*pdevLibVME->pDevDisableInterruptLevelVME) (level); } /* * devConnectInterrupt () * * !! DEPRECATED !! */ long devConnectInterrupt( epicsInterruptType intType, unsigned vectorNumber, void (*pFunction)(void *), void *parameter) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } switch(intType){ case intVME: case intVXI: return (*pdevLibVME->pDevConnectInterruptVME) (vectorNumber, pFunction, parameter); default: return S_dev_uknIntType; } } /* * * devDisconnectInterrupt() * * !! DEPRECATED !! */ long devDisconnectInterrupt( epicsInterruptType intType, unsigned vectorNumber, void (*pFunction)(void *) ) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } switch(intType){ case intVME: case intVXI: return (*pdevLibVME->pDevDisconnectInterruptVME) (vectorNumber, pFunction); default: return S_dev_uknIntType; } } /* * devEnableInterruptLevel() * * !! DEPRECATED !! */ long devEnableInterruptLevel( epicsInterruptType intType, unsigned level) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } switch(intType){ case intVME: case intVXI: return (*pdevLibVME->pDevEnableInterruptLevelVME) (level); default: return S_dev_uknIntType; } } /* * devDisableInterruptLevel() * * !! DEPRECATED !! */ long devDisableInterruptLevel ( epicsInterruptType intType, unsigned level) { long status; if (!devLibInitFlag) { status = devLibInit(); if (status) { return status; } } switch(intType){ case intVME: case intVXI: return (*pdevLibVME->pDevDisableInterruptLevelVME) (level); default: return S_dev_uknIntType; } } /* * locationProbe * * !! DEPRECATED !! */ long locationProbe (epicsAddressType addrType, char *pLocation) { return devNoResponseProbe (addrType, (size_t) pLocation, sizeof(long)); } /****************************************************************************** * * The follwing may, or may not be present in the BSP for the CPU in use. * */ /****************************************************************************** * * Routines to use to allocate and free memory present in the A24 region. * ******************************************************************************/ int devLibA24Debug = 0; /* Debugging flag */ void *devLibA24Calloc(size_t size) { void *ret; ret = devLibA24Malloc(size); if (ret == NULL) return (NULL); memset(ret, 0x00, size); return(ret); } void *devLibA24Malloc(size_t size) { void *ret; if (devLibA24Debug) epicsPrintf ("devLibA24Malloc(%u) entered\n", (unsigned int)size); ret = pdevLibVME->pDevA24Malloc(size); return(ret); } void devLibA24Free(void *pBlock) { if (devLibA24Debug) epicsPrintf("devLibA24Free(%p) entered\n", pBlock); pdevLibVME->pDevA24Free(pBlock); } base-7.0.3.1/modules/libcom/src/osi/devLibVME.h0000664000577000060420000001724713557101274017652 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devLib.h */ /* * Original Author: Marty Kraimer * Author: Jeff Hill * Date: 03-10-93 */ #ifndef INCdevLibh #define INCdevLibh 1 #include "dbDefs.h" #include "osdVME.h" #include "errMdef.h" #include "shareLib.h" #include "devLib.h" #ifdef __cplusplus extern "C" { #endif /* * epdevAddressType & EPICStovxWorksAddrType * devLib.c must change in unison */ typedef enum { atVMEA16, atVMEA24, atVMEA32, atISA, /* memory mapped ISA access (until now only on PC) */ atVMECSR, /* VME-64 CR/CSR address space */ atLast /* atLast must be the last enum in this list */ } epicsAddressType; /* * pointer to an array of strings for each of * the above address types */ epicsShareExtern const char *epicsAddressTypeName[]; #ifdef __cplusplus } #endif /* * To retain compatibility include everything by default */ #ifndef NO_DEVLIB_COMPAT # include "devLibVMEImpl.h" #endif #ifdef __cplusplus extern "C" { #endif /* * General API * * This section applies to all bus types */ epicsShareFunc long devAddressMap(void); /* print an address map */ /* * devBusToLocalAddr() * * OSI routine to translate bus addresses their local CPU address mapping */ epicsShareFunc long devBusToLocalAddr ( epicsAddressType addrType, size_t busAddr, volatile void **ppLocalAddr); /* * devReadProbe() * * a bus error safe "wordSize" read at the specified address which returns * unsuccessful status if the device isnt present */ epicsShareFunc long devReadProbe ( unsigned wordSize, volatile const void *ptr, void *pValueRead); /* * devNoResponseProbe() * * Verifies that no devices respond at naturally aligned words * within the specified address range. Return success if no devices * respond. Returns an error if a device does respond or if * a physical address for a naturally aligned word cant be mapped. * Checks all naturally aligned word sizes between char and long for * the entire specified range of bytes. */ epicsShareFunc long devNoResponseProbe( epicsAddressType addrType, size_t base, size_t size ); /* * devWriteProbe * * a bus error safe "wordSize" write at the specified address which returns * unsuccessful status if the device isnt present */ epicsShareFunc long devWriteProbe ( unsigned wordSize, volatile void *ptr, const void *pValueWritten); epicsShareFunc long devRegisterAddress( const char *pOwnerName, epicsAddressType addrType, size_t logicalBaseAddress, size_t size, /* bytes */ volatile void **pPhysicalAddress); epicsShareFunc long devUnregisterAddress( epicsAddressType addrType, size_t logicalBaseAddress, const char *pOwnerName); /* * allocate and register an unoccupied address block */ epicsShareFunc long devAllocAddress( const char *pOwnerName, epicsAddressType addrType, size_t size, unsigned alignment, /*n ls bits zero in addr*/ volatile void **pLocalAddress); /* * VME API * * Functions in this section apply only to the VME bus type */ /* * connect ISR to a VME interrupt vector */ epicsShareFunc long devConnectInterruptVME( unsigned vectorNumber, void (*pFunction)(void *), void *parameter); /* * disconnect ISR from a VME interrupt vector * * The parameter pFunction should be set to the C function pointer that * was connected. It is used as a key to prevent a driver from inadvertently * removing an interrupt handler that it didn't install */ epicsShareFunc long devDisconnectInterruptVME( unsigned vectorNumber, void (*pFunction)(void *)); /* * determine if a VME interrupt vector is in use * * returns boolean */ epicsShareFunc int devInterruptInUseVME (unsigned vectorNumber); /* * enable VME interrupt level */ epicsShareFunc long devEnableInterruptLevelVME (unsigned level); /* * disable VME interrupt level */ epicsShareFunc long devDisableInterruptLevelVME (unsigned level); /* * Routines to allocate and free memory in the A24 memory region. * */ epicsShareFunc void *devLibA24Malloc(size_t); epicsShareFunc void *devLibA24Calloc(size_t); epicsShareFunc void devLibA24Free(void *pBlock); /* * ISA API * * Functions in this section apply only to the ISA bus type */ /* * connect ISR to an ISA interrupt level * (not implemented) * (API should be reviewed) */ epicsShareFunc long devConnectInterruptISA( unsigned interruptLevel, void (*pFunction)(void *), void *parameter); /* * disconnect ISR from an ISA interrupt level * (not implemented) * (API should be reviewed) * * The parameter pFunction should be set to the C function pointer that * was connected. It is used as a key to prevent a driver from inadvertently * removing an interrupt handler that it didn't install */ epicsShareFunc long devDisconnectInterruptISA( unsigned interruptLevel, void (*pFunction)(void *)); /* * determine if an ISA interrupt level is in use * (not implemented) * * returns boolean */ epicsShareFunc int devInterruptLevelInUseISA (unsigned interruptLevel); /* * enable ISA interrupt level */ epicsShareFunc long devEnableInterruptLevelISA (unsigned level); /* * disable ISA interrupt level */ epicsShareFunc long devDisableInterruptLevelISA (unsigned level); /* * Deprecated interface */ #ifndef NO_DEVLIB_OLD_INTERFACE typedef enum {intVME, intVXI, intISA} epicsInterruptType; /* * NOTE: this routine has been deprecated. It exists * for backwards compatibility purposes only. * * Please use one of devConnectInterruptVME, devConnectInterruptPCI, * devConnectInterruptISA etc. devConnectInterrupt will be removed * in a future release. */ epicsShareFunc long devConnectInterrupt( epicsInterruptType intType, unsigned vectorNumber, void (*pFunction)(void *), void *parameter); /* * NOTE: this routine has been deprecated. It exists * for backwards compatibility purposes only. * * Please use one of devDisconnectInterruptVME, devDisconnectInterruptPCI, * devDisconnectInterruptISA etc. devDisconnectInterrupt will be removed * in a future release. */ epicsShareFunc long devDisconnectInterrupt( epicsInterruptType intType, unsigned vectorNumber, void (*pFunction)(void *)); /* * NOTE: this routine has been deprecated. It exists * for backwards compatibility purposes only. * * Please use one of devEnableInterruptLevelVME, devEnableInterruptLevelPCI, * devEnableInterruptLevelISA etc. devEnableInterruptLevel will be removed * in a future release. */ epicsShareFunc long devEnableInterruptLevel( epicsInterruptType intType, unsigned level); /* * NOTE: this routine has been deprecated. It exists * for backwards compatibility purposes only. * * Please use one of devDisableInterruptLevelVME, devDisableInterruptLevelISA, * devDisableInterruptLevelPCI etc. devDisableInterruptLevel will be removed * in a future release. */ epicsShareFunc long devDisableInterruptLevel ( epicsInterruptType intType, unsigned level); /* * NOTE: this routine has been deprecated. It exists * for backwards compatibility purposes only. * * Please use devNoResponseProbe(). locationProbe() will be removed * in a future release. */ epicsShareFunc long locationProbe (epicsAddressType addrType, char *pLocation); #endif /* NO_DEVLIB_OLD_INTERFACE */ #ifdef __cplusplus } #endif #endif /* INCdevLibh.h*/ base-7.0.3.1/modules/libcom/src/osi/devLibVMEImpl.h0000664000577000060420000000562613557101274020472 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* devLibImpl.h */ /* * Original Author: Marty Kraimer * Author: Jeff Hill * Date: 03-10-93 */ #ifndef INCdevLibImplh #define INCdevLibImplh 1 #include "dbDefs.h" #include "shareLib.h" #include "devLib.h" #ifdef __cplusplus extern "C" { #endif /* * virtual OS layer for devLib.c * * The global virtual OS table pdevLibVME controls * the behaviour of the functions defined in devLib.h. * All of which call into the functions found in this table * to perform system specific tasks. */ typedef struct devLibVME { /* * maps logical address to physical address, but does not detect * two device drivers that are using the same address range */ long (*pDevMapAddr) (epicsAddressType addrType, unsigned options, size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress); /* * a bus error safe "wordSize" read at the specified address which returns * unsuccessful status if the device isnt present */ long (*pDevReadProbe) (unsigned wordSize, volatile const void *ptr, void *pValueRead); /* * a bus error safe "wordSize" write at the specified address which returns * unsuccessful status if the device isnt present */ long (*pDevWriteProbe) (unsigned wordSize, volatile void *ptr, const void *pValueWritten); /* * connect ISR to a VME interrupt vector * (required for backwards compatibility) */ long (*pDevConnectInterruptVME) (unsigned vectorNumber, void (*pFunction)(void *), void *parameter); /* * disconnect ISR from a VME interrupt vector * (required for backwards compatibility) */ long (*pDevDisconnectInterruptVME) (unsigned vectorNumber, void (*pFunction)(void *)); /* * enable VME interrupt level */ long (*pDevEnableInterruptLevelVME) (unsigned level); /* * disable VME interrupt level */ long (*pDevDisableInterruptLevelVME) (unsigned level); /* malloc/free A24 address space */ void *(*pDevA24Malloc)(size_t nbytes); void (*pDevA24Free)(void *pBlock); long (*pDevInit)(void); /* * test if VME interrupt has an ISR connected */ int (*pDevInterruptInUseVME) (unsigned vectorNumber); }devLibVME; epicsShareExtern devLibVME *pdevLibVME; #ifndef NO_DEVLIB_COMPAT # define pdevLibVirtualOS pdevLibVME typedef devLibVME devLibVirtualOS; #endif #ifdef __cplusplus } #endif #endif /* INCdevLibImplh */ base-7.0.3.1/modules/libcom/src/osi/epicsAssert.h0000664000577000060420000000301713557101274020350 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * EPICS assert * * Author: Jeffrey O. Hill * Date: 022795 */ #ifndef INC_epicsAssert_H #define INC_epicsAssert_H #include "shareLib.h" #include "compilerDependencies.h" #ifdef __cplusplus extern "C" { #endif #ifndef epicsAssertAuthor # define epicsAssertAuthor 0 #endif #undef assert #ifdef NDEBUG # define assert(ignore) ((void) 0) #else /* NDEBUG */ epicsShareFunc void epicsAssert (const char *pFile, const unsigned line, const char *pExp, const char *pAuthorName); # define assert(exp) ((exp) ? (void)0 : \ epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) #endif /* NDEBUG */ /* Compile-time checks */ #if __cplusplus>=201103L #define STATIC_ASSERT(expr) static_assert(expr, #expr) #else #define STATIC_JOIN(x, y) STATIC_JOIN2(x, y) #define STATIC_JOIN2(x, y) x ## y #define STATIC_ASSERT(expr) \ typedef int STATIC_JOIN(static_assert_failed_at_line_, __LINE__) \ [ (expr) ? 1 : -1 ] EPICS_UNUSED #endif #ifdef __cplusplus } #endif #endif /* INC_epicsAssert_H */ base-7.0.3.1/modules/libcom/src/osi/epicsAtomic.h0000664000577000060420000001471313557101274020330 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2011 LANS LLC, as Operator of * Los Alamos National Laboratory. * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef epicsAtomic_h #define epicsAtomic_h #include /* define size_t */ #include "compilerSpecific.h" #define EPICS_ATOMIC_INLINE static EPICS_ALWAYS_INLINE #ifdef __cplusplus extern "C" { #endif typedef void * EpicsAtomicPtrT; /* load target into cache */ EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void); /* push cache version of target into target */ EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void); /* * lock out other smp processors from accessing the target, * load target into cache, add one to target, flush cache * to target, allow other smp processors to access the target, * return new value of target as modified by this operation */ EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ); EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ); /* * lock out other smp processors from accessing the target, * load target into cache, subtract one from target, flush cache * to target, allow out other smp processors to access the target, * return new value of target as modified by this operation */ EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ); EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ); /* * lock out other smp processors from accessing the target, * load target into cache, add/sub delta to/from target, flush cache * to target, allow other smp processors to access the target, * return new value of target as modified by this operation */ EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta ); EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ); EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ); /* * set cache version of target, flush cache to target */ EPICS_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newValue ); EPICS_ATOMIC_INLINE void epicsAtomicSetIntT ( int * pTarget, int newValue ); EPICS_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT newValue ); /* * fetch target into cache, return new value of target */ EPICS_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ); EPICS_ATOMIC_INLINE int epicsAtomicGetIntT ( const int * pTarget ); EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget ); /* * lock out other smp processors from accessing the target, * load target into cache, if target is equal to oldVal set target * to newVal, flush cache to target, allow other smp processors * to access the target, return the original value stored in the * target */ EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, size_t oldVal, size_t newVal ); EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, int oldVal, int newVal ); EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ); #ifdef __cplusplus } /* end of extern "C" */ #endif /* * options for in-line compiler intrinsic or OS specific * implementations of the above function prototypes * * for some of the compilers we must define the * in-line functions before they get used in the c++ * in-line functions below */ #include "epicsAtomicCD.h" #ifdef __cplusplus namespace epics { namespace atomic { /* * overloaded c++ interface */ /************* incr ***************/ EPICS_ATOMIC_INLINE size_t increment ( size_t & v ) { return epicsAtomicIncrSizeT ( & v ); } EPICS_ATOMIC_INLINE int increment ( int & v ) { return epicsAtomicIncrIntT ( & v ); } /************* decr ***************/ EPICS_ATOMIC_INLINE size_t decrement ( size_t & v ) { return epicsAtomicDecrSizeT ( & v ); } EPICS_ATOMIC_INLINE int decrement ( int & v ) { return epicsAtomicDecrIntT ( & v ); } /************* add ***************/ EPICS_ATOMIC_INLINE size_t add ( size_t & v, size_t delta ) { return epicsAtomicAddSizeT ( & v, delta ); } EPICS_ATOMIC_INLINE int add ( int & v, int delta ) { return epicsAtomicAddIntT ( & v, delta ); } /************* sub ***************/ EPICS_ATOMIC_INLINE size_t subtract ( size_t & v, size_t delta ) { return epicsAtomicSubSizeT ( & v, delta ); } EPICS_ATOMIC_INLINE int subtract ( int & v, int delta ) { return epicsAtomicAddIntT ( & v, -delta ); } /************* set ***************/ EPICS_ATOMIC_INLINE void set ( size_t & v , size_t newValue ) { epicsAtomicSetSizeT ( & v, newValue ); } EPICS_ATOMIC_INLINE void set ( int & v, int newValue ) { epicsAtomicSetIntT ( & v, newValue ); } EPICS_ATOMIC_INLINE void set ( EpicsAtomicPtrT & v, EpicsAtomicPtrT newValue ) { epicsAtomicSetPtrT ( & v, newValue ); } /************* get ***************/ EPICS_ATOMIC_INLINE size_t get ( const size_t & v ) { return epicsAtomicGetSizeT ( & v ); } EPICS_ATOMIC_INLINE int get ( const int & v ) { return epicsAtomicGetIntT ( & v ); } EPICS_ATOMIC_INLINE EpicsAtomicPtrT get ( const EpicsAtomicPtrT & v ) { return epicsAtomicGetPtrT ( & v ); } /************* cas ***************/ EPICS_ATOMIC_INLINE size_t compareAndSwap ( size_t & v, size_t oldVal, size_t newVal ) { return epicsAtomicCmpAndSwapSizeT ( & v, oldVal, newVal ); } EPICS_ATOMIC_INLINE int compareAndSwap ( int & v, int oldVal, int newVal ) { return epicsAtomicCmpAndSwapIntT ( & v, oldVal, newVal ); } EPICS_ATOMIC_INLINE EpicsAtomicPtrT compareAndSwap ( EpicsAtomicPtrT & v, EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) { return epicsAtomicCmpAndSwapPtrT ( & v, oldVal, newVal ); } } /* end of name space atomic */ } /* end of name space epics */ #endif /* ifdef __cplusplus */ #endif /* epicsAtomic_h */ base-7.0.3.1/modules/libcom/src/osi/epicsAtomicDefault.h0000664000577000060420000001223713557101274021634 0ustar anjaesctl /*************************************************************************\ * Copyright (c) 2011 LANS LLC, as Operator of * Los Alamos National Laboratory. * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef epicsAtomicDefault_h #define epicsAtomicDefault_h #ifdef __cpluplus extern "C" { #endif /* * struct EpicsAtomicLockKey; * epicsShareFunc void epicsAtomicReadMemoryBarrier (); * epicsShareFunc void epicsAtomicWriteMemoryBarrier (); * epicsShareFunc void epicsAtomicLock ( struct EpicsAtomicLockKey * ); * epicsShareFunc void epicsAtomicUnock ( struct EpicsAtomicLockKey * ); */ /* * incr */ #ifndef EPICS_ATOMIC_INCR_INTT EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) { EpicsAtomicLockKey key; int result; epicsAtomicLock ( & key ); result = ++(*pTarget); epicsAtomicUnlock ( & key ); return result; } #endif #ifndef EPICS_ATOMIC_INCR_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) { EpicsAtomicLockKey key; size_t result; epicsAtomicLock ( & key ); result = ++(*pTarget); epicsAtomicUnlock ( & key ); return result; } #endif /* * decr */ #ifndef EPICS_ATOMIC_DECR_INTT EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ) { EpicsAtomicLockKey key; int result; epicsAtomicLock ( & key ); result = --(*pTarget); epicsAtomicUnlock ( & key ); return result; } #endif #ifndef EPICS_ATOMIC_DECR_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) { EpicsAtomicLockKey key; size_t result; epicsAtomicLock ( & key ); result = --(*pTarget); epicsAtomicUnlock ( & key ); return result; } #endif /* * add/sub */ #ifndef EPICS_ATOMIC_ADD_INTT EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ) { EpicsAtomicLockKey key; int result; epicsAtomicLock ( & key ); result = *pTarget += delta; epicsAtomicUnlock ( & key ); return result; } #endif #ifndef EPICS_ATOMIC_ADD_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta ) { EpicsAtomicLockKey key; size_t result; epicsAtomicLock ( & key ); result = *pTarget += delta; epicsAtomicUnlock ( & key ); return result; } #endif #ifndef EPICS_ATOMIC_SUB_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ) { EpicsAtomicLockKey key; size_t result; epicsAtomicLock ( & key ); result = *pTarget -= delta; epicsAtomicUnlock ( & key ); return result; } #endif /* * set */ #ifndef EPICS_ATOMIC_SET_INTT EPICS_ATOMIC_INLINE void epicsAtomicSetIntT ( int * pTarget, int newVal ) { *pTarget = newVal; epicsAtomicWriteMemoryBarrier (); } #endif #ifndef EPICS_ATOMIC_SET_SIZET EPICS_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) { *pTarget = newVal; epicsAtomicWriteMemoryBarrier (); } #endif #ifndef EPICS_ATOMIC_SET_PTRT EPICS_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT newVal ) { *pTarget = newVal; epicsAtomicWriteMemoryBarrier (); } #endif /* * get */ #ifndef EPICS_ATOMIC_GET_INTT EPICS_ATOMIC_INLINE int epicsAtomicGetIntT ( const int * pTarget ) { epicsAtomicReadMemoryBarrier (); return *pTarget; } #endif #ifndef EPICS_ATOMIC_GET_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) { epicsAtomicReadMemoryBarrier (); return *pTarget; } #endif #ifndef EPICS_ATOMIC_GET_PTRT EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget ) { epicsAtomicReadMemoryBarrier (); return *pTarget; } #endif /* * cmp and swap */ #ifndef EPICS_ATOMIC_CAS_INTT EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, int oldval, int newval ) { EpicsAtomicLockKey key; int cur; epicsAtomicLock ( & key ); cur = *pTarget; if ( cur == oldval ) { *pTarget = newval; } epicsAtomicUnlock ( & key ); return cur; } #endif #ifndef EPICS_ATOMIC_CAS_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, size_t oldval, size_t newval ) { EpicsAtomicLockKey key; size_t cur; epicsAtomicLock ( & key ); cur = *pTarget; if ( cur == oldval ) { *pTarget = newval; } epicsAtomicUnlock ( & key ); return cur; } #endif #ifndef EPICS_ATOMIC_CAS_PTRT EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT oldval, EpicsAtomicPtrT newval ) { EpicsAtomicLockKey key; EpicsAtomicPtrT cur; epicsAtomicLock ( & key ); cur = *pTarget; if ( cur == oldval ) { *pTarget = newval; } epicsAtomicUnlock ( & key ); return cur; } #endif #ifdef __cpluplus } /* end of extern "C" */ #endif #endif /* epicsAtomicDefault_h */ base-7.0.3.1/modules/libcom/src/osi/epicsEndian.h0000664000577000060420000000206613557101274020310 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_epicsEndian_H #define INC_epicsEndian_H /* This file must be usable from both C and C++ */ #define EPICS_ENDIAN_LITTLE 1234 #define EPICS_ENDIAN_BIG 4321 /* The following OS Dependent file defines the macros * EPICS_BYTE_ORDER and EPICS_FLOAT_WORD_ORDER to be * one of the above EPICS_ENDIAN_ values. */ #include "osdWireConfig.h" #ifndef EPICS_BYTE_ORDER #error osdWireConfig.h didnt define EPICS_BYTE_ORDER #endif #ifndef EPICS_FLOAT_WORD_ORDER #error osdWireConfig.h didnt define EPICS_FLOAT_WORD_ORDER #endif #endif /* INC_epicsEndian_H */ base-7.0.3.1/modules/libcom/src/osi/epicsEvent.cpp0000664000577000060420000000614113557101274020524 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsMutex.c */ /* Author: Jeff Hill */ #include #include #define epicsExportSharedSymbols #include "epicsEvent.h" #include "epicsStdio.h" #include "cantProceed.h" // vxWorks 5.4 gcc fails during compile when I use std::exception using namespace std; // exception payload class epicsEvent::invalidSemaphore : public exception { const char * what () const throw (); }; const char * epicsEvent::invalidSemaphore::what () const throw () { return "epicsEvent::invalidSemaphore()"; } // // Its probably preferable to not make these inline because they are in // the sharable library interface. The use of inline or not here is probably // not an issue because all of this ends up in the operating system in system // calls // epicsEvent::epicsEvent ( epicsEventInitialState initial ) : id ( epicsEventCreate ( initial ) ) { if ( this->id == 0 ) { throw std::bad_alloc (); } } epicsEvent::~epicsEvent () { epicsEventDestroy ( this->id ); } void epicsEvent::trigger () { epicsEventStatus status = epicsEventTrigger (this->id); if (status != epicsEventOK) { throw invalidSemaphore (); } } void epicsEvent::wait () { epicsEventStatus status = epicsEventWait (this->id); if (status != epicsEventOK) { throw invalidSemaphore (); } } bool epicsEvent::wait (double timeOut) { epicsEventStatus status = epicsEventWaitWithTimeout (this->id, timeOut); if (status == epicsEventOK) { return true; } else if (status == epicsEventWaitTimeout) { return false; } throw invalidSemaphore (); } bool epicsEvent::tryWait () { epicsEventStatus status = epicsEventTryWait (this->id); if (status == epicsEventOK) { return true; } else if (status == epicsEventWaitTimeout) { return false; } throw invalidSemaphore (); } void epicsEvent::show ( unsigned level ) const { epicsEventShow ( this->id, level ); } // epicsEventMust... convenience routines for C code extern "C" { epicsShareFunc epicsEventId epicsEventMustCreate ( epicsEventInitialState initialState) { epicsEventId id = epicsEventCreate (initialState); if (!id) cantProceed ("epicsEventMustCreate"); return id; } epicsShareFunc void epicsEventMustTrigger (epicsEventId id) { epicsEventStatus status = epicsEventTrigger (id); if (status != epicsEventOK) cantProceed ("epicsEventMustTrigger"); } epicsShareFunc void epicsEventMustWait (epicsEventId id) { epicsEventStatus status = epicsEventWait (id); if (status != epicsEventOK) cantProceed ("epicsEventMustWait"); } } // extern "C" base-7.0.3.1/modules/libcom/src/osi/epicsEvent.h0000664000577000060420000000472513557101274020177 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef epicsEventh #define epicsEventh #include "shareLib.h" typedef struct epicsEventOSD *epicsEventId; typedef enum { epicsEventOK = 0, epicsEventWaitTimeout, epicsEventError } epicsEventStatus; /* Backwards compatibility */ #define epicsEventWaitStatus epicsEventStatus #define epicsEventWaitOK epicsEventOK #define epicsEventWaitError epicsEventError typedef enum { epicsEventEmpty, epicsEventFull } epicsEventInitialState; #ifdef __cplusplus class epicsShareClass epicsEvent { public: epicsEvent ( epicsEventInitialState initial = epicsEventEmpty ); ~epicsEvent (); void trigger (); void signal () { this->trigger(); } void wait (); /* blocks until full */ bool wait ( double timeOut ); /* false if still empty at time out */ bool tryWait (); /* false if empty */ void show ( unsigned level ) const; class invalidSemaphore; /* exception payload */ private: epicsEvent ( const epicsEvent & ); epicsEvent & operator = ( const epicsEvent & ); epicsEventId id; }; extern "C" { #endif /*__cplusplus */ epicsShareFunc epicsEventId epicsEventCreate( epicsEventInitialState initialState); epicsShareFunc epicsEventId epicsEventMustCreate ( epicsEventInitialState initialState); epicsShareFunc void epicsEventDestroy(epicsEventId id); epicsShareFunc epicsEventStatus epicsEventTrigger( epicsEventId id); epicsShareFunc void epicsEventMustTrigger(epicsEventId id); #define epicsEventSignal(ID) epicsEventMustTrigger(ID) epicsShareFunc epicsEventStatus epicsEventWait( epicsEventId id); epicsShareFunc void epicsEventMustWait(epicsEventId id); epicsShareFunc epicsEventStatus epicsEventWaitWithTimeout( epicsEventId id, double timeOut); epicsShareFunc epicsEventStatus epicsEventTryWait( epicsEventId id); epicsShareFunc void epicsEventShow( epicsEventId id, unsigned int level); #ifdef __cplusplus } #endif /*__cplusplus */ #include "osdEvent.h" #endif /* epicsEventh */ base-7.0.3.1/modules/libcom/src/osi/epicsFindSymbol.h0000664000577000060420000000152213557101274021154 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef epicsFindSymbolh #define epicsFindSymbolh #ifdef __cplusplus extern "C" { #endif #include "shareLib.h" epicsShareFunc void * epicsLoadLibrary(const char *name); epicsShareFunc const char *epicsLoadError(void); epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name); #ifdef __cplusplus } #endif #endif /* epicsFindSymbolh */ base-7.0.3.1/modules/libcom/src/osi/epicsGeneralTime.c0000664000577000060420000004447413557101274021312 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2008 Diamond Light Source Ltd * Copyright (c) 2004 Oak Ridge National Laboratory * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Original Authors: David H. Thompson & Sheng Peng (ORNL) */ #include #include #define epicsExportSharedSymbols #include "epicsTypes.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "epicsMessageQueue.h" #include "epicsString.h" #include "epicsStdioRedirect.h" #include "epicsThread.h" #include "epicsTime.h" #include "epicsTimer.h" #include "epicsInterrupt.h" #include "osiSock.h" #include "ellLib.h" #include "errlog.h" #include "cantProceed.h" #include "envDefs.h" #include "generalTimeSup.h" #include "epicsGeneralTime.h" /* Change 'undef' to 'define' to turn on debug statements: */ #undef DEBUG_GENERAL_TIME #ifdef DEBUG_GENERAL_TIME int generalTimeDebug = 10; # define IFDEBUG(n) \ if (generalTimeDebug >= n) /* block or statement */ #else # define IFDEBUG(n) \ if(0) /* Compiler will elide the block or statement */ #endif /* Declarations */ typedef struct { ELLNODE node; char *name; int priority; union { TIMECURRENTFUN Time; TIMEEVENTFUN Event; } get; union { TIMECURRENTFUN Time; TIMEEVENTFUN Event; } getInt; } gtProvider; static struct { epicsMutexId timeListLock; ELLLIST timeProviders; gtProvider *lastTimeProvider; epicsTimeStamp lastProvidedTime; epicsMutexId eventListLock; ELLLIST eventProviders; gtProvider *lastEventProvider; epicsTimeStamp eventTime[NUM_TIME_EVENTS]; epicsTimeStamp lastProvidedBestTime; int ErrorCounts; } gtPvt; static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; static const char * const tsfmt = "%Y-%m-%d %H:%M:%S.%09f"; /* defined in osiClockTime.c or osdTime.cpp */ int osdTimeGetCurrent ( epicsTimeStamp *pDest ); /* cleared if/when gtPvt.timeProviders contains more than the default osdTimeGetCurrent() */ static int useOsdGetCurrent = 1; /* Implementation */ static void generalTime_InitOnce(void *dummy) { ellInit(>Pvt.timeProviders); gtPvt.timeListLock = epicsMutexMustCreate(); ellInit(>Pvt.eventProviders); gtPvt.eventListLock = epicsMutexMustCreate(); IFDEBUG(1) printf("General Time Initialized\n"); } void generalTime_Init(void) { epicsThreadOnce(&onceId, generalTime_InitOnce, NULL); } int generalTimeGetExceptPriority(epicsTimeStamp *pDest, int *pPrio, int ignore) { gtProvider *ptp; int status = S_time_noProvider; if(useOsdGetCurrent) return osdTimeGetCurrent(pDest); generalTime_Init(); IFDEBUG(2) printf("generalTimeGetExceptPriority(ignore=%d)\n", ignore); epicsMutexMustLock(gtPvt.timeListLock); for (ptp = (gtProvider *)ellFirst(>Pvt.timeProviders); ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { if ((ignore > 0 && ptp->priority == ignore) || (ignore < 0 && ptp->priority != -ignore)) continue; status = ptp->get.Time(pDest); if (status == epicsTimeOK) { /* No ratchet, time from this routine may go backwards */ if (pPrio) *pPrio = ptp->priority; break; } else IFDEBUG(2) printf("gTGExP provider '%s' returned error\n", ptp->name); } epicsMutexUnlock(gtPvt.timeListLock); IFDEBUG(2) { if (ptp && status == epicsTimeOK) { char buff[40]; epicsTimeToStrftime(buff, sizeof(buff), tsfmt, pDest); printf("gTGExP returning %s from provider '%s'\n", buff, ptp->name); } else printf("gTGExP returning error\n"); } return status; } int epicsShareAPI epicsTimeGetCurrent(epicsTimeStamp *pDest) { gtProvider *ptp; int status = S_time_noProvider; epicsTimeStamp ts; if(useOsdGetCurrent) return osdTimeGetCurrent(pDest); generalTime_Init(); IFDEBUG(20) printf("epicsTimeGetCurrent()\n"); epicsMutexMustLock(gtPvt.timeListLock); for (ptp = (gtProvider *)ellFirst(>Pvt.timeProviders); ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { status = ptp->get.Time(&ts); if (status == epicsTimeOK) { /* check time is monotonic */ if (epicsTimeGreaterThanEqual(&ts, >Pvt.lastProvidedTime)) { *pDest = ts; gtPvt.lastProvidedTime = ts; gtPvt.lastTimeProvider = ptp; } else { int key; *pDest = gtPvt.lastProvidedTime; key = epicsInterruptLock(); gtPvt.ErrorCounts++; epicsInterruptUnlock(key); IFDEBUG(10) { char last[40], buff[40]; epicsTimeToStrftime(last, sizeof(last), tsfmt, >Pvt.lastProvidedTime); epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts); printf("eTGC provider '%s' returned older time\n" " %s, using %s instead\n", ptp->name, buff, last); } } break; } } if (status) gtPvt.lastTimeProvider = NULL; epicsMutexUnlock(gtPvt.timeListLock); IFDEBUG(20) { if (ptp && status == epicsTimeOK) { char buff[40]; epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts); printf("eTGC returning %s from provider '%s'\n", buff, ptp->name); } else printf("eTGC returning error\n"); } return status; } int epicsTimeGetMonotonic ( epicsTimeStamp * pDest ) { epicsUInt64 now = epicsMonotonicGet(); pDest->nsec = now%1000000000ul; pDest->secPastEpoch = now/1000000000ul; return 0; } int epicsTimeGetCurrentInt(epicsTimeStamp *pDest) { gtProvider *ptp = gtPvt.lastTimeProvider; if (ptp == NULL || ptp->getInt.Time == NULL) { IFDEBUG(20) epicsInterruptContextMessage("eTGCInt: No support\n"); return S_time_noProvider; } return ptp->getInt.Time(pDest); } static int generalTimeGetEventPriority(epicsTimeStamp *pDest, int eventNumber, int *pPrio) { gtProvider *ptp; int status = S_time_noProvider; epicsTimeStamp ts; STATIC_ASSERT ( epicsTimeEventBestTime == -1 ); generalTime_Init(); IFDEBUG(2) printf("generalTimeGetEventPriority(eventNum=%d)\n", eventNumber); if (eventNumber < epicsTimeEventBestTime) return S_time_badEvent; epicsMutexMustLock(gtPvt.eventListLock); for (ptp = (gtProvider *)ellFirst(>Pvt.eventProviders); ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { status = ptp->get.Event(&ts, eventNumber); if (status == epicsTimeOK) { gtPvt.lastEventProvider = ptp; if (pPrio) *pPrio = ptp->priority; if (eventNumber >= NUM_TIME_EVENTS) { *pDest = ts; } else if (eventNumber == epicsTimeEventBestTime) { if (epicsTimeGreaterThanEqual(&ts, >Pvt.lastProvidedBestTime)) { *pDest = ts; gtPvt.lastProvidedBestTime = ts; } else { int key; *pDest = gtPvt.lastProvidedBestTime; key = epicsInterruptLock(); gtPvt.ErrorCounts++; epicsInterruptUnlock(key); IFDEBUG(10) { char last[40], buff[40]; epicsTimeToStrftime(last, sizeof(last), tsfmt, >Pvt.lastProvidedBestTime); epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts); printf("gTGEvP provider '%s' returned older time\n" " %s, using %s instead\n", ptp->name, buff, last); } } } else { if (epicsTimeGreaterThanEqual(&ts, >Pvt.eventTime[eventNumber])) { *pDest = ts; gtPvt.eventTime[eventNumber] = ts; } else { int key; *pDest = gtPvt.eventTime[eventNumber]; key = epicsInterruptLock(); gtPvt.ErrorCounts++; epicsInterruptUnlock(key); IFDEBUG(10) { char last[40], buff[40]; epicsTimeToStrftime(last, sizeof(last), tsfmt, >Pvt.lastProvidedBestTime); epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts); printf("gTGEvP provider '%s' returned older time\n" " %s, using %s instead\n", ptp->name, buff, last); } } } break; } else IFDEBUG(2) printf("gTGEvP provider '%s' returned error\n", ptp->name); } if (status) gtPvt.lastEventProvider = NULL; epicsMutexUnlock(gtPvt.eventListLock); IFDEBUG(10) { if (ptp && status == epicsTimeOK) { char buff[40]; epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts); printf("gTGEvP returning %s from provider '%s'\n", buff, ptp->name); } else printf("gTGEvP returning error\n"); } return status; } int epicsShareAPI epicsTimeGetEvent(epicsTimeStamp *pDest, int eventNumber) { if (eventNumber == epicsTimeEventCurrentTime) { return epicsTimeGetCurrent(pDest); } else { return generalTimeGetEventPriority(pDest, eventNumber, NULL); } } int epicsTimeGetEventInt(epicsTimeStamp *pDest, int eventNumber) { if (eventNumber == epicsTimeEventCurrentTime) { return epicsTimeGetCurrentInt(pDest); } else { gtProvider *ptp = gtPvt.lastEventProvider; if (ptp == NULL || ptp->getInt.Event == NULL) { IFDEBUG(20) epicsInterruptContextMessage("eTGEvInt: No support\n"); return S_time_noProvider; } return ptp->getInt.Event(pDest, eventNumber); } } /* Provider Registration */ static void insertProvider(gtProvider *ptp, ELLLIST *plist, epicsMutexId lock) { gtProvider *ptpref; epicsMutexMustLock(lock); for (ptpref = (gtProvider *)ellFirst(plist); ptpref; ptpref = (gtProvider *)ellNext(&ptpref->node)) { if (ptpref->priority > ptp->priority) break; } if (ptpref) { /* Found a provider below the new one */ ptpref = (gtProvider *)ellPrevious(&ptpref->node); ellInsert(plist, &ptpref->node, &ptp->node); } else { ellAdd(plist, &ptp->node); } /* Check to see if we have more than just the OS default time source */ if(plist==>Pvt.timeProviders && (ellCount(plist)!=1 || ptp->get.Time!=&osdTimeGetCurrent)) { useOsdGetCurrent = 0; } epicsMutexUnlock(lock); } static gtProvider * findProvider(ELLLIST *plist, epicsMutexId lock, const char *name, int priority) { gtProvider *ptp; epicsMutexMustLock(lock); for (ptp = (gtProvider *)ellFirst(plist); ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { if (ptp->priority == priority && !strcmp(ptp->name, name)) break; } epicsMutexUnlock(lock); return ptp; } int generalTimeRegisterEventProvider(const char *name, int priority, TIMEEVENTFUN getEvent) { gtProvider *ptp; generalTime_Init(); if (name == NULL || getEvent == NULL) return S_time_badArgs; ptp = (gtProvider *)malloc(sizeof(gtProvider)); if (ptp == NULL) return S_time_noMemory; ptp->name = epicsStrDup(name); ptp->priority = priority; ptp->get.Event = getEvent; ptp->getInt.Event = NULL; insertProvider(ptp, >Pvt.eventProviders, gtPvt.eventListLock); IFDEBUG(1) printf("Registered event provider '%s' at %d\n", name, priority); return epicsTimeOK; } int generalTimeAddIntEventProvider(const char *name, int priority, TIMEEVENTFUN getEvent) { gtProvider *ptp = findProvider(>Pvt.eventProviders, gtPvt.eventListLock, name, priority); if (ptp == NULL) return S_time_noProvider; ptp->getInt.Event = getEvent; IFDEBUG(1) printf("Event provider '%s' is interrupt-callable\n", name); return epicsTimeOK; } int generalTimeRegisterCurrentProvider(const char *name, int priority, TIMECURRENTFUN getTime) { gtProvider *ptp; generalTime_Init(); if (name == NULL || getTime == NULL) return S_time_badArgs; ptp = (gtProvider *)malloc(sizeof(gtProvider)); if (ptp == NULL) return S_time_noMemory; ptp->name = epicsStrDup(name); ptp->priority = priority; ptp->get.Time = getTime; ptp->getInt.Time = NULL; insertProvider(ptp, >Pvt.timeProviders, gtPvt.timeListLock); IFDEBUG(1) printf("Registered time provider '%s' at %d\n", name, priority); return epicsTimeOK; } int generalTimeAddIntCurrentProvider(const char *name, int priority, TIMECURRENTFUN getTime) { gtProvider *ptp = findProvider(>Pvt.timeProviders, gtPvt.timeListLock, name, priority); if (ptp == NULL) return S_time_noProvider; ptp->getInt.Time = getTime; IFDEBUG(1) printf("Time provider '%s' is interrupt-callable\n", name); return epicsTimeOK; } /* * Provide an optional "last resort" provider for Event Time. * * This is deliberately optional, as it represents site policy. * It is intended to be installed as an EventTime provider at the lowest * priority, to return the current time for an event if there is no * better time provider for event times. * * Typically, this will only be used during startup, or a time-provider * resynchronisation, where events are being generated by the event system * but the time provided by the system is not yet valid. */ static int lastResortGetEvent(epicsTimeStamp *timeStamp, int eventNumber) { return epicsTimeGetCurrent(timeStamp); } int installLastResortEventProvider(void) { return generalTimeEventTpRegister("Last Resort Event", LAST_RESORT_PRIORITY, lastResortGetEvent); } /* Status Report */ long generalTimeReport(int level) { int items; if (onceId == EPICS_THREAD_ONCE_INIT) { printf("General time framework not yet initialized.\n"); return epicsTimeOK; } printf("Backwards time errors prevented %u times.\n\n", generalTimeGetErrorCounts()); /* Use an output buffer to avoid holding mutexes during printing */ printf("Current Time Providers:\n"); epicsMutexMustLock(gtPvt.timeListLock); if ((items = ellCount(>Pvt.timeProviders))) { gtProvider *ptp; char *message; /* Temporary output buffer */ char *pout; message = calloc(items, 80 * 2); /* Each provider needs 2 lines */ if (!message) { epicsMutexUnlock(gtPvt.timeListLock); printf("Out of memory\n"); return S_time_noMemory; } pout = message; for (ptp = (gtProvider *)ellFirst(>Pvt.timeProviders); ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { pout += sprintf(pout, " \"%s\", priority = %d\n", ptp->name, ptp->priority); if (level) { epicsTimeStamp tempTS; if (ptp->get.Time(&tempTS) == epicsTimeOK) { char tempTSText[40]; epicsTimeToStrftime(tempTSText, sizeof(tempTSText), "%Y-%m-%d %H:%M:%S.%06f", &tempTS); pout += sprintf(pout, "\tCurrent Time is %s.\n", tempTSText); } else { pout += sprintf(pout, "\tCurrent Time not available\n"); } } } epicsMutexUnlock(gtPvt.timeListLock); puts(message); free(message); } else { epicsMutexUnlock(gtPvt.timeListLock); printf("\tNo Providers registered.\n"); } printf("Event Time Providers:\n"); epicsMutexMustLock(gtPvt.eventListLock); if ((items = ellCount(>Pvt.eventProviders))) { gtProvider *ptp; char *message; /* Temporary output buffer */ char *pout; message = calloc(items, 80); /* Each provider needs 1 line, */ if (!message) { epicsMutexUnlock(gtPvt.eventListLock); printf("Out of memory\n"); return S_time_noMemory; } pout = message; for (ptp = (gtProvider *)ellFirst(>Pvt.eventProviders); ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { pout += sprintf(pout, " \"%s\", priority = %d\n", ptp->name, ptp->priority); } epicsMutexUnlock(gtPvt.eventListLock); puts(message); free(message); } else { epicsMutexUnlock(gtPvt.eventListLock); printf("\tNo Providers registered.\n"); } return epicsTimeOK; } /* * Access to internal status values. */ void generalTimeResetErrorCounts(void) { int key = epicsInterruptLock(); gtPvt.ErrorCounts = 0; epicsInterruptUnlock(key); } int generalTimeGetErrorCounts(void) { int key = epicsInterruptLock(); int errors = gtPvt.ErrorCounts; epicsInterruptUnlock(key); return errors; } const char * generalTimeCurrentProviderName(void) { if (gtPvt.lastTimeProvider) return gtPvt.lastTimeProvider->name; return NULL; } const char * generalTimeEventProviderName(void) { if (gtPvt.lastEventProvider) return gtPvt.lastEventProvider->name; return NULL; } const char * generalTimeHighestCurrentName(void) { gtProvider *ptp; epicsMutexMustLock(gtPvt.timeListLock); ptp = (gtProvider *)ellFirst(>Pvt.timeProviders); epicsMutexUnlock(gtPvt.timeListLock); return ptp ? ptp->name : NULL; } base-7.0.3.1/modules/libcom/src/osi/epicsGeneralTime.h0000664000577000060420000000323013557101274021300 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2008 Diamond Light Source Ltd * Copyright (c) 2004 Oak Ridge National Laboratory * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_epicsGeneralTime_H #define INC_epicsGeneralTime_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif #define NUM_TIME_EVENTS 256 /* Time Events numbered 0 through (NUM_TIME_EVENTS-1) are validated by */ /* code in epicsGeneralTime.c that tests for advancing timestamps and */ /* enforces that restriction. Event numbers greater than or equal to */ /* NUM_TIME_EVENTS are now allowed if supported by a custom time provider */ /* which should provide its own advancing timestamp validation. */ epicsShareFunc void generalTime_Init(void); epicsShareFunc int installLastResortEventProvider(void); epicsShareFunc void generalTimeResetErrorCounts(void); epicsShareFunc int generalTimeGetErrorCounts(void); epicsShareFunc const char * generalTimeCurrentProviderName(void); epicsShareFunc const char * generalTimeEventProviderName(void); epicsShareFunc const char * generalTimeHighestCurrentName(void); /* Original names, for compatibility */ #define generalTimeCurrentTpName generalTimeCurrentProviderName #define generalTimeEventTpName generalTimeEventProviderName epicsShareFunc long generalTimeReport(int interest); #ifdef __cplusplus } #endif #endif /* INC_epicsGeneralTime_H */ base-7.0.3.1/modules/libcom/src/osi/epicsInterrupt.h0000664000577000060420000000163213557101274021104 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef epicsInterrupth #define epicsInterrupth #ifdef __cplusplus extern "C" { #endif #include "shareLib.h" epicsShareFunc int epicsInterruptLock(void); epicsShareFunc void epicsInterruptUnlock(int key); epicsShareFunc int epicsInterruptIsInterruptContext(void); epicsShareFunc void epicsInterruptContextMessage(const char *message); #ifdef __cplusplus } #endif #include "osdInterrupt.h" #endif /* epicsInterrupth */ base-7.0.3.1/modules/libcom/src/osi/epicsMath.cpp0000664000577000060420000000162513557101274020336 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonna LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsMath.cpp */ #define epicsExportSharedSymbols #include #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4723) #endif #ifndef NAN static float makeNAN ( void ) { float a = 0, b = 0; return a / b; } #define NAN makeNAN() #endif #ifndef INFINITY static float makeINF ( void ) { float a = 1, b = 0; return a / b; } #define INFINITY makeINF() #endif extern "C" { epicsShareDef float epicsNAN = NAN; epicsShareDef float epicsINF = INFINITY; } #ifdef _MSC_VER #pragma warning(pop) #endif base-7.0.3.1/modules/libcom/src/osi/epicsMessageQueue.cpp0000664000577000060420000000403213557101274022031 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author W. Eric Norum * norume@aps.anl.gov * 630 252 4793 */ #include #define epicsExportSharedSymbols #include "epicsMessageQueue.h" #include "epicsStdio.h" epicsMessageQueue::epicsMessageQueue(unsigned int aCapacity, unsigned int aMaxMessageSize) : id ( epicsMessageQueueCreate(aCapacity, aMaxMessageSize) ) { if (id == NULL) throw std::bad_alloc (); } epicsMessageQueue::~epicsMessageQueue() { epicsMessageQueueDestroy(id); } int epicsMessageQueue::trySend(void *message, unsigned int size) { return epicsMessageQueueTrySend(id, message, size); } int epicsMessageQueue::send(void *message, unsigned int size) { return epicsMessageQueueSend(id, message, size); } int epicsMessageQueue::send(void *message, unsigned int size, double timeout) { return epicsMessageQueueSendWithTimeout(id, message, size, timeout); } int epicsMessageQueue::tryReceive(void *message, unsigned int size ) { return epicsMessageQueueTryReceive(id, message, size); } int epicsMessageQueue::receive(void *message, unsigned int size ) { return epicsMessageQueueReceive(id, message, size); } int epicsMessageQueue::receive(void *message, unsigned int size, double timeout) { return epicsMessageQueueReceiveWithTimeout(id, message, size, timeout); } unsigned int epicsMessageQueue::pending() { return epicsMessageQueuePending(id); } void epicsMessageQueue::show(unsigned int level) { epicsMessageQueueShow(id, level); } base-7.0.3.1/modules/libcom/src/osi/epicsMessageQueue.h0000664000577000060420000000620513557101274021502 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author W. Eric Norum * norume@aps.anl.gov * 630 252 4793 */ /* * Interthread message passing */ #ifndef epicsMessageQueueh #define epicsMessageQueueh #include "epicsAssert.h" #include "shareLib.h" typedef struct epicsMessageQueueOSD *epicsMessageQueueId; #ifdef __cplusplus class epicsShareClass epicsMessageQueue { public: epicsMessageQueue ( unsigned int capacity, unsigned int maximumMessageSize ); ~epicsMessageQueue (); int trySend ( void *message, unsigned int messageSize ); int send ( void *message, unsigned int messageSize); int send ( void *message, unsigned int messageSize, double timeout ); int tryReceive ( void *message, unsigned int size ); int receive ( void *message, unsigned int size ); int receive ( void *message, unsigned int size, double timeout ); void show ( unsigned int level = 0 ); unsigned int pending (); private: /* Prevent compiler-generated member functions */ /* default constructor, copy constructor, assignment operator */ epicsMessageQueue(); epicsMessageQueue(const epicsMessageQueue &); epicsMessageQueue& operator=(const epicsMessageQueue &); epicsMessageQueueId id; }; extern "C" { #endif /*__cplusplus */ epicsShareFunc epicsMessageQueueId epicsShareAPI epicsMessageQueueCreate( unsigned int capacity, unsigned int maximumMessageSize); epicsShareFunc void epicsShareAPI epicsMessageQueueDestroy( epicsMessageQueueId id); epicsShareFunc int epicsShareAPI epicsMessageQueueTrySend( epicsMessageQueueId id, void *message, unsigned int messageSize); epicsShareFunc int epicsShareAPI epicsMessageQueueSend( epicsMessageQueueId id, void *message, unsigned int messageSize); epicsShareFunc int epicsShareAPI epicsMessageQueueSendWithTimeout( epicsMessageQueueId id, void *message, unsigned int messageSize, double timeout); epicsShareFunc int epicsShareAPI epicsMessageQueueTryReceive( epicsMessageQueueId id, void *message, unsigned int size); epicsShareFunc int epicsShareAPI epicsMessageQueueReceive( epicsMessageQueueId id, void *message, unsigned int size); epicsShareFunc int epicsShareAPI epicsMessageQueueReceiveWithTimeout( epicsMessageQueueId id, void *message, unsigned int size, double timeout); epicsShareFunc int epicsShareAPI epicsMessageQueuePending( epicsMessageQueueId id); epicsShareFunc void epicsShareAPI epicsMessageQueueShow( epicsMessageQueueId id, int level); #ifdef __cplusplus } #endif /* __cplusplus */ #include "osdMessageQueue.h" #endif /* epicsMessageQueueh */ base-7.0.3.1/modules/libcom/src/osi/epicsMutex.cpp0000664000577000060420000002351313557101274020547 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* epicsMutex.cpp */ /* Author: Marty Kraimer and Jeff Hill Date: 03APR01 */ /* * NOTES: * 1) LOG_LAST_OWNER feature is normally commented out because * it slows down the system at run time, anfd because its not * currently safe to convert a thread id to a thread name because * the thread may have exited making the thread id invalid. */ #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsStdio.h" #include "epicsThread.h" #include "valgrind/valgrind.h" #include "ellLib.h" #include "errlog.h" #include "epicsMutex.h" #include "epicsThread.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; // vxWorks 5.4 gcc fails during compile when I use std::exception using namespace std; // exception payload class epicsMutex::mutexCreateFailed : public exception { const char * what () const throw (); }; const char * epicsMutex::mutexCreateFailed::what () const throw () { return "epicsMutex::mutexCreateFailed()"; } // exception payload class epicsMutex::invalidMutex : public exception { const char * what () const throw (); }; 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 epicsShareAPI epicsMutexOsiCreate( const char *pFileName,int lineno) { epicsMutexOSD * id; epicsThreadOnce(&epicsMutexOsiOnce, epicsMutexOsiInit, 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); } epicsMutexId epicsShareAPI epicsMutexOsiMustCreate( const char *pFileName,int lineno) { epicsMutexId id = epicsMutexOsiCreate(pFileName,lineno); assert(id); return(id ); } void epicsShareAPI 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 epicsShareAPI epicsMutexUnlock(epicsMutexId pmutexNode) { epicsMutexOsdUnlock(pmutexNode->id); } epicsMutexLockStatus epicsShareAPI epicsMutexLock( epicsMutexId pmutexNode) { epicsMutexLockStatus status = epicsMutexOsdLock(pmutexNode->id); # ifdef LOG_LAST_OWNER if ( status == epicsMutexLockOK ) { pmutexNode->lastOwner = epicsThreadGetIdSelf(); } # endif return status; } epicsMutexLockStatus epicsShareAPI 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); } epicsMutexOsdUnlock(epicsMutexGlobalLock); } void epicsShareAPI 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 if ( level > 0 ) { epicsMutexOsdShow(pmutexNode->id,level-1); } } void epicsShareAPI epicsMutexShowAll(int onlyLocked,unsigned int level) { epicsMutexParm *pmutexNode; if (epicsMutexOsiOnce == EPICS_THREAD_ONCE_INIT) return; printf("ellCount(&mutexList) %d ellCount(&freeList) %d\n", ellCount(&mutexList),ellCount(&freeList)); epicsMutexLockStatus lockStat = epicsMutexOsdLock(epicsMutexGlobalLock); assert ( lockStat == epicsMutexLockOK ); pmutexNode = reinterpret_cast < epicsMutexParm * > ( ellFirst(&mutexList) ); while(pmutexNode) { if(onlyLocked) { epicsMutexLockStatus status; status = epicsMutexOsdTryLock(pmutexNode->id); if(status==epicsMutexLockOK) { epicsMutexOsdUnlock(pmutexNode->id); pmutexNode = reinterpret_cast < epicsMutexParm * > ( ellNext(&pmutexNode->node) ); continue; } } epicsMutexShow(pmutexNode, level); pmutexNode = reinterpret_cast < epicsMutexParm * > ( ellNext(&pmutexNode->node) ); } epicsMutexOsdUnlock(epicsMutexGlobalLock); } #if !defined(__GNUC__) || __GNUC__<4 || (__GNUC__==4 && __GNUC_MINOR__<8) epicsMutex :: epicsMutex () : id ( epicsMutexCreate () ) { if ( this->id == 0 ) { throw mutexCreateFailed (); } } #endif epicsMutex :: epicsMutex ( const char *pFileName, int lineno ) : id ( epicsMutexOsiCreate (pFileName, lineno) ) { if ( this->id == 0 ) { throw mutexCreateFailed (); } } epicsMutex ::~epicsMutex () { epicsMutexDestroy ( this->id ); } void epicsMutex::lock () { epicsMutexLockStatus status = epicsMutexLock ( this->id ); if ( status != epicsMutexLockOK ) { throw invalidMutex (); } } bool epicsMutex::tryLock () { epicsMutexLockStatus status = epicsMutexTryLock ( this->id ); if ( status == epicsMutexLockOK ) { return true; } else if ( status != epicsMutexLockTimeout ) { throw invalidMutex (); } return false; } void epicsMutex::unlock () { epicsMutexUnlock ( this->id ); } void epicsMutex :: show ( unsigned level ) const { epicsMutexShow ( this->id, level ); } static epicsThreadPrivate < epicsDeadlockDetectMutex > * pCurrentMutexLevel = 0; static epicsThreadOnceId epicsDeadlockDetectMutexInit = EPICS_THREAD_ONCE_INIT; extern "C" void epicsDeadlockDetectMutexInitFunc ( void * ) { pCurrentMutexLevel = new epicsThreadPrivate < epicsDeadlockDetectMutex > (); } epicsDeadlockDetectMutex:: epicsDeadlockDetectMutex ( hierarchyLevel_t level ) : hierarchyLevel ( level ), pPreviousLevel ( 0 ) { epicsThreadOnce ( & epicsDeadlockDetectMutexInit, epicsDeadlockDetectMutexInitFunc, 0 ); } epicsDeadlockDetectMutex::~epicsDeadlockDetectMutex () { } void epicsDeadlockDetectMutex::show ( unsigned level ) const { this->mutex.show ( level ); } void epicsDeadlockDetectMutex::lock () { epicsDeadlockDetectMutex * pPrev = pCurrentMutexLevel->get(); if ( pPrev && pPrev != this ) { if ( pPrev->hierarchyLevel >= this->hierarchyLevel ) { errlogPrintf ( "!!!! Deadlock Vulnerability Detected !!!! " "at level %u and moving to level %u\n", pPrev->hierarchyLevel, this->hierarchyLevel ); } } this->mutex.lock (); if ( pPrev && pPrev != this ) { pCurrentMutexLevel->set ( this ); this->pPreviousLevel = pPrev; } } void epicsDeadlockDetectMutex::unlock () { pCurrentMutexLevel->set ( this->pPreviousLevel ); this->mutex.unlock (); } bool epicsDeadlockDetectMutex::tryLock () { bool success = this->mutex.tryLock (); if ( success ) { this->pPreviousLevel = pCurrentMutexLevel->get(); pCurrentMutexLevel->set ( this ); } return success; } base-7.0.3.1/modules/libcom/src/osi/epicsMutex.h0000664000577000060420000001020313557101274020204 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #ifndef epicsMutexh #define epicsMutexh #include "epicsAssert.h" #include "shareLib.h" typedef struct epicsMutexParm *epicsMutexId; typedef enum { epicsMutexLockOK,epicsMutexLockTimeout,epicsMutexLockError } epicsMutexLockStatus; #ifdef __cplusplus #include "compilerDependencies.h" #include "epicsGuard.h" #define newEpicsMutex new epicsMutex(__FILE__,__LINE__) class epicsShareClass epicsMutex { public: typedef epicsGuard guard_t; typedef epicsGuard release_t; class mutexCreateFailed; /* exception payload */ class invalidMutex; /* exception payload */ #if !defined(__GNUC__) || __GNUC__<4 || (__GNUC__==4 && __GNUC_MINOR__<8) epicsMutex (); epicsMutex ( const char *pFileName, int lineno ); #else epicsMutex ( const char *pFileName = __builtin_FILE(), int lineno = __builtin_LINE() ); #endif ~epicsMutex (); void show ( unsigned level ) const; void lock (); /* blocks until success */ void unlock (); bool tryLock (); /* true if successful */ private: epicsMutexId id; epicsMutex ( const epicsMutex & ); epicsMutex & operator = ( const epicsMutex & ); }; class epicsShareClass epicsDeadlockDetectMutex { public: typedef epicsGuard guard_t; typedef epicsGuard release_t; typedef unsigned hierarchyLevel_t; epicsDeadlockDetectMutex ( unsigned hierarchyLevel_t ); ~epicsDeadlockDetectMutex (); void show ( unsigned level ) const; void lock (); /* blocks until success */ void unlock (); bool tryLock (); /* true if successful */ private: epicsMutex mutex; const hierarchyLevel_t hierarchyLevel; class epicsDeadlockDetectMutex * pPreviousLevel; epicsDeadlockDetectMutex ( const epicsDeadlockDetectMutex & ); epicsDeadlockDetectMutex & operator = ( const epicsDeadlockDetectMutex & ); }; #endif /*__cplusplus*/ #ifdef __cplusplus extern "C" { #endif /*__cplusplus*/ #define epicsMutexCreate() epicsMutexOsiCreate(__FILE__,__LINE__) epicsShareFunc epicsMutexId epicsShareAPI epicsMutexOsiCreate( const char *pFileName,int lineno); #define epicsMutexMustCreate() epicsMutexOsiMustCreate(__FILE__,__LINE__) epicsShareFunc epicsMutexId epicsShareAPI epicsMutexOsiMustCreate( const char *pFileName,int lineno); epicsShareFunc void epicsShareAPI epicsMutexDestroy(epicsMutexId id); epicsShareFunc void epicsShareAPI epicsMutexUnlock(epicsMutexId id); epicsShareFunc epicsMutexLockStatus epicsShareAPI epicsMutexLock( epicsMutexId id); #define epicsMutexMustLock(ID) { \ epicsMutexLockStatus status = epicsMutexLock(ID); \ assert(status == epicsMutexLockOK); \ } epicsShareFunc epicsMutexLockStatus epicsShareAPI epicsMutexTryLock( epicsMutexId id); epicsShareFunc void epicsShareAPI epicsMutexShow( epicsMutexId id,unsigned int level); epicsShareFunc void epicsShareAPI epicsMutexShowAll( int onlyLocked,unsigned int level); /*NOTES: epicsMutex MUST implement recursive locking epicsMutex should implement priority inheritance and deletion safe */ /* * The following is the interface 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 __cplusplus } #endif #include "osdMutex.h" #endif /* epicsMutexh */ base-7.0.3.1/modules/libcom/src/osi/epicsReadline.c0000664000577000060420000000752213557101274020632 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Eric Norum */ #include #include #include #define epicsExportSharedSymbols #include "envDefs.h" #include "epicsReadline.h" /* Basic command-line input, no editing or history: */ #define EPICS_COMMANDLINE_LIBRARY_EPICS 0 /* OS-specific command-line editing and/or history: */ #define EPICS_COMMANDLINE_LIBRARY_LIBTECLA 1 #define EPICS_COMMANDLINE_LIBRARY_LEDLIB 1 #define EPICS_COMMANDLINE_LIBRARY_OTHER 1 /* GNU readline, or Apple's libedit wrapper: */ #define EPICS_COMMANDLINE_LIBRARY_READLINE 2 #define EPICS_COMMANDLINE_LIBRARY_READLINE_CURSES 2 #define EPICS_COMMANDLINE_LIBRARY_READLINE_NCURSES 2 #ifndef EPICS_COMMANDLINE_LIBRARY # define EPICS_COMMANDLINE_LIBRARY EPICS_COMMANDLINE_LIBRARY_EPICS #endif struct osdContext; struct readlineContext { FILE *in; char *line; struct osdContext *osd; }; static void osdReadlineBegin(struct readlineContext *); static char * osdReadline(const char *prompt, struct readlineContext *); static void osdReadlineEnd(struct readlineContext *); #if EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_EPICS static void osdReadlineBegin(struct readlineContext *rc) {} static char * osdReadline(const char *prompt, struct readlineContext *rc) { return NULL; } static void osdReadlineEnd(struct readlineContext *rc) {} #elif EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_READLINE # include "gnuReadline.c" #else # include "osdReadline.c" #endif /* * Create a command-line context */ void * epicsShareAPI epicsReadlineBegin(FILE *in) { struct readlineContext *rc = calloc(1, sizeof(*rc)); if (rc) { rc->in = in; rc->line = NULL; if (!envGetConfigParamPtr(&IOCSH_HISTEDIT_DISABLE)) osdReadlineBegin(rc); } return rc; } /* * Read a line of input */ char * epicsShareAPI epicsReadline (const char *prompt, void *context) { struct readlineContext *rc = context; FILE *in; char *line; int c; /* char is unsigned on some archs, EOF is -ve */ int linelen = 0; int linesize = 50; if (rc->osd) return osdReadline(prompt, rc); free(rc->line); rc->line = NULL; if ((in = rc->in) == NULL) { in = stdin; if (prompt) { fputs(prompt, stdout); fflush(stdout); } } line = (char *)malloc(linesize); if (line == NULL) { printf("Out of memory!\n"); return NULL; } while ((c = getc(in)) != '\n') { if (c == EOF) { if (ferror(in)) { if ((errno == EINTR) || (errno == EPIPE)) { clearerr(in); continue; } } free (line); return NULL; } if ((linelen + 1) >= linesize) { char *cp; linesize += 50; cp = (char *)realloc(line, linesize); if (cp == NULL) { printf("Out of memory!\n"); free(line); return NULL; } line = cp; } line[linelen++] = c; } line[linelen] = '\0'; rc->line = line; return line; } /* * Destroy a command-line context */ void epicsShareAPI epicsReadlineEnd (void *context) { if (context) { struct readlineContext *rc = context; if (rc->osd) osdReadlineEnd(rc); else free(rc->line); free(rc); } } base-7.0.3.1/modules/libcom/src/osi/epicsReadline.h0000664000577000060420000000163313557101274020634 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_epicsReadline_H #define INC_epicsReadline_H #ifdef __cplusplus extern "C" { #endif #include #include epicsShareFunc void * epicsShareAPI epicsReadlineBegin (FILE *in); epicsShareFunc char * epicsShareAPI epicsReadline (const char *prompt, void *context); epicsShareFunc void epicsShareAPI epicsReadlineEnd (void *context); #ifdef __cplusplus } #endif #endif /* INC_epicsReadline_H */ base-7.0.3.1/modules/libcom/src/osi/epicsSignal.h0000664000577000060420000000274713557101274020335 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_epicsSignal_H #define INC_epicsSignal_H #ifdef __cplusplus extern "C" { #endif #include "shareLib.h" /* * The requests in this interface are typically ignored on OS that do not implement * POSIX signals. */ struct epicsThreadOSD; /* * Required to avoid problems with soft IOCs getting SIGHUPs when a * Channel Access client disconnects */ epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ); /* * Required to avoid terminating a process which is blocking in a * socket send() call when the SIGPIPE signal is generated by the OS. */ epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ); /* * required only if shutdown() and close() do not interrupt a thread blocking in * a socket system call */ epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ); epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * ); #ifdef __cplusplus } #endif #endif /* INC_epicsSignal_H */ base-7.0.3.1/modules/libcom/src/osi/epicsSpin.h0000664000577000060420000000162113557101274020017 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * Copyright (c) 2012 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef epicsSpinh #define epicsSpinh #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif typedef struct epicsSpin *epicsSpinId; epicsShareFunc epicsSpinId epicsSpinCreate(void); epicsShareFunc epicsSpinId epicsSpinMustCreate(void); epicsShareFunc void epicsSpinDestroy(epicsSpinId); epicsShareFunc void epicsSpinLock(epicsSpinId); epicsShareFunc int epicsSpinTryLock(epicsSpinId); epicsShareFunc void epicsSpinUnlock(epicsSpinId); #ifdef __cplusplus } #endif #endif /* epicsSpinh */ base-7.0.3.1/modules/libcom/src/osi/epicsStackTrace.c0000664000577000060420000000521413557101274021127 0ustar anjaesctl/* * Copyright: Stanford University / SLAC National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * Author: Till Straumann , 2011, 2014 */ #include #define epicsExportSharedSymbols #include "epicsThread.h" #include "epicsMutex.h" #include "errlog.h" #include "epicsStackTracePvt.h" #include "epicsStackTrace.h" /* How many stack frames to capture */ #define MAXDEPTH 100 static epicsThreadOnceId stackTraceInitId = EPICS_THREAD_ONCE_INIT; static epicsMutexId stackTraceMtx; static void stackTraceInit(void *unused) { stackTraceMtx = epicsMutexMustCreate(); } static void stackTraceLock(void) { epicsThreadOnce( &stackTraceInitId, stackTraceInit, 0 ); epicsMutexLock( stackTraceMtx ); } static void stackTraceUnlock(void) { epicsMutexUnlock( stackTraceMtx ); } static int dumpInfo(void *addr, epicsSymbol *sym_p) { int rval = 0; rval += errlogPrintf("[%*p]", (int)(sizeof(addr)*2 + 2), addr); if ( sym_p ) { if ( sym_p->f_nam ) { rval += errlogPrintf(": %s", sym_p->f_nam); } if ( sym_p->s_nam ) { rval += errlogPrintf("(%s+0x%lx)", sym_p->s_nam, (unsigned long)((char*)addr - (char*)sym_p->s_val)); } else { rval += errlogPrintf("()"); } } rval += errlogPrintf("\n"); errlogFlush(); return rval; } void epicsStackTrace(void) { void **buf; int i,n; epicsSymbol sym; if ( 0 == epicsStackTraceGetFeatures() ) { /* unsupported on this platform */ return; } if ( ! (buf = malloc(sizeof(*buf) * MAXDEPTH))) { free(buf); errlogPrintf("epicsStackTrace(): not enough memory for backtrace\n"); return; } n = epicsBackTrace(buf, MAXDEPTH); if ( n > 0 ) { stackTraceLock(); errlogPrintf("Dumping a stack trace of thread '%s':\n", epicsThreadGetNameSelf()); errlogFlush(); for ( i=0; i, 2011, 2014 */ #ifndef INC_epicsStackTrace_H #define INC_epicsStackTrace_H #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /* Dump a stack trace to the errlog */ epicsShareFunc void epicsStackTrace(void); /* Inquire about functionality implemented on your system */ /* StackTrace provides numerical addresses */ #define EPICS_STACKTRACE_ADDRESSES (1<<0) /* StackTrace is able to lookup dynamic symbols */ #define EPICS_STACKTRACE_DYN_SYMBOLS (1<<1) /* StackTrace is able to lookup global symbols */ #define EPICS_STACKTRACE_GBL_SYMBOLS (1<<2) /* StackTrace is able to lookup local symbols */ #define EPICS_STACKTRACE_LCL_SYMBOLS (1<<3) /* returns ORed bitset of supported features */ epicsShareFunc int epicsStackTraceGetFeatures(void); #ifdef __cplusplus } #endif #endif base-7.0.3.1/modules/libcom/src/osi/epicsStackTracePvt.h0000664000577000060420000000251113557101274021623 0ustar anjaesctl/* * Copyright: Stanford University / SLAC National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * Author: Till Straumann , 2011, 2014 */ #ifndef INC_epicsStackTracePvt_H #define INC_epicsStackTracePvt_H #include "shareLib.h" typedef struct epicsSymbol { const char *f_nam; /* file where the symbol is defined */ const char *s_nam; /* symbol name */ void *s_val; /* symbol value */ } epicsSymbol; #ifdef __cplusplus extern "C" { #endif /* Take a snapshot of the stack into 'buf' (limited to buf_sz entries) * RETURNS: actual number of entries in 'buf' */ epicsShareFunc int epicsBackTrace(void **buf, int buf_sz); /* Find symbol closest to 'addr'. * * If successful the routine fills in the members of *sym_p but * note that 'f_nam' and/or 's_nam' may be NULL if the address * cannot be resolved. * * RETURNS: 0 on success, nonzero on failure (not finding an address * is not considered an error). */ epicsShareFunc int epicsFindAddr(void *addr, epicsSymbol *sym_p); /* report supported features (as reported by epicsStackTraceGetFeatures) */ epicsShareFunc int epicsFindAddrGetFeatures(); #ifdef __cplusplus } #endif #endif base-7.0.3.1/modules/libcom/src/osi/epicsStdio.c0000664000577000060420000000643413557101274020172 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsStdio.c */ /* Author: Marty Kraimer */ #include #include #include #include #include #include #define epicsExportSharedSymbols #define epicsStdioStdStreams #include "shareLib.h" #include "epicsThread.h" #include "epicsStdio.h" static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; static epicsThreadPrivateId stdinThreadPrivateId; static epicsThreadPrivateId stdoutThreadPrivateId; static epicsThreadPrivateId stderrThreadPrivateId = 0; static void once(void *junk) { stdinThreadPrivateId = epicsThreadPrivateCreate(); stdoutThreadPrivateId = epicsThreadPrivateCreate(); stderrThreadPrivateId = epicsThreadPrivateCreate(); } FILE * epicsShareAPI epicsGetStdin(void) { FILE *fp = epicsGetThreadStdin(); if (!fp) fp = stdin; return fp; } FILE * epicsShareAPI epicsGetStdout(void) { FILE *fp = epicsGetThreadStdout(); if (!fp) fp = stdout; return fp; } FILE * epicsShareAPI epicsGetStderr(void) { FILE *fp = epicsGetThreadStderr(); if (!fp) fp = stderr; return fp; } FILE * epicsShareAPI epicsGetThreadStdin(void) { epicsThreadOnce(&onceId,once,0); return epicsThreadPrivateGet(stdinThreadPrivateId); } FILE * epicsShareAPI epicsGetThreadStdout(void) { epicsThreadOnce(&onceId,once,0); return epicsThreadPrivateGet(stdoutThreadPrivateId); } FILE * epicsShareAPI epicsGetThreadStderr(void) { /* Deliberately don't do the epicsThreadOnce() here; epicsThreadInit() * is allowed to use stderr inside its once() routine, in which case we * must return the OS's stderr instead. There may be a tiny chance of a * race happening here during initialization for some architectures, so * we only use it for stderr to reduce the chance of that happening. */ if (!stderrThreadPrivateId) return NULL; return epicsThreadPrivateGet(stderrThreadPrivateId); } void epicsShareAPI epicsSetThreadStdin(FILE *fp) { epicsThreadOnce(&onceId,once,0); epicsThreadPrivateSet(stdinThreadPrivateId,fp); } void epicsShareAPI epicsSetThreadStdout(FILE *fp) { epicsThreadOnce(&onceId,once,0); epicsThreadPrivateSet(stdoutThreadPrivateId,fp); } void epicsShareAPI epicsSetThreadStderr(FILE *fp) { epicsThreadOnce(&onceId,once,0); epicsThreadPrivateSet(stderrThreadPrivateId,fp); } int epicsShareAPI epicsStdoutPrintf(const char *pFormat, ...) { va_list pvar; int nchar; FILE *stream = epicsGetStdout(); va_start(pvar, pFormat); nchar = vfprintf(stream, pFormat, pvar); va_end(pvar); return nchar; } int epicsShareAPI epicsStdoutPuts(const char *str) { return fprintf(epicsGetStdout(), "%s\n", str); } int epicsShareAPI epicsStdoutPutchar(int c) { return putc(c, epicsGetStdout()); } base-7.0.3.1/modules/libcom/src/osi/epicsStdio.h0000664000577000060420000000624513557101274020177 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsStdio.h */ #ifndef epicsStdioh #define epicsStdioh #include #include #include "shareLib.h" #include "compilerDependencies.h" #include "epicsTempFile.h" #ifdef __cplusplus extern "C" { #endif #ifndef epicsStdioStdStreams # undef stdin # define stdin epicsGetStdin() # undef stdout # define stdout epicsGetStdout() # undef stderr # define stderr epicsGetStderr() #endif /* Make printf, puts and putchar use *our* version of stdout */ #ifndef epicsStdioStdPrintfEtc # ifdef printf # undef printf # endif # define printf epicsStdoutPrintf # ifdef puts # undef puts # endif # define puts epicsStdoutPuts # ifdef putchar # undef putchar # endif # define putchar epicsStdoutPutchar #endif epicsShareFunc int epicsShareAPI epicsSnprintf( char *str, size_t size, const char *format, ...) EPICS_PRINTF_STYLE(3,4); epicsShareFunc int epicsShareAPI epicsVsnprintf( char *str, size_t size, const char *format, va_list ap); /* * truncate to specified size (we dont use truncate() * because it is not portable) * * pFileName - name (and optionally path) of file * size - the new file size (if file is curretly larger) * * returns TF_OK if the file is less than size bytes * or if it was successfully truncated. Returns * TF_ERROR if the file could not be truncated. */ enum TF_RETURN {TF_OK=0, TF_ERROR=1}; epicsShareFunc enum TF_RETURN truncateFile ( const char *pFileName, unsigned long size ); /* The following are for redirecting stdin,stdout,stderr */ epicsShareFunc FILE * epicsShareAPI epicsGetStdin(void); epicsShareFunc FILE * epicsShareAPI epicsGetStdout(void); epicsShareFunc FILE * epicsShareAPI epicsGetStderr(void); /* These are intended for iocsh only */ epicsShareFunc FILE * epicsShareAPI epicsGetThreadStdin(void); epicsShareFunc FILE * epicsShareAPI epicsGetThreadStdout(void); epicsShareFunc FILE * epicsShareAPI epicsGetThreadStderr(void); epicsShareFunc void epicsShareAPI epicsSetThreadStdin(FILE *); epicsShareFunc void epicsShareAPI epicsSetThreadStdout(FILE *); epicsShareFunc void epicsShareAPI epicsSetThreadStderr(FILE *); epicsShareFunc int epicsShareAPI epicsStdoutPrintf( const char *pformat, ...) EPICS_PRINTF_STYLE(1,2); epicsShareFunc int epicsShareAPI epicsStdoutPuts(const char *str); epicsShareFunc int epicsShareAPI epicsStdoutPutchar(int c); #ifdef __cplusplus } /* Also pull functions into the std namespace (see lp:1786927) */ #if !defined(__GNUC__) || (__GNUC__ > 2) namespace std { using ::epicsGetStdin; using ::epicsGetStdout; using ::epicsGetStderr; using ::epicsStdoutPrintf; using ::epicsStdoutPuts; using ::epicsStdoutPutchar; } #endif /* __GNUC__ > 2 */ #endif /* __cplusplus */ #endif /* epicsStdioh */ base-7.0.3.1/modules/libcom/src/osi/epicsStdioRedirect.h0000664000577000060420000000142713557101274021656 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsStdioRedirect.h */ /* This file is now obselete, and is likely to be * deleted in a future release of EPICS Base. * * Use the epicsStdio.h header file instead. */ #ifndef epicsStdioRedirecth #define epicsStdioRedirecth #include #endif /* epicsStdioRedirecth */ base-7.0.3.1/modules/libcom/src/osi/epicsTempFile.h0000664000577000060420000000142013557101274020610 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsTempFile.h */ #ifndef INC_epicsTempFile_H #define INC_epicsTempFile_H #include #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc FILE * epicsShareAPI epicsTempFile(void); #ifdef __cplusplus } #endif #endif /* INC_epicsTempFile_H */ base-7.0.3.1/modules/libcom/src/osi/epicsThread.cpp0000664000577000060420000002606513557101274020661 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ // // Author: Jeff Hill // #include #include #include #include #include #include // The following is required for Solaris builds #undef __EXTENSIONS__ #define epicsExportSharedSymbols #include "epicsAlgorithm.h" #include "epicsTime.h" #include "epicsThread.h" #include "epicsAssert.h" #include "epicsGuard.h" #include "errlog.h" using namespace std; epicsThreadId epicsShareAPI epicsThreadCreate ( const char * name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr,void * parm ) { epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; opts.priority = priority; opts.stackSize = stackSize; opts.joinable = 0; return epicsThreadCreateOpt(name, funptr, parm, &opts); } epicsThreadRunable::~epicsThreadRunable () {} void epicsThreadRunable::run () {} void epicsThreadRunable::show ( unsigned int ) const {} class epicsThread :: unableToCreateThread : public std :: exception { public: const char * what () const throw (); }; const char * epicsThread :: unableToCreateThread :: what () const throw () { return "unable to create thread"; } void epicsThread :: printLastChanceExceptionMessage ( const char * pExceptionTypeName, const char * pExceptionContext ) { char date[64]; try { epicsTime cur = epicsTime :: getCurrent (); cur.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); } catch ( ... ) { strcpy ( date, "" ); } char name [128]; epicsThreadGetName ( this->id, name, sizeof ( name ) ); errlogPrintf ( "epicsThread: Unexpected C++ exception \"%s\" " "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 (); } extern "C" void epicsThreadCallEntryPoint ( void * pPvt ) { epicsThread * pThread = static_cast ( pPvt ); bool threadDestroyed = false; try { pThread->pThreadDestroyed = & threadDestroyed; if ( pThread->beginWait () ) { pThread->runable.run (); // The run() routine may have destroyed the epicsThread // object by now; pThread can only be used below here // when the threadDestroyed flag is false. } } catch ( const epicsThread::exitException & ) { } catch ( std :: exception & except ) { if ( ! threadDestroyed ) { pThread->printLastChanceExceptionMessage ( typeid ( except ).name (), except.what () ); } } catch ( ... ) { if ( ! threadDestroyed ) { pThread->printLastChanceExceptionMessage ( "catch ( ... )", "Non-standard C++ exception" ); } } if ( ! threadDestroyed ) { epicsGuard < epicsMutex > guard ( pThread->mutex ); pThread->pThreadDestroyed = NULL; pThread->terminated = true; pThread->exitEvent.signal (); // After the terminated flag is set and guard's destructor // releases the lock, pThread must never be used again. } } bool epicsThread::beginWait () throw () { epicsGuard < epicsMutex > guard ( this->mutex ); while ( ! this->begin && ! this->cancel ) { epicsGuardRelease < epicsMutex > unguard ( guard ); this->event.wait (); } return this->begin && ! this->cancel; } void epicsThread::exit () { throw exitException (); } void epicsThread::exitWait () throw () { bool success = this->exitWait ( DBL_MAX ); assert ( success ); } bool epicsThread::exitWait ( const double delay ) throw () { try { // When called (usually by a destructor) in the context of // the managed thread we can't wait for the thread to exit. // Set the threadDestroyed flag and return success. if ( this->isCurrentThread() ) { if ( this->pThreadDestroyed ) { *this->pThreadDestroyed = true; } bool j; { epicsGuard < epicsMutex > guard ( this->mutex ); j = joined; joined = true; } if(!j) { epicsThreadMustJoin(this->id); } return true; } epicsTime exitWaitBegin = epicsTime::getMonotonic (); double exitWaitElapsed = 0.0; epicsGuard < epicsMutex > guard ( this->mutex ); this->cancel = true; while ( ! this->terminated && exitWaitElapsed < delay ) { epicsGuardRelease < epicsMutex > unguard ( guard ); this->event.signal (); this->exitEvent.wait ( delay - exitWaitElapsed ); epicsTime current = epicsTime::getMonotonic (); exitWaitElapsed = current - exitWaitBegin; } if(this->terminated && !joined) { joined = true; epicsGuardRelease < epicsMutex > unguard ( guard ); epicsThreadMustJoin(this->id); } } catch ( std :: exception & except ) { errlogPrintf ( "epicsThread::exitWait(): Unexpected exception " " \"%s\"\n", except.what () ); epicsThreadSleep ( epicsMin ( delay, 5.0 ) ); } catch ( ... ) { errlogPrintf ( "Non-standard unexpected exception in " "epicsThread::exitWait()\n" ); epicsThreadSleep ( epicsMin ( delay, 5.0 ) ); } // the event mechanism is used for other purposes this->event.signal (); return this->terminated; } epicsThread::epicsThread ( epicsThreadRunable & runableIn, const char * pName, unsigned stackSize, unsigned priority ) : runable ( runableIn ), id ( 0 ), pThreadDestroyed ( 0 ), begin ( false ), cancel ( false ), terminated ( false ), joined ( false ) { epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; opts.stackSize = stackSize; opts.priority = priority; opts.joinable = 1; this->id = epicsThreadCreateOpt( pName, epicsThreadCallEntryPoint, static_cast < void * > ( this ), &opts); if ( ! this->id ) { throw unableToCreateThread (); } } epicsThread::~epicsThread () throw () { while ( ! this->exitWait ( 10.0 ) ) { char nameBuf [256]; this->getName ( nameBuf, sizeof ( nameBuf ) ); fprintf ( stderr, "epicsThread::~epicsThread(): \"%s\" blocking for thread \"%s\" to exit\n", getNameSelf(), nameBuf ); fprintf ( stderr, "was epicsThread object destroyed before thread exit ?\n"); } } void epicsThread::start () throw () { { epicsGuard < epicsMutex > guard ( this->mutex ); this->begin = true; } this->event.signal (); } bool epicsThread::isCurrentThread () const throw () { return ( epicsThreadGetIdSelf () == this->id ); } void epicsThread::resume () throw () { epicsThreadResume ( this->id ); } void epicsThread::getName ( char *name, size_t size ) const throw () { epicsThreadGetName ( this->id, name, size ); } epicsThreadId epicsThread::getId () const throw () { return this->id; } unsigned int epicsThread::getPriority () const throw () { return epicsThreadGetPriority (this->id); } void epicsThread::setPriority (unsigned int priority) throw () { epicsThreadSetPriority (this->id, priority); } bool epicsThread::priorityIsEqual (const epicsThread &otherThread) const throw () { if ( epicsThreadIsEqual (this->id, otherThread.id) ) { return true; } return false; } bool epicsThread::isSuspended () const throw () { if ( epicsThreadIsSuspended (this->id) ) { return true; } return false; } bool epicsThread::operator == (const epicsThread &rhs) const throw () { return (this->id == rhs.id); } void epicsThread::suspendSelf () throw () { epicsThreadSuspendSelf (); } void epicsThread::sleep (double seconds) throw () { epicsThreadSleep (seconds); } const char *epicsThread::getNameSelf () throw () { return epicsThreadGetNameSelf (); } bool epicsThread::isOkToBlock () throw () { return epicsThreadIsOkToBlock() != 0; } void epicsThread::setOkToBlock(bool isOkToBlock) throw () { epicsThreadSetOkToBlock(static_cast(isOkToBlock)); } void epicsThreadPrivateBase::throwUnableToCreateThreadPrivate () { throw epicsThreadPrivateBase::unableToCreateThreadPrivate (); } void epicsThread :: show ( unsigned level ) const throw () { ::printf ( "epicsThread at %p\n", this->id ); if ( level > 0u ) { epicsThreadShow ( this->id, level - 1 ); if ( level > 1u ) { ::printf ( "pThreadDestroyed = %p\n", this->pThreadDestroyed ); ::printf ( "begin = %c, cancel = %c, terminated = %c\n", this->begin ? 'T' : 'F', this->cancel ? 'T' : 'F', this->terminated ? 'T' : 'F' ); this->runable.show ( level - 2u ); this->mutex.show ( level - 2u ); ::printf ( "general purpose event\n" ); this->event.show ( level - 2u ); ::printf ( "exit event\n" ); this->exitEvent.show ( level - 2u ); } } } extern "C" { static epicsThreadOnceId okToBlockOnce = EPICS_THREAD_ONCE_INIT; epicsThreadPrivateId okToBlockPrivate; static const int okToBlockNo = 0; static const int okToBlockYes = 1; static void epicsThreadOnceIdInit(void *) { okToBlockPrivate = epicsThreadPrivateCreate(); } int epicsShareAPI epicsThreadIsOkToBlock(void) { const int *pokToBlock; epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL); pokToBlock = (int *) epicsThreadPrivateGet(okToBlockPrivate); return (pokToBlock ? *pokToBlock : 0); } void epicsShareAPI epicsThreadSetOkToBlock(int isOkToBlock) { const int *pokToBlock; epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL); pokToBlock = (isOkToBlock) ? &okToBlockYes : &okToBlockNo; epicsThreadPrivateSet(okToBlockPrivate, (void *)pokToBlock); } epicsThreadId epicsShareAPI epicsThreadMustCreate ( const char *name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr,void *parm) { epicsThreadId id = epicsThreadCreate ( name, priority, stackSize, funptr, parm ); assert ( id ); return id; } } // extern "C" // Ensure the main thread gets a unique ID epicsThreadId epicsThreadMainId = epicsThreadGetIdSelf(); base-7.0.3.1/modules/libcom/src/osi/epicsThread.h0000664000577000060420000003462213557101274020324 0ustar anjaesctl/*************************************************************************\ * 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) 2013 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef epicsThreadh #define epicsThreadh #include #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif typedef void (*EPICSTHREADFUNC)(void *parm); #define epicsThreadPriorityMax 99 #define epicsThreadPriorityMin 0 /* some generic values */ #define epicsThreadPriorityLow 10 #define epicsThreadPriorityMedium 50 #define epicsThreadPriorityHigh 90 /* some iocCore specific values */ #define epicsThreadPriorityCAServerLow 20 #define epicsThreadPriorityCAServerHigh 40 #define epicsThreadPriorityScanLow 60 #define epicsThreadPriorityScanHigh 70 #define epicsThreadPriorityIocsh 91 #define epicsThreadPriorityBaseMax 91 /* stack sizes for each stackSizeClass are implementation and CPU dependent */ typedef enum { epicsThreadStackSmall, epicsThreadStackMedium, epicsThreadStackBig } epicsThreadStackSizeClass; typedef enum { epicsThreadBooleanStatusFail, epicsThreadBooleanStatusSuccess } epicsThreadBooleanStatus; /** Lookup target specific default stack size */ epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize( epicsThreadStackSizeClass size); /* (epicsThreadId)0 is guaranteed to be an invalid thread id */ typedef struct epicsThreadOSD *epicsThreadId; typedef epicsThreadId epicsThreadOnceId; #define EPICS_THREAD_ONCE_INIT 0 /** Perform one-time initialization. * * Run the provided function if it has not run, and is not running. * * @post The provided function has been run. * * @code * static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; * static void myInitFunc(void *arg) { ... } * static void some Function(void) { * epicsThreadOnce(&onceId, &myInitFunc, NULL); * } * @endcode */ epicsShareFunc void epicsShareAPI epicsThreadOnce( epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg); /* When real-time scheduling is active, attempt any post-init operations * that preserve real-time performance. For POSIX targets this locks the * process into RAM, preventing swap-related VM faults. */ epicsShareFunc void epicsThreadRealtimeLock(void); epicsShareFunc void epicsShareAPI epicsThreadExitMain(void); /** For use with epicsThreadCreateOpt() */ typedef struct epicsThreadOpts { /** Thread priority in OSI range (cf. epicsThreadPriority*) */ unsigned int priority; /** Thread stack size, either in bytes for this architecture or * an enum epicsThreadStackSizeClass value. */ unsigned int stackSize; /** Should thread be joinable? (default (0) is not joinable). * If joinable=1, then epicsThreadMustJoin() must be called for cleanup thread resources. */ unsigned int joinable; } epicsThreadOpts; /** Default initial values for epicsThreadOpts * Applications should always use this macro to initialize an epicsThreadOpts * structure. Additional fields may be added in the future, and the order of * the fields might also change, thus code that assumes the above definition * might break if these rules are not followed. */ #define EPICS_THREAD_OPTS_INIT { \ epicsThreadPriorityLow, epicsThreadStackMedium, 0} /** @brief Allocate and start a new OS thread. * @param name A name describing this thread. Appears in various log and error message. * @param funptr The thread main function. * @param parm Passed to thread main function. * @param opts Modifiers for the new thread, or NULL to use target specific defaults. * @return NULL on error */ epicsShareFunc epicsThreadId epicsThreadCreateOpt ( const char * name, EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts ); /** Short-hand for epicsThreadCreateOpt() to create an un-joinable thread. */ epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate ( const char * name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr,void * parm ); /** Short-hand for epicsThreadCreateOpt() to create an un-joinable thread. * On error calls cantProceed() */ epicsShareFunc epicsThreadId epicsShareAPI epicsThreadMustCreate ( const char * name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr,void * parm ); /* This gets undefined in osdThread.h on VxWorks < 6.9 */ #define EPICS_THREAD_CAN_JOIN /** Wait for a joinable thread to exit (return from its main function) */ epicsShareFunc void epicsThreadMustJoin(epicsThreadId id); /** Block the current thread until epicsThreadResume(). */ epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf(void); /** Resume a thread suspended with epicsThreadSuspendSelf() */ epicsShareFunc void epicsShareAPI epicsThreadResume(epicsThreadId id); /** Return thread OSI priority */ epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPriority( epicsThreadId id); /** Return thread OSI priority */ epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPrioritySelf(void); /** Change OSI priority of target thread. */ epicsShareFunc void epicsShareAPI epicsThreadSetPriority( epicsThreadId id,unsigned int priority); /** Lookup the next usage OSI priority such that priority > *pPriorityJustBelow * if this is possible with the current target configuration and privlages. */ epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadHighestPriorityLevelBelow ( unsigned int priority, unsigned *pPriorityJustBelow); /** Lookup the next usage OSI priority such that priority < *pPriorityJustBelow * if this is possible with the current target configuration and privlages. */ epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadLowestPriorityLevelAbove ( unsigned int priority, unsigned *pPriorityJustAbove); /** Test if two thread IDs actually refer to the same OS thread */ epicsShareFunc int epicsShareAPI epicsThreadIsEqual( epicsThreadId id1, epicsThreadId id2); /** Test if thread has been suspended with epicsThreadSuspendSelf() */ epicsShareFunc int epicsShareAPI epicsThreadIsSuspended(epicsThreadId id); /** @brief Block the calling thread for at least the specified time. * @param seconds Time to wait in seconds. Values <=0 blocks for the shortest possible time. */ epicsShareFunc void epicsShareAPI epicsThreadSleep(double seconds); /** @brief Query a value approximating the OS timer/scheduler resolution. * @return A value in seconds >=0 * * @warning On targets other than vxWorks and RTEMS, the quantum value often isn't * meaningful. Use of this function is discouraged in portable code. */ epicsShareFunc double epicsShareAPI epicsThreadSleepQuantum(void); /** Find an epicsThreadId associated with the current thread. * For non-EPICS threads, a new epicsThreadId may be allocated. */ epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetIdSelf(void); /** Attempt to find the first instance of a thread by name. * @return An epicsThreadId, or NULL if no such thread is currently running. * Note that a non-NULL ID may still be invalid if this call races * with thread exit. * * @warning Safe use of this function requires external knowledge that this * thread will not return. */ epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetId(const char *name); /** Return a value approximating the number of threads which this target * can run in parallel. This value is advisory. * @return >=1 */ epicsShareFunc int epicsThreadGetCPUs(void); /** Return the name of the current thread. * * @return Never NULL. Storage lifetime tied to epicsThreadId. * * This is either a copy of the string passed to epicsThread*Create*(), * or an arbitrary unique string for non-EPICS threads. */ epicsShareFunc const char * epicsShareAPI epicsThreadGetNameSelf(void); /** Copy out the thread name into the provided buffer. * * Guaranteed to be null terminated. * size is number of bytes in buffer to hold name (including terminator). * Failure results in an empty string stored in name. */ epicsShareFunc void epicsShareAPI epicsThreadGetName( epicsThreadId id, char *name, size_t size); epicsShareFunc int epicsShareAPI epicsThreadIsOkToBlock(void); epicsShareFunc void epicsShareAPI epicsThreadSetOkToBlock(int isOkToBlock); /** Print to stdout information about all running EPICS threads. * @param level 0 prints minimal output. Higher values print more details. */ epicsShareFunc void epicsShareAPI epicsThreadShowAll(unsigned int level); /** Print info about a single EPICS thread. */ epicsShareFunc void epicsShareAPI epicsThreadShow( epicsThreadId id,unsigned int level); /* Hooks called when a thread starts, map function called once for every thread */ typedef void (*EPICS_THREAD_HOOK_ROUTINE)(epicsThreadId id); epicsShareFunc int epicsThreadHookAdd(EPICS_THREAD_HOOK_ROUTINE hook); epicsShareFunc int epicsThreadHookDelete(EPICS_THREAD_HOOK_ROUTINE hook); epicsShareFunc void epicsThreadHooksShow(void); epicsShareFunc void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func); /** Thread local storage */ typedef struct epicsThreadPrivateOSD * epicsThreadPrivateId; /** Allocate a new thread local variable. * This variable will initially hold NULL for each thread. */ epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate(void); /** Free a thread local variable */ epicsShareFunc void epicsShareAPI epicsThreadPrivateDelete(epicsThreadPrivateId id); /** Update thread local variable */ epicsShareFunc void epicsShareAPI epicsThreadPrivateSet(epicsThreadPrivateId,void *); /** Fetch the current value of a thread local variable */ epicsShareFunc void * epicsShareAPI epicsThreadPrivateGet(epicsThreadPrivateId); #ifdef __cplusplus } #endif #ifdef __cplusplus #include "epicsEvent.h" #include "epicsMutex.h" //! Interface used with class epicsThread class epicsShareClass epicsThreadRunable { public: virtual ~epicsThreadRunable () = 0; //! Thread main function. //! C++ exceptions which propagate from this method will be caught and a warning printed. //! No other action is taken. virtual void run () = 0; //! Optional. Called via epicsThread::show() virtual void show ( unsigned int level ) const; }; extern "C" void epicsThreadCallEntryPoint ( void * ); /** @brief An OS thread * * A wrapper around the epicsThread* C API. * * @note Threads must be start() ed. */ class epicsShareClass epicsThread { public: /** Create a new thread with the provided information. * * cf. epicsThreadOpts * @note Threads must be start() ed. * @throws epicsThread::unableToCreateThread on error. */ epicsThread ( epicsThreadRunable &,const char *name, unsigned int stackSize, unsigned int priority=epicsThreadPriorityLow ); ~epicsThread () throw (); //! Actually start the thread. void start () throw (); //! Wait for the thread epicsRunnable::run() to return. void exitWait () throw (); //! Wait for the thread epicsRunnable::run() to return. //! @param delay Wait up to this many seconds. //! @returns true if run() returned. false on timeout. bool exitWait ( const double delay ) throw (); //! @throws A special exitException which will be caught and ignored. //! @note This exitException doesn't not derive from std::exception static void exit (); //! cf. epicsThreadResume() void resume () throw (); //! cf. epicsThreadGetName(); void getName ( char * name, size_t size ) const throw (); //! cf. epicsThreadGetIdSelf()() epicsThreadId getId () const throw (); //! cf. epicsThreadGetPriority() unsigned int getPriority () const throw (); //! cf. epicsThreadSetPriority() void setPriority ( unsigned int ) throw (); bool priorityIsEqual ( const epicsThread & ) const throw (); bool isSuspended () const throw (); //! @return true if call through this thread's epicsRunnable::run() bool isCurrentThread () const throw (); bool operator == ( const epicsThread & ) const throw (); //! Say something interesting about this thread to stdout. void show ( unsigned level ) const throw (); /* these operate on the current thread */ static void suspendSelf () throw (); static void sleep (double seconds) throw (); static const char * getNameSelf () throw (); static bool isOkToBlock () throw (); static void setOkToBlock ( bool isOkToBlock ) throw (); /* exceptions */ class unableToCreateThread; private: epicsThreadRunable & runable; epicsThreadId id; epicsMutex mutex; epicsEvent event; epicsEvent exitEvent; bool * pThreadDestroyed; bool begin; bool cancel; bool terminated; bool joined; bool beginWait () throw (); epicsThread ( const epicsThread & ); epicsThread & operator = ( const epicsThread & ); friend void epicsThreadCallEntryPoint ( void * ); void printLastChanceExceptionMessage ( const char * pExceptionTypeName, const char * pExceptionContext ); /* exceptions */ class exitException {}; }; class epicsShareClass epicsThreadPrivateBase { public: class unableToCreateThreadPrivate {}; /* exception */ protected: static void throwUnableToCreateThreadPrivate (); }; template < class T > class epicsThreadPrivate : private epicsThreadPrivateBase { public: epicsThreadPrivate (); ~epicsThreadPrivate () throw (); T * get () const throw (); void set (T *) throw (); private: epicsThreadPrivateId id; }; #endif /* __cplusplus */ #include "osdThread.h" #ifdef __cplusplus template inline epicsThreadPrivate::epicsThreadPrivate () { this->id = epicsThreadPrivateCreate (); if ( this->id == 0 ) { epicsThreadPrivateBase::throwUnableToCreateThreadPrivate (); } } template inline epicsThreadPrivate::~epicsThreadPrivate () throw () { epicsThreadPrivateDelete ( this->id ); } template inline T *epicsThreadPrivate::get () const throw () { return static_cast ( epicsThreadPrivateGet (this->id) ); } template inline void epicsThreadPrivate::set (T *pIn) throw () { epicsThreadPrivateSet ( this->id, static_cast (pIn) ); } #endif /* ifdef __cplusplus */ #endif /* epicsThreadh */ base-7.0.3.1/modules/libcom/src/osi/epicsTime.cpp0000664000577000060420000010405213557101274020341 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2011 LANS LLC, as Operator of * Los Alamos National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsTime.cpp */ /* Author Jeffrey O. Hill */ // Notes: // 1) The epicsTime::nSec field is not public and so it could be // changed to work more like the fractional seconds field in the NTP time // stamp. That would significantly improve the precision of epicsTime on // 64 bit architectures. // #include #include #include #include #include #include #include #include // vxWorks 6.0 requires this include #define epicsExportSharedSymbols #include "locationException.h" #include "epicsAssert.h" #include "epicsVersion.h" #include "envDefs.h" #include "epicsTime.h" #include "osiSock.h" /* pull in struct timeval */ #include "epicsStdio.h" static const char pEpicsTimeVersion[] = "@(#) " EPICS_VERSION_STRING ", Common Utilities Library " __DATE__; // // useful public constants // static const unsigned mSecPerSec = 1000u; static const unsigned uSecPerMSec = 1000u; static const unsigned uSecPerSec = uSecPerMSec * mSecPerSec; static const unsigned nSecPerUSec = 1000u; static const unsigned nSecPerSec = nSecPerUSec * uSecPerSec; static const unsigned nSecFracDigits = 9u; // Timescale conversion data static const unsigned long NTP_TIME_AT_POSIX_EPOCH = 2208988800ul; static const unsigned long NTP_TIME_AT_EPICS_EPOCH = NTP_TIME_AT_POSIX_EPOCH + POSIX_TIME_AT_EPICS_EPOCH; // // epicsTime (const unsigned long secIn, const unsigned long nSecIn) // inline epicsTime::epicsTime (const unsigned long secIn, const unsigned long nSecIn) : secPastEpoch ( secIn ), nSec ( nSecIn ) { if (nSecIn >= nSecPerSec) { this->secPastEpoch += nSecIn / nSecPerSec; this->nSec = nSecIn % nSecPerSec; } } // // epicsTimeLoadTimeInit // class epicsTimeLoadTimeInit { public: epicsTimeLoadTimeInit (); double epicsEpochOffset; // seconds double time_tSecPerTick; // seconds (both NTP and EPICS use int sec) unsigned long epicsEpochOffsetAsAnUnsignedLong; bool useDiffTimeOptimization; }; // // epicsTimeLoadTimeInit () // epicsTimeLoadTimeInit::epicsTimeLoadTimeInit () { // All we know about time_t is that it is an arithmetic type. time_t t_zero = static_cast (0); time_t t_one = static_cast (1); this->time_tSecPerTick = difftime (t_one, t_zero); /* The EPICS epoch (1/1/1990 00:00:00UTC) was 631152000 seconds after * the ANSI epoch (1/1/1970 00:00:00UTC) * Convert this offset into time_t units, however this must not be * calculated using local time (i.e. using mktime() or similar), since * in the UK the ANSI Epoch had daylight saving time in effect, and * the value calculated would be 3600 seconds wrong.*/ this->epicsEpochOffset = (double) POSIX_TIME_AT_EPICS_EPOCH / this->time_tSecPerTick; if (this->time_tSecPerTick == 1.0 && this->epicsEpochOffset <= ULONG_MAX && this->epicsEpochOffset >= 0) { // We can use simpler code on Posix-compliant systems this->useDiffTimeOptimization = true; this->epicsEpochOffsetAsAnUnsignedLong = static_cast(this->epicsEpochOffset); } else { // Forced to use the slower but correct code this->useDiffTimeOptimization = false; this->epicsEpochOffsetAsAnUnsignedLong = 0; } } // // private epicsTime::addNanoSec () // // Most formats keep the nSec value as an unsigned long, so are +ve. // struct timeval's tv_usec may be -1, but I think that means error, // so this private method never needs to handle -ve offsets. // void epicsTime :: addNanoSec ( long nSecAdj ) { if (nSecAdj <= 0) return; if (static_cast(nSecAdj) >= nSecPerSec) { this->secPastEpoch += nSecAdj / nSecPerSec; nSecAdj %= nSecPerSec; } this->nSec += nSecAdj; // Can't overflow if (this->nSec >= nSecPerSec) { this->secPastEpoch++; this->nSec -= nSecPerSec; } } // // epicsTime (const time_t_wrapper &tv) // epicsTime::epicsTime ( const time_t_wrapper & ansiTimeTicks ) { // avoid c++ static initialization order issues static epicsTimeLoadTimeInit & lti = * new epicsTimeLoadTimeInit (); // // try to directly map time_t into an unsigned long integer because this is // faster on systems w/o hardware floating point and a simple integer type time_t. // if ( lti.useDiffTimeOptimization ) { // LONG_MAX is used here and not ULONG_MAX because some systems (linux) // still store time_t as a long. if ( ansiTimeTicks.ts > 0 && ansiTimeTicks.ts <= LONG_MAX ) { unsigned long ticks = static_cast < unsigned long > ( ansiTimeTicks.ts ); if ( ticks >= lti.epicsEpochOffsetAsAnUnsignedLong ) { this->secPastEpoch = ticks - lti.epicsEpochOffsetAsAnUnsignedLong; } else { this->secPastEpoch = ( ULONG_MAX - lti.epicsEpochOffsetAsAnUnsignedLong ) + ticks; } this->nSec = 0; return; } } // // otherwise map time_t, which ANSI C and POSIX define as any arithmetic type, // into type double // double sec = ansiTimeTicks.ts * lti.time_tSecPerTick - lti.epicsEpochOffset; // // map into the the EPICS time stamp range (which allows rollover) // static double uLongMax = static_cast (ULONG_MAX); if ( sec < 0.0 ) { if ( sec < -uLongMax ) { sec = sec + static_cast ( -sec / uLongMax ) * uLongMax; } sec += uLongMax; } else if ( sec > uLongMax ) { sec = sec - static_cast ( sec / uLongMax ) * uLongMax; } this->secPastEpoch = static_cast ( sec ); this->nSec = static_cast ( ( sec-this->secPastEpoch ) * nSecPerSec ); } epicsTime::epicsTime (const epicsTimeStamp &ts) { if ( ts.nsec < nSecPerSec ) { this->secPastEpoch = ts.secPastEpoch; this->nSec = ts.nsec; } else { throw std::logic_error ( "epicsTimeStamp has overflow in nano-seconds field" ); } } epicsTime::epicsTime () : secPastEpoch(0u), nSec(0u) {} epicsTime epicsTime::getCurrent () { epicsTimeStamp current; int status = epicsTimeGetCurrent (¤t); if (status) { throwWithLocation ( unableToFetchCurrentTime () ); } return epicsTime ( current ); } epicsTime epicsTime::getMonotonic() { epicsTimeStamp current; epicsTimeGetMonotonic (¤t); // can't fail return epicsTime ( current ); } epicsTime epicsTime::getEvent (const epicsTimeEvent &event) { epicsTimeStamp current; int status = epicsTimeGetEvent (¤t, event); if (status) { throwWithLocation ( unableToFetchCurrentTime () ); } return epicsTime ( current ); } // // operator time_t_wrapper () // epicsTime::operator time_t_wrapper () const { // avoid c++ static initialization order issues static epicsTimeLoadTimeInit & lti = * new epicsTimeLoadTimeInit (); time_t_wrapper wrap; if ( lti.useDiffTimeOptimization ) { if ( this->secPastEpoch < ULONG_MAX - lti.epicsEpochOffsetAsAnUnsignedLong ) { wrap.ts = static_cast ( this->secPastEpoch + lti.epicsEpochOffsetAsAnUnsignedLong ); return wrap; } } // // map type double into time_t which ansi C defines as some arithmetic type // double tmp = (this->secPastEpoch + lti.epicsEpochOffset) / lti.time_tSecPerTick; tmp += (this->nSec / lti.time_tSecPerTick) / nSecPerSec; wrap.ts = static_cast ( tmp ); return wrap; } // // convert to ANSI C struct tm (with nano seconds) adjusted for the local time zone // epicsTime::operator local_tm_nano_sec () const { time_t_wrapper ansiTimeTicks = *this; local_tm_nano_sec tm; int status = epicsTime_localtime ( &ansiTimeTicks.ts, &tm.ansi_tm ); if ( status ) { throw std::logic_error ( "epicsTime_localtime failed" ); } tm.nSec = this->nSec; return tm; } // // convert to ANSI C struct tm (with nano seconds) adjusted for UTC // epicsTime::operator gm_tm_nano_sec () const { time_t_wrapper ansiTimeTicks = *this; gm_tm_nano_sec tm; int status = epicsTime_gmtime ( &ansiTimeTicks.ts, &tm.ansi_tm ); if ( status ) { throw std::logic_error ( "epicsTime_gmtime failed" ); } tm.nSec = this->nSec; return tm; } // // epicsTime (const local_tm_nano_sec &tm) // epicsTime::epicsTime (const local_tm_nano_sec &tm) { struct tm tmp = tm.ansi_tm; time_t_wrapper ansiTimeTicks = { mktime (&tmp) }; static const time_t mktimeError = static_cast (-1); if (ansiTimeTicks.ts == mktimeError) { throwWithLocation ( formatProblemWithStructTM () ); } *this = epicsTime(ansiTimeTicks); this->addNanoSec(tm.nSec); } // // epicsTime (const gm_tm_nano_sec &tm) // // do conversion avoiding the timezone mechanism static inline int is_leap(int year) { if (year % 400 == 0) return 1; if (year % 100 == 0) return 0; if (year % 4 == 0) return 1; return 0; } static inline int days_from_0(int year) { year--; return 365 * year + (year / 400) - (year / 100) + (year / 4); } static inline int days_from_1970(int year) { static const int days_from_0_to_1970 = days_from_0(1970); return days_from_0(year) - days_from_0_to_1970; } static inline int days_from_1jan(int year, int month, int day) { static const int days[2][12] = { { 0,31,59,90,120,151,181,212,243,273,304,334}, { 0,31,60,91,121,152,182,213,244,274,305,335} }; return days[is_leap(year)][month-1] + day - 1; } epicsTime::epicsTime (const gm_tm_nano_sec &tm) { int year = tm.ansi_tm.tm_year + 1900; int month = tm.ansi_tm.tm_mon; if (month > 11) { year += month / 12; month %= 12; } else if (month < 0) { int years_diff = (-month + 11) / 12; year -= years_diff; month += 12 * years_diff; } month++; int day = tm.ansi_tm.tm_mday; int day_of_year = days_from_1jan(year, month, day); int days_since_epoch = days_from_1970(year) + day_of_year; time_t_wrapper ansiTimeTicks; ansiTimeTicks.ts = ((days_since_epoch * 24 + tm.ansi_tm.tm_hour) * 60 + tm.ansi_tm.tm_min) * 60 + tm.ansi_tm.tm_sec; *this = epicsTime(ansiTimeTicks); this->addNanoSec(tm.nSec); } // // operator struct timespec () // epicsTime::operator struct timespec () const { struct timespec ts; time_t_wrapper ansiTimeTicks; ansiTimeTicks = *this; ts.tv_sec = ansiTimeTicks.ts; ts.tv_nsec = static_cast (this->nSec); return ts; } // // epicsTime (const struct timespec &ts) // epicsTime::epicsTime (const struct timespec &ts) { time_t_wrapper ansiTimeTicks; ansiTimeTicks.ts = ts.tv_sec; *this = epicsTime (ansiTimeTicks); this->addNanoSec (ts.tv_nsec); } // // operator struct timeval () // epicsTime::operator struct timeval () const { struct timeval ts; time_t_wrapper ansiTimeTicks; ansiTimeTicks = *this; // On Posix systems timeval :: tv_sec is a time_t so this can be // a direct assignment. On other systems I dont know that we can // guarantee that time_t and timeval :: tv_sec will have the // same epoch or have the same scaling factor to discrete seconds. // For example, on windows time_t changed recently to a 64 bit // quantity but timeval is still a long. That can cause problems // on 32 bit systems. So technically, we should have an os // dependent conversion between time_t and timeval :: tv_sec? ts.tv_sec = ansiTimeTicks.ts; ts.tv_usec = static_cast < long > ( this->nSec / nSecPerUSec ); return ts; } // // epicsTime (const struct timeval &ts) // epicsTime::epicsTime (const struct timeval &ts) { time_t_wrapper ansiTimeTicks; // On Posix systems timeval :: tv_sec is a time_t so this can be // a direct assignment. On other systems I dont know that we can // guarantee that time_t and timeval :: tv_sec will have the // same epoch or have the same scaling factor to discrete seconds. // For example, on windows time_t changed recently to a 64 bit // quantity but timeval is still a long. That can cause problems // on 32 bit systems. So technically, we should have an os // dependent conversion between time_t and timeval :: tv_sec? ansiTimeTicks.ts = ts.tv_sec; *this = epicsTime (ansiTimeTicks); this->addNanoSec (ts.tv_usec * nSecPerUSec); } static const double NTP_FRACTION_DENOMINATOR = 1.0 + 0xffffffff; struct l_fp { /* NTP time stamp */ epicsUInt32 l_ui; /* sec past NTP epoch */ epicsUInt32 l_uf; /* fractional seconds */ }; // // epicsTime::l_fp () // epicsTime::operator l_fp () const { l_fp ts; ts.l_ui = this->secPastEpoch + NTP_TIME_AT_EPICS_EPOCH; ts.l_uf = static_cast < unsigned long > ( ( this->nSec * NTP_FRACTION_DENOMINATOR ) / nSecPerSec ); return ts; } // // epicsTime::epicsTime ( const l_fp & ts ) // epicsTime::epicsTime ( const l_fp & ts ) { this->secPastEpoch = ts.l_ui - NTP_TIME_AT_EPICS_EPOCH; this->nSec = static_cast < unsigned long > ( ( ts.l_uf / NTP_FRACTION_DENOMINATOR ) * nSecPerSec ); } epicsTime::operator epicsTimeStamp () const { if ( this->nSec >= nSecPerSec ) { throw std::logic_error ( "epicsTimeStamp has overflow in nano-seconds field?" ); } epicsTimeStamp ts; // // truncation by design // ------------------- // epicsTime::secPastEpoch is based on ulong and has much greater range // on 64 bit hosts than the original epicsTimeStamp::secPastEpoch. The // epicsTimeStamp::secPastEpoch is based on epicsUInt32 so that it will // match the original network protocol. Of course one can anticipate // that eventually, a epicsUInt64 based network time stamp will be // introduced when 64 bit architectures are more ubiquitous. // // Truncation usually works fine here because the routines in this code // that compute time stamp differences and compare time stamps produce // good results when the operands are on either side of a time stamp // rollover as long as the difference between the operands does not exceed // 1/2 of full range. // ts.secPastEpoch = static_cast < epicsUInt32 > ( this->secPastEpoch ); ts.nsec = static_cast < epicsUInt32 > ( this->nSec ); return ts; } // Break up a format string into "%0f" // (where in an unsigned integer) // Result: // A) Copies a prefix which is valid for ANSI strftime into the supplied // buffer setting the buffer to an empty string if no prefix is present. // B) Indicates whether a valid "%0f]" is present or not and if so // specifying its nnnn // C) returning a pointer to the postfix (which might be passed again // to fracFormatFind. static const char * fracFormatFind ( const char * const pFormat, char * const pPrefixBuf, const size_t prefixBufLen, bool & fracFmtFound, unsigned long & fracFmtWidth ) { assert ( prefixBufLen > 1 ); unsigned long width = ULONG_MAX; bool fracFound = false; const char * pAfter = pFormat; const char * pFmt = pFormat; while ( *pFmt != '\0' ) { if ( *pFmt == '%' ) { if ( pFmt[1] == '%' ) { pFmt++; } else if ( pFmt[1] == 'f' ) { fracFound = true; pAfter = & pFmt[2]; break; } else { errno = 0; char * pAfterTmp; unsigned long result = strtoul ( pFmt + 1, & pAfterTmp, 10 ); if ( errno == 0 && *pAfterTmp == 'f' && result > 0 ) { width = result; fracFound = true; pAfter = pAfterTmp + 1; break; } } } pFmt++; pAfter = pFmt; } size_t len = pFmt - pFormat; if ( len < prefixBufLen ) { strncpy ( pPrefixBuf, pFormat, len ); pPrefixBuf [ len ] = '\0'; if ( fracFound ) { fracFmtFound = true; fracFmtWidth = width; } else { fracFmtFound = false; } } else { strncpy ( pPrefixBuf, "", prefixBufLen - 1 ); pPrefixBuf [ prefixBufLen - 1 ] = '\0'; fracFmtFound = false; pAfter = ""; } return pAfter; } // // size_t epicsTime::strftime () // size_t epicsTime::strftime ( char * pBuff, size_t bufLength, const char * pFormat ) const { if ( bufLength == 0u ) { return 0u; } // presume that EPOCH date is an uninitialized time stamp if ( this->secPastEpoch == 0 && this->nSec == 0u ) { strncpy ( pBuff, "", bufLength ); pBuff[bufLength-1] = '\0'; return strlen ( pBuff ); } char * pBufCur = pBuff; const char * pFmt = pFormat; size_t bufLenLeft = bufLength; while ( *pFmt != '\0' && bufLenLeft > 1 ) { // look for "%0f" at the end (used for fractional second formatting) char strftimePrefixBuf [256]; bool fracFmtFound; unsigned long fracWid = 0; pFmt = fracFormatFind ( pFmt, strftimePrefixBuf, sizeof ( strftimePrefixBuf ), fracFmtFound, fracWid ); // nothing more in the string, then quit if ( ! ( strftimePrefixBuf[0] != '\0' || fracFmtFound ) ) { break; } // all but fractional seconds use strftime formatting if ( strftimePrefixBuf[0] != '\0' ) { local_tm_nano_sec tmns = *this; size_t strftimeNumChar = :: strftime ( pBufCur, bufLenLeft, strftimePrefixBuf, & tmns.ansi_tm ); pBufCur [ strftimeNumChar ] = '\0'; pBufCur += strftimeNumChar; bufLenLeft -= strftimeNumChar; } // fractional seconds formating if ( fracFmtFound && bufLenLeft > 1 ) { if ( fracWid > nSecFracDigits ) { fracWid = nSecFracDigits; } // verify that there are enough chars left for the fractional seconds if ( fracWid < bufLenLeft ) { local_tm_nano_sec tmns = *this; if ( tmns.nSec < nSecPerSec ) { // divisors for fraction (see below) static const unsigned long div[] = { static_cast < unsigned long > ( 1e9 ), static_cast < unsigned long > ( 1e8 ), static_cast < unsigned long > ( 1e7 ), static_cast < unsigned long > ( 1e6 ), static_cast < unsigned long > ( 1e5 ), static_cast < unsigned long > ( 1e4 ), static_cast < unsigned long > ( 1e3 ), static_cast < unsigned long > ( 1e2 ), static_cast < unsigned long > ( 1e1 ), static_cast < unsigned long > ( 1e0 ) }; // round without overflowing into whole seconds unsigned long frac = tmns.nSec + div[fracWid] / 2; if (frac >= nSecPerSec) frac = nSecPerSec - 1; // convert nanosecs to integer of correct range frac /= div[fracWid]; char fracFormat[32]; sprintf ( fracFormat, "%%0%lulu", fracWid ); int status = epicsSnprintf ( pBufCur, bufLenLeft, fracFormat, frac ); if ( status > 0 ) { unsigned long nChar = static_cast < unsigned long > ( status ); if ( nChar >= bufLenLeft ) { nChar = bufLenLeft - 1; } pBufCur[nChar] = '\0'; pBufCur += nChar; bufLenLeft -= nChar; } } else { static const char pOVF [] = "OVF"; size_t tmpLen = sizeof ( pOVF ) - 1; if ( tmpLen >= bufLenLeft ) { tmpLen = bufLenLeft - 1; } strncpy ( pBufCur, pOVF, tmpLen ); pBufCur[tmpLen] = '\0'; pBufCur += tmpLen; bufLenLeft -= tmpLen; } } else { static const char pDoesntFit [] = "************"; size_t tmpLen = sizeof ( pDoesntFit ) - 1; if ( tmpLen >= bufLenLeft ) { tmpLen = bufLenLeft - 1; } strncpy ( pBufCur, pDoesntFit, tmpLen ); pBufCur[tmpLen] = '\0'; pBufCur += tmpLen; bufLenLeft -= tmpLen; break; } } } return pBufCur - pBuff; } // // epicsTime::show (unsigned) // void epicsTime::show ( unsigned level ) const { char bigBuffer[256]; size_t numChar = this->strftime ( bigBuffer, sizeof ( bigBuffer ), "%a %b %d %Y %H:%M:%S.%09f" ); if ( numChar > 0 ) { printf ( "epicsTime: %s\n", bigBuffer ); } if ( level > 1 ) { // this also suppresses the "defined, but not used" // warning message printf ( "epicsTime: revision \"%s\"\n", pEpicsTimeVersion ); } } // // epicsTime::operator + (const double &rhs) // // rhs has units seconds // epicsTime epicsTime::operator + (const double &rhs) const { unsigned long newSec, newNSec, secOffset, nSecOffset; double fnsec; if (rhs >= 0) { secOffset = static_cast (rhs); fnsec = rhs - secOffset; nSecOffset = static_cast ( (fnsec * nSecPerSec) + 0.5 ); newSec = this->secPastEpoch + secOffset; // overflow expected newNSec = this->nSec + nSecOffset; if (newNSec >= nSecPerSec) { newSec++; // overflow expected newNSec -= nSecPerSec; } } else { secOffset = static_cast (-rhs); fnsec = rhs + secOffset; nSecOffset = static_cast ( (-fnsec * nSecPerSec) + 0.5 ); newSec = this->secPastEpoch - secOffset; // underflow expected if (this->nSec>=nSecOffset) { newNSec = this->nSec - nSecOffset; } else { // borrow newSec--; // underflow expected newNSec = this->nSec + (nSecPerSec - nSecOffset); } } return epicsTime (newSec, newNSec); } // // operator - // // To make this code robust during timestamp rollover events // time stamp differences greater than one half full scale are // interpreted as rollover situations: // // when RHS is greater than THIS: // RHS-THIS > one half full scale => return THIS + (ULONG_MAX-RHS) // RHS-THIS <= one half full scale => return -(RHS-THIS) // // when THIS is greater than or equal to RHS // THIS-RHS > one half full scale => return -(RHS + (ULONG_MAX-THIS)) // THIS-RHS <= one half full scale => return THIS-RHS // double epicsTime::operator - (const epicsTime &rhs) const { double nSecRes, secRes; // // first compute the difference between the nano-seconds members // // nano sec member is not allowed to be greater that 1/2 full scale // so the unsigned to signed conversion is ok // if (this->nSec>=rhs.nSec) { nSecRes = this->nSec - rhs.nSec; } else { nSecRes = rhs.nSec - this->nSec; nSecRes = -nSecRes; } // // next compute the difference between the seconds members // and invert the sign of the nano seconds result if there // is a range violation // if (this->secPastEpochsecPastEpoch; if (secRes > ULONG_MAX/2) { // // In this situation where the difference is more than // 68 years assume that the seconds counter has rolled // over and compute the "wrap around" difference // secRes = 1 + (ULONG_MAX-secRes); nSecRes = -nSecRes; } else { secRes = -secRes; } } else { secRes = this->secPastEpoch - rhs.secPastEpoch; if (secRes > ULONG_MAX/2) { // // In this situation where the difference is more than // 68 years assume that the seconds counter has rolled // over and compute the "wrap around" difference // secRes = 1 + (ULONG_MAX-secRes); secRes = -secRes; nSecRes = -nSecRes; } } return secRes + nSecRes/nSecPerSec; } // // operator <= // bool epicsTime::operator <= (const epicsTime &rhs) const { bool rc; if (this->secPastEpochsecPastEpoch < ULONG_MAX/2) { // // In this situation where the difference is less than // 69 years compute the expected result // rc = true; } else { // // In this situation where the difference is more than // 69 years assume that the seconds counter has rolled // over and compute the "wrap around" result // rc = false; } } else if (this->secPastEpoch>rhs.secPastEpoch) { if (this->secPastEpoch-rhs.secPastEpoch < ULONG_MAX/2) { // // In this situation where the difference is less than // 69 years compute the expected result // rc = false; } else { // // In this situation where the difference is more than // 69 years assume that the seconds counter has rolled // over and compute the "wrap around" result // rc = true; } } else { if (this->nSec<=rhs.nSec) { rc = true; } else { rc = false; } } return rc; } // // operator < // bool epicsTime::operator < (const epicsTime &rhs) const { bool rc; if (this->secPastEpochsecPastEpoch < ULONG_MAX/2) { // // In this situation where the difference is less than // 69 years compute the expected result // rc = true; } else { // // In this situation where the difference is more than // 69 years assume that the seconds counter has rolled // over and compute the "wrap around" result // rc = false; } } else if (this->secPastEpoch>rhs.secPastEpoch) { if (this->secPastEpoch-rhs.secPastEpoch < ULONG_MAX/2) { // // In this situation where the difference is less than // 69 years compute the expected result // rc = false; } else { // // In this situation where the difference is more than // 69 years assume that the seconds counter has rolled // over and compute the "wrap around" result // rc = true; } } else { if (this->nSec epicsTime (*pRight); } catch ( ... ) { return 0; } } epicsShareFunc int epicsShareAPI epicsTimeGreaterThanEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) { try { return epicsTime (*pLeft) >= epicsTime (*pRight); } catch ( ... ) { return 0; } } epicsShareFunc size_t epicsShareAPI epicsTimeToStrftime (char *pBuff, size_t bufLength, const char *pFormat, const epicsTimeStamp *pTS) { try { return epicsTime(*pTS).strftime (pBuff, bufLength, pFormat); } catch ( ... ) { return 0; } } epicsShareFunc void epicsShareAPI epicsTimeShow (const epicsTimeStamp *pTS, unsigned interestLevel) { try { epicsTime(*pTS).show (interestLevel); } catch ( ... ) { printf ( "Invalid epicsTimeStamp\n" ); } } } base-7.0.3.1/modules/libcom/src/osi/epicsTime.h0000664000577000060420000003030013557101274020000 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsTime.h */ /* Author Jeffrey O. Hill */ #ifndef epicsTimehInclude #define epicsTimehInclude #include #include "shareLib.h" #include "epicsTypes.h" #include "osdTime.h" #include "errMdef.h" /* The EPICS Epoch is 00:00:00 Jan 1, 1990 UTC */ #define POSIX_TIME_AT_EPICS_EPOCH 631152000u /* epics time stamp for C interface*/ typedef struct epicsTimeStamp { epicsUInt32 secPastEpoch; /* seconds since 0000 Jan 1, 1990 */ epicsUInt32 nsec; /* nanoseconds within second */ } epicsTimeStamp; /*TS_STAMP is deprecated */ #define TS_STAMP epicsTimeStamp struct timespec; /* POSIX real time */ struct timeval; /* BSD */ struct l_fp; /* NTP timestamp */ #ifdef __cplusplus /* * extend ANSI C RTL "struct tm" to include nano seconds within a second * and a struct tm that is adjusted for the local timezone */ struct local_tm_nano_sec { struct tm ansi_tm; /* ANSI C time details */ unsigned long nSec; /* nano seconds extension */ }; /* * extend ANSI C RTL "struct tm" to includes nano seconds within a second * and a struct tm that is adjusted for GMT (UTC) */ struct gm_tm_nano_sec { struct tm ansi_tm; /* ANSI C time details */ unsigned long nSec; /* nano seconds extension */ }; /* * wrapping this in a struct allows conversion to and * from ANSI time_t but does not allow unexpected * conversions to occur */ struct time_t_wrapper { time_t ts; }; class epicsShareClass epicsTimeEvent { public: epicsTimeEvent (const int &number); operator int () const; private: int eventNumber; }; class epicsShareClass epicsTime { public: /* exceptions */ class unableToFetchCurrentTime {}; class formatProblemWithStructTM {}; epicsTime (); static epicsTime getEvent ( const epicsTimeEvent & ); static epicsTime getCurrent (); static epicsTime getMonotonic (); /* convert to and from EPICS epicsTimeStamp format */ operator epicsTimeStamp () const; epicsTime ( const epicsTimeStamp & ts ); epicsTime & operator = ( const epicsTimeStamp & ); /* convert to and from ANSI time_t */ operator time_t_wrapper () const; epicsTime ( const time_t_wrapper & ); epicsTime & operator = ( const time_t_wrapper & ); /* * convert to and from ANSI Cs "struct tm" (with nano seconds) * adjusted for the local time zone */ operator local_tm_nano_sec () const; epicsTime ( const local_tm_nano_sec & ); epicsTime & operator = ( const local_tm_nano_sec & ); /* * convert to and from ANSI Cs "struct tm" (with nano seconds) * adjusted for GM time (UTC) */ operator gm_tm_nano_sec () const; epicsTime ( const gm_tm_nano_sec & ); epicsTime & operator = ( const gm_tm_nano_sec & ); /* convert to and from POSIX RTs "struct timespec" */ operator struct timespec () const; epicsTime ( const struct timespec & ); epicsTime & operator = ( const struct timespec & ); /* convert to and from BSDs "struct timeval" */ operator struct timeval () const; epicsTime ( const struct timeval & ); epicsTime & operator = ( const struct timeval & ); /* convert to and from NTP timestamp format */ operator l_fp () const; epicsTime ( const l_fp & ); epicsTime & operator = ( const l_fp & ); /* convert to and from WIN32s FILETIME (implemented only on WIN32) */ operator struct _FILETIME () const; epicsTime ( const struct _FILETIME & ); epicsTime & operator = ( const struct _FILETIME & ); /* arithmetic operators */ double operator- ( const epicsTime & ) const; /* returns seconds */ epicsTime operator+ ( const double & ) const; /* add rhs seconds */ epicsTime operator- ( const double & ) const; /* subtract rhs seconds */ epicsTime operator+= ( const double & ); /* add rhs seconds */ epicsTime operator-= ( const double & ); /* subtract rhs seconds */ /* comparison operators */ bool operator == ( const epicsTime & ) const; bool operator != ( const epicsTime & ) const; bool operator <= ( const epicsTime & ) const; bool operator < ( const epicsTime & ) const; bool operator >= ( const epicsTime & ) const; bool operator > ( const epicsTime & ) const; /* convert current state to user-specified string */ size_t strftime ( char * pBuff, size_t bufLength, const char * pFormat ) const; /* dump current state to standard out */ void show ( unsigned interestLevel ) const; private: /* * private because: * a) application does not break when EPICS epoch is changed * b) no assumptions about internal storage or internal precision * in the application * c) it would be easy to forget which argument is nanoseconds * and which argument is seconds (no help from compiler) */ epicsTime ( const unsigned long secPastEpoch, const unsigned long nSec ); void addNanoSec ( long nanoSecAdjust ); unsigned long secPastEpoch; /* seconds since O000 Jan 1, 1990 */ unsigned long nSec; /* nanoseconds within second */ }; extern "C" { #endif /* __cplusplus */ /* epicsTime routines return S_time_ error status values */ #define epicsTimeOK 0 #define S_time_noProvider (M_time| 1) /*No time provider*/ #define S_time_badEvent (M_time| 2) /*Bad event number*/ #define S_time_badArgs (M_time| 3) /*Invalid arguments*/ #define S_time_noMemory (M_time| 4) /*Out of memory*/ #define S_time_unsynchronized (M_time| 5) /*Provider not synchronized*/ #define S_time_timezone (M_time| 6) /*Invalid timeone*/ #define S_time_conversion (M_time| 7) /*Time conversion error*/ /*Some special values for eventNumber*/ #define epicsTimeEventCurrentTime 0 #define epicsTimeEventBestTime -1 #define epicsTimeEventDeviceTime -2 /* These are implemented in the "generalTime" framework */ epicsShareFunc int epicsShareAPI epicsTimeGetCurrent ( epicsTimeStamp * pDest ); epicsShareFunc int epicsShareAPI epicsTimeGetEvent ( epicsTimeStamp *pDest, int eventNumber); epicsShareFunc int epicsTimeGetMonotonic ( epicsTimeStamp * pDest ); /* These are callable from an Interrupt Service Routine */ epicsShareFunc int epicsTimeGetCurrentInt(epicsTimeStamp *pDest); epicsShareFunc int epicsTimeGetEventInt(epicsTimeStamp *pDest, int eventNumber); /* convert to and from ANSI C's "time_t" */ epicsShareFunc int epicsShareAPI epicsTimeToTime_t ( time_t * pDest, const epicsTimeStamp * pSrc ); epicsShareFunc int epicsShareAPI epicsTimeFromTime_t ( epicsTimeStamp * pDest, time_t src ); /* convert to and from ANSI C's "struct tm" with nano seconds */ epicsShareFunc int epicsShareAPI epicsTimeToTM ( struct tm * pDest, unsigned long * pNSecDest, const epicsTimeStamp * pSrc ); epicsShareFunc int epicsShareAPI epicsTimeToGMTM ( struct tm * pDest, unsigned long * pNSecDest, const epicsTimeStamp * pSrc ); epicsShareFunc int epicsShareAPI epicsTimeFromTM ( epicsTimeStamp * pDest, const struct tm * pSrc, unsigned long nSecSrc ); epicsShareFunc int epicsShareAPI epicsTimeFromGMTM ( epicsTimeStamp * pDest, const struct tm * pSrc, unsigned long nSecSrc ); /* convert to and from POSIX RT's "struct timespec" */ epicsShareFunc int epicsShareAPI epicsTimeToTimespec ( struct timespec * pDest, const epicsTimeStamp * pSrc ); epicsShareFunc int epicsShareAPI epicsTimeFromTimespec ( epicsTimeStamp * pDest, const struct timespec * pSrc ); /* convert to and from BSD's "struct timeval" */ epicsShareFunc int epicsShareAPI epicsTimeToTimeval ( struct timeval * pDest, const epicsTimeStamp * pSrc ); epicsShareFunc int epicsShareAPI epicsTimeFromTimeval ( epicsTimeStamp * pDest, const struct timeval * pSrc ); /*arithmetic operations */ epicsShareFunc double epicsShareAPI epicsTimeDiffInSeconds ( const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight );/* left - right */ epicsShareFunc void epicsShareAPI epicsTimeAddSeconds ( epicsTimeStamp * pDest, double secondsToAdd ); /* adds seconds to *pDest */ /*comparison operations: returns (0,1) if (false,true) */ epicsShareFunc int epicsShareAPI epicsTimeEqual ( const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); epicsShareFunc int epicsShareAPI epicsTimeNotEqual ( const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); epicsShareFunc int epicsShareAPI epicsTimeLessThan ( const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); /*true if left < right */ epicsShareFunc int epicsShareAPI epicsTimeLessThanEqual ( const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); /*true if left <= right) */ epicsShareFunc int epicsShareAPI epicsTimeGreaterThan ( const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); /*true if left > right */ epicsShareFunc int epicsShareAPI epicsTimeGreaterThanEqual ( const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); /*true if left >= right */ /*convert to ASCII string */ epicsShareFunc size_t epicsShareAPI epicsTimeToStrftime ( char * pBuff, size_t bufLength, const char * pFormat, const epicsTimeStamp * pTS ); /* dump current state to standard out */ epicsShareFunc void epicsShareAPI epicsTimeShow ( const epicsTimeStamp *, unsigned interestLevel ); /* OS dependent reentrant versions of the ANSI C interface because */ /* vxWorks gmtime_r interface does not match POSIX standards */ epicsShareFunc int epicsShareAPI epicsTime_localtime ( const time_t * clock, struct tm * result ); epicsShareFunc int epicsShareAPI epicsTime_gmtime ( const time_t * clock, struct tm * result ); /* Advertised monotonic counter resolution (may not be accurate). * Minimum non-zero difference between two calls to epicsMonotonicGet() */ epicsShareFunc epicsUInt64 epicsMonotonicResolution(void); /* Fetch monotonic counter, return is nano-seconds since an unspecified time */ epicsShareFunc epicsUInt64 epicsMonotonicGet(void); #ifdef EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE epicsShareFunc void osdMonotonicInit(void); #endif #ifdef __cplusplus } #endif /* __cplusplus */ /* inline member functions ,*/ #ifdef __cplusplus /* epicsTimeEvent */ inline epicsTimeEvent::epicsTimeEvent (const int &number) : eventNumber(number) {} inline epicsTimeEvent::operator int () const { return this->eventNumber; } /* epicsTime */ inline epicsTime epicsTime::operator - ( const double & rhs ) const { return epicsTime::operator + ( -rhs ); } inline epicsTime epicsTime::operator += ( const double & rhs ) { *this = epicsTime::operator + ( rhs ); return *this; } inline epicsTime epicsTime::operator -= ( const double & rhs ) { *this = epicsTime::operator + ( -rhs ); return *this; } inline bool epicsTime::operator == ( const epicsTime & rhs ) const { if ( this->secPastEpoch == rhs.secPastEpoch && this->nSec == rhs.nSec ) { return true; } return false; } inline bool epicsTime::operator != ( const epicsTime & rhs ) const { return !epicsTime::operator == ( rhs ); } inline bool epicsTime::operator >= ( const epicsTime & rhs ) const { return ! ( *this < rhs ); } inline bool epicsTime::operator > ( const epicsTime & rhs ) const { return ! ( *this <= rhs ); } inline epicsTime & epicsTime::operator = ( const local_tm_nano_sec & rhs ) { return *this = epicsTime ( rhs ); } inline epicsTime & epicsTime::operator = ( const gm_tm_nano_sec & rhs ) { return *this = epicsTime ( rhs ); } inline epicsTime & epicsTime::operator = ( const struct timespec & rhs ) { *this = epicsTime ( rhs ); return *this; } inline epicsTime & epicsTime::operator = ( const epicsTimeStamp & rhs ) { *this = epicsTime ( rhs ); return *this; } inline epicsTime & epicsTime::operator = ( const l_fp & rhs ) { *this = epicsTime ( rhs ); return *this; } inline epicsTime & epicsTime::operator = ( const time_t_wrapper & rhs ) { *this = epicsTime ( rhs ); return *this; } #endif /* __cplusplus */ #endif /* epicsTimehInclude */ base-7.0.3.1/modules/libcom/src/osi/generalTimeSup.h0000664000577000060420000000307613557101274021014 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2008 Diamond Light Source Ltd * Copyright (c) 2004 Oak Ridge National Laboratory * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_generalTimeSup_H #define INC_generalTimeSup_H #include "epicsTime.h" #include "epicsTimer.h" #include "shareLib.h" #define LAST_RESORT_PRIORITY 999 #ifdef __cplusplus extern "C" { #endif typedef int (*TIMECURRENTFUN)(epicsTimeStamp *pDest); typedef int (*TIMEEVENTFUN)(epicsTimeStamp *pDest, int event); epicsShareFunc int generalTimeRegisterCurrentProvider(const char *name, int priority, TIMECURRENTFUN getTime); epicsShareFunc int generalTimeRegisterEventProvider(const char *name, int priority, TIMEEVENTFUN getEvent); /* Original names, for compatibility */ #define generalTimeCurrentTpRegister generalTimeRegisterCurrentProvider #define generalTimeEventTpRegister generalTimeRegisterEventProvider epicsShareFunc int generalTimeAddIntCurrentProvider(const char *name, int priority, TIMECURRENTFUN getTime); epicsShareFunc int generalTimeAddIntEventProvider(const char *name, int priority, TIMEEVENTFUN getEvent); epicsShareFunc int generalTimeGetExceptPriority(epicsTimeStamp *pDest, int *pPrio, int ignorePrio); #ifdef __cplusplus } #endif #endif /* INC_generalTimeSup_H */ base-7.0.3.1/modules/libcom/src/osi/os/Darwin/epicsMath.h0000664000577000060420000000140713557101274021646 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef epicsMathh #define epicsMathh #include #include #define finite(x) isfinite(x) #ifdef __cplusplus extern "C" { #endif epicsShareExtern float epicsNAN; epicsShareExtern float epicsINF; #ifdef __cplusplus } #endif #endif /* epicsMathh */ base-7.0.3.1/modules/libcom/src/osi/os/Darwin/osdBackTrace.cpp0000664000577000060420000000047513557101274022615 0ustar anjaesctl/* * Copyright: Stanford University / SLAC National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * Author: Till Straumann , 2011, 2014 */ #include "osdExecinfoBackTrace.cpp" base-7.0.3.1/modules/libcom/src/osi/os/Darwin/osdEnv.c0000664000577000060420000000335413557101274021165 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osdEnv.c */ /* * Author: Eric Norum * Date: May 7, 2001 * * Routines to modify/display environment variables and EPICS parameters * */ #include #include #include #include #include /* * Starting in Mac OS X 10.5 (Leopard) shared libraries and * bundles don't have direct access to environ (man environ). */ #include #define environ (*_NSGetEnviron()) #define epicsExportSharedSymbols #include "epicsStdio.h" #include "envDefs.h" #include "iocsh.h" /* * Set the value of an environment variable */ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) { if (!name) return; iocshEnvClear(name); setenv(name, value, 1); } /* * Unset an environment variable */ epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name) { iocshEnvClear(name); unsetenv(name); } /* * Show the value of the specified, or all, environment variables */ epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) { if (name == NULL) { extern char **environ; char **sp; for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) printf ("%s\n", *sp); } else { const char *cp = getenv (name); if (cp == NULL) printf ("%s is not an environment variable.\n", name); else printf ("%s=%s\n", name, cp); } } base-7.0.3.1/modules/libcom/src/osi/os/Darwin/osdFindAddr.c0000664000577000060420000000175213557101274022110 0ustar anjaesctl/* * Copyright: Stanford University / SLAC National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * Author: Till Straumann , 2011, 2014 */ /* Make sure dladdr() is visible */ #define _DARWIN_C_SOURCE #include #define epicsExportSharedSymbols #include "epicsStackTrace.h" #include "epicsStackTracePvt.h" /* Darwin's finds local symbols, too :-) */ int epicsFindAddr(void *addr, epicsSymbol *sym_p) { Dl_info inf; if ( ! dladdr(addr, &inf) ) { sym_p->f_nam = 0; sym_p->s_nam = 0; sym_p->s_val = 0; } else { sym_p->f_nam = inf.dli_fname; sym_p->s_nam = inf.dli_sname; sym_p->s_val = inf.dli_saddr; } return 0; } int epicsFindAddrGetFeatures(void) { return EPICS_STACKTRACE_LCL_SYMBOLS | EPICS_STACKTRACE_GBL_SYMBOLS | EPICS_STACKTRACE_DYN_SYMBOLS; } base-7.0.3.1/modules/libcom/src/osi/os/Darwin/osdMonotonic.c0000664000577000060420000000170113557101274022374 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2017 Michael Davidsaver * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #define epicsExportSharedSymbols #define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "dbDefs.h" #include "errlog.h" #include "epicsTime.h" #include "generalTimeSup.h" /* see https://developer.apple.com/library/content/qa/qa1398/_index.html */ static mach_timebase_info_data_t tbinfo; void osdMonotonicInit(void) { (void)mach_timebase_info(&tbinfo); } epicsUInt64 epicsMonotonicResolution(void) { return 1e-9 * tbinfo.numer / tbinfo.denom; } epicsUInt64 epicsMonotonicGet(void) { uint64_t val = mach_absolute_time(); return val * tbinfo.numer / tbinfo.denom; } base-7.0.3.1/modules/libcom/src/osi/os/Darwin/osdSock.h0000664000577000060420000000355713557101274021346 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Eric Norum */ #ifndef osdSockH #define osdSockH #include #include #include /* for MAXHOSTNAMELEN */ #include #include #include #include #include #include #include #include #include /* close() and others */ typedef int SOCKET; #define INVALID_SOCKET (-1) #define SOCKERRNO errno #define socket_ioctl(A,B,C) ioctl(A,B,C) typedef int osiSockIoctl_t; typedef socklen_t osiSocklen_t; typedef int osiSockOptMcastLoop_t; typedef unsigned char osiSockOptMcastTTL_t; #define FD_IN_FDSET(FD) ((FD)ifr_addr.sa_len + sizeof((pifreq)->ifr_name)) #endif /*osdSockH*/ base-7.0.3.1/modules/libcom/src/osi/os/Darwin/osdSockAddrReuse.cpp0000664000577000060420000000267013557101274023473 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeff Hill */ #define epicsExportSharedSymbols #include "osiSock.h" #include "errlog.h" epicsShareFunc void epicsShareAPI epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) { int yes = true; int status; status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, (char *) & yes, sizeof ( yes ) ); if ( status < 0 ) { errlogPrintf ( "epicsSocketEnablePortUseForDatagramFanout: " "unable to set SO_REUSEADDR?\n"); } } /* * SO_REUSEPORT is not in POSIX */ epicsShareFunc void epicsShareAPI epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) { int yes = true; int status; status = setsockopt ( s, SOL_SOCKET, SO_REUSEPORT, (char *) & yes, sizeof ( yes ) ); if ( status < 0 ) { errlogPrintf ( "epicsSocketEnablePortUseForDatagramFanout: " "unable to set SO_REUSEPORT?\n"); } } base-7.0.3.1/modules/libcom/src/osi/os/Darwin/osdSockUnsentCount.c0000664000577000060420000000116313557101274023536 0ustar anjaesctl/*************************************************************************\ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #define EPICS_PRIVATE_API #include "osiSock.h" /* * epicsSocketUnsentCount () * See https://www.unix.com/man-page/osx/2/setsockopt */ int epicsSocketUnsentCount(SOCKET sock) { int unsent; socklen_t len = sizeof(unsent); if (getsockopt(sock, SOL_SOCKET, SO_NWRITE, &unsent, &len) == 0) return unsent; return -1; } base-7.0.3.1/modules/libcom/src/osi/os/Darwin/osdTime.cpp0000664000577000060420000000452213557101274021671 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include #include #include #include #include #define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "osiSock.h" #define epicsExportSharedSymbols #include "cantProceed.h" #include "epicsTime.h" #include "generalTimeSup.h" static clock_serv_t host_clock; extern "C" { int osdTimeGetCurrent (epicsTimeStamp *pDest) { mach_timespec_t mts; struct timespec ts; clock_get_time(host_clock, &mts); ts.tv_sec = mts.tv_sec; ts.tv_nsec = mts.tv_nsec; *pDest = epicsTime(ts); return epicsTimeOK; } } // extern "C" static int timeRegister(void) { host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &host_clock); generalTimeCurrentTpRegister("MachTime", \ LAST_RESORT_PRIORITY, osdTimeGetCurrent); osdMonotonicInit(); return 1; } static int done = timeRegister(); int epicsTime_gmtime(const time_t *pAnsiTime, struct tm *pTM) { return gmtime_r(pAnsiTime, pTM) ? epicsTimeOK : errno; } int epicsTime_localtime(const time_t *clock, struct tm *result) { return localtime_r(clock, result) ? epicsTimeOK : errno; } extern "C" epicsShareFunc void convertDoubleToWakeTime(double timeout, struct timespec *wakeTime) { mach_timespec_t now; struct timespec wait; if (timeout < 0.0) timeout = 0.0; else if (timeout > 60 * 60 * 24 * 3652.5) timeout = 60 * 60 * 24 * 3652.5; /* 10 years */ clock_get_time(host_clock, &now); wait.tv_sec = static_cast< time_t >(timeout); wait.tv_nsec = static_cast< long >((timeout - (double)wait.tv_sec) * 1e9); wakeTime->tv_sec = now.tv_sec + wait.tv_sec; wakeTime->tv_nsec = now.tv_nsec + wait.tv_nsec; if (wakeTime->tv_nsec >= 1000000000L) { wakeTime->tv_nsec -= 1000000000L; ++wakeTime->tv_sec; } } base-7.0.3.1/modules/libcom/src/osi/os/Darwin/osdTime.h0000664000577000060420000000121613557101274021333 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Eric Norum */ #ifndef osdTimeh #define osdTimeh #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ epicsShareFunc void convertDoubleToWakeTime(double timeout, struct timespec *wakeTime); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* ifndef osdTimeh */ base-7.0.3.1/modules/libcom/src/osi/os/Darwin/osdgetexec.c0000664000577000060420000000174513557101274022063 0ustar anjaesctl #include #include #include #define epicsExportSharedSymbols #include char *epicsGetExecName(void) { uint32_t max = 64u; char *ret = NULL; while(1) { char *temp = realloc(ret, max); if(!temp) { /* we treat alloc failure as terminal */ free(ret); ret = NULL; break; } ret = temp; /* cf. "man 3 dyld" */ if(_NSGetExecutablePath(ret, &max)==0) { /* max left unchanged */ ret[max-1] = '\0'; break; } /* max has been updated with required size */ } /* TODO: _NSGetExecutablePath() doesn't follow symlinks */ return ret; } char *epicsGetExecDir(void) { char *ret = epicsGetExecName(); if(ret) { char *sep = strrchr(ret, '/'); if(sep) { /* nil the charactor after the / */ sep[1] = '\0'; } } return ret; } base-7.0.3.1/modules/libcom/src/osi/os/Darwin/osiFileName.h0000664000577000060420000000075613557101274022132 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * Author: Eric Norum */ #ifndef osiFileNameH #define osiFileNameH #include "unixFileName.h" #endif /* osiFileNameH */ base-7.0.3.1/modules/libcom/src/osi/os/Linux/osdBackTrace.cpp0000664000577000060420000000047513557101274022470 0ustar anjaesctl/* * Copyright: Stanford University / SLAC National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * Author: Till Straumann , 2011, 2014 */ #include "osdExecinfoBackTrace.cpp" base-7.0.3.1/modules/libcom/src/osi/os/Linux/osdFindAddr.c0000664000577000060420000000046513557101274021763 0ustar anjaesctl/* * Copyright: Stanford University / SLAC National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * Author: Till Straumann , 2011, 2014 */ #include "osdElfFindAddr.c" base-7.0.3.1/modules/libcom/src/osi/os/Linux/osdSock.h0000664000577000060420000000400113557101274021202 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Linux specific socket include */ #ifndef osdSockH #define osdSockH #include #include #include /* for MAXHOSTNAMELEN */ #include #include #include #include #include #include #include #include #include /* close() and others */ typedef int SOCKET; #define INVALID_SOCKET (-1) #define SOCKERRNO errno #define socket_ioctl(A,B,C) ioctl(A,B,C) typedef int osiSockIoctl_t; typedef socklen_t osiSocklen_t; typedef int osiSockOptMcastLoop_t; typedef int osiSockOptMcastTTL_t; #define FD_IN_FDSET(FD) ((FD)ifr_name)) #endif /*osdSockH*/ base-7.0.3.1/modules/libcom/src/osi/os/Linux/osdSockUnsentCount.c0000664000577000060420000000110313557101274023403 0ustar anjaesctl/*************************************************************************\ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #define EPICS_PRIVATE_API #include "osiSock.h" /* * epicsSocketUnsentCount () * See https://linux.die.net/man/7/tcp */ int epicsSocketUnsentCount(SOCKET sock) { int unsent; if (ioctl(sock, SIOCOUTQ, &unsent) == 0) return unsent; return -1; } base-7.0.3.1/modules/libcom/src/osi/os/Linux/osdThread.h0000664000577000060420000000302713557101274021521 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_osdThread_H #define INC_osdThread_H #include #include #include "shareLib.h" #include "ellLib.h" #include "epicsEvent.h" #ifdef __cplusplus extern "C" { #endif typedef struct epicsThreadOSD { ELLNODE node; int refcnt; pthread_t tid; pid_t lwpId; pthread_attr_t attr; struct sched_param schedParam; int schedPolicy; EPICSTHREADFUNC createFunc; void *createArg; epicsEventId suspendEvent; int isSuspended; int isEpicsThread; int isRealTimeScheduled; int isOnThreadList; unsigned int osiPriority; int joinable; char name[1]; /* actually larger */ } epicsThreadOSD; epicsShareFunc pthread_t epicsThreadGetPosixThreadId(epicsThreadId id); epicsShareFunc int epicsThreadGetPosixPriority(epicsThreadId id); #ifdef __cplusplus } #endif #endif /* INC_osdThread_H */ base-7.0.3.1/modules/libcom/src/osi/os/Linux/osdThreadExtra.c0000664000577000060420000000470113557101274022520 0ustar anjaesctl/*************************************************************************\ * 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) 2012 ITER Organization * 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 */ /* This differs from the posix implementation of epicsThread by: * - printing the Linux LWP ID instead of the POSIX thread ID in the show routines * - installing a default thread start hook, that sets the Linux thread name to the * EPICS thread name to make it visible on OS level, and discovers the LWP ID */ #include #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsStdio.h" #include "ellLib.h" #include "epicsEvent.h" #include "epicsThread.h" void epicsThreadShowInfo(epicsThreadId pthreadInfo, unsigned int level) { if (!pthreadInfo) { fprintf(epicsGetStdout(), " NAME EPICS ID " "LWP ID OSIPRI OSSPRI STATE\n"); } else { struct sched_param param; int priority = 0; if (pthreadInfo->tid) { int policy; int status = pthread_getschedparam(pthreadInfo->tid, &policy, ¶m); if (!status) priority = param.sched_priority; } fprintf(epicsGetStdout(),"%16.16s %14p %8lu %3d%8d %8.8s\n", pthreadInfo->name,(void *) pthreadInfo,(unsigned long)pthreadInfo->lwpId, pthreadInfo->osiPriority,priority, pthreadInfo->isSuspended ? "SUSPEND" : "OK"); } } static void thread_hook(epicsThreadId pthreadInfo) { /* Set the name of the thread's process. Limited to 16 characters. */ char comm[16]; if (strcmp(pthreadInfo->name, "_main_")) { snprintf(comm, sizeof(comm), "%s", pthreadInfo->name); prctl(PR_SET_NAME, comm, 0l, 0l, 0l); } pthreadInfo->lwpId = syscall(SYS_gettid); } epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault = thread_hook; epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain = thread_hook; base-7.0.3.1/modules/libcom/src/osi/os/Linux/osdTime.h0000664000577000060420000000144613557101274021213 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * Author: Eric Norum */ #ifndef osdTimeh #define osdTimeh /* * Linux needs this include file since the POSIX version * causes `struct timespec' to be defined in more than one place. */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ epicsShareFunc void epicsShareAPI convertDoubleToWakeTime(double timeout,struct timespec *wakeTime); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* ifndef osdTimeh */ base-7.0.3.1/modules/libcom/src/osi/os/Linux/osdgetexec.c0000664000577000060420000000205713557101274021733 0ustar anjaesctl #include #include #include #include #define epicsExportSharedSymbols #include #ifndef PATH_MAX # define PATH_MAX 100 #endif char *epicsGetExecName(void) { size_t max = PATH_MAX; char *ret = NULL; ssize_t n; while(1) { char *temp = realloc(ret, max); if(!temp) { /* we treat alloc failure as terminal */ free(ret); ret = NULL; break; } ret = temp; n = readlink("/proc/self/exe", ret, max); if(n == -1) { free(ret); ret = NULL; break; } else if(n < max) { /* readlink() never adds a nil */ ret[n] = '\0'; break; } max += 64; } return ret; } char *epicsGetExecDir(void) { char *ret = epicsGetExecName(); if(ret) { char *sep = strrchr(ret, '/'); if(sep) { /* nil the charactor after the / */ sep[1] = '\0'; } } return ret; } base-7.0.3.1/modules/libcom/src/osi/os/Linux/osiFileName.h0000664000577000060420000000124213557101274021774 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * osiFileName.h * Author: Jeff Hill */ #ifndef osiFileNameH #define osiFileNameH #include "unixFileName.h" #endif /* osiFileNameH */ base-7.0.3.1/modules/libcom/src/osi/os/Linux/osiUnistd.h0000664000577000060420000000154213557101274021565 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include /* * Some systems fail to provide prototypes of these functions. * Others provide different prototypes. * There seems to be no way to handle this automatically, so * if you get compile errors, just make the appropriate changes here. */ base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/devLibVMEOSD.c0000664000577000060420000002167413557101274021525 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* RTEMS port by Till Straumann, * 3/2002 * */ #include #include #include #include #include "devLibVME.h" #include #if defined(__PPC__) || defined(__mcf528x__) #if defined(__PPC__) #include #include #endif typedef void myISR (void *pParam); static myISR *isrFetch(unsigned vectorNumber, void **parg); /* * this routine needs to be in the symbol table * for this code to work correctly */ static void unsolicitedHandlerEPICS(int vectorNumber); static myISR *defaultHandlerAddr[]={ (myISR*)unsolicitedHandlerEPICS, }; /* * Make sure that the CR/CSR addressing mode is defined. * (it may not be in some BSPs). */ #ifndef VME_AM_CSR # define VME_AM_CSR (0x2f) #endif /* * we use a translation between an EPICS encoding * and a vxWorks encoding here * to reduce dependency of drivers on vxWorks * * we assume that the BSP are configured to use these * address modes by default */ #define EPICSAddrTypeNoConvert -1 int EPICStovxWorksAddrType[] = { VME_AM_SUP_SHORT_IO, VME_AM_STD_SUP_DATA, VME_AM_EXT_SUP_DATA, EPICSAddrTypeNoConvert, VME_AM_CSR }; /* * maps logical address to physical address, but does not detect * two device drivers that are using the same address range */ static long rtemsDevMapAddr (epicsAddressType addrType, unsigned options, size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress); /* * a bus error safe "wordSize" read at the specified address which returns * unsuccessful status if the device isnt present */ static long rtemsDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue); /* * a bus error safe "wordSize" write at the specified address which returns * unsuccessful status if the device isnt present */ static long rtemsDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue); static long rtemsDevConnectInterruptVME ( unsigned vectorNumber, void (*pFunction)(), void *parameter); static long rtemsDevDisconnectInterruptVME ( unsigned vectorNumber, void (*pFunction)() ); static long rtemsDevEnableInterruptLevelVME (unsigned level); static long rtemsDevDisableInterruptLevelVME (unsigned level); static int rtemsDevInterruptInUseVME (unsigned vectorNumber); /* RTEMS specific init */ /*devA24Malloc and devA24Free are not implemented*/ static void *devA24Malloc(size_t size) { return 0;} static void devA24Free(void *pBlock) {}; static long rtemsDevInit(void); /* * used by bind in devLib.c */ static devLibVME rtemsVirtualOS = { rtemsDevMapAddr, rtemsDevReadProbe, rtemsDevWriteProbe, rtemsDevConnectInterruptVME, rtemsDevDisconnectInterruptVME, rtemsDevEnableInterruptLevelVME, rtemsDevDisableInterruptLevelVME, devA24Malloc,devA24Free,rtemsDevInit,rtemsDevInterruptInUseVME }; devLibVME *pdevLibVME = &rtemsVirtualOS; /* RTEMS specific initialization */ static long rtemsDevInit(void) { /* assume the vme bridge has been initialized by bsp */ /* init BSP extensions [memProbe etc.] */ return bspExtInit(); } /* * devConnectInterruptVME * * wrapper to minimize driver dependency on OS */ static long rtemsDevConnectInterruptVME ( unsigned vectorNumber, void (*pFunction)(), void *parameter) { int status; if (devInterruptInUseVME(vectorNumber)) { return S_dev_vectorInUse; } status = BSP_installVME_isr( vectorNumber, pFunction, parameter); if (status) { return S_dev_vecInstlFail; } return 0; } /* * * devDisconnectInterruptVME() * * wrapper to minimize driver dependency on OS * * The parameter pFunction should be set to the C function pointer that * was connected. It is used as a key to prevent a driver from removing * an interrupt handler that was installed by another driver * */ static long rtemsDevDisconnectInterruptVME ( unsigned vectorNumber, void (*pFunction)() ) { void (*psub)(); void *arg; int status; /* * If pFunction not connected to this vector * then they are probably disconnecting from the wrong vector */ psub = isrFetch(vectorNumber, &arg); if(psub != pFunction){ return S_dev_vectorNotInUse; } status = BSP_removeVME_isr( vectorNumber, psub, arg) || BSP_installVME_isr( vectorNumber, (BSP_VME_ISR_t)unsolicitedHandlerEPICS, (void*)vectorNumber); if(status){ return S_dev_vecInstlFail; } return 0; } /* * enable VME interrupt level */ static long rtemsDevEnableInterruptLevelVME (unsigned level) { return BSP_enableVME_int_lvl(level); } /* * disable VME interrupt level */ static long rtemsDevDisableInterruptLevelVME (unsigned level) { return BSP_disableVME_int_lvl(level); } /* * rtemsDevMapAddr () */ static long rtemsDevMapAddr (epicsAddressType addrType, unsigned options, size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress) { long status; if (ppPhysicalAddress==NULL) { return S_dev_badArgument; } if (EPICStovxWorksAddrType[addrType] == EPICSAddrTypeNoConvert) { *ppPhysicalAddress = (void *) logicalAddress; } else { status = BSP_vme2local_adrs(EPICStovxWorksAddrType[addrType], logicalAddress, (unsigned long *)ppPhysicalAddress); if (status) { return S_dev_addrMapFail; } } return 0; } /* * a bus error safe "wordSize" read at the specified address which returns * unsuccessful status if the device isnt present */ rtems_status_code bspExtMemProbe(void *addr, int write, int size, void *pval); static long rtemsDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue) { long status; /* * this global variable exists in the nivxi library */ status = bspExtMemProbe ((void*)ptr, 0/*read*/, wordSize, pValue); if (status!=RTEMS_SUCCESSFUL) { return S_dev_noDevice; } return 0; } /* * a bus error safe "wordSize" write at the specified address which returns * unsuccessful status if the device isnt present */ static long rtemsDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue) { long status; /* * this global variable exists in the nivxi library */ status = bspExtMemProbe ((void*)ptr, 1/*write*/, wordSize, (void*)pValue); if (status!=RTEMS_SUCCESSFUL) { return S_dev_noDevice; } return 0; } /* * isrFetch() */ static myISR *isrFetch(unsigned vectorNumber, void **parg) { /* * fetch the handler or C stub attached at this vector */ return (myISR *) BSP_getVME_isr(vectorNumber,parg); } /* * determine if a VME interrupt vector is in use */ static int rtemsDevInterruptInUseVME (unsigned vectorNumber) { int i; myISR *psub; void *arg; psub = isrFetch (vectorNumber,&arg); if (!psub) return FALSE; /* * its a C routine. Does it match a default handler? */ for (i=0; i #include "epicsMMIO.h" #include "compilerSpecific.h" #include "epicsInterrupt.h" #define EPICS_ATOMIC_OS_NAME "RTEMS" typedef struct EpicsAtomicLockKey { int key; } EpicsAtomicLockKey; #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) { epicsAtomicMemoryBarrierFallback(); } #endif #ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) { rwbarr(); } #endif EPICS_ATOMIC_INLINE void epicsAtomicLock ( struct EpicsAtomicLockKey * pkey ) { pkey->key = epicsInterruptLock(); } EPICS_ATOMIC_INLINE void epicsAtomicUnlock ( struct EpicsAtomicLockKey * pkey ) { epicsInterruptUnlock(pkey->key); } #ifdef __cplusplus } /* end of extern "C" */ #endif /* __cplusplus */ #include "epicsAtomicDefault.h" #endif /* epicsAtomicOSD_h */ base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/epicsMMIO.h0000664000577000060420000000472313557101274021170 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver */ #ifndef EPICSMMIO_H #define EPICSMMIO_H #include #include #include #if defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC) # include /*NOTE: All READ/WRITE operations have an implicit read or write barrier */ # define ioread8(A) in_8((volatile epicsUInt8*)(A)) # define iowrite8(A,D) out_8((volatile epicsUInt8*)(A), D) # define le_ioread16(A) in_le16((volatile epicsUInt16*)(A)) # define le_ioread32(A) in_le32((volatile epicsUInt32*)(A)) # define le_iowrite16(A,D) out_le16((volatile epicsUInt16*)(A), D) # define le_iowrite32(A,D) out_le32((volatile epicsUInt32*)(A), D) # define be_ioread16(A) in_be16((volatile epicsUInt16*)(A)) # define be_ioread32(A) in_be32((volatile epicsUInt32*)(A)) # define be_iowrite16(A,D) out_be16((volatile epicsUInt16*)(A), D) # define be_iowrite32(A,D) out_be32((volatile epicsUInt32*)(A), D) # define rbarr() iobarrier_r() # define wbarr() iobarrier_w() # define rwbarr() iobarrier_rw() /* Define native operations */ # define nat_ioread16 be_ioread16 # define nat_ioread32 be_ioread32 # define nat_iowrite16 be_iowrite16 # define nat_iowrite32 be_iowrite32 static EPICS_ALWAYS_INLINE epicsUInt16 bswap16(epicsUInt16 value) { return (((epicsUInt16)(value) & 0x00ff) << 8) | (((epicsUInt16)(value) & 0xff00) >> 8); } static EPICS_ALWAYS_INLINE epicsUInt32 bswap32(epicsUInt32 value) { return (((epicsUInt32)(value) & 0x000000ff) << 24) | (((epicsUInt32)(value) & 0x0000ff00) << 8) | (((epicsUInt32)(value) & 0x00ff0000) >> 8) | (((epicsUInt32)(value) & 0xff000000) >> 24); } #elif defined(i386) || defined(__i386__) || defined(__i386) || defined(__m68k__) /* X86 does not need special handling for read/write width. * * TODO: Memory barriers? */ #include "epicsMMIODef.h" #else # warning I/O operations not defined for this RTEMS architecture #include "epicsMMIODef.h" #endif /* if defined PPC */ #endif /* EPICSMMIO_H */ base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/epicsMath.h0000664000577000060420000000137413557101274021317 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef epicsMathh #define epicsMathh #include #include #include #ifdef __cplusplus extern "C" { #endif epicsShareExtern float epicsNAN; epicsShareExtern float epicsINF; #ifdef __cplusplus } #endif #endif /* epicsMathh */ base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdEnv.c0000664000577000060420000000276413557101274020637 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osdEnv.c */ /* * Author: Eric Norum * Date: May 7, 2001 * * Routines to modify/display environment variables and EPICS parameters * */ #include #include #define epicsExportSharedSymbols #include "epicsStdio.h" #include "envDefs.h" #include "osiUnistd.h" #include "iocsh.h" /* * Set the value of an environment variable */ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) { iocshEnvClear(name); setenv(name, value, 1); } /* * Unset an environment variable */ epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name) { iocshEnvClear(name); unsetenv(name); } /* * Show the value of the specified, or all, environment variables */ epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) { if (name == NULL) { extern char **environ; char **sp; for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) printf ("%s\n", *sp); } else { const char *cp = getenv (name); if (cp == NULL) printf ("%s is not an environment variable.\n", name); else printf ("%s=%s\n", name, cp); } } base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdEvent.c0000664000577000060420000001241713557101274021164 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * RTEMS osdEvent.c * Author: W. Eric Norum * eric@cls.usask.ca * (306) 966-6055 */ /* * We want to access information which is * normally hidden from application programs. */ #define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 #include #include #include #include "epicsEvent.h" #include "epicsThread.h" #include "errlog.h" /* #define EPICS_RTEMS_SEMAPHORE_STATS */ /* * Some performance tuning instrumentation */ #ifdef EPICS_RTEMS_SEMAPHORE_STATS unsigned long semEstat[4]; #define SEMSTAT(i) semEstat[i]++; #else #define SEMSTAT(i) #endif /* * Create a simple binary semaphore */ epicsEventId 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'; sc = rtems_semaphore_create (rtems_build_name ('B', c3, c2, c1), initialState, RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0, &sid); if (sc != RTEMS_SUCCESSFUL) { 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; } void epicsEventDestroy(epicsEventId id) { rtems_id sid = (rtems_id)id; rtems_status_code sc; sc = rtems_semaphore_delete (sid); if (sc != RTEMS_SUCCESSFUL) errlogPrintf ("Can't destroy semaphore: %s\n", rtems_status_text (sc)); } epicsEventStatus epicsEventTrigger(epicsEventId id) { rtems_id sid = (rtems_id)id; rtems_status_code sc; sc = rtems_semaphore_release (sid); if (sc == RTEMS_SUCCESSFUL) return epicsEventOK; errlogPrintf ("Can't release semaphore: %s\n", rtems_status_text (sc)); return epicsEventError; } epicsEventStatus epicsEventWait(epicsEventId id) { rtems_id sid = (rtems_id)id; rtems_status_code sc; SEMSTAT(0) sc = rtems_semaphore_obtain (sid, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (sc != RTEMS_SUCCESSFUL) return epicsEventError; return epicsEventOK; } epicsEventStatus epicsEventWaitWithTimeout(epicsEventId id, double timeOut) { rtems_id sid = (rtems_id)id; rtems_status_code sc; rtems_interval delay; extern double rtemsTicksPerSecond_double; if (timeOut <= 0.0) return epicsEventTryWait(id); SEMSTAT(1) delay = timeOut * rtemsTicksPerSecond_double; if (delay == 0) delay++; sc = rtems_semaphore_obtain (sid, RTEMS_WAIT, delay); if (sc == RTEMS_SUCCESSFUL) return epicsEventOK; else if (sc == RTEMS_TIMEOUT) return epicsEventWaitTimeout; else return epicsEventError; } epicsEventStatus epicsEventTryWait(epicsEventId id) { rtems_id sid = (rtems_id)id; rtems_status_code sc; SEMSTAT(2) sc = rtems_semaphore_obtain (sid, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT); if (sc == RTEMS_SUCCESSFUL) return epicsEventOK; else if (sc == RTEMS_UNSATISFIED) return epicsEventWaitTimeout; else return epicsEventError; } void epicsEventShow(epicsEventId id, unsigned int level) { #if __RTEMS_VIOLATE_KERNEL_VISIBILITY__ rtems_id sid = (rtems_id)id; Semaphore_Control *the_semaphore; Semaphore_Control semaphore; Objects_Locations location; the_semaphore = _Semaphore_Get (sid, &location); if (location != OBJECTS_LOCAL) return; /* * Yes, there's a race condition here since an interrupt might * change things while the copy is in progress, but the information * is only for display, so it's not that critical. */ semaphore = *the_semaphore; _Thread_Enable_dispatch(); printf (" %8.8x ", (int)sid); if (_Attributes_Is_counting_semaphore (semaphore.attribute_set)) { printf ("Count: %d", (int)semaphore.Core_control.semaphore.count); } else { if (_CORE_mutex_Is_locked(&semaphore.Core_control.mutex)) { char name[30]; epicsThreadGetName ((epicsThreadId)semaphore.Core_control.mutex.holder_id, name, sizeof name); printf ("Held by:%8.8x (%s) Nest count:%d", (unsigned int)semaphore.Core_control.mutex.holder_id, name, (int)semaphore.Core_control.mutex.nest_count); } else { printf ("Not Held"); } } printf ("\n"); #endif } base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdEvent.h0000664000577000060420000000100713557101274021162 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * RTEMS osdEvent.h * Author: W. Eric Norum * eric@cls.usask.ca * (306) 966-6055 */ /* osdEvent.h not needed */ base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdFindSymbol.c0000664000577000060420000000173413557101274022151 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osi/os/RTEMS/osdFindSymbol.c */ /* GESYS could support this, but must use compile-time detection * as the code must build for non-GESYS systems as well. */ #define epicsExportSharedSymbols #include "epicsFindSymbol.h" epicsShareFunc void * epicsLoadLibrary(const char *name) { return 0; } epicsShareFunc const char *epicsLoadError(void) { return "epicsLoadLibrary not implemented"; } epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name) { return 0; } base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdInterrupt.c0000664000577000060420000000502313557101274022072 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * RTEMS osdInterrupt.c * Author: W. Eric Norum * eric@cls.usask.ca * (306) 966-6055 */ #include #include #include #include "errlog.h" #include "epicsInterrupt.h" #include "epicsThread.h" #define INTERRUPT_CONTEXT_MESSAGE_QUEUE_COUNT 100 static rtems_id interruptContextMessageQueue; int epicsInterruptLock (void) { rtems_interrupt_level level; rtems_interrupt_disable (level); return level; } void epicsInterruptUnlock (int key) { rtems_interrupt_level level = key; rtems_interrupt_enable (level); } int epicsInterruptIsInterruptContext (void) { return rtems_interrupt_is_in_progress (); } /* * Pass a message from an interrupt context. * Note that this passes a pointer to the message, not the message itself. * This implies that the message must remain valid after the * interrupt context is no longer active. */ void epicsInterruptContextMessage (const char *message) { rtems_message_queue_send (interruptContextMessageQueue, &message, sizeof message); } /* * Daemon to process interrupt context messages */ void InterruptContextMessageDaemon (void *unused) { const char *message; size_t size; rtems_status_code sc; sc = rtems_message_queue_create (rtems_build_name ('I', 'C', 'M', 'Q'), INTERRUPT_CONTEXT_MESSAGE_QUEUE_COUNT, sizeof message, RTEMS_FIFO | RTEMS_LOCAL, &interruptContextMessageQueue); if (sc != RTEMS_SUCCESSFUL) { errlogPrintf ("Can't create interrupt context message queue: %s\n", rtems_status_text (sc)); epicsThreadSuspendSelf (); } for (;;) { sc = rtems_message_queue_receive (interruptContextMessageQueue, &message, &size, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (sc != RTEMS_SUCCESSFUL) { errlogPrintf ("Can't receive message from interrupt context: %s\n", rtems_status_text (sc)); epicsThreadSuspendSelf (); } if (size == sizeof message) syslog (LOG_ERR, "%s", message); else errlogPrintf ("Received %u-byte message from interrupt context", (unsigned int)size); } } base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdInterrupt.h0000664000577000060420000000122113557101274022073 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Daemon to soak up and report messages from interrupt contexts */ extern void InterruptContextMessageDaemon (void *); base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdMessageQueue.c0000664000577000060420000001520113557101274022466 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author W. Eric Norum * norume@aps.anl.gov * 630 252 4793 */ /* * We want to access information which is * normally hidden from application programs. */ #define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 #define epicsExportSharedSymbols #include #include #include #include #include #include #include "epicsMessageQueue.h" #include "errlog.h" epicsShareFunc epicsMessageQueueId epicsShareAPI epicsMessageQueueCreate(unsigned int capacity, unsigned int maximumMessageSize) { rtems_status_code sc; epicsMessageQueueId id = calloc(1, sizeof(*id)); rtems_interrupt_level level; static char c1 = 'a'; static char c2 = 'a'; static char c3 = 'a'; if(!id) return NULL; sc = rtems_message_queue_create (rtems_build_name ('Q', c3, c2, c1), capacity, maximumMessageSize, RTEMS_FIFO|RTEMS_LOCAL, &id->id); if (sc != RTEMS_SUCCESSFUL) { free(id); errlogPrintf ("Can't create message queue: %s\n", rtems_status_text (sc)); return NULL; } id->maxSize = maximumMessageSize; id->localBuf = 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 id; } static rtems_status_code rtems_message_queue_send_timeout( rtems_id id, void *buffer, uint32_t size, rtems_interval timeout) { Message_queue_Control *the_message_queue; Objects_Locations location; CORE_message_queue_Status msg_status; the_message_queue = _Message_queue_Get( id, &location ); switch ( location ) { case OBJECTS_ERROR: return RTEMS_INVALID_ID; case OBJECTS_LOCAL: msg_status = _CORE_message_queue_Send( &the_message_queue->message_queue, buffer, size, id, NULL, 1, timeout ); _Thread_Enable_dispatch(); /* * If we had to block, then this is where the task returns * after it wakes up. The returned status is correct for * non-blocking operations but if we blocked, then we need * to look at the status in our TCB. */ if ( msg_status == CORE_MESSAGE_QUEUE_STATUS_UNSATISFIED_WAIT ) msg_status = _Thread_Executing->Wait.return_code; return _Message_queue_Translate_core_message_queue_return_code( msg_status ); } return RTEMS_INTERNAL_ERROR; /* unreached - only to remove warnings */ } epicsShareFunc int epicsShareAPI epicsMessageQueueSend( epicsMessageQueueId id, void *message, unsigned int messageSize) { if (rtems_message_queue_send_timeout(id->id, message, messageSize, RTEMS_NO_TIMEOUT) == RTEMS_SUCCESSFUL) return 0; else return -1; } epicsShareFunc int epicsShareAPI epicsMessageQueueSendWithTimeout( epicsMessageQueueId id, void *message, unsigned int messageSize, double timeout) { rtems_interval delay; extern double rtemsTicksPerSecond_double; /* * Convert time to ticks */ if (timeout <= 0.0) return epicsMessageQueueTrySend(id, message, messageSize); delay = (int)(timeout * rtemsTicksPerSecond_double); if (delay == 0) delay++; if (rtems_message_queue_send_timeout(id->id, message, messageSize, delay) == RTEMS_SUCCESSFUL) return 0; else return -1; } static int receiveMessage( epicsMessageQueueId id, void *buffer, uint32_t size, uint32_t wait, rtems_interval delay) { size_t rsize; rtems_status_code sc; if (size < id->maxSize) { if (id->localBuf == NULL) { id->localBuf = malloc(id->maxSize); if (id->localBuf == NULL) return -1; } rsize = receiveMessage(id, id->localBuf, id->maxSize, wait, delay); if (rsize > size) return -1; memcpy(buffer, id->localBuf, rsize); } else { sc = rtems_message_queue_receive(id->id, buffer, &rsize, wait, delay); if (sc != RTEMS_SUCCESSFUL) return -1; } return rsize; } epicsShareFunc int epicsShareAPI epicsMessageQueueTryReceive( epicsMessageQueueId id, void *message, unsigned int size) { return receiveMessage(id, message, size, RTEMS_NO_WAIT, 0); } epicsShareFunc int epicsShareAPI epicsMessageQueueReceive( epicsMessageQueueId id, void *message, unsigned int size) { return receiveMessage(id, message, size, RTEMS_WAIT, RTEMS_NO_TIMEOUT); } epicsShareFunc int epicsShareAPI epicsMessageQueueReceiveWithTimeout( epicsMessageQueueId id, void *message, unsigned int size, double timeout) { rtems_interval delay; uint32_t wait; extern double rtemsTicksPerSecond_double; /* * Convert time to ticks */ if (timeout <= 0.0) { wait = RTEMS_NO_WAIT; delay = 0; } else { wait = RTEMS_WAIT; delay = (int)(timeout * rtemsTicksPerSecond_double); if (delay == 0) delay++; } return receiveMessage(id, message, size, wait, delay); } epicsShareFunc int epicsShareAPI epicsMessageQueuePending( epicsMessageQueueId id) { uint32_t count; rtems_status_code sc; sc = rtems_message_queue_get_number_pending(id->id, &count); if (sc != RTEMS_SUCCESSFUL) { errlogPrintf("Message queue %x get number pending failed: %s\n", (unsigned int)id, rtems_status_text(sc)); return -1; } return count; } epicsShareFunc void epicsShareAPI epicsMessageQueueShow( epicsMessageQueueId id, int level) { int pending = epicsMessageQueuePending(id); if (pending >= 0) printf ("Message queue %lx -- Pending: %d\n", (unsigned long)id, pending); } base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdMessageQueue.h0000664000577000060420000000175113557101274022500 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author W. Eric Norum * norume@aps.anl.gov * 630 252 4793 */ /* * Very thin shims around RTEMS routines */ #include struct epicsMessageQueueOSD { rtems_id id; unsigned int maxSize; void *localBuf; }; #define epicsMessageQueueDestroy(q) (rtems_message_queue_delete((q)->id)) #define epicsMessageQueueTrySend(q,m,l) (rtems_message_queue_send((q)->id, (m), (l)) == RTEMS_SUCCESSFUL ? 0 : -1) base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdMutex.c0000664000577000060420000001214113557101274021177 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * RTEMS osdMutex.c * Author: W. Eric Norum * eric@cls.usask.ca * (306) 966-6055 */ /* * We want to access information which is * normally hidden from application programs. */ #define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 #include #include #include #include #include "epicsStdio.h" #include "epicsMutex.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) { rtems_status_code sc; rtems_id sid; rtems_interrupt_level level; static char c1 = 'a'; static char c2 = 'a'; static char c3 = 'a'; sc = rtems_semaphore_create (rtems_build_name ('M', c3, c2, c1), 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; } 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; the_semaphore = _Semaphore_Get( sid, &location ); _Thread_Enable_dispatch(); return (struct epicsMutexOSD *)the_semaphore; } #endif return (struct epicsMutexOSD *)sid; } void epicsMutexOsdDestroy(struct epicsMutexOSD * id) { rtems_status_code sc; rtems_id sid; #ifdef RTEMS_FAST_MUTEX Semaphore_Control *the_semaphore = (Semaphore_Control *)id; 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)); } void epicsMutexOsdUnlock(struct epicsMutexOSD * id) { #ifdef RTEMS_FAST_MUTEX Semaphore_Control *the_semaphore = (Semaphore_Control *)id; _Thread_Disable_dispatch(); _CORE_mutex_Surrender ( &the_semaphore->Core_control.mutex, the_semaphore->Object.id, NULL ); _Thread_Enable_dispatch(); #else epicsEventSignal (id); #endif } epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * id) { #ifdef RTEMS_FAST_MUTEX Semaphore_Control *the_semaphore = (Semaphore_Control *)id; ISR_Level level; SEMSTAT(0) _ISR_Disable( level ); _CORE_mutex_Seize( &the_semaphore->Core_control.mutex, the_semaphore->Object.id, 1, /* TRUE or FALSE */ 0, /* same as passed to obtain -- ticks */ level ); if (_Thread_Executing->Wait.return_code == 0) return epicsMutexLockOK; else return epicsMutexLockError; #else SEMSTAT(0) return((epicsEventWait (id) == epicsEventWaitOK) ?epicsMutexLockOK : epicsMutexLockError); #endif } epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * id) { #ifdef RTEMS_FAST_MUTEX Semaphore_Control *the_semaphore = (Semaphore_Control *)id; ISR_Level level; SEMSTAT(2) _ISR_Disable( level ); _CORE_mutex_Seize( &the_semaphore->Core_control.mutex, the_semaphore->Object.id, 0, /* TRUE or FALSE */ 0, /* same as passed to obtain -- ticks */ level ); if (_Thread_Executing->Wait.return_code == CORE_MUTEX_STATUS_SUCCESSFUL) return epicsMutexLockOK; else if (_Thread_Executing->Wait.return_code == CORE_MUTEX_STATUS_UNSATISFIED_NOWAIT) return epicsMutexLockTimeout; else return epicsMutexLockError; #else epicsEventWaitStatus status; SEMSTAT(2) status = epicsEventTryWait(id); return((status==epicsEventWaitOK ? epicsMutexLockOK : (status==epicsEventWaitTimeout) ? epicsMutexLockTimeout : epicsMutexLockError)); #endif } epicsShareFunc void epicsMutexOsdShow(struct epicsMutexOSD * id,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); } base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdMutex.h0000664000577000060420000000100513557101274021201 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * RTEMS osdMutex.h * Author: W. Eric Norum * eric@cls.usask.ca * (306) 966-6055 */ /* osdSem.h not needed */ base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdPoolStatus.c0000664000577000060420000000171213557101274022214 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include #define epicsExportSharedSymbols #include "osiPoolStatus.h" /* * osiSufficentSpaceInPool () */ epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ) { rtems_malloc_statistics_t s; unsigned long n; malloc_get_statistics(&s); n = s.space_available - (unsigned long)(s.lifetime_allocated - s.lifetime_freed); return (n > (50000 + contiguousBlockSize)); } base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdProcess.c0000664000577000060420000000257313557101274021523 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Operating System Dependent Implementation of osiProcess.h * * Author: Jeff Hill * */ #include #include #define epicsExportSharedSymbols #include "osiProcess.h" epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn) { const char *pName = "rtems"; unsigned uiLength; size_t len; len = strlen (pName); if ( len>UINT_MAX || len<=0 ) { return osiGetUserNameFail; } uiLength = (unsigned) len; if ( uiLength + 1 >= bufSizeIn ) { return osiGetUserNameFail; } strncpy ( pBuf, pName, (size_t) bufSizeIn ); return osiGetUserNameSuccess; } epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess (const char *pProcessName, const char *pBaseExecutableName) { return osiSpawnDetachedProcessNoSupport; } base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdReadline.c0000664000577000060420000000314113557101274021620 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Eric Norum Date: 12DEC2001 */ /* * This file is included by epicsReadline.c which has already included the * headers stdio.h, stdlib.h, errno.h, envDefs.h and epicsReadline.h */ #include #include struct osdContext {}; /* * Create a command-line context */ static void osdReadlineBegin (struct readlineContext *context) { GetLine *gl; long i = 50; envGetLongConfigParam(&IOCSH_HISTSIZE, &i); if (i < 0) i = 0; gl = new_GetLine(200, i * 40); if (gl) { context->osd = (struct osdContext *) gl; if (context->in) gl_change_terminal(gl, context->in, stdout, NULL); } } /* * Read a line of input */ static char * osdReadline (const char *prompt, struct readlineContext *context) { GetLine *gl = (GetLine *) context->osd; char *line; line = gl_get_line(gl, prompt ? prompt : "", NULL, -1); if (line) { char *nl = strchr(line, '\n'); if (nl) *nl = '\0'; } return line; } /* * Destroy a command-line context */ static void osdReadlineEnd(struct readlineContext *context) { GetLine *gl = (GetLine *) context->osd; del_GetLine(gl); } base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdSignal.cpp0000664000577000060420000000172513557101274021660 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #define epicsExportSharedSymbols #include "epicsSignal.h" /* * All NOOPs if the os isnt POSIX */ epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ) {} epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ) {} epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * /* threadId */ ) {} base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdSock.h0000664000577000060420000000465013557101274021007 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * RTEMS osdSock.h * Author: W. Eric Norum * eric@cls.usask.ca * (306) 966-6055 */ #ifndef osdSockH #define osdSockH #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); #ifdef __cplusplus } #endif typedef int SOCKET; #define INVALID_SOCKET (-1) #define SOCKERRNO errno #define socket_ioctl(A,B,C) ioctl(A,B,C) typedef int osiSockIoctl_t; typedef socklen_t osiSocklen_t; typedef char osiSockOptMcastLoop_t; typedef unsigned char osiSockOptMcastTTL_t; #define FD_IN_FDSET(FD) ((FD) #include #include #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK (u_long)0x7F000001 #endif #ifndef INADDR_NONE # define INADDR_NONE (0xffffffff) #endif /* * For shutdown() */ #ifndef SHUT_RD # define SHUT_RD 0 #endif #ifndef SHUT_WR # define SHUT_WR 1 #endif #ifndef SHUT_RDWR # define SHUT_RDWR 2 #endif /* * Ensure that we get the right network code in default/osdNetIntf.c. */ #define ifreq_size(pifreq) (pifreq->ifr_addr.sa_len + sizeof(pifreq->ifr_name)) #endif /*osdSockH*/ base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdSpin.c0000664000577000060420000000546313557101274021017 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * Copyright (c) 2012 ITER Organization. * Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2013 Brookhaven Science Assoc. as Operator of Brookhaven * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Authors: Ralph Lange * Andrew Johnson * Michael Davidsaver * * Inspired by Linux UP spinlocks implemention * include/linux/spinlock_api_up.h */ /* * RTEMS (single CPU): LOCK INTERRUPT and DISABLE PREEMPTION * * CAVEAT: * This implementation is intended for UP architectures only. */ #define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 #include #include #include "cantProceed.h" #include "epicsSpin.h" typedef struct epicsSpin { rtems_interrupt_level level; unsigned int locked; } epicsSpin; epicsSpinId epicsSpinCreate(void) { return calloc(1, sizeof(epicsSpin)); } epicsSpinId epicsSpinMustCreate(void) { epicsSpinId ret = epicsSpinCreate(); if (!ret) cantProceed("epicsSpinMustCreate: epicsSpinCreate failed."); return ret; } void epicsSpinDestroy(epicsSpinId spin) { free(spin); } void epicsSpinLock(epicsSpinId spin) { rtems_interrupt_level level; rtems_interrupt_disable(level); _Thread_Disable_dispatch(); if (spin->locked) { rtems_interrupt_enable(level); _Thread_Enable_dispatch(); if (!rtems_interrupt_is_in_progress()) { printk("epicsSpinLock(%p): Deadlock.\n", spin); cantProceed("Recursive lock, missed unlock or block when locked."); } else { printk("epicsSpinLock(%p): Deadlock in ISR.\n" "Recursive lock, missed unlock or block when locked.\n", spin); } return; } spin->level = level; spin->locked = 1; } int epicsSpinTryLock(epicsSpinId spin) { rtems_interrupt_level level; rtems_interrupt_disable(level); _Thread_Disable_dispatch(); if (spin->locked) { rtems_interrupt_enable(level); _Thread_Enable_dispatch(); return 1; } spin->level = level; spin->locked = 1; return 0; } void epicsSpinUnlock(epicsSpinId spin) { rtems_interrupt_level level = spin->level; if (!spin->locked) { printk("epicsSpinUnlock(%p): not locked\n", spin); return; } spin->level = spin->locked = 0; rtems_interrupt_enable (level); _Thread_Enable_dispatch(); } base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdStrtod.h0000664000577000060420000000071613557101274021366 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * epicsStrtod() for systems with working strtod() routine */ #define epicsStrtod strtod base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdThread.c0000664000577000060420000005663213557101274021321 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * RTEMS osdThread.c * Author: W. Eric Norum */ /* * We want to print out some task information which is * normally hidden from application programs. */ #define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 #include #include #include #include #include #include #include #include #include #include #include "epicsStdio.h" #include "errlog.h" #include "epicsMutex.h" #include "epicsString.h" #include "epicsThread.h" #include "cantProceed.h" #include "osiUnistd.h" #include "osdInterrupt.h" #include "epicsExit.h" #include "epicsAtomic.h" epicsShareFunc void osdThreadHooksRun(epicsThreadId id); epicsShareFunc void osdThreadHooksRunMain(epicsThreadId id); /* * Per-task variables */ struct taskVar { struct taskVar *forw; struct taskVar *back; char *name; rtems_id id; rtems_id join_barrier; /* only valid if joinable */ int refcnt; int joinable; EPICSTHREADFUNC funptr; void *parm; unsigned int threadVariableCapacity; void **threadVariables; }; static struct epicsMutexOSD *taskVarMutex; static struct taskVar *taskVarHead; #define RTEMS_NOTEPAD_TASKVAR 11 /* * 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"); } } /* * Just map osi 0 to 99 into RTEMS 199 to 100 * For RTEMS lower number means higher priority * RTEMS = 100 + (99 - osi) * = 199 - osi * osi = 199 - RTEMS */ int epicsThreadGetOsiPriorityValue(int ossPriority) { if (ossPriority < 100) { return epicsThreadPriorityMax; } else if (ossPriority > 199) { return epicsThreadPriorityMin; } else { return (199u - (unsigned int)ossPriority); } } int epicsThreadGetOssPriorityValue(unsigned int osiPriority) { if (osiPriority > 99) { return 100; } else { return (199 - (signed int)osiPriority); } } /* * epicsThreadLowestPriorityLevelAbove () */ epicsShareFunc epicsThreadBooleanStatus epicsThreadLowestPriorityLevelAbove (unsigned int priority, unsigned *pPriorityJustAbove) { unsigned newPriority = priority + 1; newPriority = priority + 1; if (newPriority <= 99) { *pPriorityJustAbove = newPriority; return epicsThreadBooleanStatusSuccess; } return epicsThreadBooleanStatusFail; } /* * epicsThreadHighestPriorityLevelBelow () */ epicsShareFunc epicsThreadBooleanStatus epicsThreadHighestPriorityLevelBelow (unsigned int priority, unsigned *pPriorityJustBelow) { unsigned newPriority = priority - 1; if (newPriority <= 99) { *pPriorityJustBelow = newPriority; return epicsThreadBooleanStatusSuccess; } return epicsThreadBooleanStatusFail; } unsigned int epicsThreadGetStackSize (epicsThreadStackSizeClass size) { unsigned int stackSize = 11000; switch(size) { case epicsThreadStackSmall: stackSize = 5000; break; case epicsThreadStackMedium: stackSize = 8000; break; case epicsThreadStackBig: break; default: errlogPrintf("epicsThreadGetStackSize illegal argument"); break; } if (stackSize < RTEMS_MINIMUM_STACK_SIZE) stackSize = RTEMS_MINIMUM_STACK_SIZE; return stackSize; } /* * Ensure integrity of task variable list */ static void taskVarLock (void) { epicsMutexOsdMustLock (taskVarMutex); } static void taskVarUnlock (void) { epicsMutexOsdUnlock (taskVarMutex); } static void taskUnref(struct taskVar *v) { int ref = epicsAtomicDecrIntT(&v->refcnt); assert(ref>=0); if(ref>0) return; if (v->joinable) { rtems_barrier_delete(v->join_barrier); } free (v->threadVariables); free (v->name); free (v); } /* * EPICS threads destroy themselves by returning from the thread entry function. * This simple wrapper provides the same semantics on RTEMS. */ static rtems_task threadWrapper (rtems_task_argument arg) { struct taskVar *v = (struct taskVar *)arg; osdThreadHooksRun((epicsThreadId)v->id); (*v->funptr)(v->parm); epicsExitCallAtThreadExits (); taskVarLock (); if (v->back) v->back->forw = v->forw; else taskVarHead = v->forw; if (v->forw) v->forw->back = v->back; taskVarUnlock (); if(v->joinable) { rtems_status_code sc = rtems_barrier_wait(v->join_barrier, RTEMS_NO_TIMEOUT); if(sc!=RTEMS_SUCCESSFUL) cantProceed("oops %s\n", rtems_status_text(sc)); } taskUnref(v); rtems_task_delete (RTEMS_SELF); } /* * The task wrapper takes care of cleanup */ void epicsThreadExitMain (void) { } static rtems_status_code setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr, void *parm, int joinable) { struct taskVar *v; uint32_t note; rtems_status_code sc = RTEMS_SUCCESSFUL; v = mallocMustSucceed (sizeof *v, "epicsThreadCreate_vars"); v->name = epicsStrDup(name); v->id = tid; v->funptr = funptr; v->parm = parm; v->joinable = joinable; v->refcnt = joinable ? 2 : 1; v->threadVariableCapacity = 0; v->threadVariables = NULL; if (joinable) { char c[3]; strncpy(c, v->name, 3); sc = rtems_barrier_create(rtems_build_name('~', c[0], c[1], c[2]), RTEMS_BARRIER_AUTOMATIC_RELEASE | RTEMS_LOCAL, 2, &v->join_barrier); if (sc != RTEMS_SUCCESSFUL) { free(v); return sc; } } note = (uint32_t)v; rtems_task_set_note (tid, RTEMS_NOTEPAD_TASKVAR, note); taskVarLock (); v->forw = taskVarHead; v->back = NULL; if (v->forw) v->forw->back = v; taskVarHead = v; taskVarUnlock (); if (funptr) { sc = rtems_task_start (tid, threadWrapper, (rtems_task_argument)v); } if (sc != RTEMS_SUCCESSFUL) { if (joinable) { rtems_barrier_delete(v->join_barrier); } free(v); } return sc; } /* * OS-dependent initialization * No need to worry about making this thread-safe since * it must be called before epicsThreadCreate creates * any new threads. */ static void epicsThreadInit (void) { if (!initialized) { rtems_id tid; 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"); rtems_task_ident (RTEMS_SELF, 0, &tid); if(setThreadInfo (tid, "_main_", NULL, NULL, 0) != RTEMS_SUCCESSFUL) cantProceed("epicsThreadInit() unable to setup _main_"); osdThreadHooksRunMain((epicsThreadId)tid); initialized = 1; epicsThreadCreate ("ImsgDaemon", 99, epicsThreadGetStackSize (epicsThreadStackSmall), InterruptContextMessageDaemon, NULL); } } void epicsThreadRealtimeLock(void) {} /* * Create and start a new thread */ epicsThreadId epicsThreadCreateOpt ( const char * name, EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts ) { unsigned int stackSize; rtems_id tid; rtems_status_code sc; char c[4]; if (!initialized) epicsThreadInit(); if (!opts) { static const epicsThreadOpts opts_default = EPICS_THREAD_OPTS_INIT; opts = &opts_default; } stackSize = opts->stackSize; if (stackSize <= epicsThreadStackBig) stackSize = epicsThreadGetStackSize(stackSize); if (stackSize < RTEMS_MINIMUM_STACK_SIZE) { errlogPrintf ("Warning: epicsThreadCreate %s illegal stackSize %d\n", name, stackSize); stackSize = RTEMS_MINIMUM_STACK_SIZE; } strncpy (c, name, sizeof c); sc = rtems_task_create (rtems_build_name (c[0], c[1], c[2], c[3]), epicsThreadGetOssPriorityValue (opts->priority), stackSize, RTEMS_PREEMPT|RTEMS_NO_TIMESLICE|RTEMS_NO_ASR|RTEMS_INTERRUPT_LEVEL(0), RTEMS_FLOATING_POINT|RTEMS_LOCAL, &tid); if (sc != RTEMS_SUCCESSFUL) { errlogPrintf ("epicsThreadCreate create failure for %s: %s\n", name, rtems_status_text(sc)); return 0; } sc = setThreadInfo (tid, name, funptr, parm, opts->joinable); if (sc != RTEMS_SUCCESSFUL) { errlogPrintf ("epicsThreadCreate create failure during setup for %s: %s\n", name, rtems_status_text(sc)); rtems_task_delete(tid); return 0; } return (epicsThreadId)tid; } epicsThreadId threadMustCreate (const char *name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr,void *parm) { epicsThreadId tid = epicsThreadCreate(name, priority, stackSize, funptr, parm); if (tid == NULL) cantProceed(0); return tid; } void epicsThreadMustJoin(epicsThreadId id) { rtems_id target_tid = (rtems_id)id, self_tid; struct taskVar *v = 0; rtems_task_ident (RTEMS_SELF, 0, &self_tid); { uint32_t note; rtems_task_get_note (target_tid, RTEMS_NOTEPAD_TASKVAR, ¬e); v = (void *)note; } /* 'v' may be NULL if 'id' represents a non-EPICS thread other than _main_. */ if(!v || !v->joinable) { if(epicsThreadGetIdSelf()==id) { errlogPrintf("Warning: %s thread self-join of unjoinable\n", v ? v->name : "non-EPICS thread"); } else { /* try to error nicely, however in all likelyhood 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", v->name); } return; } else if(target_tid!=self_tid) { /* wait for target to complete */ rtems_status_code sc = rtems_barrier_wait(v->join_barrier, RTEMS_NO_TIMEOUT); if(sc!=RTEMS_SUCCESSFUL) cantProceed("oopsj %s\n", rtems_status_text(sc)); if(sc != RTEMS_SUCCESSFUL) { errlogPrintf("epicsThreadMustJoin('%s') -> %s\n", v->name, rtems_status_text(sc)); } } v->joinable = 0; taskUnref(v); /* target task may be deleted. * self task is not deleted, even for self join. */ } void epicsThreadSuspendSelf (void) { rtems_status_code sc; sc = rtems_task_suspend (RTEMS_SELF); if (sc != RTEMS_SUCCESSFUL) errlogPrintf("epicsThreadSuspendSelf failed: %s\n", rtems_status_text(sc)); } void epicsThreadResume(epicsThreadId id) { rtems_id tid = (rtems_id)id; rtems_status_code sc; sc = rtems_task_resume (tid); if (sc != RTEMS_SUCCESSFUL) errlogPrintf("epicsThreadResume failed: %s\n", rtems_status_text (sc)); } unsigned int epicsThreadGetPriority(epicsThreadId id) { rtems_id tid = (rtems_id)id; rtems_status_code sc; rtems_task_priority pri; sc = rtems_task_set_priority (tid, RTEMS_CURRENT_PRIORITY, &pri); if (sc != RTEMS_SUCCESSFUL) errlogPrintf("epicsThreadGetPriority failed: %s\n", rtems_status_text(sc)); return epicsThreadGetOsiPriorityValue(pri); } unsigned int epicsThreadGetPrioritySelf(void) { return epicsThreadGetPriority((epicsThreadId)RTEMS_SELF); } void epicsThreadSetPriority (epicsThreadId id,unsigned int osip) { rtems_id tid = (rtems_id)id; rtems_status_code sc; rtems_task_priority pri = epicsThreadGetOssPriorityValue(osip); sc = rtems_task_set_priority (tid, pri, &pri); if (sc != RTEMS_SUCCESSFUL) errlogPrintf("epicsThreadSetPriority failed: %s\n", rtems_status_text(sc)); } int epicsThreadIsEqual (epicsThreadId id1, epicsThreadId id2) { return (id1 == id2); } int epicsThreadIsSuspended (epicsThreadId id) { rtems_id tid = (rtems_id)id; rtems_status_code sc; switch (sc = rtems_task_is_suspended (tid)) { case RTEMS_SUCCESSFUL: return 0; case RTEMS_ALREADY_SUSPENDED: return 1; default: return 1; } } void epicsThreadSleep (double seconds) { rtems_status_code sc; rtems_interval delay; extern double rtemsTicksPerSecond_double; if (seconds > 0.0) { seconds *= rtemsTicksPerSecond_double; seconds += 0.99999999; /* 8 9s here is optimal */ delay = (seconds >= INT_MAX) ? INT_MAX : (int) seconds; } else { /* seconds <= 0 or NAN */ delay = 0; } sc = rtems_task_wake_after (delay); if(sc != RTEMS_SUCCESSFUL) errlogPrintf("epicsThreadSleep: %s\n", rtems_status_text(sc)); } epicsThreadId epicsThreadGetIdSelf (void) { rtems_id tid; rtems_task_ident (RTEMS_SELF, 0, &tid); return (epicsThreadId)tid; } const char *epicsThreadGetNameSelf(void) { uint32_t note; struct taskVar *v; rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, ¬e); v = (void *)note; return v->name; } void epicsThreadGetName (epicsThreadId id, char *name, size_t size) { rtems_id tid = (rtems_id)id; struct taskVar *v; int haveName = 0; taskVarLock (); for (v=taskVarHead ; v != NULL ; v=v->forw) { if (v->id == tid) { strncpy(name, v->name, size); haveName = 1; break; } } taskVarUnlock (); if (!haveName) { #if (__RTEMS_MAJOR__>4 || \ (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>8) || \ (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==8 && __RTEMS_REVISION__>=99)) if (_Objects_Get_name_as_string((rtems_id)id, size, name) != NULL) haveName = 1; #else /* * Try to get the RTEMS task name */ Thread_Control *thr; Objects_Locations l; if ((thr=_Thread_Get(tid, &l)) != NULL) { if (OBJECTS_LOCAL == l) { int length; Objects_Information *oi = _Objects_Get_information(tid); if (oi->name_length >= size) length = size - 1; else length = oi->name_length; if (oi->is_string) strncpy(name, thr->Object.name, length); else _Objects_Copy_name_raw( &thr->Object.name, name, length); name[length] = '\0'; haveName = 1; } _Thread_Enable_dispatch(); } #endif } if (!haveName) snprintf(name, size, "0x%lx", (long)tid); name[size-1] = '\0'; } epicsThreadId epicsThreadGetId (const char *name) { struct taskVar *v; rtems_id tid = 0; /* * Linear search is good enough since this routine * is invoked only by command-line calls. */ taskVarLock (); for (v = taskVarHead ; v != NULL ; v = v->forw) { if (strcmp (name, v->name) == 0) { tid = v->id; break; } } taskVarUnlock (); return (epicsThreadId)tid; } /* * Ensure func() is run only once. */ void epicsThreadOnce(epicsThreadOnceId *id, void(*func)(void *), void *arg) { #define EPICS_THREAD_ONCE_DONE (epicsThreadId) 1 if (!initialized) epicsThreadInit(); epicsMutexOsdMustLock(onceMutex); if (*id != EPICS_THREAD_ONCE_DONE) { if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */ *id = epicsThreadGetIdSelf(); /* mark active */ epicsMutexOsdUnlock(onceMutex); func(arg); epicsMutexOsdMustLock(onceMutex); *id = EPICS_THREAD_ONCE_DONE; /* mark done */ } else if (*id == epicsThreadGetIdSelf()) { epicsMutexOsdUnlock(onceMutex); cantProceed("Recursive epicsThreadOnce() initialization\n"); } else while (*id != EPICS_THREAD_ONCE_DONE) { /* Another thread is in the above func(arg) call. */ epicsMutexOsdUnlock(onceMutex); epicsThreadSleep(epicsThreadSleepQuantum()); epicsMutexOsdMustLock(onceMutex); } } epicsMutexOsdUnlock(onceMutex); } /* * Thread private storage implementation based on the vxWorks * implementation by Andrew Johnson APS/ASD. */ epicsThreadPrivateId epicsThreadPrivateCreate () { unsigned int taskVarIndex; static volatile unsigned int threadVariableCount = 0; if (!initialized) epicsThreadInit (); taskVarLock (); taskVarIndex = ++threadVariableCount; taskVarUnlock (); return (epicsThreadPrivateId)taskVarIndex; } void epicsThreadPrivateDelete (epicsThreadPrivateId id) { /* empty */ } void epicsThreadPrivateSet (epicsThreadPrivateId id, void *pvt) { unsigned int varIndex = (unsigned int)id; uint32_t note; struct taskVar *v; int i; rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, ¬e); v = (struct taskVar *)note; if (varIndex >= v->threadVariableCapacity) { v->threadVariables = realloc (v->threadVariables, (varIndex + 1) * sizeof(void *)); if (v->threadVariables == NULL) cantProceed("epicsThreadPrivateSet realloc failed\n"); for (i = v->threadVariableCapacity ; i < varIndex ; i++) v->threadVariables[i] = NULL; v->threadVariableCapacity = varIndex + 1; } v->threadVariables[varIndex] = pvt; } void * epicsThreadPrivateGet (epicsThreadPrivateId id) { unsigned int varIndex = (unsigned int)id; uint32_t note; struct taskVar *v; rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, ¬e); v = (struct taskVar *)note; if (varIndex >= v->threadVariableCapacity) return NULL; return v->threadVariables[varIndex]; } /* * Show task info */ struct bitmap { char *msg; unsigned long mask; unsigned long state; }; static void showBitmap (char *cbuf, unsigned long bits, const struct bitmap *bp) { for ( ; bp->msg != NULL ; bp++) { if ((bp->mask & bits) == bp->state) { strcpy (cbuf, bp->msg); cbuf += strlen (bp->msg); } } } static void showInternalTaskInfo (rtems_id tid) { #ifdef __RTEMS_VIOLATE_KERNEL_VISIBILITY__ int epicsPri; Thread_Control *the_thread; Objects_Locations location; static Thread_Control thread; static char bitbuf[120]; static const struct bitmap taskState[] = { { "RUN", STATES_ALL_SET, STATES_READY }, { "DORM", STATES_DORMANT, STATES_DORMANT }, { "SUSP", STATES_SUSPENDED, STATES_SUSPENDED }, { "TRANS", STATES_TRANSIENT, STATES_TRANSIENT }, { "Delay", STATES_DELAYING, STATES_DELAYING }, { "Wtime", STATES_WAITING_FOR_TIME, STATES_WAITING_FOR_TIME }, { "Wbuf", STATES_WAITING_FOR_BUFFER, STATES_WAITING_FOR_BUFFER }, { "Wseg", STATES_WAITING_FOR_SEGMENT, STATES_WAITING_FOR_SEGMENT }, { "Wmsg" , STATES_WAITING_FOR_MESSAGE, STATES_WAITING_FOR_MESSAGE }, { "Wevnt", STATES_WAITING_FOR_EVENT, STATES_WAITING_FOR_EVENT }, { "Wsem", STATES_WAITING_FOR_SEMAPHORE,STATES_WAITING_FOR_SEMAPHORE }, { "Wmtx", STATES_WAITING_FOR_MUTEX, STATES_WAITING_FOR_MUTEX }, { "Wjoin", STATES_WAITING_FOR_JOIN_AT_EXIT,STATES_WAITING_FOR_JOIN_AT_EXIT }, { "Wrpc", STATES_WAITING_FOR_RPC_REPLY,STATES_WAITING_FOR_RPC_REPLY }, { "Wrate", STATES_WAITING_FOR_PERIOD, STATES_WAITING_FOR_PERIOD }, { "Wsig", STATES_WAITING_FOR_SIGNAL, STATES_WAITING_FOR_SIGNAL }, { NULL, 0, 0 }, }; the_thread = _Thread_Get (tid, &location); if (location != OBJECTS_LOCAL) { fprintf(epicsGetStdout(),"%-30s", " *** RTEMS task gone! ***"); return; } thread = *the_thread; _Thread_Enable_dispatch(); /* * Show both real and current priorities if they differ. * Note that the epicsThreadGetOsiPriorityValue routine is not used here. * If a thread has a priority outside the normal EPICS range then * that priority should be displayed, not the value truncated to * the EPICS range. */ epicsPri = 199-thread.real_priority; if (epicsPri < 0) fprintf(epicsGetStdout()," <0"); else if (epicsPri > 99) fprintf(epicsGetStdout()," >99"); else fprintf(epicsGetStdout()," %4d", epicsPri); if (thread.current_priority == thread.real_priority) fprintf(epicsGetStdout(),"%4d ", (int)thread.current_priority); else fprintf(epicsGetStdout(),"%4d/%-3d", (int)thread.real_priority, (int)thread.current_priority); showBitmap (bitbuf, thread.current_state, taskState); fprintf(epicsGetStdout(),"%8.8s", bitbuf); if (thread.current_state & (STATES_WAITING_FOR_SEMAPHORE | STATES_WAITING_FOR_MUTEX | STATES_WAITING_FOR_MESSAGE)) fprintf(epicsGetStdout()," %8.8x", (int)thread.Wait.id); else fprintf(epicsGetStdout()," %8.8s", ""); #endif } static void epicsThreadShowInfo (struct taskVar *v, unsigned int level) { if (!v) { fprintf(epicsGetStdout()," PRIORITY\n"); fprintf(epicsGetStdout()," ID EPICS RTEMS STATE WAIT NAME\n"); fprintf(epicsGetStdout(),"+--------+-----------+--------+--------+---------------------+\n"); } else { fprintf(epicsGetStdout(),"%9.8x", (int)v->id); showInternalTaskInfo (v->id); fprintf(epicsGetStdout()," %s\n", v->name); } } void epicsThreadShow (epicsThreadId id, unsigned int level) { struct taskVar *v; if (!id) { epicsThreadShowInfo (NULL, level); return; } taskVarLock (); for (v = taskVarHead ; v != NULL ; v = v->forw) { if ((rtems_id)id == v->id) { epicsThreadShowInfo (v, level); taskVarUnlock (); return; } } taskVarUnlock (); fprintf(epicsGetStdout(),"*** Thread %x does not exist.\n", (unsigned int)id); } void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func) { struct taskVar *v; taskVarLock (); /* * Map tasks in the order of creation (backwards through list) */ for (v = taskVarHead ; v != NULL && v->forw != NULL ; v = v->forw) continue; while (v) { func ((epicsThreadId)v->id); v = v->back; } taskVarUnlock (); } void epicsThreadShowAll (unsigned int level) { struct taskVar *v; epicsThreadShowInfo (NULL, level); taskVarLock (); /* * Show tasks in the order of creation (backwards through list) */ for (v = taskVarHead ; v != NULL && v->forw != NULL ; v = v->forw) continue; while (v) { epicsThreadShowInfo (v, level); v = v->back; } taskVarUnlock (); } double epicsThreadSleepQuantum ( void ) { extern double rtemsTicksPerSecond_double; return 1.0 / rtemsTicksPerSecond_double; } epicsShareFunc int epicsThreadGetCPUs(void) { #if defined(RTEMS_SMP) return rtems_smp_get_number_of_processors(); #else return 1; #endif } base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdThread.h0000664000577000060420000000131113557101274021306 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_osdThread_H #define INC_osdThread_H #ifdef __cplusplus extern "C" { #endif int epicsThreadGetOssPriorityValue(unsigned int osiPriority); #ifdef __cplusplus } #endif #endif /* INC_osdThread_H */ base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdThreadExtra.c0000664000577000060420000000120313557101274022305 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 ITER Organization * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Ralph Lange Date: 26 Jun 2012 */ /* Null default thread hooks for all platforms that do not do anything special */ #define epicsExportSharedSymbols #include "epicsThread.h" epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdTime.cpp0000664000577000060420000001067713557101274021347 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: W. Eric Norum */ #define __BSD_VISIBLE 1 #include #include #define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include #include #include #include #include #include #include #include #include "epicsTime.h" #include "osdTime.h" #include "osiNTPTime.h" #include "osiClockTime.h" #include "generalTimeSup.h" extern "C" { extern rtems_interval rtemsTicksPerSecond; int rtems_bsdnet_get_ntp(int, int(*)(), struct timespec *); static int ntpSocket = -1; void osdTimeRegister(void) { /* Init NTP first so it can be used to sync ClockTime */ NTPTime_Init(100); ClockTime_Init(CLOCKTIME_SYNC); osdMonotonicInit(); } int osdNTPGet(struct timespec *ts) { static unsigned bequiet; ssize_t ret; if (ntpSocket < 0) return -1; /* rtems_bsdnet_get_ntp() will send an NTP request, then * call recvfrom() exactly once to process the expected reply. * Any leftovers in the socket buffer (ie. duplicates of * previous replies) will cause problems. * So flush out the socket buffer first. */ do { char junk[16]; ret = recvfrom(ntpSocket, junk, sizeof(junk), MSG_DONTWAIT, NULL, NULL); if (ret == -1 && errno == EAGAIN) { break; } else if (ret == -1) { if (!bequiet) { printf("osdNTPGet cleaner error: %s\n", strerror(errno)); bequiet = 1; } break; } else { bequiet = 0; } } while (ret > 0); return rtems_bsdnet_get_ntp(ntpSocket, NULL, ts); } void osdNTPInit(void) { struct sockaddr_in myAddr; ntpSocket = socket (AF_INET, SOCK_DGRAM, 0); if (ntpSocket < 0) { printf("osdNTPInit() Can't create socket: %s\n", strerror (errno)); return; } memset (&myAddr, 0, sizeof myAddr); myAddr.sin_family = AF_INET; myAddr.sin_port = htons (0); myAddr.sin_addr.s_addr = htonl (INADDR_ANY); if (bind (ntpSocket, (struct sockaddr *)&myAddr, sizeof myAddr) < 0) { printf("osdNTPInit() Can't bind socket: %s\n", strerror (errno)); close (ntpSocket); ntpSocket = -1; } } void osdNTPReport(void) { } int osdTickGet(void) { rtems_interval t; rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &t); return t; } int osdTickRateGet(void) { return rtemsTicksPerSecond; } /* * Use reentrant versions of time access */ int epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM ) { struct tm * pRet = gmtime_r ( pAnsiTime, pTM ); if ( pRet ) { return epicsTimeOK; } else { return errno; } } int epicsTime_localtime ( const time_t *clock, struct tm *result ) { struct tm * pRet = localtime_r ( clock, result ); if ( pRet ) { return epicsTimeOK; } else { return errno; } } rtems_interval rtemsTicksPerSecond; double rtemsTicksPerSecond_double, rtemsTicksPerTwoSeconds_double; } // extern "C" /* * Static constructors are run too early in a standalone binary * to be able to initialize the NTP time provider (the network * is not available yet), so the RTEMS standalone startup code * explicitly calls osdTimeRegister() at the appropriate time. * However if we are loaded dynamically we *do* register our * standard time providers at static constructor time; in this * case the network is available already. */ static int staticTimeRegister(void) { rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &rtemsTicksPerSecond); rtemsTicksPerSecond_double = rtemsTicksPerSecond; rtemsTicksPerTwoSeconds_double = rtemsTicksPerSecond_double * 2.0; /* If networking is already up at the time static constructors * are executed then we are probably run-time loaded and it's * OK to osdTimeRegister() at this point. */ if (rtems_bsdnet_ticks_per_second != 0) osdTimeRegister(); return 1; } static int done = staticTimeRegister(); base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdTime.h0000664000577000060420000000144313557101274021003 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_osdTime_H #define INC_osdTime_H #ifdef __cplusplus extern "C" { #endif void osdTimeRegister(void); void osdNTPInit(void); int osdNTPGet(struct timespec *); void osdNTPReport(void); int osdTickRateGet(void); int osdTickGet(void); #ifdef __cplusplus } #endif #endif /* INC_osdTime_H */ base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osdVME.h0000664000577000060420000000132513557101274020533 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * OS-dependent VME support */ #ifndef __i386__ #ifndef __mc68000 #ifndef __arm__ #include #endif #endif #endif void bcopyLongs(char *source, char *destination, int nlongs); base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osiFileName.h0000664000577000060420000000111513557101274021566 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * RTEMS osiFileName.h * Author: W. Eric Norum * eric@cls.usask.ca * (306) 966-6055 */ #ifndef osiFileNameH #define osiFileNameH #include "unixFileName.h" #endif /* osiFileNameH */ base-7.0.3.1/modules/libcom/src/osi/os/RTEMS/osiUnistd.h0000664000577000060420000000226613557101274021364 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include /* * Some systems fail to provide prototypes of these functions. * Others provide different prototypes. * There seems to be no way to handle this automatically, so * if you get compile errors, just make the appropriate changes here. */ #ifdef __cplusplus extern "C" { #endif int putenv (char *); char *strdup (const char *); char *strtok_r(char*, const char*, char**); int snprintf(char *str, size_t size, const char *format, ...); #include int vsnprintf(char *str, size_t size, const char *format, va_list ap); #ifdef __cplusplus } #endif base-7.0.3.1/modules/libcom/src/osi/os/WIN32/epicsAtomicMS.h0000664000577000060420000001624213557101274022012 0ustar anjaesctl /*************************************************************************\ * Copyright (c) 2011 LANS LLC, as Operator of * Los Alamos National Laboratory. * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef epicsAtomicMS_h #define epicsAtomicMS_h #include "epicsAssert.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #ifndef EPICS_ATOMIC_INCR_INTT #define EPICS_ATOMIC_INCR_INTT EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) { STATIC_ASSERT ( sizeof ( MS_LONG ) == sizeof ( int ) ); MS_LONG * const pTarg = ( MS_LONG * ) pTarget; return MS_InterlockedIncrement ( pTarg ); } #endif #ifndef EPICS_ATOMIC_DECR_INTT #define EPICS_ATOMIC_DECR_INTT EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ) { STATIC_ASSERT ( sizeof ( MS_LONG ) == sizeof ( int ) ); MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); return MS_InterlockedDecrement ( pTarg ); } #endif #ifndef EPICS_ATOMIC_ADD_INTT #define EPICS_ATOMIC_ADD_INTT EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ) { STATIC_ASSERT ( sizeof ( MS_LONG ) == sizeof ( int ) ); MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); /* we dont use InterlockedAdd because only latest windows is supported */ return delta + ( int ) MS_InterlockedExchangeAdd ( pTarg, ( MS_LONG ) delta ); } #endif #ifndef EPICS_ATOMIC_CAS_INTT #define EPICS_ATOMIC_CAS_INTT EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, int oldVal, int newVal ) { STATIC_ASSERT ( sizeof ( MS_LONG ) == sizeof ( int ) ); MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); return (int) MS_InterlockedCompareExchange ( pTarg, (MS_LONG) newVal, (MS_LONG) oldVal ); } #endif #if ! defined ( MS_ATOMIC_64 ) /* * necessary for next three functions * * looking at the MS documentation it appears that they will * keep type long the same size as an int on 64 bit builds */ STATIC_ASSERT ( sizeof ( MS_LONG ) == sizeof ( size_t ) ); #ifndef EPICS_ATOMIC_INCR_SIZET #define EPICS_ATOMIC_INCR_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) { MS_LONG * const pTarg = ( MS_LONG * ) pTarget; return MS_InterlockedIncrement ( pTarg ); } #endif #ifndef EPICS_ATOMIC_DECR_SIZET #define EPICS_ATOMIC_DECR_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) { MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); return MS_InterlockedDecrement ( pTarg ); } #endif #ifndef EPICS_ATOMIC_ADD_SIZET #define EPICS_ATOMIC_ADD_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta ) { MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); /* we dont use InterlockedAdd because only latest windows is supported */ return delta + ( size_t ) MS_InterlockedExchangeAdd ( pTarg, ( MS_LONG ) delta ); } #endif #ifndef EPICS_ATOMIC_SUB_SIZET #define EPICS_ATOMIC_SUB_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ) { MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); MS_LONG ldelta = (MS_LONG) delta; /* we dont use InterlockedAdd because only latest windows is supported */ return ( ( size_t ) MS_InterlockedExchangeAdd ( pTarg, -ldelta ) ) - delta; } #endif #ifndef EPICS_ATOMIC_CAS_SIZET #define EPICS_ATOMIC_CAS_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, size_t oldVal, size_t newVal ) { MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); return (size_t) MS_InterlockedCompareExchange ( pTarg, (MS_LONG) newVal, (MS_LONG) oldVal ); } #endif #ifndef EPICS_ATOMIC_CAS_PTRT #define EPICS_ATOMIC_CAS_PTRT EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) { MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); return (EpicsAtomicPtrT) MS_InterlockedCompareExchange ( pTarg, (MS_LONG) newVal, (MS_LONG) oldVal ); } #endif #else /* ! MS_ATOMIC_64 */ /* * necessary for next three functions */ STATIC_ASSERT ( sizeof ( MS_LONGLONG ) == sizeof ( size_t ) ); #ifndef EPICS_ATOMIC_INCR_SIZET #define EPICS_ATOMIC_INCR_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) { MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) pTarget; return ( size_t ) MS_InterlockedIncrement64 ( pTarg ); } #endif #ifndef EPICS_ATOMIC_DECR_SIZET #define EPICS_ATOMIC_DECR_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) { MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget ); return ( size_t ) MS_InterlockedDecrement64 ( pTarg ); } #endif #ifndef EPICS_ATOMIC_ADD_SIZET #define EPICS_ATOMIC_ADD_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta ) { MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget ); /* we dont use InterlockedAdd64 because only latest windows is supported */ return delta + ( size_t ) MS_InterlockedExchangeAdd64 ( pTarg, ( MS_LONGLONG ) delta ); } #endif #ifndef EPICS_ATOMIC_SUB_SIZET #define EPICS_ATOMIC_SUB_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ) { MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget ); MS_LONGLONG ldelta = (MS_LONGLONG) delta; /* we dont use InterlockedAdd64 because only latest windows is supported */ return (( size_t ) MS_InterlockedExchangeAdd64 ( pTarg, -ldelta )) - delta; } #endif #ifndef EPICS_ATOMIC_CAS_SIZET #define EPICS_ATOMIC_CAS_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, size_t oldVal, size_t newVal ) { MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget ); return (size_t) MS_InterlockedCompareExchange64 ( pTarg, (MS_LONGLONG) newVal, (MS_LONGLONG) oldVal ); } #endif #ifndef EPICS_ATOMIC_CAS_PTRT #define EPICS_ATOMIC_CAS_PTRT EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) { MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget ); return (EpicsAtomicPtrT) MS_InterlockedCompareExchange64 ( pTarg, (MS_LONGLONG) newVal, (MS_LONGLONG) oldVal ); } #endif #endif /* ! MS_ATOMIC_64 */ #ifdef __cplusplus } /* end of extern "C" */ #endif /* __cplusplus */ #endif /* ifdef epicsAtomicMS_h */ base-7.0.3.1/modules/libcom/src/osi/os/WIN32/epicsAtomicOSD.h0000664000577000060420000000350513557101274022116 0ustar anjaesctl /*************************************************************************\ * Copyright (c) 2011 LANS LLC, as Operator of * Los Alamos National Laboratory. * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef epicsAtomicOSD_h #define epicsAtomicOSD_h #define EPICS_ATOMIC_OS_NAME "WIN32" #ifdef VC_EXTRALEAN # define VC_EXTRALEAN_DETECTED_epicsAtomicOSD_h #else # define VC_EXTRALEAN #endif #ifdef STRICT # define STRICT_DETECTED_epicsAtomicOSD_h #else # define STRICT #endif #include "windows.h" #ifndef VC_EXTRALEAN_DETECTED_epicsAtomicOSD_h # undef VC_EXTRALEAN #endif #ifndef STRICT_DETECTED_epicsAtomicOSD_h # undef STRICT #endif #if defined ( _WIN64 ) # define MS_ATOMIC_64 #endif #define MS_LONG LONG #define MS_InterlockedExchange InterlockedExchange #define MS_InterlockedCompareExchange InterlockedCompareExchange #define MS_InterlockedIncrement InterlockedIncrement #define MS_InterlockedDecrement InterlockedDecrement #define MS_InterlockedExchange InterlockedExchange #define MS_InterlockedExchangeAdd InterlockedExchangeAdd #if defined ( MS_ATOMIC_64 ) # define MS_LONGLONG LONGLONG # define MS_InterlockedIncrement64 InterlockedIncrement64 # define MS_InterlockedDecrement64 InterlockedDecrement64 # define MS_InterlockedExchange64 InterlockedExchange64 # define MS_InterlockedExchangeAdd64 InterlockedExchangeAdd64 # define MS_InterlockedCompareExchange64 InterlockedCompareExchange64 #endif #include "epicsAtomicMS.h" #include "epicsAtomicDefault.h" #endif /* epicsAtomicOSD_h */ base-7.0.3.1/modules/libcom/src/osi/os/WIN32/epicsGetopt.c0000664000577000060420000000771013557101274021573 0ustar anjaesctl#ifndef _MINGW /* * Copyright (c) 1987, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95"; #endif /* LIBC_SCCS and not lint */ #include #include #include #define epicsExportSharedSymbols #include #define _getprogname() nargv[0] epicsShareDef int opterr = 1, /* if error message should be printed */ optind = 1, /* index into parent argv vector */ optopt; /* character checked for validity */ int optreset; /* reset getopt */ epicsShareDef char *optarg; /* argument associated with option */ #define BADCH (int)'?' #define BADARG (int)':' #define EMSG "" /* * getopt -- * Parse argc/argv argument vector. */ int getopt(nargc, nargv, ostr) int nargc; char * const *nargv; const char *ostr; { static char *place = EMSG; /* option letter processing */ char *oli; /* option letter list index */ if (optreset || !*place) { /* update scanning pointer */ optreset = 0; if (optind >= nargc || *(place = nargv[optind]) != '-') { place = EMSG; return (-1); } if (place[1] && *++place == '-') { /* found "--" */ ++optind; place = EMSG; return (-1); } } /* option letter okay? */ if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, optopt))) { /* * if the user didn't specify '-' as an option, * assume it means -1. */ if (optopt == (int)'-') return (-1); if (!*place) ++optind; if (opterr && *ostr != ':' && optopt != BADCH) (void)fprintf(stderr, "%s: illegal option -- %c\n", _getprogname(), optopt); return (BADCH); } if (*++oli != ':') { /* don't need argument */ optarg = NULL; if (!*place) ++optind; } else { /* need an argument */ if (*place) /* no white space */ optarg = place; else if (nargc <= ++optind) { /* no arg */ place = EMSG; if (*ostr == ':') return (BADARG); if (opterr) (void)fprintf(stderr, "%s: option requires an argument -- %c\n", _getprogname(), optopt); return (BADCH); } else /* white space */ optarg = nargv[optind]; place = EMSG; ++optind; } return (optopt); /* dump back option letter */ } #endif /* !_MINGW */ base-7.0.3.1/modules/libcom/src/osi/os/WIN32/epicsGetopt.h0000664000577000060420000000201613557101274021572 0ustar anjaesctl/*************************************************************************\ * 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) 2002 Berliner Elektronenspeicherringgesellschaft fuer * Synchrotronstrahlung. * 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. \*************************************************************************/ #ifndef _EPICS_GETOPT_H #define _EPICS_GETOPT_H #ifdef _MINGW #include #else /* _MINGW */ #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif epicsShareFunc int getopt(int argc, char * const argv[], const char *optstring); epicsShareExtern char *optarg; epicsShareExtern int optind, opterr, optopt; #ifdef __cplusplus } #endif #endif /* _MINGW */ #endif /* _EPICS_GETOPT_H */ base-7.0.3.1/modules/libcom/src/osi/os/WIN32/epicsMath.h0000664000577000060420000000164613557101274021231 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef epicsMathh #define epicsMathh #include #include #include #ifndef finite #define finite(D) _finite(D) #endif #ifndef isnan #define isnan(D) _isnan(D) #endif #ifndef isinf #define isinf(D) ( !_finite(D) && !_isnan(D) ) #endif #ifdef __cplusplus extern "C" { #endif epicsShareExtern float epicsNAN; epicsShareExtern float epicsINF; #ifdef __cplusplus } #endif #endif /* epicsMathh */ base-7.0.3.1/modules/libcom/src/osi/os/WIN32/epicsSocketConvertErrnoToString.cpp0000664000577000060420000000310413557101274026153 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #define epicsExportSharedSymbols #include "osiSock.h" #include "epicsStdio.h" /* * epicsSocketConvertErrorToString () */ void epicsSocketConvertErrorToString ( char * pBuf, unsigned bufSize, int theSockError ) { if ( bufSize ) { /* * this does not work on systems prior to W2K */ DWORD success = FormatMessage ( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, theSockError, MAKELANGID ( LANG_NEUTRAL, SUBLANG_DEFAULT ), /* Default language */ pBuf, bufSize, NULL ); if ( ! success ) { int status = epicsSnprintf ( pBuf, bufSize, "WINSOCK Error %d", theSockError ); if ( status <= 0 ) { strncpy ( pBuf, "WINSOCK Error", bufSize ); pBuf [bufSize - 0] = '\0'; } } } } /* * epicsSocketConvertErrnoToString () */ void epicsSocketConvertErrnoToString ( char * pBuf, unsigned bufSize ) { epicsSocketConvertErrorToString ( pBuf, bufSize, SOCKERRNO ); } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/epicsTempFile.cpp0000664000577000060420000000353713557101274022401 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeff Hill */ #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsTempFile.h" // // epicsTmpFile // // allow the teporary file directory to be set with the // TMP environment varianble // extern "C" epicsShareFunc FILE * epicsShareAPI epicsTempFile () { char * pName = _tempnam ( "c:\\tmp", "epics" ); if( ! pName ) { return 0; } // We use open followed by fdopen so that the _O_EXCL // flag can be used. This causes detection of a race // condition where two programs end up receiving the // same temporary file name. // // _O_CREAT create if non-existant // _O_EXCL file must not exist // _O_RDWR read and write the file // _O_TEMPORARY delete file on close // _O_BINARY no translation // _O_SHORT_LIVED avoid flush to disk // const int openFlag = _O_CREAT | _O_EXCL | _O_RDWR | _O_SHORT_LIVED | _O_BINARY | _O_TEMPORARY; int fd = open ( pName, openFlag, _S_IWRITE ); FILE * pNewFile = 0; if ( fd >=0 ) { pNewFile = _fdopen ( fd, "w+b" ); } else { printf ( "Temporary file \"%s\" open failed because " "\"%s\"\n", pName, strerror ( errno ) ); } free ( pName ); return pNewFile; } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/forceBadAllocException.cpp0000664000577000060420000000302213557101274024174 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ // // On older version of ms visual c++ operator new // does not throw a bad_alloc exception // when it fails. It simply returns a null pointer. // This behavior is not in conformance with the // ANSI / ISO C++. // #if _MSC_VER > 1000 && _MSC_VER < 1400 #include #include // instruct loader to call this gllobal object // constructor before user global object constructors #pragma warning (disable: 4073) #pragma init_seg(lib) #pragma warning (default: 4073) static int my_new_handler (size_t size) { throw std::bad_alloc(); return 0; } class my_new_handler_obj { public: my_new_handler_obj() { //_old_new_mode = _set_new_mode(1); // cause malloc to throw like new _old_new_handler = _set_new_handler(my_new_handler); } ~my_new_handler_obj() { _set_new_handler(_old_new_handler); //_set_new_mode(_old_new_mode); } private: _PNH _old_new_handler; //int _old_new_mode; }; static my_new_handler_obj _g_new_handler_obj; #endif base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdBackTrace.cpp0000664000577000060420000000132113557101274022162 0ustar anjaesctl/* * Copyright: Stanford University / SLAC National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * Author: Till Straumann , 2014 */ #include #define epicsExportSharedSymbols #include "epicsStackTracePvt.h" int epicsBackTrace(void **buf, int buf_sz) { #ifdef CaptureStackBackTrace /* Docs say that (for some windows versions) the sum of * skipped + captured frames must be less than 63 */ if ( buf_sz >= 63 ) buf_sz = 62; return CaptureStackBackTrace(0, buf_sz, buf, 0); #else /* Older versions of MinGW */ return -1; #endif } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdEnv.c0000664000577000060420000000441313557101274020540 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osdEnv.c */ /* * Author: Eric Norum * Date: May 7, 2001 * * Routines to modify/display environment variables and EPICS parameters * */ #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsStdio.h" #include "errlog.h" #include "cantProceed.h" #include "envDefs.h" #include "osiUnistd.h" #include "epicsFindSymbol.h" #include "iocsh.h" /* * Set the value of an environment variable * Leaks memory, but the assumption is that this routine won't be * called often enough for the leak to be a problem. */ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) { char *cp; iocshEnvClear(name); cp = mallocMustSucceed (strlen (name) + strlen (value) + 2, "epicsEnvSet"); strcpy (cp, name); strcat (cp, "="); strcat (cp, value); if (putenv (cp) < 0) { errPrintf( -1L, __FILE__, __LINE__, "Failed to set environment parameter \"%s\" to \"%s\": %s\n", name, value, strerror (errno)); free (cp); } } /* * Unset an environment variable * Using putenv with a an existing name plus "=" (without value) deletes */ epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name) { iocshEnvClear(name); if (getenv(name) != NULL) epicsEnvSet((char*)name, ""); } /* * Show the value of the specified, or all, environment variables */ epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) { if (name == NULL) { extern char **environ; char **sp; for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) printf ("%s\n", *sp); } else { const char *cp = getenv (name); if (cp == NULL) printf ("%s is not an environment variable.\n", name); else printf ("%s=%s\n", name, cp); } } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdEvent.c0000664000577000060420000000620313557101274021070 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osdEvent.c */ /* * WIN32 version * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 * */ #include #define VC_EXTRALEAN #define STRICT #include #define epicsExportSharedSymbols #include "shareLib.h" #include "epicsEvent.h" typedef struct epicsEventOSD { HANDLE handle; } epicsEventOSD; /* * epicsEventCreate () */ epicsShareFunc epicsEventId epicsEventCreate ( epicsEventInitialState initialState ) { epicsEventOSD *pSem; pSem = malloc ( sizeof ( *pSem ) ); if ( pSem ) { pSem->handle = CreateEvent ( NULL, FALSE, initialState?TRUE:FALSE, NULL ); if ( pSem->handle == 0 ) { free ( pSem ); pSem = 0; } } return pSem; } /* * epicsEventDestroy () */ epicsShareFunc void epicsEventDestroy ( epicsEventId pSem ) { CloseHandle ( pSem->handle ); free ( pSem ); } /* * epicsEventTrigger () */ epicsShareFunc epicsEventStatus epicsEventTrigger ( epicsEventId pSem ) { BOOL status; status = SetEvent ( pSem->handle ); return status ? epicsEventOK : epicsEventError; } /* * epicsEventWait () */ epicsShareFunc epicsEventStatus epicsEventWait ( epicsEventId pSem ) { DWORD status; status = WaitForSingleObject (pSem->handle, INFINITE); if ( status == WAIT_OBJECT_0 ) { return epicsEventOK; } else { return epicsEventError; } } /* * epicsEventWaitWithTimeout () */ epicsShareFunc epicsEventStatus epicsEventWaitWithTimeout ( epicsEventId pSem, double timeOut ) { static const unsigned mSecPerSec = 1000; DWORD status; DWORD tmo; if ( timeOut <= 0.0 ) { tmo = 0u; } else if ( timeOut >= INFINITE / mSecPerSec ) { tmo = INFINITE - 1; } else { tmo = ( DWORD ) ( ( timeOut * mSecPerSec ) + 0.5 ); if ( tmo == 0 ) { tmo = 1; } } status = WaitForSingleObject ( pSem->handle, tmo ); if ( status == WAIT_OBJECT_0 ) { return epicsEventOK; } else if ( status == WAIT_TIMEOUT ) { return epicsEventWaitTimeout; } else { return epicsEventError; } } /* * epicsEventTryWait () */ epicsShareFunc epicsEventStatus epicsEventTryWait ( epicsEventId pSem ) { DWORD status; status = WaitForSingleObject ( pSem->handle, 0 ); if ( status == WAIT_OBJECT_0 ) { return epicsEventOK; } else if ( status == WAIT_TIMEOUT ) { return epicsEventWaitTimeout; } else { return epicsEventError; } } /* * epicsEventShow () */ epicsShareFunc void epicsEventShow ( epicsEventId id, unsigned level ) { } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdEvent.h0000664000577000060420000000130013557101274021066 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* osdEvent.c */ /* * WIN32 version * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ /* osdEvent.h not needed */ base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdFindAddr.c0000664000577000060420000000715413557101274021470 0ustar anjaesctl/*************************************************************************\ * Copyright (C) 2017 Freddie Akeroyd * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #define epicsExportSharedSymbols #include "epicsStackTracePvt.h" #include "epicsStackTrace.h" #include "epicsString.h" #include "epicsStdio.h" #include "gpHash.h" #define MAX_SYM_SIZE 255 /* * we cache a list of previously located symbols, currently just to * avoid a memory leak in allocating sym_p->s_nam and not * to save on a SymFromAddr() call. This was done in case libraries * were dynamically unloaded/reloaded and the cached answer might not * then be correct, but this may be over cautious. */ static struct gphPvt* symbol_table; typedef struct { char* name; void* value; char* file; char* fileAndLine; size_t line; } SymbolData; int epicsFindAddr(void *addr, epicsSymbol *sym_p) { static int first_call = 1; static HANDLE process = NULL; DWORD64 displacement64 = 0; DWORD displacement = 0; SYMBOL_INFO *symbol; IMAGEHLP_LINE64 line; GPHENTRY* symbol_entry; SymbolData* symbol_data; size_t len; if (first_call) { SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS); process = GetCurrentProcess(); SymInitialize(process, NULL, TRUE); gphInitPvt(&symbol_table, 256); /* table size must be a power of 2 in range 256 to 65536 */ first_call = 0; } symbol = (SYMBOL_INFO *)calloc(sizeof(SYMBOL_INFO) + (MAX_SYM_SIZE + 1) * sizeof(char), 1); symbol->MaxNameLen = MAX_SYM_SIZE; symbol->SizeOfStruct = sizeof(SYMBOL_INFO); if (!SymFromAddr(process, (DWORD64)addr, &displacement64, symbol)) { sym_p->s_nam = 0; sym_p->s_val = 0; sym_p->f_nam = 0; free(symbol); return -1; } if ( (symbol_entry = gphFind(symbol_table, symbol->Name, addr)) != NULL ) { symbol_data = (SymbolData*)symbol_entry->userPvt; } else { symbol_entry = gphAdd(symbol_table, symbol->Name, addr); symbol_data = (SymbolData*)calloc(sizeof(SymbolData), 1); symbol_data->name = strdup(symbol->Name); symbol_data->value = (void*)symbol->Address; line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); if (SymGetLineFromAddr64(process, (DWORD64)addr, &displacement, &line)) { symbol_data->file = strdup(line.FileName); symbol_data->line = line.LineNumber; len = strlen(line.FileName) + 32; /* add enough for [line %lu] */ symbol_data->fileAndLine = calloc((len + 1) * sizeof(char), 1); epicsSnprintf(symbol_data->fileAndLine, len, "%s[line %lu]", line.FileName, (unsigned long)line.LineNumber); } symbol_entry->userPvt = symbol_data; } sym_p->s_nam = symbol_data->name; sym_p->s_val = symbol_data->value; sym_p->f_nam = symbol_data->fileAndLine; free(symbol); return 0; } int epicsFindAddrGetFeatures(void) { #if defined(_MINGW) /* 64bit MINGW can lookup DLL public symbols, 32bit cannot - but we need to be a dynamic build to have a DLL to lookup */ # if defined(_WIN64) && defined(EPICS_CALL_DLL) return EPICS_STACKTRACE_DYN_SYMBOLS; # else return 0; /* only able to print addresses */ # endif /* _WIN64 && EPICS_CALL_DLL */ #else return EPICS_STACKTRACE_LCL_SYMBOLS | EPICS_STACKTRACE_GBL_SYMBOLS | EPICS_STACKTRACE_DYN_SYMBOLS; #endif /* _MINGW */ } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdFindSymbol.c0000664000577000060420000000207313557101274022056 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 Dirk Zimoch, PSI * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osi/os/WIN32/osdFindSymbol.c */ #include #define epicsExportSharedSymbols #include "epicsFindSymbol.h" static int epicsLoadErrorCode = 0; epicsShareFunc void * epicsLoadLibrary(const char *name) { HMODULE lib; epicsLoadErrorCode = 0; lib = LoadLibrary(name); if (lib == NULL) { epicsLoadErrorCode = GetLastError(); } return lib; } epicsShareFunc const char *epicsLoadError(void) { static char buffer[100]; FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, epicsLoadErrorCode, 0, buffer, sizeof(buffer)-1, NULL ); return buffer; } epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name) { return GetProcAddress(0, name); } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdMonotonic.c0000664000577000060420000000264613557101274021763 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Michael Davidsaver * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #define epicsExportSharedSymbols #define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "dbDefs.h" #include "errlog.h" #include "epicsTime.h" #include "generalTimeSup.h" static unsigned char osdUsePrefCounter; static epicsUInt64 osdMonotonicResolution; void osdMonotonicInit(void) { LARGE_INTEGER freq, val; if(!QueryPerformanceFrequency(&freq) || !QueryPerformanceCounter(&val)) { double period = 1.0/freq.QuadPart; osdMonotonicResolution = period*1e9; osdUsePrefCounter = 1; } else { osdMonotonicResolution = 1e6; /* 1 ms TODO place holder */ } } epicsUInt64 epicsMonotonicResolution(void) { return osdMonotonicResolution; } epicsUInt64 epicsMonotonicGet(void) { LARGE_INTEGER val; if(osdUsePrefCounter) { if(!QueryPerformanceCounter(&val)) { errMessage(errlogMinor, "Warning: failed to fetch performance counter\n"); return 0; } else return val.QuadPart; } else { epicsUInt64 ret = GetTickCount(); ret *= 1000000; return ret; } } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdMutex.c0000664000577000060420000001102413557101274021106 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* osdMutex.c */ /* * WIN32 version * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 * */ #include #include #define VC_EXTRALEAN #define STRICT /* * Defining this allows the *much* faster critical * section mutex primitive to be used. Unfortunately, * using certain of these functions drops support for W95\W98\WME * unless we specify "delay loading" when we link with the * DLL so that DLL entry points are not resolved until they * are used. The code does have run time switches so * that the more advanced calls are not called unless * they are available in the windows OS, but this feature * isnt going to be very useful unless we specify "delay * loading" when we link with the DLL. * * It appears that the only entry point used here that causes * portability problems with W95\W98\WME is TryEnterCriticalSection. */ #ifndef _WIN32_WINNT # define _WIN32_WINNT 0x0400 #endif #include #define epicsExportSharedSymbols #include "shareLib.h" #include "epicsMutex.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 ) { epicsMutexOSD * pSem; if ( ! weHaveInitialized ) { BOOL status; OSVERSIONINFO osInfo; osInfo.dwOSVersionInfoSize = sizeof ( OSVERSIONINFO ); status = GetVersionEx ( & osInfo ); thisIsNT = status && ( osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ); weHaveInitialized = 1; } 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; } /* * epicsMutexOsdDestroy () */ void epicsMutexOsdDestroy ( epicsMutexOSD * pSem ) { if ( thisIsNT ) { DeleteCriticalSection ( &pSem->os.criticalSection ); } else { CloseHandle ( pSem->os.mutex ); } free ( pSem ); } /* * epicsMutexOsdUnlock () */ void epicsMutexOsdUnlock ( epicsMutexOSD * pSem ) { if ( thisIsNT ) { LeaveCriticalSection ( &pSem->os.criticalSection ); } else { BOOL success = ReleaseMutex ( pSem->os.mutex ); assert ( success ); } } /* * epicsMutexOsdLock () */ epicsMutexLockStatus epicsMutexOsdLock ( epicsMutexOSD * pSem ) { if ( thisIsNT ) { EnterCriticalSection ( &pSem->os.criticalSection ); } else { DWORD status = WaitForSingleObject ( pSem->os.mutex, INFINITE ); if ( status != WAIT_OBJECT_0 ) { return epicsMutexLockError; } } return epicsMutexLockOK; } /* * epicsMutexOsdTryLock () */ epicsMutexLockStatus epicsMutexOsdTryLock ( epicsMutexOSD * pSem ) { 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; } /* * epicsMutexOsdShow () */ void epicsMutexOsdShow ( epicsMutexOSD * pSem, 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 ); } } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdMutex.h0000664000577000060420000000134113557101274021114 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* osdMutex.h */ /* * WIN32 version * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef osdMutexh #define osdMutexh #endif /* osdMutexh */ base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdNetIntf.c0000664000577000060420000001664213557101274021366 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * WIN32 specific initialisation for bsd sockets, * based on Chris Timossi's base/src/ca/windows_depend.c, * and also further additions by Kay Kasemir when this was in * dllmain.cc * * 7-1-97 -joh- * */ /* * ANSI C */ #include #include /* * WIN32 specific */ #define VC_EXTRALEAN #define STRICT #include #include /* * EPICS */ #define epicsExportSharedSymbols #include "osiSock.h" #include "errlog.h" #include "epicsThread.h" #include "epicsVersion.h" static osiSockAddr osiLocalAddrResult; static epicsThreadOnceId osiLocalAddrId = EPICS_THREAD_ONCE_INIT; /* * osiLocalAddr () */ static void osiLocalAddrOnce ( void *raw ) { SOCKET *psocket = raw; osiSockAddr addr; int status; INTERFACE_INFO *pIfinfo; INTERFACE_INFO *pIfinfoList = NULL; unsigned nelem; DWORD numifs; DWORD cbBytesReturned; memset ( (void *) &addr, '\0', sizeof ( addr ) ); addr.sa.sa_family = AF_UNSPEC; /* only valid for winsock 2 and above */ if ( wsaMajorVersion() < 2 ) { goto fail; } nelem = 100; pIfinfoList = (INTERFACE_INFO *) calloc ( nelem, sizeof (INTERFACE_INFO) ); if (!pIfinfoList) { errlogPrintf ("calloc failed\n"); goto fail; } status = WSAIoctl (*psocket, SIO_GET_INTERFACE_LIST, NULL, 0, (LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO), &cbBytesReturned, NULL, NULL); if (status != 0 || cbBytesReturned == 0) { errlogPrintf ("WSAIoctl SIO_GET_INTERFACE_LIST failed %d\n",WSAGetLastError()); goto fail; } numifs = cbBytesReturned / sizeof(INTERFACE_INFO); for (pIfinfo = pIfinfoList; pIfinfo < (pIfinfoList+numifs); pIfinfo++){ /* * dont use interfaces that have been disabled */ if (!(pIfinfo->iiFlags & IFF_UP)) { continue; } /* * dont use the loop back interface */ if (pIfinfo->iiFlags & IFF_LOOPBACK) { continue; } addr.sa = pIfinfo->iiAddress.Address; /* Work around MS Winsock2 bugs */ if (addr.sa.sa_family == 0) { addr.sa.sa_family = AF_INET; } osiLocalAddrResult = addr; free ( pIfinfoList ); return; } errlogPrintf ( "osiLocalAddr(): only loopback found\n"); fail: /* fallback to loopback */ memset ( (void *) &addr, '\0', sizeof ( addr ) ); addr.ia.sin_family = AF_INET; addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK); osiLocalAddrResult = addr; free ( pIfinfoList ); } epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket) { epicsThreadOnce(&osiLocalAddrId, osiLocalAddrOnce, (void*)&socket); return osiLocalAddrResult; } /* * osiSockDiscoverBroadcastAddresses () */ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses (ELLLIST *pList, SOCKET socket, const osiSockAddr *pMatchAddr) { int status; INTERFACE_INFO *pIfinfo; INTERFACE_INFO *pIfinfoList; unsigned nelem; int numifs; DWORD cbBytesReturned; osiSockAddrNode *pNewNode; if ( pMatchAddr->sa.sa_family == AF_INET ) { if ( pMatchAddr->ia.sin_addr.s_addr == htonl (INADDR_LOOPBACK) ) { pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) ); if ( pNewNode == NULL ) { return; } pNewNode->addr.ia.sin_family = AF_INET; pNewNode->addr.ia.sin_port = htons ( 0 ); pNewNode->addr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK); ellAdd ( pList, &pNewNode->node ); return; } } /* only valid for winsock 2 and above */ if (wsaMajorVersion() < 2 ) { fprintf(stderr, "Need to set EPICS_CA_AUTO_ADDR_LIST=NO for winsock 1\n"); return; } nelem = 100; pIfinfoList = (INTERFACE_INFO *) calloc(nelem, sizeof(INTERFACE_INFO)); if(!pIfinfoList){ return; } status = WSAIoctl (socket, SIO_GET_INTERFACE_LIST, NULL, 0, (LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO), &cbBytesReturned, NULL, NULL); if (status != 0 || cbBytesReturned == 0) { fprintf(stderr, "WSAIoctl SIO_GET_INTERFACE_LIST failed %d\n",WSAGetLastError()); free(pIfinfoList); return; } numifs = cbBytesReturned/sizeof(INTERFACE_INFO); for (pIfinfo = pIfinfoList; pIfinfo < (pIfinfoList+numifs); pIfinfo++){ /* * dont bother with interfaces that have been disabled */ if (!(pIfinfo->iiFlags & IFF_UP)) { continue; } if (pIfinfo->iiFlags & IFF_LOOPBACK) { continue; } /* * work around WS2 bug */ if (pIfinfo->iiAddress.Address.sa_family != AF_INET) { if (pIfinfo->iiAddress.Address.sa_family == 0) { pIfinfo->iiAddress.Address.sa_family = AF_INET; } } /* * if it isnt a wildcarded interface then look for * an exact match */ if (pMatchAddr->sa.sa_family != AF_UNSPEC) { if (pIfinfo->iiAddress.Address.sa_family != pMatchAddr->sa.sa_family) { continue; } if (pIfinfo->iiAddress.Address.sa_family != AF_INET) { continue; } if (pMatchAddr->sa.sa_family != AF_INET) { continue; } if (pMatchAddr->ia.sin_addr.s_addr != htonl(INADDR_ANY)) { if (pIfinfo->iiAddress.AddressIn.sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr) { continue; } } } pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode)); if (pNewNode==NULL) { errlogPrintf ("osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n"); return; } if (pIfinfo->iiAddress.Address.sa_family == AF_INET && pIfinfo->iiFlags & IFF_BROADCAST) { const unsigned mask = pIfinfo->iiNetmask.AddressIn.sin_addr.s_addr; const unsigned bcast = pIfinfo->iiBroadcastAddress.AddressIn.sin_addr.s_addr; const unsigned addr = pIfinfo->iiAddress.AddressIn.sin_addr.s_addr; unsigned result = (addr & mask) | (bcast &~mask); pNewNode->addr.ia.sin_family = AF_INET; pNewNode->addr.ia.sin_addr.s_addr = result; pNewNode->addr.ia.sin_port = htons ( 0 ); } else { pNewNode->addr.sa = pIfinfo->iiBroadcastAddress.Address; } /* * LOCK applied externally */ ellAdd (pList, &pNewNode->node); } free (pIfinfoList); } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdPoolStatus.c0000664000577000060420000000140713557101274022125 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #define epicsExportSharedSymbols #include "osiPoolStatus.h" /* * osiSufficentSpaceInPool () * * @@@@@ not implemented @@@@@ * */ epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ) { return 1; } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdPoolStatus.h0000664000577000060420000000114013557101274022124 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #ifdef osdPoolStatush #define osdPoolStatush #endif /* osdPoolStatush */ base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdProcess.c0000664000577000060420000001030713557101274021425 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Operating System Dependent Implementation of osiProcess.h * * Author: Jeff Hill * */ #ifndef _WIN32 #error This source is specific to WIN32 #endif #include #include #define STRICT #include #define epicsExportSharedSymbols #include "osiProcess.h" epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn) { DWORD bufsize; if ( bufSizeIn > 0xffffffff ) { return osiGetUserNameFail; } bufsize = (DWORD) bufSizeIn; if ( ! GetUserName (pBuf, &bufsize) ) { return osiGetUserNameFail; } if ( *pBuf == '\0' ) { return osiGetUserNameFail; } return osiGetUserNameSuccess; } epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess ( const char *pProcessName, const char *pBaseExecutableName ) { BOOL status; STARTUPINFO startupInfo; PROCESS_INFORMATION processInfo; GetStartupInfo ( &startupInfo ); startupInfo.lpReserved = NULL; startupInfo.lpTitle = (char *) pProcessName; startupInfo.dwFlags = STARTF_USESHOWWINDOW; startupInfo.wShowWindow = SW_SHOWMINNOACTIVE; status = CreateProcess ( NULL, /* pointer to name of executable module (not required if command line is specified) */ (char *) pBaseExecutableName, /* pointer to command line string */ NULL, /* pointer to process security attributes */ NULL, /* pointer to thread security attributes */ FALSE, /* handle inheritance flag */ CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS, /* creation flags */ NULL, /* pointer to new environment block (defaults to caller's environement) */ NULL, /* pointer to current directory name (defaults to caller's current directory) */ &startupInfo, /* pointer to STARTUPINFO */ &processInfo /* pointer to PROCESS_INFORMATION */ ); if ( status == 0 ) { DWORD W32status; LPVOID errStrMsgBuf; LPVOID complteMsgBuf; W32status = FormatMessage ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ (LPTSTR) &errStrMsgBuf, 0, NULL ); if ( W32status ) { char *pFmtArgs[6]; pFmtArgs[0] = "Failed to start executable -"; pFmtArgs[1] = (char *) pBaseExecutableName; pFmtArgs[2] = errStrMsgBuf; pFmtArgs[3] = "Changes may be required in your \"path\" environment variable."; W32status = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY | 80, "%1 \"%2\". %3 %4", 0, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ (LPTSTR) &complteMsgBuf, 0, pFmtArgs ); if (W32status) { fprintf (stderr, "%s\n", (char *) complteMsgBuf); LocalFree (complteMsgBuf); } else { fprintf (stderr, "%s\n", (char *) errStrMsgBuf); } /* Free the buffer. */ LocalFree (errStrMsgBuf); } else { fprintf (stderr, "!!WARNING!!\n"); fprintf (stderr, "Unable to locate executable \"%s\".\n", pBaseExecutableName); fprintf (stderr, "You may need to modify your \"path\" environment variable.\n"); } return osiSpawnDetachedProcessFail; } return osiSpawnDetachedProcessSuccess; } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdSignal.cpp0000664000577000060420000000167313557101274021572 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #define epicsExportSharedSymbols #include "epicsSignal.h" /* * NOOP */ epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ) {} epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ) {} epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * /* threadid */ ) {} base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdSock.c0000664000577000060420000001026013557101274020704 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * WIN32 specific initialisation for bsd sockets, * based on Chris Timossi's base/src/ca/windows_depend.c, * and also further additions by Kay Kasemir when this was in * dllmain.cc * * 7-1-97 -joh- * */ /* * ANSI C */ #include #include /* * WIN32 specific */ #define VC_EXTRALEAN #define STRICT #include #define epicsExportSharedSymbols #include "osiSock.h" #include "errlog.h" #include "epicsVersion.h" static unsigned nAttached = 0; static WSADATA WsaData; /* version of winsock */ epicsShareFunc unsigned epicsShareAPI wsaMajorVersion () { return (unsigned) LOBYTE( WsaData.wVersion ); } /* * osiSockAttach() */ epicsShareFunc int epicsShareAPI osiSockAttach() { int status; if (nAttached) { nAttached++; return TRUE; } #if _DEBUG /* for gui applications, setup console for error messages */ if (AllocConsole()) { char title[256]; DWORD titleLength = GetConsoleTitle(title, sizeof(title)); if (titleLength) { titleLength = strlen (title); strncat (title, " " EPICS_VERSION_STRING, sizeof(title)); } else { strncpy(title, EPICS_VERSION_STRING, sizeof(title)); } title[sizeof(title)-1]= '\0'; SetConsoleTitle(title); freopen( "CONOUT$", "a", stderr ); } #endif /* * attach to win sock */ status = WSAStartup(MAKEWORD(/*major*/2,/*minor*/2), &WsaData); if (status != 0) { fprintf(stderr, "Unable to attach to windows sockets version 2. error=%d\n", status); fprintf(stderr, "A Windows Sockets II update for windows 95 is available at\n"); fprintf(stderr, "http://www.microsoft.com/windows95/info/ws2.htm"); return FALSE; } # if defined ( _DEBUG ) && 0 fprintf(stderr, "EPICS attached to winsock version %s\n", WsaData.szDescription); # endif nAttached = 1u; return TRUE; } /* * osiSockRelease() */ epicsShareFunc void epicsShareAPI osiSockRelease() { if (nAttached) { if (--nAttached==0u) { WSACleanup(); # if defined ( _DEBUG ) && 0 fprintf(stderr, "EPICS released winsock version %s\n", WsaData.szDescription); # endif memset (&WsaData, '\0', sizeof(WsaData)); } } } epicsShareFunc SOCKET epicsShareAPI epicsSocketCreate ( int domain, int type, int protocol ) { return socket ( domain, type, protocol ); } epicsShareFunc int epicsShareAPI epicsSocketAccept ( int sock, struct sockaddr * pAddr, osiSocklen_t * addrlen ) { return accept ( sock, pAddr, addrlen ); } epicsShareFunc void epicsShareAPI epicsSocketDestroy ( SOCKET s ) { int status = closesocket ( s ); if ( status < 0 ) { char buf [ 64 ]; epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); errlogPrintf ( "epicsSocketDestroy: failed to " "close a socket because \"%s\"\n", buf ); } } /* * ipAddrToHostName */ epicsShareFunc unsigned epicsShareAPI ipAddrToHostName (const struct in_addr *pAddr, char *pBuf, unsigned bufSize) { struct hostent *ent; if (bufSize<1) { return 0; } ent = gethostbyaddr((char *) pAddr, sizeof (*pAddr), AF_INET); if (ent) { strncpy (pBuf, ent->h_name, bufSize); pBuf[bufSize-1] = '\0'; return strlen (pBuf); } return 0; } /* * hostToIPAddr () */ epicsShareFunc int epicsShareAPI hostToIPAddr (const char *pHostName, struct in_addr *pIPA) { struct hostent *phe; phe = gethostbyname (pHostName); if (phe && phe->h_addr_list[0]) { if (phe->h_addrtype==AF_INET && phe->h_length<=sizeof(struct in_addr)) { struct in_addr *pInAddrIn = (struct in_addr *) phe->h_addr_list[0]; *pIPA = *pInAddrIn; /* * success */ return 0; } } /* * return indicating an error */ return -1; } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdSock.h0000664000577000060420000000425013557101274020713 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef osdSockH #define osdSockH #include #include /* * winsock2.h changes the structure alignment to 4 if * WIN32 isnt set which can be a source of confusion */ #ifndef WIN32 # define WIN32 #endif #include #include #define SOCKERRNO WSAGetLastError() #define socket_ioctl(A,B,C) ioctlsocket(A,B,C) typedef u_long FAR osiSockIoctl_t; typedef int osiSocklen_t; typedef BOOL osiSockOptMcastLoop_t; typedef DWORD osiSockOptMcastTTL_t; #ifndef SHUT_RD # define SHUT_RD SD_RECEIVE #endif #ifndef SHUT_WR # define SHUT_WR SD_SEND #endif #ifndef SHUT_RDWR # define SHUT_RDWR SD_BOTH #endif #define MAXHOSTNAMELEN 75 #define IPPORT_USERRESERVED 5000U #define SOCK_EWOULDBLOCK WSAEWOULDBLOCK #define SOCK_ENOBUFS WSAENOBUFS #define SOCK_ECONNRESET WSAECONNRESET #define SOCK_ETIMEDOUT WSAETIMEDOUT #define SOCK_EACCES WSAEACCES #define SOCK_EADDRINUSE WSAEADDRINUSE #define SOCK_EADDRNOTAVAIL WSAEADDRNOTAVAIL #define SOCK_ECONNREFUSED WSAECONNREFUSED #define SOCK_ECONNABORTED WSAECONNABORTED #define SOCK_EINPROGRESS WSAEINPROGRESS #define SOCK_EISCONN WSAEISCONN #define SOCK_EALREADY WSAEALREADY #define SOCK_EINVAL WSAEINVAL #define SOCK_EINTR WSAEINTR #define SOCK_EPIPE EPIPE #define SOCK_EMFILE WSAEMFILE #define SOCK_SHUTDOWN WSAESHUTDOWN #define SOCK_ENOTSOCK WSAENOTSOCK #define SOCK_EBADF WSAENOTSOCK /* * Under WIN32, FD_SETSIZE is the max. number of sockets, * not the max. fd value that you use in select(). * * Therefore, it is difficult to detemine if any given * fd can be used with FD_SET(), FD_CLR(), and FD_ISSET(). */ #define FD_IN_FDSET(FD) (1) epicsShareFunc unsigned epicsShareAPI wsaMajorVersion (); #endif /*osdSockH*/ base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdSockAddrReuse.cpp0000664000577000060420000000271113557101274023045 0ustar anjaesctl /*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeff Hill */ #define epicsExportSharedSymbols #include "osiSock.h" #include "errlog.h" /* * Note: WINSOCK appears to assign a different functionality for * SO_REUSEADDR compared to other OS. With WINSOCK SO_REUSEADDR indicates * that simultaneously servers can bind to the same TCP port on the same host! * Also, servers are always enabled to reuse a port immediately after * they exit ( even if SO_REUSEADDR isnt set ). */ epicsShareFunc void epicsShareAPI epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) { } epicsShareFunc void epicsShareAPI epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) { int yes = true; int status; status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, (char *) & yes, sizeof ( yes ) ); if ( status < 0 ) { errlogPrintf ( "epicsSocketEnablePortUseForDatagramFanout: " "unable to set SO_REUSEADDR?\n"); } } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdSockUnsentCount.c0000664000577000060420000000162413557101274023116 0ustar anjaesctl/*************************************************************************\ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #define epicsExportSharedSymbols #define EPICS_PRIVATE_API #include "osiSock.h" #include /* * epicsSocketUnsentCount () * See https://docs.microsoft.com/en-us/windows/win32/api/mstcpip/ns-mstcpip-tcp_info_v0 */ int epicsSocketUnsentCount(SOCKET sock) { #ifdef SIO_TCP_INFO /* Windows 10 Version 1703 / Server 2016 */ DWORD infoVersion = 0, bytesReturned; TCP_INFO_v0 tcpInfo; int status; if ((status = WSAIoctl(sock, SIO_TCP_INFO, &infoVersion, sizeof(infoVersion), &tcpInfo, sizeof(tcpInfo), &bytesReturned, NULL, NULL)) == 0) return tcpInfo.BytesInFlight; #endif return -1; } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdStdio.c0000664000577000060420000000241313557101274021070 0ustar anjaesctl/* osdStdio.c */ /*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #ifndef _MSC_VER /* Older versions of MinGW omitted this prototype from stdio.h */ _CRTIMP int __cdecl __MINGW_NOTHROW _vscprintf (const char*, va_list); #endif #define epicsExportSharedSymbols #include "epicsStdio.h" int epicsShareAPI epicsVsnprintf(char *str, size_t len, const char *fmt, va_list ap) { int retval = _vsnprintf(str, len, fmt, ap); int needed = _vscprintf(fmt, ap); if ((int) len < needed + 1) { str[len - 1] = 0; return needed; } return retval; } int epicsShareAPI epicsSnprintf (char *str, size_t len, const char *fmt, ...) { int rtn; va_list pvar; va_start (pvar, fmt); rtn = epicsVsnprintf (str, len, fmt, pvar); va_end (pvar); return (rtn); } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdStrtod.h0000664000577000060420000000154713557101274021301 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * This header fragment is intended to be included as part of epicsString.h */ #ifdef __cplusplus extern "C" { #endif /* * epicsStrtod() for systems with broken strtod() routine */ epicsShareFunc double epicsStrtod(const char *str, char **endp); /* * Microsoft apparently added strto[u]ll() in VS2013 * Older compilers have these equivalents though */ #if !defined(_MINGW) && (_MSC_VER < 1800) # define strtoll _strtoi64 # define strtoull _strtoui64 #endif #ifdef __cplusplus } #endif base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdThread.c0000664000577000060420000007657013557101274021234 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeff Hill */ #include #include #include #include #define VC_EXTRALEAN #define STRICT #ifndef _WIN32_WINNT # define _WIN32_WINNT 0x400 /* No support for W95 */ #endif #include #include /* for _endthread() etc */ #define epicsExportSharedSymbols #include "epicsStdio.h" #include "shareLib.h" #include "epicsThread.h" #include "cantProceed.h" #include "epicsAssert.h" #include "ellLib.h" #include "epicsExit.h" #include "epicsAtomic.h" epicsShareFunc void osdThreadHooksRun(epicsThreadId id); void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName ); typedef struct win32ThreadGlobal { CRITICAL_SECTION mutex; ELLLIST threadList; DWORD tlsIndexThreadLibraryEPICS; } win32ThreadGlobal; typedef struct epicsThreadOSD { ELLNODE node; int refcnt; HANDLE handle; EPICSTHREADFUNC funptr; void * parm; char * pName; DWORD id; unsigned epicsPriority; char isSuspended; char joinable; } win32ThreadParam; typedef struct epicsThreadPrivateOSD { DWORD key; } epicsThreadPrivateOSD; #ifndef STACK_SIZE_PARAM_IS_A_RESERVATION # define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 #endif #define osdOrdinaryPriorityStateCount 5u static const int osdOrdinaryPriorityList [osdOrdinaryPriorityStateCount] = { THREAD_PRIORITY_LOWEST, /* -2 on >= W2K ??? on W95 */ THREAD_PRIORITY_BELOW_NORMAL, /* -1 on >= W2K ??? on W95 */ THREAD_PRIORITY_NORMAL, /* 0 on >= W2K ??? on W95 */ THREAD_PRIORITY_ABOVE_NORMAL, /* 1 on >= W2K ??? on W95 */ THREAD_PRIORITY_HIGHEST /* 2 on >= W2K ??? on W95 */ }; # define osdRealtimePriorityStateCount 14u static const int osdRealtimePriorityList [osdRealtimePriorityStateCount] = { -7, /* allowed on >= W2k, but no #define supplied */ -6, /* allowed on >= W2k, but no #define supplied */ -5, /* allowed on >= W2k, but no #define supplied */ -4, /* allowed on >= W2k, but no #define supplied */ -3, /* allowed on >= W2k, but no #define supplied */ THREAD_PRIORITY_LOWEST, /* -2 on >= W2K ??? on W95 */ THREAD_PRIORITY_BELOW_NORMAL, /* -1 on >= W2K ??? on W95 */ THREAD_PRIORITY_NORMAL, /* 0 on >= W2K ??? on W95 */ THREAD_PRIORITY_ABOVE_NORMAL, /* 1 on >= W2K ??? on W95 */ THREAD_PRIORITY_HIGHEST, /* 2 on >= W2K ??? on W95 */ 3, /* allowed on >= W2k, but no #define supplied */ 4, /* allowed on >= W2k, but no #define supplied */ 5, /* allowed on >= W2k, but no #define supplied */ 6 /* allowed on >= W2k, but no #define supplied */ }; #if defined(EPICS_BUILD_DLL) BOOL WINAPI DllMain ( HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved ) { static DWORD dllHandleIndex; HMODULE dllHandle = 0; BOOL success = TRUE; switch ( dwReason ) { case DLL_PROCESS_ATTACH: dllHandleIndex = TlsAlloc (); if ( dllHandleIndex == TLS_OUT_OF_INDEXES ) { success = FALSE; } break; case DLL_PROCESS_DETACH: success = TlsFree ( dllHandleIndex ); break; case DLL_THREAD_ATTACH: /* * Dont allow user's explicitly calling FreeLibrary for Com.dll to yank * the carpet out from under EPICS threads that are still using Com.dll */ #if _WIN32_WINNT >= 0x0501 /* * Only in WXP * Thats a shame because this is probably much faster */ success = GetModuleHandleEx ( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, ( LPCTSTR ) DllMain, & dllHandle ); #else { char name[256]; DWORD nChar = GetModuleFileName ( hModule, name, sizeof ( name ) ); if ( nChar && nChar < sizeof ( name ) ) { dllHandle = LoadLibrary ( name ); if ( ! dllHandle ) { success = FALSE; } } else { success = FALSE; } } #endif if ( success ) { success = TlsSetValue ( dllHandleIndex, dllHandle ); } break; case DLL_THREAD_DETACH: /* * Thread is exiting, release Com.dll. I am assuming that windows is * smart enough to postpone the unload until this function returns. */ dllHandle = TlsGetValue ( dllHandleIndex ); if ( dllHandle ) { success = FreeLibrary ( dllHandle ); } break; } return success; } #endif /* * fetchWin32ThreadGlobal () * Search for "Synchronization and Multiprocessor Issues" in ms doc * to understand why this is necessary and why this works on smp systems. */ static win32ThreadGlobal * fetchWin32ThreadGlobal ( void ) { static win32ThreadGlobal * pWin32ThreadGlobal = 0; static LONG initStarted = 0; static LONG initCompleted = 0; LONG started; LONG done; done = InterlockedCompareExchange ( & initCompleted, 0, 0 ); if ( done ) { return pWin32ThreadGlobal; } started = InterlockedCompareExchange ( & initStarted, 0, 1 ); if ( started ) { unsigned tries = 0u; while ( ! InterlockedCompareExchange ( & initCompleted, 0, 0 ) ) { /* * I am not fond of busy loops, but since this will * collide very infrequently and this is the lowest * level init then perhaps this is ok */ Sleep ( 1 ); if ( tries++ > 1000 ) { return 0; } } return pWin32ThreadGlobal; } pWin32ThreadGlobal = ( win32ThreadGlobal * ) calloc ( 1, sizeof ( * pWin32ThreadGlobal ) ); if ( ! pWin32ThreadGlobal ) { InterlockedExchange ( & initStarted, 0 ); return 0; } InitializeCriticalSection ( & pWin32ThreadGlobal->mutex ); ellInit ( & pWin32ThreadGlobal->threadList ); pWin32ThreadGlobal->tlsIndexThreadLibraryEPICS = TlsAlloc(); if ( pWin32ThreadGlobal->tlsIndexThreadLibraryEPICS == 0xFFFFFFFF ) { DeleteCriticalSection ( & pWin32ThreadGlobal->mutex ); free ( pWin32ThreadGlobal ); pWin32ThreadGlobal = 0; return 0; } InterlockedExchange ( & initCompleted, 1 ); return pWin32ThreadGlobal; } static void epicsParmCleanupWIN32 ( win32ThreadParam * pParm ) { win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); if ( ! pGbl ) { fprintf ( stderr, "epicsParmCleanupWIN32: unable to find ctx\n" ); return; } if ( pParm ) { int cnt = epicsAtomicDecrIntT(&pParm->refcnt); if(cnt > 0) return; assert(cnt==0); /* fprintf ( stderr, "thread %s is exiting\n", pParm->pName ); */ EnterCriticalSection ( & pGbl->mutex ); ellDelete ( & pGbl->threadList, & pParm->node ); LeaveCriticalSection ( & pGbl->mutex ); CloseHandle ( pParm->handle ); free ( pParm ); } } /* * epicsThreadExitMain () */ epicsShareFunc void epicsShareAPI epicsThreadExitMain ( void ) { _endthread (); } /* * osdPriorityMagFromPriorityOSI () */ static unsigned osdPriorityMagFromPriorityOSI ( unsigned osiPriority, unsigned priorityStateCount ) { unsigned magnitude; /* optimizer will remove this one if epicsThreadPriorityMin is zero */ /* and osiPriority is unsigned */ if ( osiPriority < epicsThreadPriorityMin ) { osiPriority = epicsThreadPriorityMin; } if ( osiPriority > epicsThreadPriorityMax ) { osiPriority = epicsThreadPriorityMax; } magnitude = osiPriority * priorityStateCount; magnitude /= ( epicsThreadPriorityMax - epicsThreadPriorityMin ) + 1; return magnitude; } epicsShareFunc void epicsThreadRealtimeLock(void) {} /* * epicsThreadGetOsdPriorityValue () */ static int epicsThreadGetOsdPriorityValue ( unsigned osiPriority ) { const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () ); const int * pStateList; unsigned stateCount; unsigned magnitude; if ( priorityClass == REALTIME_PRIORITY_CLASS ) { stateCount = osdRealtimePriorityStateCount; pStateList = osdRealtimePriorityList; } else { stateCount = osdOrdinaryPriorityStateCount; pStateList = osdOrdinaryPriorityList; } magnitude = osdPriorityMagFromPriorityOSI ( osiPriority, stateCount ); return pStateList[magnitude]; } /* * osiPriorityMagFromMagnitueOSD () */ static unsigned osiPriorityMagFromMagnitueOSD ( unsigned magnitude, unsigned osdPriorityStateCount ) { unsigned osiPriority; osiPriority = magnitude * ( epicsThreadPriorityMax - epicsThreadPriorityMin ); osiPriority /= osdPriorityStateCount - 1u; osiPriority += epicsThreadPriorityMin; return osiPriority; } /* * epicsThreadGetOsiPriorityValue () */ static unsigned epicsThreadGetOsiPriorityValue ( int osdPriority ) { const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () ); const int * pStateList; unsigned stateCount; unsigned magnitude; if ( priorityClass == REALTIME_PRIORITY_CLASS ) { stateCount = osdRealtimePriorityStateCount; pStateList = osdRealtimePriorityList; } else { stateCount = osdOrdinaryPriorityStateCount; pStateList = osdOrdinaryPriorityList; } for ( magnitude = 0u; magnitude < stateCount; magnitude++ ) { if ( osdPriority == pStateList[magnitude] ) { break; } } if ( magnitude >= stateCount ) { fprintf ( stderr, "Unrecognized WIN32 thread priority level %d.\n", osdPriority ); fprintf ( stderr, "Mapping to EPICS thread priority level epicsThreadPriorityMin.\n" ); return epicsThreadPriorityMin; } return osiPriorityMagFromMagnitueOSD ( magnitude, stateCount ); } /* * epicsThreadLowestPriorityLevelAbove () */ epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadLowestPriorityLevelAbove ( unsigned int priority, unsigned * pPriorityJustAbove ) { const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () ); epicsThreadBooleanStatus status; unsigned stateCount; unsigned magnitude; if ( priorityClass == REALTIME_PRIORITY_CLASS ) { stateCount = osdRealtimePriorityStateCount; } else { stateCount = osdOrdinaryPriorityStateCount; } magnitude = osdPriorityMagFromPriorityOSI ( priority, stateCount ); if ( magnitude < ( stateCount - 1 ) ) { *pPriorityJustAbove = osiPriorityMagFromMagnitueOSD ( magnitude + 1u, stateCount ); status = epicsThreadBooleanStatusSuccess; } else { status = epicsThreadBooleanStatusFail; } return status; } /* * epicsThreadHighestPriorityLevelBelow () */ epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadHighestPriorityLevelBelow ( unsigned int priority, unsigned * pPriorityJustBelow ) { const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () ); epicsThreadBooleanStatus status; unsigned stateCount; unsigned magnitude; if ( priorityClass == REALTIME_PRIORITY_CLASS ) { stateCount = osdRealtimePriorityStateCount; } else { stateCount = osdOrdinaryPriorityStateCount; } magnitude = osdPriorityMagFromPriorityOSI ( priority, stateCount ); if ( magnitude > 0u ) { *pPriorityJustBelow = osiPriorityMagFromMagnitueOSD ( magnitude - 1u, stateCount ); status = epicsThreadBooleanStatusSuccess; } else { status = epicsThreadBooleanStatusFail; } return status; } /* * epicsThreadGetStackSize () */ epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize ( epicsThreadStackSizeClass stackSizeClass ) { #define STACK_SIZE(f) (f * 0x10000 * sizeof(void *)) static const unsigned stackSizeTable[epicsThreadStackBig+1] = { STACK_SIZE(1), STACK_SIZE(2), STACK_SIZE(4) }; if (stackSizeClassepicsThreadStackBig) { fprintf ( stderr, "epicsThreadGetStackSize illegal argument (too large)"); return stackSizeTable[epicsThreadStackBig]; } return stackSizeTable[stackSizeClass]; } /* * epicsWin32ThreadEntry() */ static unsigned WINAPI epicsWin32ThreadEntry ( LPVOID lpParameter ) { win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); win32ThreadParam * pParm = ( win32ThreadParam * ) lpParameter; unsigned retStat = 0u; BOOL success; if ( pGbl ) { setThreadName ( pParm->id, pParm->pName ); success = TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, pParm ); if ( success ) { osdThreadHooksRun ( ( epicsThreadId ) pParm ); /* printf ( "starting thread %d\n", pParm->id ); */ ( *pParm->funptr ) ( pParm->parm ); /* printf ( "terminating thread %d\n", pParm->id ); */ retStat = 1; } else { fprintf ( stderr, "epicsWin32ThreadEntry: unable to set private\n" ); } } else { fprintf ( stderr, "epicsWin32ThreadEntry: unable to find ctx\n" ); } epicsExitCallAtThreadExits (); /* * CAUTION: !!!! the thread id might continue to be used after this thread exits !!!! */ TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, (void*)0xdeadbeef ); epicsParmCleanupWIN32 ( pParm ); return retStat; /* this indirectly closes the thread handle */ } static win32ThreadParam * epicsThreadParmCreate ( const char *pName ) { win32ThreadParam *pParmWIN32; pParmWIN32 = calloc ( 1, sizeof ( *pParmWIN32 ) + strlen ( pName ) + 1 ); if ( pParmWIN32 ) { pParmWIN32->pName = (char *) ( pParmWIN32 + 1 ); strcpy ( pParmWIN32->pName, pName ); pParmWIN32->isSuspended = 0; epicsAtomicIncrIntT(&pParmWIN32->refcnt); } return pParmWIN32; } static win32ThreadParam * epicsThreadImplicitCreate ( void ) { win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); DWORD id = GetCurrentThreadId (); win32ThreadParam * pParm; char name[64]; HANDLE handle; BOOL success; if ( ! pGbl ) { fprintf ( stderr, "epicsThreadImplicitCreate: unable to find ctx\n" ); return 0; } success = DuplicateHandle ( GetCurrentProcess (), GetCurrentThread (), GetCurrentProcess (), & handle, 0, FALSE, DUPLICATE_SAME_ACCESS ); if ( ! success ) { return 0; } { unsigned long idForFormat = id; sprintf ( name, "win%lx", idForFormat ); } pParm = epicsThreadParmCreate ( name ); if ( pParm ) { int win32ThreadPriority; pParm->handle = handle; pParm->id = id; win32ThreadPriority = GetThreadPriority ( pParm->handle ); assert ( win32ThreadPriority != THREAD_PRIORITY_ERROR_RETURN ); pParm->epicsPriority = epicsThreadGetOsiPriorityValue ( win32ThreadPriority ); success = TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, pParm ); if ( ! success ) { epicsParmCleanupWIN32 ( pParm ); pParm = 0; } else { EnterCriticalSection ( & pGbl->mutex ); ellAdd ( & pGbl->threadList, & pParm->node ); LeaveCriticalSection ( & pGbl->mutex ); } } return pParm; } /* * epicsThreadCreate () */ epicsThreadId epicsThreadCreateOpt ( const char * pName, EPICSTHREADFUNC pFunc, void * pParm, const epicsThreadOpts *opts ) { win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); win32ThreadParam * pParmWIN32; unsigned int stackSize; int osdPriority; DWORD wstat; BOOL bstat; if ( ! pGbl ) { return NULL; } if (!opts) { static const epicsThreadOpts opts_default = EPICS_THREAD_OPTS_INIT; opts = &opts_default; } stackSize = opts->stackSize; if (stackSize <= epicsThreadStackBig) stackSize = epicsThreadGetStackSize(stackSize); pParmWIN32 = epicsThreadParmCreate ( pName ); if ( pParmWIN32 == 0 ) { return ( epicsThreadId ) pParmWIN32; } pParmWIN32->funptr = pFunc; pParmWIN32->parm = pParm; pParmWIN32->epicsPriority = opts->priority; { unsigned threadId; pParmWIN32->handle = (HANDLE) _beginthreadex ( 0, stackSize, epicsWin32ThreadEntry, pParmWIN32, CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, & threadId ); if ( pParmWIN32->handle == 0 ) { free ( pParmWIN32 ); return NULL; } /* weird win32 interface threadId parameter inconsistency */ pParmWIN32->id = ( DWORD ) threadId ; } osdPriority = epicsThreadGetOsdPriorityValue (opts->priority); bstat = SetThreadPriority ( pParmWIN32->handle, osdPriority ); if (!bstat) { CloseHandle ( pParmWIN32->handle ); free ( pParmWIN32 ); return NULL; } EnterCriticalSection ( & pGbl->mutex ); ellAdd ( & pGbl->threadList, & pParmWIN32->node ); LeaveCriticalSection ( & pGbl->mutex ); wstat = ResumeThread ( pParmWIN32->handle ); if (wstat==0xFFFFFFFF) { EnterCriticalSection ( & pGbl->mutex ); ellDelete ( & pGbl->threadList, & pParmWIN32->node ); LeaveCriticalSection ( & pGbl->mutex ); CloseHandle ( pParmWIN32->handle ); free ( pParmWIN32 ); return NULL; } if(opts->joinable) { pParmWIN32->joinable = 1; epicsAtomicIncrIntT(&pParmWIN32->refcnt); } return ( epicsThreadId ) pParmWIN32; } void epicsThreadMustJoin(epicsThreadId id) { win32ThreadParam * pParmWIN32 = id; if(!id) { /* no-op */ } else if(!pParmWIN32->joinable) { if(epicsThreadGetIdSelf()==id) { fprintf(stderr, "Warning: %s thread self-join of unjoinable\n", pParmWIN32->pName); } else { /* try to error nicely, however in all likelyhood 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", pParmWIN32->pName); } return; } else if(epicsThreadGetIdSelf() != id) { DWORD status = WaitForSingleObject(pParmWIN32->handle, INFINITE); if(status != WAIT_OBJECT_0) { /* TODO: signal error? */ } pParmWIN32->joinable = 0; epicsParmCleanupWIN32(pParmWIN32); } else { /* join self silently does nothing */ pParmWIN32->joinable = 0; epicsParmCleanupWIN32(pParmWIN32); } } /* * epicsThreadSuspendSelf () */ epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf () { win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); win32ThreadParam * pParm; DWORD stat; assert ( pGbl ); pParm = ( win32ThreadParam * ) TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); if ( ! pParm ) { pParm = epicsThreadImplicitCreate (); } if ( pParm ) { EnterCriticalSection ( & pGbl->mutex ); pParm->isSuspended = 1; LeaveCriticalSection ( & pGbl->mutex ); } stat = SuspendThread ( GetCurrentThread () ); assert ( stat != 0xFFFFFFFF ); } /* * epicsThreadResume () */ epicsShareFunc void epicsShareAPI epicsThreadResume ( epicsThreadId id ) { win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); win32ThreadParam * pParm = ( win32ThreadParam * ) id; DWORD stat; assert ( pGbl ); EnterCriticalSection ( & pGbl->mutex ); stat = ResumeThread ( pParm->handle ); pParm->isSuspended = 0; LeaveCriticalSection ( & pGbl->mutex ); assert ( stat != 0xFFFFFFFF ); } /* * epicsThreadGetPriority () */ epicsShareFunc unsigned epicsShareAPI epicsThreadGetPriority (epicsThreadId id) { win32ThreadParam * pParm = ( win32ThreadParam * ) id; return pParm->epicsPriority; } /* * epicsThreadGetPrioritySelf () */ epicsShareFunc unsigned epicsShareAPI epicsThreadGetPrioritySelf () { win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); win32ThreadParam * pParm; assert ( pGbl ); pParm = ( win32ThreadParam * ) TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); if ( ! pParm ) { pParm = epicsThreadImplicitCreate (); } if ( pParm ) { return pParm->epicsPriority; } else { int win32ThreadPriority = GetThreadPriority ( GetCurrentThread () ); assert ( win32ThreadPriority != THREAD_PRIORITY_ERROR_RETURN ); return epicsThreadGetOsiPriorityValue ( win32ThreadPriority ); } } /* * epicsThreadSetPriority () */ epicsShareFunc void epicsShareAPI epicsThreadSetPriority ( epicsThreadId id, unsigned priority ) { win32ThreadParam * pParm = ( win32ThreadParam * ) id; BOOL stat = SetThreadPriority ( pParm->handle, epicsThreadGetOsdPriorityValue (priority) ); assert (stat); } /* * epicsThreadIsEqual () */ epicsShareFunc int epicsShareAPI epicsThreadIsEqual ( epicsThreadId id1, epicsThreadId id2 ) { win32ThreadParam * pParm1 = ( win32ThreadParam * ) id1; win32ThreadParam * pParm2 = ( win32ThreadParam * ) id2; return ( id1 == id2 && pParm1->id == pParm2->id ); } /* * epicsThreadIsSuspended () */ epicsShareFunc int epicsShareAPI epicsThreadIsSuspended ( epicsThreadId id ) { win32ThreadParam *pParm = ( win32ThreadParam * ) id; DWORD exitCode; BOOL stat; stat = GetExitCodeThread ( pParm->handle, & exitCode ); if ( stat ) { if ( exitCode != STILL_ACTIVE ) { return 1; } else { return pParm->isSuspended; } } else { return 1; } } /* * epicsThreadSleep () */ epicsShareFunc void epicsShareAPI epicsThreadSleep ( double seconds ) { static const unsigned mSecPerSec = 1000; DWORD milliSecDelay; if ( seconds > 0.0 ) { seconds *= mSecPerSec; seconds += 0.99999999; /* 8 9s here is optimal */ milliSecDelay = ( seconds >= INFINITE ) ? INFINITE - 1 : ( DWORD ) seconds; } else { /* seconds <= 0 or NAN */ milliSecDelay = 0u; } Sleep ( milliSecDelay ); } /* * epicsThreadSleepQuantum () */ double epicsShareAPI epicsThreadSleepQuantum () { /* * Its worth noting here that the sleep quantum on windows can * mysteriously get better. I eventually tracked this down to * codes that call timeBeginPeriod(1). Calling timeBeginPeriod() * specifying a better timer resolution also increases the interrupt * load. This appears to be related to java applet activity. * The function timeGetDevCaps can tell us the range of periods * that can be specified to timeBeginPeriod, but alas there * appears to be no way to find out what the value of the global * minimum of all timeBeginPeriod calls for all processes is. */ static const double secPerTick = 100e-9; DWORD adjustment; DWORD delay; BOOL disabled; BOOL success; success = GetSystemTimeAdjustment ( & adjustment, & delay, & disabled ); if ( success ) { return delay * secPerTick; } else { return 0.0; } } /* * epicsThreadGetIdSelf () */ epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetIdSelf (void) { win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); win32ThreadParam * pParm; assert ( pGbl ); pParm = ( win32ThreadParam * ) TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); if ( ! pParm ) { pParm = epicsThreadImplicitCreate (); assert ( pParm ); /* very dangerous to allow non-unique thread id into use */ } return ( epicsThreadId ) pParm; } epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetId ( const char * pName ) { win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); win32ThreadParam * pParm; if ( ! pGbl ) { return 0; } EnterCriticalSection ( & pGbl->mutex ); for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList ); pParm; pParm = ( win32ThreadParam * ) ellNext ( & pParm->node ) ) { if ( pParm->pName ) { if ( strcmp ( pParm->pName, pName ) == 0 ) { break; } } } LeaveCriticalSection ( & pGbl->mutex ); /* !!!! warning - the thread parm could vanish at any time !!!! */ return ( epicsThreadId ) pParm; } /* * epicsThreadGetNameSelf () */ epicsShareFunc const char * epicsShareAPI epicsThreadGetNameSelf (void) { win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); win32ThreadParam * pParm; if ( ! pGbl ) { return "thread library not initialized"; } pParm = ( win32ThreadParam * ) TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); if ( ! pParm ) { pParm = epicsThreadImplicitCreate (); } if ( pParm ) { if ( pParm->pName ) { return pParm->pName; } } return "anonymous"; } /* * epicsThreadGetName () */ epicsShareFunc void epicsShareAPI epicsThreadGetName ( epicsThreadId id, char * pName, size_t size ) { win32ThreadParam * pParm = ( win32ThreadParam * ) id; if ( size ) { size_t sizeMinusOne = size-1; strncpy ( pName, pParm->pName, sizeMinusOne ); pName [sizeMinusOne] = '\0'; } } /* * epics_GetThreadPriorityAsString () */ static const char * epics_GetThreadPriorityAsString ( HANDLE thr ) { const char * pPriName = "?????"; switch ( GetThreadPriority ( thr ) ) { case THREAD_PRIORITY_TIME_CRITICAL: pPriName = "tm-crit"; break; case THREAD_PRIORITY_HIGHEST: pPriName = "high"; break; case THREAD_PRIORITY_ABOVE_NORMAL: pPriName = "normal+"; break; case THREAD_PRIORITY_NORMAL: pPriName = "normal"; break; case THREAD_PRIORITY_BELOW_NORMAL: pPriName = "normal-"; break; case THREAD_PRIORITY_LOWEST: pPriName = "low"; break; case THREAD_PRIORITY_IDLE: pPriName = "idle"; break; } return pPriName; } /* * epicsThreadShowInfo () */ static void epicsThreadShowInfo ( epicsThreadId id, unsigned level ) { win32ThreadParam * pParm = ( win32ThreadParam * ) id; if ( pParm ) { unsigned long idForFormat = pParm->id; fprintf ( epicsGetStdout(), "%-15s %-8p %-8lx %-9u %-9s %-7s", pParm->pName, (void *) pParm, idForFormat, pParm->epicsPriority, epics_GetThreadPriorityAsString ( pParm->handle ), epicsThreadIsSuspended ( id ) ? "suspend" : "ok" ); if ( level ) { fprintf (epicsGetStdout(), " %-8p %-8p ", (void *) pParm->handle, (void *) pParm->parm ); } } else { fprintf (epicsGetStdout(), "NAME EPICS-ID WIN32-ID EPICS-PRI WIN32-PRI STATE " ); if ( level ) { fprintf (epicsGetStdout(), " HANDLE FUNCTION PARAMETER" ); } } fprintf (epicsGetStdout(),"\n" ); } /* * epicsThreadMap () */ epicsShareFunc void epicsThreadMap ( EPICS_THREAD_HOOK_ROUTINE func ) { win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); win32ThreadParam * pParm; if ( ! pGbl ) { return; } EnterCriticalSection ( & pGbl->mutex ); for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList ); pParm; pParm = ( win32ThreadParam * ) ellNext ( & pParm->node ) ) { func ( ( epicsThreadId ) pParm ); } LeaveCriticalSection ( & pGbl->mutex ); } /* * epicsThreadShowAll () */ epicsShareFunc void epicsShareAPI epicsThreadShowAll ( unsigned level ) { win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); win32ThreadParam * pParm; if ( ! pGbl ) { return; } EnterCriticalSection ( & pGbl->mutex ); epicsThreadShowInfo ( 0, level ); for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList ); pParm; pParm = ( win32ThreadParam * ) ellNext ( & pParm->node ) ) { epicsThreadShowInfo ( ( epicsThreadId ) pParm, level ); } LeaveCriticalSection ( & pGbl->mutex ); } /* * epicsThreadShow () */ epicsShareFunc void epicsShareAPI epicsThreadShow ( epicsThreadId id, unsigned level ) { epicsThreadShowInfo ( 0, level ); epicsThreadShowInfo ( id, level ); } /* * epicsThreadOnce () */ epicsShareFunc void epicsShareAPI epicsThreadOnce ( epicsThreadOnceId *id, void (*func)(void *), void *arg ) { static struct epicsThreadOSD threadOnceComplete; #define EPICS_THREAD_ONCE_DONE & threadOnceComplete win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); assert ( pGbl ); EnterCriticalSection ( & pGbl->mutex ); if ( *id != EPICS_THREAD_ONCE_DONE ) { if ( *id == EPICS_THREAD_ONCE_INIT ) { /* first call */ *id = epicsThreadGetIdSelf(); /* mark active */ LeaveCriticalSection ( & pGbl->mutex ); func ( arg ); EnterCriticalSection ( & pGbl->mutex ); *id = EPICS_THREAD_ONCE_DONE; /* mark done */ } else if ( *id == epicsThreadGetIdSelf() ) { LeaveCriticalSection ( & pGbl->mutex ); cantProceed( "Recursive epicsThreadOnce() initialization\n" ); } else while ( *id != EPICS_THREAD_ONCE_DONE ) { /* Another thread is in the above func(arg) call. */ LeaveCriticalSection ( & pGbl->mutex ); epicsThreadSleep ( epicsThreadSleepQuantum() ); EnterCriticalSection ( & pGbl->mutex ); } } LeaveCriticalSection ( & pGbl->mutex ); } /* * epicsThreadPrivateCreate () */ epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate () { epicsThreadPrivateOSD *p = ( epicsThreadPrivateOSD * ) malloc ( sizeof ( *p ) ); if ( p ) { p->key = TlsAlloc (); if ( p->key == 0xFFFFFFFF ) { free ( p ); p = 0; } } return p; } /* * epicsThreadPrivateDelete () */ epicsShareFunc void epicsShareAPI epicsThreadPrivateDelete ( epicsThreadPrivateId p ) { BOOL stat = TlsFree ( p->key ); assert ( stat ); free ( p ); } /* * epicsThreadPrivateSet () */ epicsShareFunc void epicsShareAPI epicsThreadPrivateSet ( epicsThreadPrivateId pPvt, void *pVal ) { BOOL stat = TlsSetValue ( pPvt->key, (void *) pVal ); assert (stat); } /* * epicsThreadPrivateGet () */ epicsShareFunc void * epicsShareAPI epicsThreadPrivateGet ( epicsThreadPrivateId pPvt ) { return ( void * ) TlsGetValue ( pPvt->key ); } /* * epicsThreadGetCPUs () */ epicsShareFunc int epicsThreadGetCPUs ( void ) { SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); if (sysinfo.dwNumberOfProcessors > 0) return sysinfo.dwNumberOfProcessors; return 1; } #ifdef TEST_CODES void testPriorityMapping () { unsigned i; for (i=epicsThreadPriorityMin; i #include #include #include // // WIN32 // #define VC_EXTRALEAN #define STRICT #include #include // // EPICS // #define epicsExportSharedSymbols #define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTime.h" #include "generalTimeSup.h" #include "epicsTimer.h" #include "errlog.h" #include "epicsAssert.h" #include "epicsThread.h" #if defined ( DEBUG ) # define debugPrintf(argsInParen) ::printf argsInParen #else # define debugPrintf(argsInParen) #endif extern "C" void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName ); extern "C" int osdTimeGetCurrent ( epicsTimeStamp *pDest ); // for mingw #if !defined ( MAXLONGLONG ) # define MAXLONGLONG 0x7fffffffffffffffLL #endif #if !defined ( MINLONGLONG ) # define MINLONGLONG ~0x7fffffffffffffffLL #endif #ifndef STACK_SIZE_PARAM_IS_A_RESERVATION # define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 #endif static const LONGLONG epicsEpochInFileTime = 0x01b41e2a18d64000LL; static unsigned __stdcall _pllThreadEntry ( void * pCurrentTimeIn ); class currentTime { public: currentTime (); ~currentTime (); void getCurrentTime ( epicsTimeStamp & dest ); void startPLL (); private: CRITICAL_SECTION mutex; LONGLONG lastPerfCounter; LONGLONG perfCounterFreq; LONGLONG epicsTimeLast; // nano-sec since the EPICS epoch LONGLONG perfCounterFreqPLL; LONGLONG lastPerfCounterPLL; LONGLONG lastFileTimePLL; HANDLE threadHandle; unsigned threadId; bool perfCtrPresent; bool threadShutdownCmd; bool threadHasExited; void updatePLL (); static const int pllDelay; /* integer seconds */ // cant be static because of diff btw __stdcall and __cdecl friend unsigned __stdcall _pllThreadEntry ( void * pCurrentTimeIn ); }; const int currentTime :: pllDelay = 5; static currentTime * pCurrentTime = 0; static const LONGLONG FILE_TIME_TICKS_PER_SEC = 10000000; static const LONGLONG EPICS_TIME_TICKS_PER_SEC = 1000000000; static const LONGLONG ET_TICKS_PER_FT_TICK = EPICS_TIME_TICKS_PER_SEC / FILE_TIME_TICKS_PER_SEC; // // Start and register time provider // static int timeRegister(void) { pCurrentTime = new currentTime (); generalTimeCurrentTpRegister("PerfCounter", 150, osdTimeGetCurrent); pCurrentTime->startPLL (); osdMonotonicInit(); return 1; } static int done = timeRegister(); // // osdTimeGetCurrent () // int osdTimeGetCurrent ( epicsTimeStamp *pDest ) { assert ( pCurrentTime ); pCurrentTime->getCurrentTime ( *pDest ); return epicsTimeOK; } // synthesize a reentrant gmtime on WIN32 int epicsShareAPI epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM ) { struct tm * pRet = gmtime ( pAnsiTime ); if ( pRet ) { *pTM = *pRet; return epicsTimeOK; } else { return errno; } } // synthesize a reentrant localtime on WIN32 int epicsShareAPI epicsTime_localtime ( const time_t * pAnsiTime, struct tm * pTM ) { struct tm * pRet = localtime ( pAnsiTime ); if ( pRet ) { *pTM = *pRet; return epicsTimeOK; } else { return errno; } } currentTime::currentTime () : lastPerfCounter ( 0 ), perfCounterFreq ( 0 ), epicsTimeLast ( 0 ), perfCounterFreqPLL ( 0 ), lastPerfCounterPLL ( 0 ), lastFileTimePLL ( 0 ), threadHandle ( 0 ), threadId ( 0 ), perfCtrPresent ( false ), threadShutdownCmd ( false ), threadHasExited ( false ) { InitializeCriticalSection ( & this->mutex ); // avoid interruptions by briefly becoming a time critical thread int originalPriority = GetThreadPriority ( GetCurrentThread () ); SetThreadPriority ( GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL ); FILETIME ft; GetSystemTimeAsFileTime ( & ft ); LARGE_INTEGER tmp; QueryPerformanceCounter ( & tmp ); this->lastPerfCounter = tmp.QuadPart; // if no high resolution counters then default to low res file time if ( QueryPerformanceFrequency ( & tmp ) ) { this->perfCounterFreq = tmp.QuadPart; this->perfCtrPresent = true; } SetThreadPriority ( GetCurrentThread (), originalPriority ); LARGE_INTEGER liFileTime; liFileTime.LowPart = ft.dwLowDateTime; liFileTime.HighPart = ft.dwHighDateTime; if ( liFileTime.QuadPart >= epicsEpochInFileTime ) { // the windows file time has a maximum resolution of 100 nS // and a nominal resolution of 10 mS - 16 mS this->epicsTimeLast = ( liFileTime.QuadPart - epicsEpochInFileTime ) * ET_TICKS_PER_FT_TICK; } else { errlogPrintf ( "win32 osdTime.cpp init detected questionable " "system date prior to EPICS epoch, epics time will not advance\n" ); this->epicsTimeLast = 0; } this->perfCounterFreqPLL = this->perfCounterFreq; this->lastPerfCounterPLL = this->lastPerfCounter; this->lastFileTimePLL = liFileTime.QuadPart; } void currentTime :: startPLL () { // create frequency estimation thread when needed if ( this->perfCtrPresent && ! this->threadHandle ) { this->threadHandle = (HANDLE) _beginthreadex ( 0, 4096, _pllThreadEntry, this, CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, & this->threadId ); assert ( this->threadHandle ); BOOL bstat = SetThreadPriority ( this->threadHandle, THREAD_PRIORITY_HIGHEST ); assert ( bstat ); DWORD wstat = ResumeThread ( this->threadHandle ); assert ( wstat != 0xFFFFFFFF ); } } currentTime::~currentTime () { EnterCriticalSection ( & this->mutex ); this->threadShutdownCmd = true; while ( ! this->threadHasExited ) { LeaveCriticalSection ( & this->mutex ); Sleep ( 250 /* mS */ ); EnterCriticalSection ( & this->mutex ); } LeaveCriticalSection ( & this->mutex ); DeleteCriticalSection ( & this->mutex ); } void currentTime::getCurrentTime ( epicsTimeStamp & dest ) { if ( this->perfCtrPresent ) { EnterCriticalSection ( & this->mutex ); LARGE_INTEGER curPerfCounter; QueryPerformanceCounter ( & curPerfCounter ); LONGLONG offset; if ( curPerfCounter.QuadPart >= this->lastPerfCounter ) { offset = curPerfCounter.QuadPart - this->lastPerfCounter; } else { // // must have been a timer roll-over event // // It takes 9.223372036855e+18/perf_freq sec to roll over this // counter. This is currently about 245118 years using the perf // counter freq value on my system (1193182). Nevertheless, I // have code for this situation because the performance // counter resolution will more than likely improve over time. // offset = ( MAXLONGLONG - this->lastPerfCounter ) + ( curPerfCounter.QuadPart - MINLONGLONG ) + 1; } if ( offset < MAXLONGLONG / EPICS_TIME_TICKS_PER_SEC ) { offset *= EPICS_TIME_TICKS_PER_SEC; offset /= this->perfCounterFreq; } else { double fpOffset = static_cast < double > ( offset ); fpOffset *= EPICS_TIME_TICKS_PER_SEC; fpOffset /= static_cast < double > ( this->perfCounterFreq ); offset = static_cast < LONGLONG > ( fpOffset ); } LONGLONG epicsTimeCurrent = this->epicsTimeLast + offset; if ( this->epicsTimeLast > epicsTimeCurrent ) { double diff = static_cast < double > ( this->epicsTimeLast - epicsTimeCurrent ) / EPICS_TIME_TICKS_PER_SEC; errlogPrintf ( "currentTime::getCurrentTime(): %f sec " "time discontinuity detected\n", diff ); } this->epicsTimeLast = epicsTimeCurrent; this->lastPerfCounter = curPerfCounter.QuadPart; LeaveCriticalSection ( & this->mutex ); dest.secPastEpoch = static_cast < epicsUInt32 > ( epicsTimeCurrent / EPICS_TIME_TICKS_PER_SEC ); dest.nsec = static_cast < epicsUInt32 > ( epicsTimeCurrent % EPICS_TIME_TICKS_PER_SEC ); } else { // if high resolution performance counters are not supported then // fall back to low res file time FILETIME ft; GetSystemTimeAsFileTime ( & ft ); dest = epicsTime ( ft ); } } // // Maintain corrected version of the performance counter's frequency using // a phase locked loop. This approach is similar to NTP's. // void currentTime :: updatePLL () { EnterCriticalSection ( & this->mutex ); // avoid interruptions by briefly becoming a time critical thread LARGE_INTEGER curFileTime; LARGE_INTEGER curPerfCounter; { int originalPriority = GetThreadPriority ( GetCurrentThread () ); SetThreadPriority ( GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL ); FILETIME ft; GetSystemTimeAsFileTime ( & ft ); QueryPerformanceCounter ( & curPerfCounter ); SetThreadPriority ( GetCurrentThread (), originalPriority ); curFileTime.LowPart = ft.dwLowDateTime; curFileTime.HighPart = ft.dwHighDateTime; } LONGLONG perfCounterDiff; if ( curPerfCounter.QuadPart >= this->lastPerfCounterPLL ) { perfCounterDiff = curPerfCounter.QuadPart - this->lastPerfCounterPLL; } else { // // must have been a timer roll-over event // // It takes 9.223372036855e+18/perf_freq sec to roll over this // counter. This is currently about 245118 years using the perf // counter freq value on my system (1193182). Nevertheless, I // have code for this situation because the performance // counter resolution will more than likely improve over time. // perfCounterDiff = ( MAXLONGLONG - this->lastPerfCounterPLL ) + ( curPerfCounter.QuadPart - MINLONGLONG ) + 1; } this->lastPerfCounterPLL = curPerfCounter.QuadPart; LONGLONG fileTimeDiff = curFileTime.QuadPart - this->lastFileTimePLL; this->lastFileTimePLL = curFileTime.QuadPart; // discard glitches if ( fileTimeDiff <= 0 ) { LeaveCriticalSection( & this->mutex ); debugPrintf ( ( "currentTime: file time difference in PLL was less than zero\n" ) ); return; } LONGLONG freq = ( FILE_TIME_TICKS_PER_SEC * perfCounterDiff ) / fileTimeDiff; LONGLONG delta = freq - this->perfCounterFreqPLL; // discard glitches LONGLONG bound = this->perfCounterFreqPLL >> 10; if ( delta < -bound || delta > bound ) { LeaveCriticalSection( & this->mutex ); debugPrintf ( ( "freq est out of bounds l=%d e=%d h=%d\n", static_cast < int > ( -bound ), static_cast < int > ( delta ), static_cast < int > ( bound ) ) ); return; } // update feedback loop estimating the performance counter's frequency LONGLONG feedback = delta >> 8; this->perfCounterFreqPLL += feedback; LONGLONG perfCounterDiffSinceLastFetch; if ( curPerfCounter.QuadPart >= this->lastPerfCounter ) { perfCounterDiffSinceLastFetch = curPerfCounter.QuadPart - this->lastPerfCounter; } else { // // must have been a timer roll-over event // // It takes 9.223372036855e+18/perf_freq sec to roll over this // counter. This is currently about 245118 years using the perf // counter freq value on my system (1193182). Nevertheless, I // have code for this situation because the performance // counter resolution will more than likely improve over time. // perfCounterDiffSinceLastFetch = ( MAXLONGLONG - this->lastPerfCounter ) + ( curPerfCounter.QuadPart - MINLONGLONG ) + 1; } // discard performance counter delay measurement glitches { const LONGLONG expectedDly = this->perfCounterFreq * pllDelay; const LONGLONG bnd = expectedDly / 4; if ( perfCounterDiffSinceLastFetch <= 0 || perfCounterDiffSinceLastFetch >= expectedDly + bnd ) { LeaveCriticalSection( & this->mutex ); debugPrintf ( ( "perf ctr measured delay out of bounds m=%d max=%d\n", static_cast < int > ( perfCounterDiffSinceLastFetch ), static_cast < int > ( expectedDly + bnd ) ) ); return; } } // Update the current estimated time. this->epicsTimeLast += ( perfCounterDiffSinceLastFetch * EPICS_TIME_TICKS_PER_SEC ) / this->perfCounterFreq; this->lastPerfCounter = curPerfCounter.QuadPart; LONGLONG epicsTimeFromCurrentFileTime; { static bool firstMessageWasSent = false; if ( curFileTime.QuadPart >= epicsEpochInFileTime ) { epicsTimeFromCurrentFileTime = ( curFileTime.QuadPart - epicsEpochInFileTime ) * ET_TICKS_PER_FT_TICK; firstMessageWasSent = false; } else { /* * if the system time jumps to before the EPICS epoch * then latch to the EPICS epoch printing only one * warning message the first time that the issue is * detected */ if ( ! firstMessageWasSent ) { errlogPrintf ( "win32 osdTime.cpp time PLL update detected questionable " "system date prior to EPICS epoch, epics time will not advance\n" ); firstMessageWasSent = true; } epicsTimeFromCurrentFileTime = 0; } } delta = epicsTimeFromCurrentFileTime - this->epicsTimeLast; if ( delta > EPICS_TIME_TICKS_PER_SEC || delta < -EPICS_TIME_TICKS_PER_SEC ) { // When there is an abrupt shift in the current computed time vs // the time derived from the current file time then someone has // probably adjusted the real time clock and the best reaction // is to just assume the new time base this->epicsTimeLast = epicsTimeFromCurrentFileTime; this->perfCounterFreq = this->perfCounterFreqPLL; debugPrintf ( ( "currentTime: did someone adjust the date?\n" ) ); } else { // update the effective performance counter frequency that will bring // our calculated time base in syncy with file time one second from now. this->perfCounterFreq = ( EPICS_TIME_TICKS_PER_SEC * this->perfCounterFreqPLL ) / ( delta + EPICS_TIME_TICKS_PER_SEC ); // limit effective performance counter frequency rate of change LONGLONG lowLimit = this->perfCounterFreqPLL - bound; if ( this->perfCounterFreq < lowLimit ) { debugPrintf ( ( "currentTime: out of bounds low freq excursion %d\n", static_cast ( lowLimit - this->perfCounterFreq ) ) ); this->perfCounterFreq = lowLimit; } else { LONGLONG highLimit = this->perfCounterFreqPLL + bound; if ( this->perfCounterFreq > highLimit ) { debugPrintf ( ( "currentTime: out of bounds high freq excursion %d\n", static_cast ( this->perfCounterFreq - highLimit ) ) ); this->perfCounterFreq = highLimit; } } # if defined ( DEBUG ) LARGE_INTEGER sysFreq; QueryPerformanceFrequency ( & sysFreq ); double freqDiff = static_cast ( this->perfCounterFreq - sysFreq.QuadPart ); freqDiff /= sysFreq.QuadPart; freqDiff *= 100.0; double freqEstDiff = static_cast ( this->perfCounterFreqPLL - sysFreq.QuadPart ); freqEstDiff /= sysFreq.QuadPart; freqEstDiff *= 100.0; debugPrintf ( ( "currentTime: freq delta %f %% freq est " "delta %f %% time delta %f sec\n", freqDiff, freqEstDiff, static_cast < double > ( delta ) / EPICS_TIME_TICKS_PER_SEC ) ); # endif } LeaveCriticalSection ( & this->mutex ); } static unsigned __stdcall _pllThreadEntry ( void * pCurrentTimeIn ) { currentTime * pCT = reinterpret_cast < currentTime * > ( pCurrentTimeIn ); setThreadName ( pCT->threadId, "EPICS Time PLL" ); while ( ! pCT->threadShutdownCmd ) { Sleep ( currentTime :: pllDelay * 1000 /* mS */ ); pCT->updatePLL (); } EnterCriticalSection ( & pCT->mutex ); pCT->threadHasExited = true; LeaveCriticalSection ( & pCT->mutex ); return 1; } epicsTime::operator FILETIME () const { LARGE_INTEGER ftTicks; ftTicks.QuadPart = ( this->secPastEpoch * FILE_TIME_TICKS_PER_SEC ) + ( this->nSec / ET_TICKS_PER_FT_TICK ); ftTicks.QuadPart += epicsEpochInFileTime; FILETIME ts; ts.dwLowDateTime = ftTicks.LowPart; ts.dwHighDateTime = ftTicks.HighPart; return ts; } epicsTime::epicsTime ( const FILETIME & ts ) { LARGE_INTEGER lift; lift.LowPart = ts.dwLowDateTime; lift.HighPart = ts.dwHighDateTime; if ( lift.QuadPart > epicsEpochInFileTime ) { LONGLONG fileTimeTicksSinceEpochEPICS = lift.QuadPart - epicsEpochInFileTime; this->secPastEpoch = static_cast < epicsUInt32 > ( fileTimeTicksSinceEpochEPICS / FILE_TIME_TICKS_PER_SEC ); this->nSec = static_cast < epicsUInt32 > ( ( fileTimeTicksSinceEpochEPICS % FILE_TIME_TICKS_PER_SEC ) * ET_TICKS_PER_FT_TICK ); } else { this->secPastEpoch = 0; this->nSec = 0; } } epicsTime & epicsTime::operator = ( const FILETIME & rhs ) { *this = epicsTime ( rhs ); return *this; } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdTime.h0000664000577000060420000000220013557101274020703 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeff Hill */ #ifndef INC_osdTime_H #define INC_osdTime_H /* MinGW only has a snippet time.h not protected against multiple inclusion */ #if defined(__struct_timespec_defined) #define _TIMESPEC_DEFINED 1 #endif #if ! defined(_MINGW) || ! defined(_TIMESPEC_DEFINED) # if _MSC_VER >= 1900 # include # else #define __struct_timespec_defined 1 #define _TIMESPEC_DEFINED 1 struct timespec { time_t tv_sec; /* seconds since some epoch */ long tv_nsec; /* nanoseconds within the second */ }; # endif /* _MSC_VER */ #endif /* ! defined(_MINGW) || ! defined(_TIMESPEC_DEFINED) */ #endif /* ifndef INC_osdTime_H */ base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdWireConfig.h0000664000577000060420000000052713557101274022053 0ustar anjaesctl/* * WIN32 version of * osdWireConfig.h * * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef osdWireConfig_h #define osdWireConfig_h /* for now, assume that win32 runs only on generic little endian */ #define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE #define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER #endif /* ifdef osdWireConfig_h */ base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osdgetexec.c0000664000577000060420000000166413557101274021441 0ustar anjaesctl #include #include #include #define epicsExportSharedSymbols #include char *epicsGetExecName(void) { size_t max = 128; char *ret = NULL; DWORD n; while(1) { char *temp = realloc(ret, max); if(!temp) { /* we treat alloc failure as terminal */ free(ret); ret = NULL; break; } ret = temp; n = GetModuleFileName(NULL, ret, max); if(n == 0) { free(ret); ret = NULL; break; } else if(n < max) { ret[n] = '\0'; break; } max += 64; } return ret; } char *epicsGetExecDir(void) { char *ret = epicsGetExecName(); if(ret) { char *sep = strrchr(ret, '\\'); if(sep) { /* nil the charactor after the / */ sep[1] = '\0'; } } return ret; } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osiFileName.h0000664000577000060420000000216713557101274021506 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * osiFileName.h * Author: Jeff Hill * */ #ifndef osiFileNameH #define osiFileNameH #include #ifdef __cplusplus extern "C" { #endif #define OSI_PATH_LIST_SEPARATOR ";" #define OSI_PATH_SEPARATOR "\\" /** Return the absolute path of the current executable. @returns NULL or the path. Caller must free() */ epicsShareFunc char *epicsGetExecName(void); /** Return the absolute path of the directory containing the current executable. @returns NULL or the path. Caller must free() */ epicsShareFunc char *epicsGetExecDir(void); #ifdef __cplusplus } #endif #endif /* osiFileNameH */ base-7.0.3.1/modules/libcom/src/osi/os/WIN32/osiUnistd.h0000664000577000060420000000116013557101274021264 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include base-7.0.3.1/modules/libcom/src/osi/os/WIN32/setThreadName.cpp0000664000577000060420000000323713557101274022371 0ustar anjaesctl /*************************************************************************\ * 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. * 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. \*************************************************************************/ #define VC_EXTRALEAN #define STRICT #if _WIN64 # define _WIN32_WINNT 0x400 /* defining this drops support for W95 */ #endif #include /* * this was copied directly from an example in visual c++ 7 documentation, * It uses visual C++ specific keywords for exception handling, but is * probably only useful when using the visual c++ or later debugger. * * Usage: setThreadName (-1, "MainThread"); */ extern "C" void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName ) { #if _MSC_VER >= 1300 && defined ( _DEBUG ) typedef struct tagTHREADNAME_INFO { DWORD dwType; // must be 0x1000 LPCSTR szName; // pointer to name (in user addr space) DWORD dwThreadID; // thread ID (-1=caller thread) DWORD dwFlags; // reserved for future use, must be zero } THREADNAME_INFO; THREADNAME_INFO info; info.dwType = 0x1000; info.szName = szThreadName; info.dwThreadID = dwThreadID; info.dwFlags = 0; __try { RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (const ULONG_PTR*)&info ); } __except(EXCEPTION_CONTINUE_EXECUTION) { } #endif } base-7.0.3.1/modules/libcom/src/osi/os/WIN32/systemCallIntMech.cpp0000664000577000060420000000141513557101274023231 0ustar anjaesctl /*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeff Hill */ #define epicsExportSharedSymbols #include "osiSock.h" enum epicsSocketSystemCallInterruptMechanismQueryInfo epicsSocketSystemCallInterruptMechanismQuery () { return esscimqi_socketCloseRequired; } base-7.0.3.1/modules/libcom/src/osi/os/cygwin32/devLibVMEOSD.c0000664000577000060420000000076513557101274022276 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #define epicsExportSharedSymbols #include "devLibVME.h" epicsShareDef devLibVME *pdevLibVME = NULL; base-7.0.3.1/modules/libcom/src/osi/os/cygwin32/osdSock.h0000664000577000060420000000373613557101274021566 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * cygwin32 specific include */ #ifndef osdSockH #define osdSockH #include #include #include /* for MAXHOSTNAMELEN */ #include #include #include #include #include #include #include #include /* close() and others */ typedef int SOCKET; #define INVALID_SOCKET (-1) #define SOCKERRNO errno #define socket_ioctl(A,B,C) ioctl(A,B,C) typedef int osiSockIoctl_t; typedef int osiSocklen_t; typedef int osiSockOptMcastLoop_t; typedef int osiSockOptMcastTTL_t; #define FD_IN_FDSET(FD) ((FD)=0) #ifndef SHUT_RD #define SHUT_RD 0 #endif #ifndef SHUT_WR #define SHUT_WR 1 #endif #ifndef SHUT_RDWR # define SHUT_RDWR 2 #endif #define SOCK_EWOULDBLOCK EWOULDBLOCK #define SOCK_ENOBUFS ENOBUFS #define SOCK_ECONNRESET ECONNRESET #define SOCK_ETIMEDOUT ETIMEDOUT #define SOCK_EACCES EACCES #define SOCK_EADDRINUSE EADDRINUSE #define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL #define SOCK_ECONNREFUSED ECONNREFUSED #define SOCK_ECONNABORTED ECONNABORTED #define SOCK_EINPROGRESS EINPROGRESS #define SOCK_EISCONN EISCONN #define SOCK_EALREADY EALREADY #define SOCK_EINVAL EINVAL #define SOCK_EINTR EINTR #define SOCK_EPIPE EPIPE #define SOCK_EMFILE EMFILE #define SOCK_SHUTDOWN ESHUTDOWN #define SOCK_ENOTSOCK ENOTSOCK #define SOCK_EBADF EBADF #define ifreq_size(pifreq) (sizeof(pifreq->ifr_name)) #endif /*osdSockH*/ base-7.0.3.1/modules/libcom/src/osi/os/cygwin32/osdSockAddrReuse.cpp0000664000577000060420000000271113557101274023710 0ustar anjaesctl /*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeff Hill */ #define epicsExportSharedSymbols #include "osiSock.h" #include "errlog.h" /* * Note: WINSOCK appears to assign a different functionality for * SO_REUSEADDR compared to other OS. With WINSOCK SO_REUSEADDR indicates * that simultaneously servers can bind to the same TCP port on the same host! * Also, servers are always enabled to reuse a port immediately after * they exit ( even if SO_REUSEADDR isnt set ). */ epicsShareFunc void epicsShareAPI epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) { } epicsShareFunc void epicsShareAPI epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) { int yes = true; int status; status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, (char *) & yes, sizeof ( yes ) ); if ( status < 0 ) { errlogPrintf ( "epicsSocketEnablePortUseForDatagramFanout: " "unable to set SO_REUSEADDR?\n"); } } base-7.0.3.1/modules/libcom/src/osi/os/cygwin32/osdStrtod.h0000664000577000060420000000121513557101274022134 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * This header fragment is intended to be included as part of epicsString.h */ #ifdef __cplusplus extern "C" { #endif /* * epicsStrtod() for systems with broken strtod() routine */ epicsShareFunc double epicsStrtod(const char *str, char **endp); #ifdef __cplusplus } #endif base-7.0.3.1/modules/libcom/src/osi/os/cygwin32/osiFileName.h0000664000577000060420000000216413557101274022346 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * osiFileName.h * Author: Jeff Hill */ #ifndef osiFileNameH #define osiFileNameH #include #ifdef __cplusplus extern "C" { #endif #define OSI_PATH_LIST_SEPARATOR ";" #define OSI_PATH_SEPARATOR "\\" /** Return the absolute path of the current executable. @returns NULL or the path. Caller must free() */ epicsShareFunc char *epicsGetExecName(void); /** Return the absolute path of the directory containing the current executable. @returns NULL or the path. Caller must free() */ epicsShareFunc char *epicsGetExecDir(void); #ifdef __cplusplus } #endif #endif /* osiFileNameH */ base-7.0.3.1/modules/libcom/src/osi/os/cygwin32/systemCallIntMech.cpp0000664000577000060420000000173013557101274024074 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeff Hill */ #include #define epicsExportSharedSymbols #include "osiSock.h" enum epicsSocketSystemCallInterruptMechanismQueryInfo epicsSocketSystemCallInterruptMechanismQuery () { #if (CYGWIN_VERSION_DLL_MAJOR == 1007) && (CYGWIN_VERSION_DLL_MINOR < 15) // Behaviour changed in early Cygwin 1.7 releases, reverted later. return esscimqi_socketCloseRequired; #else return esscimqi_socketBothShutdownRequired; #endif } base-7.0.3.1/modules/libcom/src/osi/os/default/devLibVMEOSD.c0000664000577000060420000000107213557101274022245 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 The University of Chicago, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #define epicsExportSharedSymbols #include "devLibVME.h" /* This file must contain no definitions other than the following: */ epicsShareDef devLibVME *pdevLibVME; base-7.0.3.1/modules/libcom/src/osi/os/default/epicsAtomicOSD.cpp0000664000577000060420000000000013557101274023256 0ustar anjaesctlbase-7.0.3.1/modules/libcom/src/osi/os/default/epicsGetopt.h0000664000577000060420000000133213557101274022414 0ustar anjaesctl/*************************************************************************\ * 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) 2002 Berliner Elektronenspeicherringgesellschaft fuer * Synchrotronstrahlung. * 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. \*************************************************************************/ #ifndef _EPICS_GETOPT_H #define _EPICS_GETOPT_H #include #endif /* _EPICS_GETOPT_H */ base-7.0.3.1/modules/libcom/src/osi/os/default/epicsMMIO.h0000664000577000060420000000003313557101274021710 0ustar anjaesctl #include "epicsMMIODef.h" base-7.0.3.1/modules/libcom/src/osi/os/default/epicsMMIODef.h0000664000577000060420000001473013557101274022340 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver */ #ifndef EPICSMMIODEF_H #define EPICSMMIODEF_H #include #include #include #include #ifdef __cplusplus # ifndef INLINE # define INLINE inline # endif #endif /** @ingroup mmio *@{ */ /** @brief Read a single byte. */ static EPICS_ALWAYS_INLINE epicsUInt8 ioread8(volatile void* addr) { return *(volatile epicsUInt8*)(addr); } /** @brief Write a single byte. */ static EPICS_ALWAYS_INLINE void iowrite8(volatile void* addr, epicsUInt8 val) { *(volatile epicsUInt8*)(addr) = val; } /** @brief Read two bytes in host order. * Not byte swapping */ static EPICS_ALWAYS_INLINE epicsUInt16 nat_ioread16(volatile void* addr) { return *(volatile epicsUInt16*)(addr); } /** @brief Write two byte in host order. * Not byte swapping */ static EPICS_ALWAYS_INLINE void nat_iowrite16(volatile void* addr, epicsUInt16 val) { *(volatile epicsUInt16*)(addr) = val; } /** @brief Read four bytes in host order. * Not byte swapping */ static EPICS_ALWAYS_INLINE epicsUInt32 nat_ioread32(volatile void* addr) { return *(volatile epicsUInt32*)(addr); } /** @brief Write four byte in host order. * Not byte swapping */ static EPICS_ALWAYS_INLINE void nat_iowrite32(volatile void* addr, epicsUInt32 val) { *(volatile epicsUInt32*)(addr) = val; } #if EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG /** @ingroup mmio *@{ */ static EPICS_ALWAYS_INLINE epicsUInt16 bswap16(epicsUInt16 value) { return (((epicsUInt16)(value) & 0x00ff) << 8) | (((epicsUInt16)(value) & 0xff00) >> 8); } static EPICS_ALWAYS_INLINE epicsUInt32 bswap32(epicsUInt32 value) { return (((epicsUInt32)(value) & 0x000000ff) << 24) | (((epicsUInt32)(value) & 0x0000ff00) << 8) | (((epicsUInt32)(value) & 0x00ff0000) >> 8) | (((epicsUInt32)(value) & 0xff000000) >> 24); } # define be_ioread16(A) nat_ioread16(A) # define be_ioread32(A) nat_ioread32(A) # define be_iowrite16(A,D) nat_iowrite16(A,D) # define be_iowrite32(A,D) nat_iowrite32(A,D) # define le_ioread16(A) bswap16(nat_ioread16(A)) # define le_ioread32(A) bswap32(nat_ioread32(A)) # define le_iowrite16(A,D) nat_iowrite16(A,bswap16(D)) # define le_iowrite32(A,D) nat_iowrite32(A,bswap32(D)) /** @} */ #elif EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE /* Get hton[sl] declarations: */ #include /** @ingroup mmio *@{ */ /* hton* is optimized or a builtin for most compilers * so use it if possible */ #define bswap16(v) htons(v) #define bswap32(v) htonl(v) # define be_ioread16(A) bswap16(nat_ioread16(A)) # define be_ioread32(A) bswap32(nat_ioread32(A)) # define be_iowrite16(A,D) nat_iowrite16(A,bswap16(D)) # define be_iowrite32(A,D) nat_iowrite32(A,bswap32(D)) # define le_ioread16(A) nat_ioread16(A) # define le_ioread32(A) nat_ioread32(A) # define le_iowrite16(A,D) nat_iowrite16(A,D) # define le_iowrite32(A,D) nat_iowrite32(A,D) /** @} */ #else # error Unable to determine native byte order #endif /** @def bswap16 * @brief Unconditional two byte swap */ /** @def bswap32 * @brief Unconditional four byte swap */ /** @def be_ioread16 * @brief Read two byte in big endian order. */ /** @def be_iowrite16 * @brief Write two byte in big endian order. */ /** @def be_ioread32 * @brief Read four byte in big endian order. */ /** @def be_iowrite32 * @brief Write four byte in big endian order. */ /** @def le_ioread16 * @brief Read two byte in little endian order. */ /** @def le_iowrite16 * @brief Write two byte in little endian order. */ /** @def le_ioread32 * @brief Read four byte in little endian order. */ /** @def le_iowrite32 * @brief Write four byte in little endian order. */ /** @ingroup mmio *@{ */ /** @brief Explicit read memory barrier * Prevents reordering of reads around it. */ #define rbarr() do{}while(0) /** @brief Explicit write memory barrier * Prevents reordering of writes around it. */ #define wbarr() do{}while(0) /** @brief Explicit read/write memory barrier * Prevents reordering of reads or writes around it. */ #define rwbarr() do{}while(0) /** @} */ /** @defgroup mmio Memory Mapped I/O * * Safe operations on I/O memory. * *This files defines a set of macros for access to Memory Mapped I/O * *They are named T_ioread# and T_iowrite# where # can be 8, 16, or 32. *'T' can either be 'le', 'be', or 'nat' (except ioread8 and *iowrite8). * *The macros defined use OS specific extensions (when available) *to ensure the following. * *@li Width. A 16 bit operation will not be broken into two 8 bit operations, * or one half of a 32 bit operation. * *@li Order. Writes to two different registers will not be reordered. * This only applies to MMIO operations, not between MMIO and * normal memory operations. * *PCI access should use either 'le_' or 'be_' as determined by the *device byte order. * *VME access should always use 'nat_'. If the device byte order is *little endian then an explicit swap is required. * *@section mmioex Examples: * *@subsection mmioexbe Big endian device: * *@b PCI * @code be_iowrite16(base+off, 14); var = be_ioread16(base+off); @endcode * *@b VME * @code nat_iowrite16(base+off, 14); var = nat_ioread16(base+off); @endcode * *@subsection mmioexle Little endian device * *@b PCI @code le_iowrite16(base+off, 14); var = le_ioread16(base+off); @endcode *@b VME @code nat_iowrite16(base+off, bswap16(14)); var = bswap16(nat_iowrite16(base+off)); @endcode *This difference arises because VME bridges implement hardware byte *swapping on little endian systems, while PCI bridges do not. *Software accessing PCI devices must know if byte swapping is required. *This conditional swap is implemented by the 'be_' and 'le_' macros. * *This is a fundamental difference between PCI and VME. * *Software accessing PCI @b must do conditional swapping. * *Software accessing VME must @b not do conditional swapping. * *@note All read and write operations have an implicit read or write barrier. */ #endif /* EPICSMMIODEF_H */ base-7.0.3.1/modules/libcom/src/osi/os/default/epicsSocketConvertErrnoToString.cpp0000664000577000060420000000215013557101274026775 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osdSock.c */ /* * Author: Jeff Hill * Date: 04-05-94 * */ #include #define epicsExportSharedSymbols #include "osiSock.h" /* * epicsSocketConvertErrorToString() */ void epicsSocketConvertErrorToString ( char * pBuf, unsigned bufSize, int theSockError ) { if ( bufSize ) { strncpy ( pBuf, strerror ( theSockError ), bufSize ); pBuf[bufSize-1] = '\0'; } } /* * epicsSocketConvertErrnoToString() */ void epicsSocketConvertErrnoToString ( char * pBuf, unsigned bufSize ) { epicsSocketConvertErrorToString ( pBuf, bufSize, SOCKERRNO ); } base-7.0.3.1/modules/libcom/src/osi/os/default/gnuReadline.c0000664000577000060420000000567413557101274022373 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Eric Norum Date: 12DEC2001 */ /* * This file is included by epicsReadline.c which has already included the * headers stdio.h, stdlib.h, errno.h, envDefs.h and epicsReadline.h */ #include #include #include "epicsExit.h" static struct osdContext { char dummy; /* Required for older compilers */ } present; static enum {rlNone, rlIdle, rlBusy} rlState = rlNone; static void rlExit(void *dummy) { if (rlState == rlBusy) rl_cleanup_after_signal(); } /* * Create a command-line context */ static void osdReadlineBegin(struct readlineContext *context) { if (rlState == rlNone) { epicsAtExit(rlExit, NULL); rlState = rlIdle; } context->osd = &present; if (context->in == NULL) { long i = 50; envGetLongConfigParam(&IOCSH_HISTSIZE, &i); if (i < 0) i = 0; stifle_history(i); rl_bind_key('\t', rl_insert); } } /* * Read a line of input */ static char * osdReadline (const char *prompt, struct readlineContext *context) { char *line; free(context->line); context->line = NULL; if (context->in == NULL) { rlState = rlBusy; line = readline (prompt); rlState = rlIdle; } else { int c; /* char is unsigned on some archs; EOF is -ve */ int linelen = 0; int linesize = 50; line = malloc(linesize); if (line == NULL) { printf("Out of memory!\n"); return NULL; } if (prompt) { fputs(prompt, stdout); fflush(stdout); } while ((c = getc(context->in)) != '\n') { if (c == EOF) { free(line); line = NULL; break; } if ((linelen + 1) >= linesize) { char *cp; linesize += 50; cp = (char *)realloc(line, linesize); if (cp == NULL) { printf ("Out of memory!\n"); free(line); line = NULL; break; } line = cp; } line[linelen++] = c; } if (line) line[linelen] = '\0'; } context->line = line; if (line && *line) add_history(line); return line; } /* * Destroy a command-line context */ static void osdReadlineEnd (struct readlineContext *context) { if (context->osd) { free(context->line); } } base-7.0.3.1/modules/libcom/src/osi/os/default/osdAssert.c0000664000577000060420000000322713557101274022075 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeffrey Hill * Date: 02-27-95 */ #define epicsExportSharedSymbols #include "dbDefs.h" #include "epicsPrint.h" #include "epicsVersion.h" #include "epicsAssert.h" #include "epicsThread.h" #include "epicsTime.h" #include "cantProceed.h" #include "epicsStackTrace.h" void epicsAssert (const char *pFile, const unsigned line, const char *pExp, const char *pAuthorName) { epicsTimeStamp current; errlogPrintf("\n\n\n" "A call to 'assert(%s)'\n" " by thread '%s' failed in %s line %u.\n", pExp, epicsThreadGetNameSelf(), pFile, line); epicsStackTrace(); errlogPrintf("EPICS Release %s.\n", epicsReleaseVersion); if (epicsTimeGetCurrent(¤t) == 0) { char date[64]; epicsTimeToStrftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S.%f %Z", ¤t); errlogPrintf("Local time is %s\n", date); } if (!pAuthorName) { pAuthorName = "the author"; } errlogPrintf("Please E-mail this message to %s or to tech-talk@aps.anl.gov\n", pAuthorName); errlogPrintf("Calling epicsThreadSuspendSelf()\n"); epicsThreadSuspendSelf (); } base-7.0.3.1/modules/libcom/src/osi/os/default/osdBackTrace.cpp0000664000577000060420000000062613557101274023013 0ustar anjaesctl/* * Copyright: Stanford University / SLAC National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * Author: Till Straumann , 2011, 2014 */ #define epicsExportSharedSymbols #include "epicsStackTracePvt.h" int epicsBackTrace(void **buf, int buf_sz) { return -1; } base-7.0.3.1/modules/libcom/src/osi/os/default/osdEnv.c0000664000577000060420000000442313557101274021363 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osdEnv.c */ /* * Author: Eric Norum * Date: May 7, 2001 * * Routines to modify/display environment variables and EPICS parameters * */ #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsStdio.h" #include "errlog.h" #include "cantProceed.h" #include "envDefs.h" #include "osiUnistd.h" #include "epicsFindSymbol.h" #include "iocsh.h" /* * Set the value of an environment variable * Leaks memory, but the assumption is that this routine won't be * called often enough for the leak to be a problem. */ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) { char *cp; if (!name) return; iocshEnvClear(name); cp = mallocMustSucceed (strlen (name) + strlen (value) + 2, "epicsEnvSet"); strcpy (cp, name); strcat (cp, "="); strcat (cp, value); if (putenv (cp) < 0) { errPrintf( -1L, __FILE__, __LINE__, "Failed to set environment parameter \"%s\" to \"%s\": %s\n", name, value, strerror (errno)); free (cp); } } /* * Unset an environment variable * Using putenv with a an existing name but without "=..." deletes */ epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name) { iocshEnvClear(name); if (getenv(name) != NULL) putenv((char*)name); } /* * Show the value of the specified, or all, environment variables */ epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) { if (name == NULL) { extern char **environ; char **sp; for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) printf ("%s\n", *sp); } else { const char *cp = getenv (name); if (cp == NULL) printf ("%s is not an environment variable.\n", name); else printf ("%s=%s\n", name, cp); } } base-7.0.3.1/modules/libcom/src/osi/os/default/osdFindAddr.c0000664000577000060420000000106513557101274022305 0ustar anjaesctl/* * Copyright: Stanford University / SLAC National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * Author: Till Straumann , 2011, 2014 */ #define epicsExportSharedSymbols #include "epicsStackTracePvt.h" #include "epicsStackTrace.h" int epicsFindAddr(void *addr, epicsSymbol *sym_p) { sym_p->f_nam = 0; sym_p->s_nam = 0; sym_p->s_val = 0; return -1; } int epicsFindAddrGetFeatures(void) { return 0; } base-7.0.3.1/modules/libcom/src/osi/os/default/osdFindSymbol.c0000664000577000060420000000153713557101274022704 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osi/os/default/osdFindSymbol.c */ #define epicsExportSharedSymbols #include "epicsFindSymbol.h" epicsShareFunc void * epicsLoadLibrary(const char *name) { return 0; } epicsShareFunc const char *epicsLoadError(void) { return "epicsLoadLibrary not implemented"; } epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name) { return 0; } base-7.0.3.1/modules/libcom/src/osi/os/default/osdInterrupt.c0000664000577000060420000000276513557101274022636 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osi/default/osdInterrupt.c */ /* Author: Marty Kraimer Date: 15JUL99 */ #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsMutex.h" #include "epicsThread.h" #include "cantProceed.h" #include "errlog.h" #include "epicsInterrupt.h" static epicsMutexId globalLock = NULL; static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; static void initOnce(void *junk) { globalLock = epicsMutexMustCreate(); } epicsShareFunc int epicsInterruptLock() { epicsThreadOnce(&onceId, initOnce, NULL); epicsMutexMustLock(globalLock); return 0; } epicsShareFunc void epicsInterruptUnlock(int key) { if (!globalLock) cantProceed("epicsInterruptUnlock called before epicsInterruptLock\n"); epicsMutexUnlock(globalLock); } epicsShareFunc int epicsInterruptIsInterruptContext() { return 0; } epicsShareFunc void epicsInterruptContextMessage(const char *message) { errlogPrintf("%s", message); } base-7.0.3.1/modules/libcom/src/osi/os/default/osdInterrupt.h0000664000577000060420000000113713557101274022633 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #ifndef osdInterrupth #define osdInterrupth #endif /* osdInterrupth */ base-7.0.3.1/modules/libcom/src/osi/os/default/osdMessageQueue.cpp0000664000577000060420000002471413557101274023571 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author W. Eric Norum * norume@aps.anl.gov * 630 252 4793 */ #include #include #include #include #define epicsExportSharedSymbols #include "epicsMessageQueue.h" #include #include #include #include /* * Event cache */ struct eventNode { ELLNODE link; epicsEventId event; }; /* * List of threads waiting to send or receive a message */ struct threadNode { ELLNODE link; struct eventNode *evp; void *buf; unsigned int size; volatile bool eventSent; }; /* * Message info */ struct epicsMessageQueueOSD { ELLLIST sendQueue; ELLLIST receiveQueue; ELLLIST eventFreeList; int numberOfSendersWaiting; epicsMutexId mutex; unsigned long capacity; unsigned long maxMessageSize; unsigned long *buf; char *firstMessageSlot; char *lastMessageSlot; volatile char *inPtr; volatile char *outPtr; unsigned long slotSize; bool full; }; epicsShareFunc epicsMessageQueueId epicsShareAPI epicsMessageQueueCreate( unsigned int capacity, unsigned int maxMessageSize) { epicsMessageQueueId pmsg; unsigned int slotBytes, slotLongs; if(capacity == 0) return NULL; pmsg = (epicsMessageQueueId)calloc(1, sizeof(*pmsg)); if(!pmsg) return NULL; pmsg->capacity = capacity; pmsg->maxMessageSize = maxMessageSize; slotLongs = 1 + ((maxMessageSize + sizeof(unsigned long) - 1) / sizeof(unsigned long)); slotBytes = slotLongs * sizeof(unsigned long); pmsg->mutex = epicsMutexCreate(); pmsg->buf = (unsigned long*)calloc(pmsg->capacity, slotBytes); if(!pmsg->buf || !pmsg->mutex) { if(pmsg->mutex) epicsMutexDestroy(pmsg->mutex); free(pmsg->buf); free(pmsg); return NULL; } pmsg->inPtr = pmsg->outPtr = pmsg->firstMessageSlot = (char *)&pmsg->buf[0]; pmsg->lastMessageSlot = (char *)&pmsg->buf[(capacity - 1) * slotLongs]; pmsg->full = false; pmsg->slotSize = slotBytes; ellInit(&pmsg->sendQueue); ellInit(&pmsg->receiveQueue); ellInit(&pmsg->eventFreeList); return pmsg; } static void freeEventNode(struct eventNode *enode) { epicsEventDestroy(enode->event); free(enode); } epicsShareFunc void epicsShareAPI epicsMessageQueueDestroy(epicsMessageQueueId pmsg) { struct eventNode *evp; while ((evp = reinterpret_cast < struct eventNode * > ( ellGet(&pmsg->eventFreeList) ) ) != NULL) { freeEventNode(evp); } epicsMutexDestroy(pmsg->mutex); free(pmsg->buf); free(pmsg); } static struct eventNode * getEventNode(epicsMessageQueueId pmsg) { struct eventNode *evp; evp = reinterpret_cast < struct eventNode * > ( ellGet(&pmsg->eventFreeList) ); if (evp == NULL) { evp = (struct eventNode *) calloc(1, sizeof(*evp)); if (evp) { evp->event = epicsEventCreate(epicsEventEmpty); if (evp->event == NULL) { free(evp); return NULL; } } } return evp; } static int mySend(epicsMessageQueueId pmsg, void *message, unsigned int size, double timeout) { char *myInPtr, *nextPtr; struct threadNode *pthr; if(size > pmsg->maxMessageSize) return -1; /* * See if message can be sent */ epicsMutexMustLock(pmsg->mutex); if ((pmsg->numberOfSendersWaiting > 0) || (pmsg->full && (ellFirst(&pmsg->receiveQueue) == NULL))) { /* * Return if not allowed to wait */ if (timeout == 0) { epicsMutexUnlock(pmsg->mutex); return -1; } /* * Wait */ struct threadNode threadNode; threadNode.evp = getEventNode(pmsg); threadNode.eventSent = false; if (!threadNode.evp) { epicsMutexUnlock(pmsg->mutex); return -1; } ellAdd(&pmsg->sendQueue, &threadNode.link); pmsg->numberOfSendersWaiting++; epicsMutexUnlock(pmsg->mutex); epicsEventStatus status; if (timeout > 0) status = epicsEventWaitWithTimeout(threadNode.evp->event, timeout); else status = epicsEventWait(threadNode.evp->event); epicsMutexMustLock(pmsg->mutex); if(!threadNode.eventSent) ellDelete(&pmsg->sendQueue, &threadNode.link); pmsg->numberOfSendersWaiting--; ellAdd(&pmsg->eventFreeList, &threadNode.evp->link); if ((pmsg->full && (ellFirst(&pmsg->receiveQueue) == NULL)) || status != epicsEventOK) { epicsMutexUnlock(pmsg->mutex); return -1; } } /* * Copy message to waiting receiver */ if ((pthr = reinterpret_cast < struct threadNode * > ( ellGet(&pmsg->receiveQueue) ) ) != NULL) { if(size <= pthr->size) memcpy(pthr->buf, message, size); pthr->size = size; pthr->eventSent = true; epicsEventSignal(pthr->evp->event); epicsMutexUnlock(pmsg->mutex); return 0; } /* * Copy to queue */ myInPtr = (char *)pmsg->inPtr; if (myInPtr == pmsg->lastMessageSlot) nextPtr = pmsg->firstMessageSlot; else nextPtr = myInPtr + pmsg->slotSize; if (nextPtr == (char *)pmsg->outPtr) pmsg->full = true; *(volatile unsigned long *)myInPtr = size; memcpy((unsigned long *)myInPtr + 1, message, size); pmsg->inPtr = nextPtr; epicsMutexUnlock(pmsg->mutex); return 0; } epicsShareFunc int epicsShareAPI epicsMessageQueueTrySend(epicsMessageQueueId pmsg, void *message, unsigned int size) { return mySend(pmsg, message, size, 0); } epicsShareFunc int epicsShareAPI epicsMessageQueueSend(epicsMessageQueueId pmsg, void *message, unsigned int size) { return mySend(pmsg, message, size, -1); } epicsShareFunc int epicsShareAPI epicsMessageQueueSendWithTimeout(epicsMessageQueueId pmsg, void *message, unsigned int size, double timeout) { return mySend(pmsg, message, size, timeout); } static int myReceive(epicsMessageQueueId pmsg, void *message, unsigned int size, double timeout) { char *myOutPtr; unsigned long l; struct threadNode *pthr; /* * If there's a message on the queue, copy it */ epicsMutexMustLock(pmsg->mutex); myOutPtr = (char *)pmsg->outPtr; if ((myOutPtr != pmsg->inPtr) || pmsg->full) { int ret; l = *(unsigned long *)myOutPtr; if (l <= size) { memcpy(message, (unsigned long *)myOutPtr + 1, l); ret = l; } else { ret = -1; } if (myOutPtr == pmsg->lastMessageSlot) pmsg->outPtr = pmsg->firstMessageSlot; else pmsg->outPtr += pmsg->slotSize; pmsg->full = false; /* * Wake up the oldest task waiting to send */ if ((pthr = reinterpret_cast < struct threadNode * > ( ellGet(&pmsg->sendQueue) ) ) != NULL) { pthr->eventSent = true; epicsEventSignal(pthr->evp->event); } epicsMutexUnlock(pmsg->mutex); return ret; } /* * Return if not allowed to wait */ if (timeout == 0) { epicsMutexUnlock(pmsg->mutex); return -1; } /* * Wake up the oldest task waiting to send */ if ((pthr = reinterpret_cast < struct threadNode * > ( ellGet(&pmsg->sendQueue) ) ) != NULL) { pthr->eventSent = true; epicsEventSignal(pthr->evp->event); } /* * Wait for message to arrive */ struct threadNode threadNode; threadNode.evp = getEventNode(pmsg); threadNode.buf = message; threadNode.size = size; threadNode.eventSent = false; if (!threadNode.evp) { epicsMutexUnlock(pmsg->mutex); return -1; } ellAdd(&pmsg->receiveQueue, &threadNode.link); epicsMutexUnlock(pmsg->mutex); epicsEventStatus status; if (timeout > 0) status = epicsEventWaitWithTimeout(threadNode.evp->event, timeout); else status = epicsEventWait(threadNode.evp->event); epicsMutexMustLock(pmsg->mutex); if (!threadNode.eventSent) ellDelete(&pmsg->receiveQueue, &threadNode.link); ellAdd(&pmsg->eventFreeList, &threadNode.evp->link); epicsMutexUnlock(pmsg->mutex); if (threadNode.eventSent && (threadNode.size <= size) && status == epicsEventOK) return threadNode.size; return -1; } epicsShareFunc int epicsShareAPI epicsMessageQueueTryReceive(epicsMessageQueueId pmsg, void *message, unsigned int size) { return myReceive(pmsg, message, size, 0); } epicsShareFunc int epicsShareAPI epicsMessageQueueReceive(epicsMessageQueueId pmsg, void *message, unsigned int size) { return myReceive(pmsg, message, size, -1); } epicsShareFunc int epicsShareAPI epicsMessageQueueReceiveWithTimeout(epicsMessageQueueId pmsg, void *message, unsigned int size, double timeout) { return myReceive(pmsg, message, size, timeout); } epicsShareFunc int epicsShareAPI epicsMessageQueuePending(epicsMessageQueueId pmsg) { char *myInPtr, *myOutPtr; int nmsg; epicsMutexMustLock(pmsg->mutex); myInPtr = (char *)pmsg->inPtr; myOutPtr = (char *)pmsg->outPtr; if (pmsg->full) nmsg = pmsg->capacity; else if (myInPtr >= myOutPtr) nmsg = (myInPtr - myOutPtr) / pmsg->slotSize; else nmsg = pmsg->capacity - (myOutPtr - myInPtr) / pmsg->slotSize; epicsMutexUnlock(pmsg->mutex); return nmsg; } epicsShareFunc void epicsShareAPI epicsMessageQueueShow(epicsMessageQueueId pmsg, int level) { printf("Message Queue Used:%d Slots:%lu", epicsMessageQueuePending(pmsg), pmsg->capacity); if (level >= 1) printf(" Maximum size:%lu", pmsg->maxMessageSize); printf("\n"); } base-7.0.3.1/modules/libcom/src/osi/os/default/osdMessageQueue.h0000664000577000060420000000006413557101274023226 0ustar anjaesctl/* * Nothing needed for default implementation */ base-7.0.3.1/modules/libcom/src/osi/os/default/osdNetIntf.c0000664000577000060420000003016213557101274022201 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeff Hill * Date: 04-05-94 */ #include #include #include #include #define epicsExportSharedSymbols #include "osiSock.h" #include "epicsAssert.h" #include "errlog.h" #include "epicsThread.h" #ifdef DEBUG # define ifDepenDebugPrintf(argsInParen) printf argsInParen #else # define ifDepenDebugPrintf(argsInParen) #endif static osiSockAddr osiLocalAddrResult; static epicsThreadOnceId osiLocalAddrId = EPICS_THREAD_ONCE_INIT; /* * Determine the size of an ifreq structure * Made difficult by the fact that addresses larger than the structure * size may be returned from the kernel. */ static size_t ifreqSize ( struct ifreq *pifreq ) { size_t size; size = ifreq_size ( pifreq ); if ( size < sizeof ( *pifreq ) ) { size = sizeof ( *pifreq ); } return size; } /* * Move to the next ifreq structure */ static struct ifreq * ifreqNext ( struct ifreq *pifreq ) { struct ifreq *ifr; ifr = ( struct ifreq * )( ifreqSize (pifreq) + ( char * ) pifreq ); ifDepenDebugPrintf( ("ifreqNext() pifreq %p, size 0x%x, ifr 0x%p\n", pifreq, (unsigned)ifreqSize (pifreq), ifr) ); return ifr; } /* * osiSockDiscoverBroadcastAddresses () */ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses (ELLLIST *pList, SOCKET socket, const osiSockAddr *pMatchAddr) { static const unsigned nelem = 100; int status; struct ifconf ifconf; struct ifreq *pIfreqList; struct ifreq *pIfreqListEnd; struct ifreq *pifreq; struct ifreq *pnextifreq; osiSockAddrNode *pNewNode; if ( pMatchAddr->sa.sa_family == AF_INET ) { if ( pMatchAddr->ia.sin_addr.s_addr == htonl (INADDR_LOOPBACK) ) { pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) ); if ( pNewNode == NULL ) { errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" ); return; } pNewNode->addr.ia.sin_family = AF_INET; pNewNode->addr.ia.sin_port = htons ( 0 ); pNewNode->addr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK); ellAdd ( pList, &pNewNode->node ); return; } } /* * use pool so that we avoid using too much stack space * * nelem is set to the maximum interfaces * on one machine here */ pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pifreq) ); if (!pIfreqList) { errlogPrintf ("osiSockDiscoverBroadcastAddresses(): no memory to complete request\n"); return; } ifconf.ifc_len = nelem * sizeof(*pifreq); ifconf.ifc_req = pIfreqList; status = socket_ioctl (socket, SIOCGIFCONF, &ifconf); if (status < 0 || ifconf.ifc_len == 0) { errlogPrintf ("osiSockDiscoverBroadcastAddresses(): unable to fetch network interface configuration (%d)\n", status); free (pIfreqList); return; } pIfreqListEnd = (struct ifreq *) (ifconf.ifc_len + (char *) pIfreqList); pIfreqListEnd--; for ( pifreq = pIfreqList; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) { uint32_t current_ifreqsize; /* * find the next ifreq */ pnextifreq = ifreqNext (pifreq); /* determine ifreq size */ current_ifreqsize = ifreqSize ( pifreq ); /* copy current ifreq to aligned bufferspace (to start of pIfreqList buffer) */ memmove(pIfreqList, pifreq, current_ifreqsize); ifDepenDebugPrintf (("osiSockDiscoverBroadcastAddresses(): found IFACE: %s len: 0x%x current_ifreqsize: 0x%x \n", pIfreqList->ifr_name, (unsigned)ifreq_size(pifreq), (unsigned)current_ifreqsize)); /* * If its not an internet interface then dont use it */ if ( pIfreqList->ifr_addr.sa_family != AF_INET ) { ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): interface \"%s\" was not AF_INET\n", pIfreqList->ifr_name) ); continue; } /* * if it isnt a wildcarded interface then look for * an exact match */ if ( pMatchAddr->sa.sa_family != AF_UNSPEC ) { if ( pMatchAddr->sa.sa_family != AF_INET ) { continue; } if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) { struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr; if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) { ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didnt match\n", pIfreqList->ifr_name) ); continue; } } } status = socket_ioctl ( socket, SIOCGIFFLAGS, pIfreqList ); if ( status ) { errlogPrintf ("osiSockDiscoverBroadcastAddresses(): net intf flags fetch for \"%s\" failed\n", pIfreqList->ifr_name); continue; } ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" flags: %x\n", pIfreqList->ifr_name, pIfreqList->ifr_flags) ); /* * dont bother with interfaces that have been disabled */ if ( ! ( pIfreqList->ifr_flags & IFF_UP ) ) { ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" was down\n", pIfreqList->ifr_name) ); continue; } /* * dont use the loop back interface */ if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) { ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): ignoring loopback interface: \"%s\"\n", pIfreqList->ifr_name) ); continue; } pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) ); if ( pNewNode == NULL ) { errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" ); free ( pIfreqList ); return; } /* * If this is an interface that supports * broadcast fetch the broadcast address. * * Otherwise if this is a point to point * interface then use the destination address. * * Otherwise CA will not query through the * interface. */ if ( pIfreqList->ifr_flags & IFF_BROADCAST ) { osiSockAddr baddr; status = socket_ioctl (socket, SIOCGIFBRDADDR, pIfreqList); if ( status ) { errlogPrintf ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\": bcast addr fetch fail\n", pIfreqList->ifr_name); free ( pNewNode ); continue; } baddr.sa = pIfreqList->ifr_broadaddr; if (baddr.ia.sin_family==AF_INET && baddr.ia.sin_addr.s_addr != INADDR_ANY) { pNewNode->addr.sa = pIfreqList->ifr_broadaddr; ifDepenDebugPrintf ( ( "found broadcast addr = %x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) ); } else { ifDepenDebugPrintf ( ( "Ignoring broadcast addr = \n", ntohl ( baddr.ia.sin_addr.s_addr ) ) ); free ( pNewNode ); continue; } } #if defined (IFF_POINTOPOINT) else if ( pIfreqList->ifr_flags & IFF_POINTOPOINT ) { status = socket_ioctl ( socket, SIOCGIFDSTADDR, pIfreqList); if ( status ) { ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\": pt to pt addr fetch fail\n", pIfreqList->ifr_name) ); free ( pNewNode ); continue; } pNewNode->addr.sa = pIfreqList->ifr_dstaddr; } #endif else { ifDepenDebugPrintf ( ( "osiSockDiscoverBroadcastAddresses(): net intf \"%s\": not point to point or bcast?\n", pIfreqList->ifr_name ) ); free ( pNewNode ); continue; } ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" found\n", pIfreqList->ifr_name) ); /* * LOCK applied externally */ ellAdd ( pList, &pNewNode->node ); } free ( pIfreqList ); } /* * osiLocalAddr () */ static void osiLocalAddrOnce (void *raw) { SOCKET *psocket = raw; const unsigned nelem = 100; osiSockAddr addr; int status; struct ifconf ifconf; struct ifreq *pIfreqList; struct ifreq *pifreq; struct ifreq *pIfreqListEnd; struct ifreq *pnextifreq; memset ( (void *) &addr, '\0', sizeof ( addr ) ); addr.sa.sa_family = AF_UNSPEC; pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pIfreqList) ); if ( ! pIfreqList ) { errlogPrintf ( "osiLocalAddr(): no memory to complete request\n" ); goto fail; } ifconf.ifc_len = nelem * sizeof ( *pIfreqList ); ifconf.ifc_req = pIfreqList; status = socket_ioctl ( *psocket, SIOCGIFCONF, &ifconf ); if ( status < 0 || ifconf.ifc_len == 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "osiLocalAddr(): SIOCGIFCONF ioctl failed because \"%s\"\n", sockErrBuf ); goto fail; } pIfreqListEnd = (struct ifreq *) ( ifconf.ifc_len + (char *) ifconf.ifc_req ); pIfreqListEnd--; for ( pifreq = ifconf.ifc_req; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) { osiSockAddr addrCpy; uint32_t current_ifreqsize; /* * find the next if req */ pnextifreq = ifreqNext ( pifreq ); /* determine ifreq size */ current_ifreqsize = ifreqSize ( pifreq ); /* copy current ifreq to aligned bufferspace (to start of pIfreqList buffer) */ memmove(pIfreqList, pifreq, current_ifreqsize); if ( pIfreqList->ifr_addr.sa_family != AF_INET ) { ifDepenDebugPrintf ( ("osiLocalAddr(): interface %s was not AF_INET\n", pIfreqList->ifr_name) ); continue; } addrCpy.sa = pIfreqList->ifr_addr; status = socket_ioctl ( *psocket, SIOCGIFFLAGS, pIfreqList ); if ( status < 0 ) { errlogPrintf ( "osiLocalAddr(): net intf flags fetch for %s failed\n", pIfreqList->ifr_name ); continue; } if ( ! ( pIfreqList->ifr_flags & IFF_UP ) ) { ifDepenDebugPrintf ( ("osiLocalAddr(): net intf %s was down\n", pIfreqList->ifr_name) ); continue; } /* * dont use the loop back interface */ if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) { ifDepenDebugPrintf ( ("osiLocalAddr(): ignoring loopback interface: %s\n", pIfreqList->ifr_name) ); continue; } ifDepenDebugPrintf ( ("osiLocalAddr(): net intf %s found\n", pIfreqList->ifr_name) ); osiLocalAddrResult = addrCpy; free ( pIfreqList ); return; } errlogPrintf ( "osiLocalAddr(): only loopback found\n"); fail: /* fallback to loopback */ memset ( (void *) &addr, '\0', sizeof ( addr ) ); addr.ia.sin_family = AF_INET; addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK); osiLocalAddrResult = addr; free ( pIfreqList ); } epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket) { epicsThreadOnce(&osiLocalAddrId, osiLocalAddrOnce, &socket); return osiLocalAddrResult; } base-7.0.3.1/modules/libcom/src/osi/os/default/osdPoolStatus.c0000664000577000060420000000140713557101274022747 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #define epicsExportSharedSymbols #include "osiPoolStatus.h" /* * osiSufficentSpaceInPool () * * @@@@@ not implemented @@@@@ * */ epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ) { return 1; } base-7.0.3.1/modules/libcom/src/osi/os/default/osdPoolStatus.h0000664000577000060420000000114113557101274022747 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #ifndef osdPoolStatush #define osdPoolStatush #endif /* osdPoolStatush */ base-7.0.3.1/modules/libcom/src/osi/os/default/osdSignal.cpp0000664000577000060420000000172513557101274022412 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #define epicsExportSharedSymbols #include "epicsSignal.h" /* * All NOOPs if the os isnt POSIX */ epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ) {} epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ) {} epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * /* threadId */ ) {} base-7.0.3.1/modules/libcom/src/osi/os/default/osdSockUnsentCount.c0000664000577000060420000000070713557101274023741 0ustar anjaesctl/*************************************************************************\ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #define EPICS_PRIVATE_API #include "osiSock.h" /* * epicsSocketUnsentCount () */ int epicsSocketUnsentCount(SOCKET sock) { /* not implemented */ return -1; } base-7.0.3.1/modules/libcom/src/osi/os/default/osdSpin.c0000664000577000060420000000400213557101274021535 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * Copyright (c) 2012 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #define epicsExportSharedSymbols #include "cantProceed.h" #include "errlog.h" #include "epicsMutex.h" #include "epicsSpin.h" /* * Default: EPICS MUTEX IMPLEMENTATION */ typedef struct epicsSpin { epicsMutexId lock; } epicsSpin; epicsSpinId epicsSpinCreate(void) { epicsSpin *spin; spin = calloc(1, sizeof(*spin)); if (!spin) goto fail; spin->lock = epicsMutexCreate(); if (!spin->lock) goto fail; return spin; fail: free(spin); return NULL; } epicsSpinId epicsSpinMustCreate(void) { epicsSpinId ret = epicsSpinCreate(); if(!ret) cantProceed("epicsSpinMustCreate: epicsSpinCreate failed."); return ret; } void epicsSpinDestroy(epicsSpinId spin) { epicsMutexDestroy(spin->lock); free(spin); } void epicsSpinLock(epicsSpinId spin) { epicsMutexLockStatus status; status = epicsMutexLock(spin->lock); if (status != epicsMutexLockOK) { errlogPrintf("epicsSpinLock(%p): epicsMutexLock returned %s\n", spin, status == epicsMutexLockTimeout ? "epicsMutexLockTimeout" : "epicsMutexLockError"); } } int epicsSpinTryLock(epicsSpinId spin) { epicsMutexLockStatus status; status = epicsMutexTryLock(spin->lock); if (status == epicsMutexLockOK) return 0; if (status == epicsMutexLockTimeout) return 1; errlogPrintf("epicsSpinTryLock(%p): epicsMutexTryLock returned epicsMutexLockError\n", spin); return 2; } void epicsSpinUnlock(epicsSpinId spin) { epicsMutexUnlock(spin->lock); } base-7.0.3.1/modules/libcom/src/osi/os/default/osdThreadExtra.c0000664000577000060420000000120313557101274023037 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 ITER Organization * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Ralph Lange Date: 26 Jun 2012 */ /* Null default thread hooks for all platforms that do not do anything special */ #define epicsExportSharedSymbols #include "epicsThread.h" epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; base-7.0.3.1/modules/libcom/src/osi/os/default/osdThreadHooks.c0000664000577000060420000000714713557101274023054 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 ITER Organization * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Authors: Ralph Lange & Andrew Johnson */ /* Secure hooks for epicsThread */ #include #include #include #include #define epicsExportSharedSymbols #include "ellLib.h" #include "epicsMutex.h" #include "epicsThread.h" epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; typedef struct epicsThreadHook { ELLNODE node; EPICS_THREAD_HOOK_ROUTINE func; } epicsThreadHook; static ELLLIST hookList = ELLLIST_INIT; static epicsMutexId hookLock; static void threadHookOnce(void *arg) { hookLock = epicsMutexMustCreate(); if (epicsThreadHookDefault) { static epicsThreadHook defHook = {ELLNODE_INIT, NULL}; defHook.func = epicsThreadHookDefault; ellAdd(&hookList, &defHook.node); } } static void threadHookInit(void) { static epicsThreadOnceId flag = EPICS_THREAD_ONCE_INIT; epicsThreadOnce(&flag, threadHookOnce, NULL); } epicsShareFunc int epicsThreadHookAdd(EPICS_THREAD_HOOK_ROUTINE hook) { epicsThreadHook *pHook; if (!hook) return 0; threadHookInit(); pHook = calloc(1, sizeof(epicsThreadHook)); if (!pHook) { fprintf(stderr, "epicsThreadHookAdd: calloc failed\n"); return -1; } pHook->func = hook; if (epicsMutexLock(hookLock) == epicsMutexLockOK) { ellAdd(&hookList, &pHook->node); epicsMutexUnlock(hookLock); return 0; } fprintf(stderr, "epicsThreadHookAdd: Locking problem\n"); return -1; } epicsShareFunc int epicsThreadHookDelete(EPICS_THREAD_HOOK_ROUTINE hook) { if (!hook) return 0; threadHookInit(); if (epicsMutexLock(hookLock) == epicsMutexLockOK) { epicsThreadHook *pHook = (epicsThreadHook *) ellFirst(&hookList); while (pHook) { if (hook == pHook->func) { ellDelete(&hookList, &pHook->node); break; } pHook = (epicsThreadHook *) ellNext(&pHook->node); } epicsMutexUnlock(hookLock); return 0; } fprintf(stderr, "epicsThreadHookAdd: Locking problem\n"); return -1; } epicsShareFunc void osdThreadHooksRunMain(epicsThreadId id) { if (epicsThreadHookMain) epicsThreadHookMain(id); } epicsShareFunc void osdThreadHooksRun(epicsThreadId id) { threadHookInit(); if (epicsMutexLock(hookLock) == epicsMutexLockOK) { epicsThreadHook *pHook = (epicsThreadHook *) ellFirst(&hookList); while (pHook) { pHook->func(id); pHook = (epicsThreadHook *) ellNext(&pHook->node); } epicsMutexUnlock(hookLock); } else { fprintf(stderr, "osdThreadHooksRun: Locking problem\n"); } } epicsShareFunc void epicsThreadHooksShow(void) { threadHookInit(); if (epicsMutexLock(hookLock) == epicsMutexLockOK) { epicsThreadHook *pHook = (epicsThreadHook *) ellFirst(&hookList); while (pHook) { printf(" %p\n", pHook->func); pHook = (epicsThreadHook *) ellNext(&pHook->node); } epicsMutexUnlock(hookLock); } else { fprintf(stderr, "epicsThreadHooksShow: Locking problem\n"); } } base-7.0.3.1/modules/libcom/src/osi/os/default/osdVME.h0000664000577000060420000000107113557101274021263 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * OS-dependent VME support */ base-7.0.3.1/modules/libcom/src/osi/os/default/osdWireConfig.h0000664000577000060420000000414013557101274022670 0ustar anjaesctl/* * Default version of osdWireConfig.h that might * work on UNIX like systems that define * * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef osdWireConfig_h #define osdWireConfig_h /* This file must be usable from both C and C++ */ /* if compilation fails because this wasnt found then you may need to define an OS specific osdWireConfig.h */ #include #ifdef __BYTE_ORDER # if __BYTE_ORDER == __LITTLE_ENDIAN # define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE # elif __BYTE_ORDER == __BIG_ENDIAN # define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG # else # error EPICS hasnt been ported to run on the specified __BYTE_ORDER # endif #else # ifdef BYTE_ORDER # if BYTE_ORDER == LITTLE_ENDIAN # define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE # elif BYTE_ORDER == BIG_ENDIAN # define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG # else # error EPICS hasnt been ported to run on the specified BYTE_ORDER # endif # else # error doesnt specify __BYTE_ORDER or BYTE_ORDER - is an OS specific osdWireConfig.h needed? # endif #endif #ifdef __FLOAT_WORD_ORDER # if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN # define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_LITTLE # elif __FLOAT_WORD_ORDER == __BIG_ENDIAN # define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_BIG # else # error EPICS hasnt been ported to specified __FLOAT_WORD_ORDER # endif #else # ifdef FLOAT_WORD_ORDER # if FLOAT_WORD_ORDER == LITTLE_ENDIAN # define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_LITTLE # elif FLOAT_WORD_ORDER == BIG_ENDIAN # define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_BIG # else # error EPICS hasnt been ported to specified FLOAT_WORD_ORDER # endif # else /* assume that if neither __FLOAT_WORD_ORDER nor FLOAT_WORD_ORDER are defined then weird fp ordered archs like arm nwfp aren't supported */ # define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER # endif #endif #endif /* ifdef osdWireConfig_h */ base-7.0.3.1/modules/libcom/src/osi/os/default/osdWireFormat.h0000664000577000060420000001576013557101274022725 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef osdWireFormat #define osdWireFormat #ifdef __SUNPRO_CC # include #else # include #endif #include "epicsEndian.h" // // The default assumption is that the local floating point format is // IEEE and that these routines only need to perform byte swapping // as a side effect of copying an aligned operand into an unaligned // network byte stream. OS specific code can provide a alternative // for this file if that assumption is wrong. // // // EPICS_CONVERSION_REQUIRED is set if either the byte order // or the floating point word order are not exactly big endian. // This can be set by hand above for a specific architecture // should there be an architecture that is a weird middle endian // ieee floating point format that is also big endian integer. // #if EPICS_BYTE_ORDER != EPICS_ENDIAN_BIG || EPICS_FLOAT_WORD_ORDER != EPICS_BYTE_ORDER # if ! defined ( EPICS_CONVERSION_REQUIRED ) # define EPICS_CONVERSION_REQUIRED # endif #endif // // We still use a big endian wire format for CA consistent with the internet, // but inconsistent with the vast majority of CPUs // template <> inline void WireGet < epicsFloat64 > ( const epicsUInt8 * pWireSrc, epicsFloat64 & dst ) { // copy through union here // a) prevents over-aggressive optimization under strict aliasing rules // b) doesnt preclude extra copy operation being optimized away union { epicsFloat64 _f; epicsUInt32 _u[2]; } tmp; # if EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_LITTLE WireGet ( pWireSrc, tmp._u[1] ); WireGet ( pWireSrc + 4, tmp._u[0] ); # elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_BIG WireGet ( pWireSrc, tmp._u[0] ); WireGet ( pWireSrc + 4, tmp._u[1] ); # else # error unsupported floating point word order # endif dst = tmp._f; } #if defined ( __GNUC__ ) && ( __GNUC__ == 4 && __GNUC_MINOR__ <= 0 ) template <> inline void WireGet < epicsOldString > ( const epicsUInt8 * pWireSrc, epicsOldString & dst ) { memcpy ( dst, pWireSrc, sizeof ( dst ) ); } #else inline void WireGet ( const epicsUInt8 * pWireSrc, epicsOldString & dst ) { memcpy ( dst, pWireSrc, sizeof ( dst ) ); } #endif template <> inline void WireSet < epicsFloat64 > ( const epicsFloat64 & src, epicsUInt8 * pWireDst ) { // copy through union here // a) prevents over-aggressive optimization under strict aliasing rules // b) doesnt preclude extra copy operation being optimized away union { epicsFloat64 _f; epicsUInt32 _u[2]; } tmp; tmp._f = src; # if EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_LITTLE WireSet ( tmp._u[1], pWireDst ); WireSet ( tmp._u[0], pWireDst + 4 ); # elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_BIG WireSet ( tmp._u[0], pWireDst ); WireSet ( tmp._u[1], pWireDst + 4 ); # else # error unsupported floating point word order # endif } #if defined ( __GNUC__ ) && ( __GNUC__ == 4 && __GNUC_MINOR__ <= 0 ) template <> inline void WireSet < epicsOldString > ( const epicsOldString & src, epicsUInt8 * pWireDst ) { memcpy ( pWireDst, src, sizeof ( src ) ); } #else inline void WireSet ( const epicsOldString & src, epicsUInt8 * pWireDst ) { memcpy ( pWireDst, src, sizeof ( src ) ); } #endif template <> inline void AlignedWireGet < epicsUInt16 > ( const epicsUInt16 & src, epicsUInt16 & dst ) { # if EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE dst = byteSwap ( src ); # elif EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG dst = src; # else # error unsupported endian type # endif } template <> inline void AlignedWireGet < epicsUInt32 > ( const epicsUInt32 & src, epicsUInt32 & dst ) { # if EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE dst = byteSwap ( src ); # elif EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG dst = src; # else # error unsupported endian type # endif } template <> inline void AlignedWireGet < epicsFloat64 > ( const epicsFloat64 & src, epicsFloat64 & dst ) { // copy through union here // a) prevents over-aggressive optimization under strict aliasing rules // b) doesnt preclude extra copy operation being optimized away union Swapper { epicsUInt32 _u[2]; epicsFloat64 _f; }; # if EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG && EPICS_FLOAT_WORD_ORDER == EPICS_BYTE_ORDER dst = src; # elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_BIG Swapper tmp; tmp._f = src; AlignedWireGet ( tmp._u[0], tmp._u[0] ); AlignedWireGet ( tmp._u[1], tmp._u[1] ); dst = tmp._f; # elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_LITTLE Swapper srcu, dstu; srcu._f = src; AlignedWireGet ( srcu._u[1], dstu._u[0] ); AlignedWireGet ( srcu._u[0], dstu._u[1] ); dst = dstu._f; # else # error unsupported floating point word order # endif } template <> inline void AlignedWireSet < epicsUInt16 > ( const epicsUInt16 & src, epicsUInt16 & dst ) { # if EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE dst = byteSwap ( src ); # elif EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG dst = src; # else # error undefined endian type # endif } template <> inline void AlignedWireSet < epicsUInt32 > ( const epicsUInt32 & src, epicsUInt32 & dst ) { # if EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE dst = byteSwap ( src ); # elif EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG dst = src; # else # error undefined endian type # endif } template <> inline void AlignedWireSet < epicsFloat64 > ( const epicsFloat64 & src, epicsFloat64 & dst ) { // copy through union here // a) prevents over-aggressive optimization under strict aliasing rules // b) doesnt preclude extra copy operation being optimized away union Swapper { epicsUInt32 _u[2]; epicsFloat64 _f; }; # if EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG && EPICS_FLOAT_WORD_ORDER == EPICS_BYTE_ORDER dst = src; # elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_BIG Swapper tmp; tmp._f = src; AlignedWireSet ( tmp._u[0], tmp._u[0] ); AlignedWireSet ( tmp._u[1], tmp._u[1] ); dst = tmp._f; # elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_LITTLE Swapper srcu, dstu; srcu._f = src; AlignedWireSet ( srcu._u[1], dstu._u[0] ); AlignedWireSet ( srcu._u[0], dstu._u[1] ); dst = dstu._f; # else # error unsupported floating point word order # endif } #endif // osdWireFormat base-7.0.3.1/modules/libcom/src/osi/os/default/osdgetexec.c0000664000577000060420000000026413557101274022256 0ustar anjaesctl#include #define epicsExportSharedSymbols #include char *epicsGetExecName(void) { return NULL; } char *epicsGetExecDir(void) { return NULL; } base-7.0.3.1/modules/libcom/src/osi/os/freebsd/osdSock.h0000664000577000060420000000420613557101274021524 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef osdSockH #define osdSockH #include #include #include /* for MAXHOSTNAMELEN */ #include #include #include #include #include #include #include #include #include /* close() and others */ #ifndef IPPORT_USERRESERVED #define IPPORT_USERRESERVED 5000 #endif typedef int SOCKET; #define INVALID_SOCKET (-1) #define SOCKERRNO errno #define socket_ioctl(A,B,C) ioctl(A,B,C) typedef int osiSockIoctl_t; typedef socklen_t osiSocklen_t; typedef int osiSockOptMcastLoop_t; typedef unsigned char osiSockOptMcastTTL_t; #define FD_IN_FDSET(FD) ((FD)ifr_addr.sa_len + sizeof(pifreq->ifr_name)) #else # define ifreq_size(pifreq) sizeof(*pifreq) #endif #endif /*osdSockH*/ base-7.0.3.1/modules/libcom/src/osi/os/freebsd/osdTime.h0000664000577000060420000000140413557101274021520 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Eric Norum */ #ifndef osdTimeh #define osdTimeh /* * We need this include file since the POSIX version * causes `struct timespec' to be defined in more than one place. */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ epicsShareFunc void epicsShareAPI convertDoubleToWakeTime(double timeout,struct timespec *wakeTime); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* ifndef osdTimeh */ base-7.0.3.1/modules/libcom/src/osi/os/freebsd/osdgetexec.c0000664000577000060420000000245113557101274022244 0ustar anjaesctl #include #include #include #include #define epicsExportSharedSymbols #include char *epicsGetExecName(void) { size_t max = PATH_MAX; char *ret = NULL; ssize_t n; while(1) { char *temp = realloc(ret, max); if(!temp) { /* we treat alloc failure as terminal */ free(ret); ret = NULL; break; } ret = temp; n = readlink("/proc/curproc/file", ret, max); if(n == -1) { free(ret); ret = NULL; break; } else if(n < max) { /* readlink() never adds a nil */ ret[n] = '\0'; break; } max += 64; } if(!ret) { int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PATHNAME; mib[3] = -1; ret = malloc(max); if(ret) { sysctl(mib, 4, ret, &cb, NULL, 0); /* TODO: error check */ } } return ret; } char *epicsGetExecDir(void) { char *ret = epicsGetExecName(); if(ret) { char *sep = strrchr(ret, '/'); if(sep) { /* nil the charactor after the / */ sep[1] = '\0'; } } return ret; } base-7.0.3.1/modules/libcom/src/osi/os/freebsd/osiFileName.h0000664000577000060420000000117713557101274022316 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * osiFileName.h * Author: Jeff Hill */ #ifndef osiFileNameH #define osiFileNameH #include "unixFileName.h" #endif /* osiFileNameH */ base-7.0.3.1/modules/libcom/src/osi/os/freebsd/osiUnistd.h0000664000577000060420000000147713557101274022107 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include /* * Some systems fail to provide prototypes of these functions. * Others provide different prototypes. * There seems to be no way to handle this automatically, so * if you get compile errors, just make the appropriate changes here. */ base-7.0.3.1/modules/libcom/src/osi/os/iOS/epicsMath.h0000664000577000060420000000121313557101274021107 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef epicsMathh #define epicsMathh #include #include #define finite(x) isfinite(x) #ifdef __cplusplus extern "C" { #endif epicsShareExtern float epicsNAN; epicsShareExtern float epicsINF; #ifdef __cplusplus } #endif #endif /* epicsMathh */ base-7.0.3.1/modules/libcom/src/osi/os/iOS/osdEnv.c0000664000577000060420000000321513557101274020427 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osdEnv.c */ /* * Author: Eric Norum * Date: May 7, 2001 * * Routines to modify/display environment variables and EPICS parameters */ #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsStdio.h" #include #include #include #include #include "epicsFindSymbol.h" #include /* * Set the value of an environment variable */ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) { if (!name) return; iocshEnvClear(name); setenv(name, value, 1); } /* * Unset an environment variable */ epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name) { iocshEnvClear(name); unsetenv(name); } /* * Show the value of the specified, or all, environment variables */ epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) { if (name == NULL) { extern char **environ; char **sp; for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) printf ("%s\n", *sp); } else { const char *cp = getenv (name); if (cp == NULL) printf ("%s is not an environment variable.\n", name); else printf ("%s=%s\n", name, cp); } } base-7.0.3.1/modules/libcom/src/osi/os/iOS/osdMonotonic.c0000664000577000060420000000170113557101274021642 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Michael Davidsaver * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #define epicsExportSharedSymbols #define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "dbDefs.h" #include "errlog.h" #include "epicsTime.h" #include "generalTimeSup.h" /* see https://developer.apple.com/library/content/qa/qa1398/_index.html */ static mach_timebase_info_data_t tbinfo; void osdMonotonicInit(void) { (void)mach_timebase_info(&tbinfo); } epicsUInt64 epicsMonotonicResolution(void) { return 1e-9 * tbinfo.numer / tbinfo.denom; } epicsUInt64 epicsMonotonicGet(void) { uint64_t val = mach_absolute_time(); return val * tbinfo.numer / tbinfo.denom; } base-7.0.3.1/modules/libcom/src/osi/os/iOS/osdSock.h0000664000577000060420000000356013557101274020606 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Eric Norum */ #ifndef osdSockH #define osdSockH #include #include #include /* for MAXHOSTNAMELEN */ #include #include #include #include #include #include #include #include #include /* close() and others */ typedef int SOCKET; #define INVALID_SOCKET (-1) #define SOCKERRNO errno #define socket_ioctl(A,B,C) ioctl(A,B,C) typedef int osiSockIoctl_t; typedef socklen_t osiSocklen_t; typedef int osiSockOptMcastLoop_t; typedef unsigned char osiSockOptMcastTTL_t; #define FD_IN_FDSET(FD) ((FD)ifr_addr.sa_len + sizeof((pifreq)->ifr_name)) #endif /*osdSockH*/ base-7.0.3.1/modules/libcom/src/osi/os/iOS/osdSockAddrReuse.cpp0000664000577000060420000000262413557101274022740 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeff Hill */ #define epicsExportSharedSymbols #include "osiSock.h" #include "errlog.h" epicsShareFunc void epicsShareAPI epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) { int yes = true; int status; status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, (char *) & yes, sizeof ( yes ) ); if ( status < 0 ) { errlogPrintf ( "epicsSocketEnablePortUseForDatagramFanout: " "unable to set SO_REUSEADDR?\n"); } } /* * SO_REUSEPORT is not in POSIX */ epicsShareFunc void epicsShareAPI epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) { int yes = true; int status; status = setsockopt ( s, SOL_SOCKET, SO_REUSEPORT, (char *) & yes, sizeof ( yes ) ); if ( status < 0 ) { errlogPrintf ( "epicsSocketEnablePortUseForDatagramFanout: " "unable to set SO_REUSEPORT?\n"); } } base-7.0.3.1/modules/libcom/src/osi/os/iOS/osdSockUnsentCount.c0000664000577000060420000000116313557101274023004 0ustar anjaesctl/*************************************************************************\ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #define EPICS_PRIVATE_API #include "osiSock.h" /* * epicsSocketUnsentCount () * See https://www.unix.com/man-page/osx/2/setsockopt */ int epicsSocketUnsentCount(SOCKET sock) { int unsent; socklen_t len = sizeof(unsent); if (getsockopt(sock, SOL_SOCKET, SO_NWRITE, &unsent, &len) == 0) return unsent; return -1; } base-7.0.3.1/modules/libcom/src/osi/os/iOS/osdTime.h0000664000577000060420000000121613557101274020601 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Eric Norum */ #ifndef osdTimeh #define osdTimeh #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ epicsShareFunc void convertDoubleToWakeTime(double timeout, struct timespec *wakeTime); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* ifndef osdTimeh */ base-7.0.3.1/modules/libcom/src/osi/os/iOS/osiFileName.h0000664000577000060420000000072013557101274021367 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Eric Norum */ #ifndef osiFileNameH #define osiFileNameH #include "unixFileName.h" #endif /* osiFileNameH */ base-7.0.3.1/modules/libcom/src/osi/os/posix/README0000664000577000060420000000071013557101274020352 0ustar anjaesctlA knowledge of pthreads is necessary to understand osdThread.c and osdSem.c. The following are good references Programming with POSIX Threads, David R. Butenhof, Addison-weslet, 1997 Multithreaded Programming with Pthreads, Bil Lewis & Daniel J. Berg, Sun Microsystems, 1998 Pthreads Programming, Bradford Nichols etc, O'Reilly & Associates, 1996 The implementation of semMutex is based on the example code by Bil Leeis that os available via the WWW. base-7.0.3.1/modules/libcom/src/osi/os/posix/epicsAtomicOSD.cpp0000664000577000060420000000373013557101274023011 0ustar anjaesctl /*************************************************************************\ * Copyright (c) 2011 LANS LLC, as Operator of * Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Jeffrey O. Hill * johill@lanl.gov */ #include #include #include #define epicsExportSharedSymbols #include "epicsAssert.h" #include "epicsAtomic.h" /* * Slow, but probably correct on all systems. * Useful only if something more efficient isn`t * provided based on knowledge of the compiler * or OS * * A statically initialized pthread mutex doesn`t * need to be destroyed * * !!!!! * !!!!! WARNING * !!!!! * !!!!! Do not use this implementation on systems where * !!!!! code runs at interrupt context. If so, then * !!!!! an implementation must be provided that is based * !!!!! on a compiler intrinsic or an interrupt lock and or * !!!!! a spin lock primitive * !!!!! */ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void epicsAtomicLock ( EpicsAtomicLockKey * ) { unsigned countDown = 1000u; int status; while ( true ) { status = pthread_mutex_lock ( & mutex ); if ( status == 0 ) return; assert ( status == EINTR ); static const useconds_t retryDelayUSec = 100000; usleep ( retryDelayUSec ); countDown--; assert ( countDown ); } } void epicsAtomicUnlock ( EpicsAtomicLockKey * ) { const int status = pthread_mutex_unlock ( & mutex ); assert ( status == 0 ); } // Slow, but probably correct on all systems. // Useful only if something more efficient isn`t // provided based on knowledge of the compiler // or OS void epicsAtomicMemoryBarrierFallback (void) { EpicsAtomicLockKey key; epicsAtomicLock ( & key ); epicsAtomicUnlock ( & key ); } base-7.0.3.1/modules/libcom/src/osi/os/posix/epicsAtomicOSD.h0000664000577000060420000000256413557101274022462 0ustar anjaesctl /*************************************************************************\ * Copyright (c) 2011 LANS LLC, as Operator of * Los Alamos National Laboratory. * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef epicsAtomicOSD_h #define epicsAtomicOSD_h #include #define EPICS_ATOMIC_OS_NAME "POSIX" typedef struct EpicsAtomicLockKey {} EpicsAtomicLockKey; #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ epicsShareFunc void epicsAtomicLock ( struct EpicsAtomicLockKey * ); epicsShareFunc void epicsAtomicUnlock ( struct EpicsAtomicLockKey * ); epicsShareFunc void epicsAtomicMemoryBarrierFallback ( void ); #ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) { epicsAtomicMemoryBarrierFallback(); } #endif #ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) { epicsAtomicMemoryBarrierFallback(); } #endif #ifdef __cplusplus } /* end of extern "C" */ #endif /* __cplusplus */ #include "epicsAtomicDefault.h" #endif /* epicsAtomicOSD_h */ base-7.0.3.1/modules/libcom/src/osi/os/posix/epicsMath.h0000664000577000060420000000147213557101274021566 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef epicsMathh #define epicsMathh #include #include #ifdef __cplusplus extern "C" { #endif #ifdef isfinite # undef finite # define finite(x) isfinite((double)(x)) #endif epicsShareExtern float epicsNAN; epicsShareExtern float epicsINF; #ifdef __cplusplus } #endif #endif /* epicsMathh */ base-7.0.3.1/modules/libcom/src/osi/os/posix/epicsTempFile.cpp0000664000577000060420000000131213557101274022726 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include #define epicsExportSharedSymbols #include "epicsTempFile.h" extern "C" epicsShareFunc FILE * epicsShareAPI epicsTempFile ( void ) { return tmpfile (); } base-7.0.3.1/modules/libcom/src/osi/os/posix/osdElfFindAddr.c0000664000577000060420000004267113557101274022462 0ustar anjaesctl/* * Copyright: Stanford University / SLAC National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * Author: Till Straumann , 2011, 2014 */ #include #include #include #include #include #include #include #include #include #include #include #ifdef _POSIX_MAPPED_FILES #include #endif #define epicsExportSharedSymbols #include "epicsMutex.h" #include "epicsThread.h" #include "epicsTime.h" #include "errlog.h" #include "epicsStackTrace.h" #include "epicsStackTracePvt.h" /* This routine is provided by osiClockTime.c */ epicsShareExtern void ClockTime_GetProgramStart(epicsTimeStamp *pDest); #define FIND_ADDR_DEBUG 0 /* * On some systems (linux, solaris) dladdr doesn't find local symbols * or symbols in the main executable. * Hence, we want to use dladdr() to find the file name * where a symbol is defined and if not more information is available * then proceed to lookup symbols in the ELF symbol tables. */ /* Macros to handle elf32 vs. elf64 access to unions etc. */ #define FLD(c,s,f) (ELFCLASS32==c ? s.e32.f : s.e64.f ) #define ARR(c,s,i,f) (ELFCLASS32==c ? s.e32[i].f : s.e64[i].f) /* Elf header */ typedef union Ehdr_ { Elf32_Ehdr e32; Elf64_Ehdr e64; } Ehdr; /* Section header */ typedef union Shdr_ { Elf32_Shdr e32; Elf64_Shdr e64; } Shdr; /* Elf symbol */ typedef union Sym_ { void *raw; Elf32_Sym *e32; Elf64_Sym *e64; } Sym; /* Memory mapped portion of a file; we must * keep additional information because the * map's starting address + length must be * page-aligned (man mmap). */ typedef struct MMap_ { void *addr; off_t off; /* offset into the map where 'real' data start */ size_t len; size_t max; /* max offset: legal data from addr+off .. addr+off+max-1 */ void (*freeMap)(struct MMap_*); /* 'method' to destroy the mapping */ } *MMap; /* Structure describing symbol information * contained in a file. * We keep these around (so that the file * doesn't have to be opened + parsed every * time we do a lookup). */ typedef struct ESyms_ { struct ESyms_ *next; /* linked list; one struct per executable */ const char *fname; /* file name */ int fd; /* file descriptor */ uintptr_t addr; /* address where file is loaded */ MMap symMap; MMap strMap; size_t nsyms; uint8_t eclss; } *ESyms; /* LOCKING NOTE: if the ELF symbol facility is ever expanded to be truly used * in a multithreaded way then proper multiple-readers, single-writer locking * should be implemented. */ /* Linked list where we keep all our ESyms */ static ESyms elfs = 0; static epicsMutexId listMtx; static epicsThreadOnceId listMtxInitId = EPICS_THREAD_ONCE_INIT; static void listMtxInit(void *unused) { listMtx = epicsMutexMustCreate(); } static void elfsLockWrite() { epicsThreadOnce(&listMtxInitId, listMtxInit, 0); epicsMutexMustLock(listMtx); } static void elfsUnlockWrite() { epicsMutexUnlock(listMtx); } static void freeMap(MMap m) { if ( m ) { m->freeMap(m); free(m); } } /* Helper to read exactly 'sz' bytes into 'buf' * RETURNS: # chars read or negative value on error. */ static ssize_t do_read(int fd, void *buf, ssize_t sz) { ssize_t got; char *ptr=(char*)buf; while ( sz > 0 ) { if ( (got=read(fd,ptr,sz)) <= 0 ) { return got; } ptr+=got; sz -=got; } return ptr-(char*)buf; } /* Elf file access -- can either be with mmap or by sequential read */ #ifdef _POSIX_MAPPED_FILES /* Destructor for data that is mmap()ed */ static void freeMapMmap(MMap m) { if ( MAP_FAILED != m->addr ) munmap( m->addr, m->len ); } /* Obtain section data with mmap() */ static MMap getscn_mmap(int fd, uint8_t c, Shdr *shdr_p) { off_t n; MMap rval = 0; size_t pgsz = sysconf(_SC_PAGESIZE); if ( 0 == (n = (off_t)FLD(c,(*shdr_p),sh_size)) ) { errlogPrintf("elfRead - getscn() -- no section data\n"); goto bail; } if ( ! (rval = (MMap) malloc(sizeof(*rval))) ) { errlogPrintf("elfRead - getscn() -- no memory for section map\n"); goto bail; } rval->freeMap = freeMapMmap; rval->off = (off_t) (FLD(c,(*shdr_p),sh_offset) & (pgsz-1)); rval->len = (n + rval->off + (pgsz - 1)) & ~(pgsz - 1); rval->max = rval->len - rval->off; if ( MAP_FAILED == (rval->addr = mmap(0, rval->len, PROT_READ, MAP_SHARED, fd, (off_t) (FLD(c,(*shdr_p),sh_offset) & ~(pgsz-1)))) ) { errlogPrintf("elfRead - getscn() -- mapping section contents: %s\n", strerror(errno)); goto bail; } return rval; bail: freeMap(rval); return 0; } #else static MMap getscn_mmap(int fd, uint8_t c, Shrd *shdr_p) { return 0; } #endif /* Destructor for data that is read into a malloc()ed buffer */ static void freeMapMalloc(MMap m) { free(m->addr); } /* Read section data into a malloc()ed buffer */ static MMap getscn_read(int fd, uint8_t c, Shdr *shdr_p) { ssize_t n; MMap rval = 0; if ( 0 == (n = (ssize_t) FLD(c,(*shdr_p),sh_size)) ) { errlogPrintf("elfRead - getscn() -- no section data\n"); goto bail; } if ( ! (rval = (MMap) malloc(sizeof(*rval))) ) { errlogPrintf("elfRead - getscn() -- no memory for section map\n"); goto bail; } rval->freeMap = freeMapMalloc; if ( ! (rval->addr = malloc(n)) ) { errlogPrintf("elfRead - getscn() -- no memory for section data\n"); goto bail; } rval->off = 0; rval->len = n; rval->max = rval->len - rval->off; /* seek to symbol table contents */ if ( (off_t)-1 == lseek(fd, (off_t) FLD(c,(*shdr_p),sh_offset), SEEK_SET) ) { errlogPrintf("elfRead - getscn() -- seeking to sh_offset: %s\n", strerror(errno)); goto bail; } if ( n != do_read(fd, rval->addr, n) ) { errlogPrintf("elfRead - getscn() -- reading section contents: %s\n", strerror(errno)); goto bail; } return rval; bail: freeMap(rval); return 0; } static MMap getscn(int fd, uint8_t c, Shdr *shdr_p) { MMap rval = getscn_mmap(fd, c, shdr_p); if ( ! rval ) rval = getscn_read(fd, c, shdr_p); return rval; } /* Release resources but keep filename so that * a file w/o symbol table is not read over and over again. */ static void elfSymsRelease(ESyms es) { if ( es ) { freeMap(es->symMap); es->symMap = 0; freeMap(es->strMap); es->strMap = 0; if ( es->fd >= 0 ) close(es->fd); es->fd = -1; es->nsyms = 0; } } static ESyms elfRead(const char *fname, uintptr_t fbase) { int i; Ehdr ehdr; Shdr shdr; uint8_t c; ESyms es; ssize_t idx,n; const char *cp; struct stat stat_b; if ( !(es = (ESyms) malloc(sizeof(*es))) ) { /* no memory -- give up */ return 0; } memset(es, 0, sizeof(*es)); es->fd = -1; es->fname = fname; if ( (es->fd = open(fname, O_RDONLY)) < 0 ) { errlogPrintf("elfRead() -- unable to open file: %s\n", strerror(errno)); goto bail; } if ( EI_NIDENT != do_read(es->fd, &ehdr, EI_NIDENT) ) { errlogPrintf("elfRead() -- unable to read ELF e_ident: %s\n", strerror(errno)); goto bail; } if ( ELFMAG0 != ehdr.e32.e_ident[EI_MAG0] || ELFMAG1 != ehdr.e32.e_ident[EI_MAG1] || ELFMAG2 != ehdr.e32.e_ident[EI_MAG2] || ELFMAG3 != ehdr.e32.e_ident[EI_MAG3] ) { errlogPrintf("bad ELF magic number\n"); goto bail; } if ( EV_CURRENT != ehdr.e32.e_ident[EI_VERSION] ) { errlogPrintf("bad ELF version\n"); goto bail; } switch ( (es->eclss = c = ehdr.e32.e_ident[EI_CLASS]) ) { default: errlogPrintf("bad ELF class\n"); goto bail; case ELFCLASS32: n = sizeof(Elf32_Ehdr); break; case ELFCLASS64: n = sizeof(Elf64_Ehdr); break; } n -= EI_NIDENT; if ( 0 == fstat(es->fd, &stat_b) ) { epicsTimeStamp progStartStamp; time_t progStartTime; ClockTime_GetProgramStart(&progStartStamp); epicsTimeToTime_t(&progStartTime, &progStartStamp); if ( stat_b.st_mtime >= progStartTime ) { errlogPrintf("elfRead() -- WARNING: '%s' was modified after program start -- symbol information may be inaccurate or invalid\n", fname); } } /* read rest */ if ( n != do_read(es->fd, ehdr.e32.e_ident + EI_NIDENT, n) ) { errlogPrintf("elfRead() -- unable to read ELF ehdr: %s\n", strerror(errno)); goto bail; } /* seek to section header table */ if ( (off_t)-1 == lseek(es->fd, (off_t) FLD(c,ehdr,e_shoff), SEEK_SET) ) { errlogPrintf("elfRead() -- unable to seek to shoff: %s\n", strerror(errno)); goto bail; } n = ELFCLASS32 == c ? sizeof(shdr.e32) : sizeof(shdr.e64); for ( i = 0; ifd, &shdr, n) ) { errlogPrintf("elfRead() -- unable to read section header: %s\n", strerror(errno)); goto bail; } if ( SHT_SYMTAB == FLD(c,shdr,sh_type) ) break; } if ( i>=FLD(c,ehdr,e_shnum) ) { /* no SYMTAB -- try dynamic symbols */ if ( (off_t)-1 == lseek(es->fd, (off_t) FLD(c,ehdr,e_shoff), SEEK_SET) ) { errlogPrintf("elfRead() -- unable to seek to shoff: %s\n", strerror(errno)); goto bail; } for ( i = 0; ifd, &shdr, n) ) { errlogPrintf("elfRead() -- unable to read section header: %s\n", strerror(errno)); goto bail; } if ( SHT_DYNSYM == FLD(c,shdr,sh_type) ) break; } } if ( i>=FLD(c,ehdr,e_shnum) ) { errlogPrintf("elfRead() -- no symbol table found\n"); goto bail; } if ( 0 == (n = (ssize_t) FLD(c,shdr,sh_size)) ) { errlogPrintf("elfRead() -- no symbol table data\n"); goto bail; } if ( !(es->symMap = getscn(es->fd, c, &shdr)) ) { errlogPrintf("elfRead() -- unable to read ELF symtab\n"); goto bail; } es->nsyms = n / (ELFCLASS32==c ? sizeof(Elf32_Sym) : sizeof(Elf64_Sym)); /* find and read string table */ n = ELFCLASS32 == c ? sizeof(shdr.e32) : sizeof(shdr.e64); /* seek to section header table */ if ( (off_t)-1 == lseek(es->fd, (off_t) (FLD(c,ehdr,e_shoff) + n * FLD(c,shdr,sh_link)), SEEK_SET) ) { errlogPrintf("elfRead() -- unable to lseek to ELF e_shoff: %s\n", strerror(errno)); goto bail; } if ( n != do_read(es->fd, &shdr, n) ) { errlogPrintf("elfRead() -- unable to read ELF strtab section header: %s\n", strerror(errno)); goto bail; } if ( !(es->strMap = getscn(es->fd,c,&shdr)) ) { errlogPrintf("elfRead() -- unable to read ELF strtab\n"); goto bail; } /* Make sure there is a terminating NUL - unfortunately, memrchr is not portable */ cp = (char*)es->strMap->addr + es->strMap->off; for ( idx = es->strMap->max - 1; i >= 0; i-- ) { if ( !cp[i] ) break; } es->strMap->max = idx + 1; switch ( FLD(c,ehdr,e_type) ) { case ET_EXEC: /* Symbols in an executable already has absolute addresses */ es->addr = 0; break; case ET_DYN: /* Symbols in an shared library are relative to base address */ es->addr = fbase; break; default: errlogPrintf("dlLookupAddr(): Unexpected ELF object file type %u\n", FLD(c,ehdr,e_type)); goto bail; } return es; bail: elfSymsRelease(es); return es; } /* Destroy a cached ELF symbol table */ static void elfSymsDestroy(ESyms es) { if ( es ) { elfSymsRelease(es); free(es); } } /* Destroy all cached ELF symbol tables * * However - w/o proper locking for read access * this must not be used. Otherwise, readers * will hold stale pointers... * * We leave the commented code here to show * how the tables can be torn down. void elfSymTblFlush() { ESyms es; elfsLockWrite(); while ( (es = elfs) ) { elfs = es->next; es->next = 0; elfsUnlockWrite(); elfSymsDestroy(es); elfsLockWrite(); } elfsUnlockWrite(); } */ /* This routine must be called with the write-lock held */ static ESyms elfSymsFind(const char *fname) { ESyms es; for ( es=elfs; es && strcmp(fname, es->fname); es = es->next ) /* nothing else to do */; return es; } int epicsFindAddr(void *addr, epicsSymbol *sym_p) { Dl_info inf; ESyms es,nes = 0; uintptr_t minoff,off; size_t i; Sym sym; Sym nearest; const char *strtab; uint8_t c; size_t idx; if ( ! dladdr(addr, &inf) || (!inf.dli_fname && !inf.dli_sname) ) { sym_p->f_nam = 0; sym_p->s_nam = 0; /* unable to lookup */ return 0; } sym_p->f_nam = inf.dli_fname; /* If the symbol is in the main executable then solaris' dladdr returns bogus info */ #ifndef __sun if ( (sym_p->s_nam = inf.dli_sname) ) { sym_p->s_val = inf.dli_saddr; /* Have a symbol name - just use it and be done */ return 0; } #endif /* No symbol info; try to access ELF file and ready symbol table from there */ elfsLockWrite(); /* See if we have loaded this file already */ es = elfSymsFind(inf.dli_fname); if ( !es ) { elfsUnlockWrite(); if ( ! (nes = elfRead(inf.dli_fname, (uintptr_t)inf.dli_fbase)) ) { /* this path can only be taken if there is no memory for '*nes' */ return 0; } elfsLockWrite(); /* Has someone else intervened and already added this file while we were reading ? */ es = elfSymsFind(inf.dli_fname); if ( es ) { /* will undo our work in the unlikely event... */ } else { nes->next = elfs; es = elfs = nes; nes = 0; } } elfsUnlockWrite(); /* Undo our work in the unlikely event that it was redundant */ if ( nes ) elfSymsDestroy( nes ); nearest.raw = 0; minoff = (uintptr_t)-1LL; if ( es->nsyms ) { c = es->eclss; sym.raw = (char*)es->symMap->addr + es->symMap->off; strtab = (char*)es->strMap->addr + es->strMap->off; /* Do a brute-force search through the symbol table; if this is executed * very often then it would be worthwhile constructing a sorted list of * symbol addresses but for the stack trace we don't care... */ #if (FIND_ADDR_DEBUG & 1) printf("Looking for %p\n", addr); #endif if ( ELFCLASS32 == c ) { for ( i=0; insyms; i++ ) { if ( STT_FUNC != ELF32_ST_TYPE(sym.e32[i].st_info) ) continue; /* don't bother about undefined symbols */ if ( 0 == sym.e32[i].st_shndx ) continue; #if (FIND_ADDR_DEBUG & 1) printf("Trying: %s (0x%lx)\n", strtab + sym.e32[i].st_name, (unsigned long)(sym.e32[i].st_value + es->addr)); #endif if ( (uintptr_t)addr >= (uintptr_t)sym.e32[i].st_value + es->addr ) { off = (uintptr_t)addr - ((uintptr_t)sym.e32[i].st_value + es->addr); if ( off < minoff ) { minoff = off; nearest.e32 = &sym.e32[i]; } } } } else { for ( i=0; insyms; i++ ) { if ( STT_FUNC != ELF64_ST_TYPE(sym.e64[i].st_info) ) continue; /* don't bother about undefined symbols */ if ( 0 == sym.e64[i].st_shndx ) continue; #if (FIND_ADDR_DEBUG & 1) printf("Trying: %s (0x%llx)\n", strtab + sym.e64[i].st_name, (unsigned long long)(sym.e64[i].st_value + es->addr)); #endif if ( (uintptr_t)addr >= (uintptr_t)sym.e64[i].st_value + es->addr ) { off = (uintptr_t)addr - ((uintptr_t)sym.e64[i].st_value + es->addr); if ( off < minoff ) { minoff = off; nearest.e64 = &sym.e64[i]; } } } } } if ( nearest.raw && ( (idx = ARR(c,nearest,0,st_name)) < es->strMap->max ) ) { sym_p->s_nam = strtab + idx; sym_p->s_val = (char*) ARR(c, nearest, 0, st_value) + es->addr; } return 0; } int epicsFindAddrGetFeatures(void) { /* The static information given here may not be correct; * it also depends on * - compilation (frame pointer optimization) * - linkage (static vs. dynamic) * - stripping */ return EPICS_STACKTRACE_LCL_SYMBOLS | EPICS_STACKTRACE_GBL_SYMBOLS | EPICS_STACKTRACE_DYN_SYMBOLS; } base-7.0.3.1/modules/libcom/src/osi/os/posix/osdEvent.c0000664000577000060420000001162313557101274021432 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osi/os/posix/osdEvent.c */ /* Author: Marty Kraimer Date: 13AUG1999 */ #include #include #include #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsEvent.h" #include "epicsTime.h" #include "errlog.h" struct epicsEventOSD { pthread_mutex_t mutex; pthread_cond_t cond; int isFull; }; #define printStatus(status, routine, func) \ errlogPrintf("%s: %s failed: %s\n", (func), (routine), strerror(status)) #define checkStatus(status, routine, func) \ if (status) { \ printStatus(status, routine, func); \ } #define checkStatusReturn(status, routine, func) \ if (status) { \ printStatus(status, routine, func); \ return epicsEventError; \ } epicsShareFunc epicsEventId epicsEventCreate(epicsEventInitialState init) { epicsEventId pevent = malloc(sizeof(*pevent)); if (pevent) { int status = pthread_mutex_init(&pevent->mutex, 0); pevent->isFull = (init == epicsEventFull); if (status) { printStatus(status, "pthread_mutex_init", "epicsEventCreate"); } else { status = pthread_cond_init(&pevent->cond, 0); if (!status) return pevent; printStatus(status, "pthread_cond_init", "epicsEventCreate"); status = pthread_mutex_destroy(&pevent->mutex); checkStatus(status, "pthread_mutex_destroy", "epicsEventCreate"); } free(pevent); } return NULL; } epicsShareFunc void epicsEventDestroy(epicsEventId pevent) { int status = pthread_mutex_destroy(&pevent->mutex); checkStatus(status, "pthread_mutex_destroy", "epicsEventDestroy"); status = pthread_cond_destroy(&pevent->cond); checkStatus(status, "pthread_cond_destroy", "epicsEventDestroy"); free(pevent); } epicsShareFunc epicsEventStatus epicsEventTrigger(epicsEventId pevent) { int status = pthread_mutex_lock(&pevent->mutex); checkStatusReturn(status, "pthread_mutex_lock", "epicsEventTrigger"); if (!pevent->isFull) { pevent->isFull = 1; status = pthread_cond_signal(&pevent->cond); checkStatus(status, "pthread_cond_signal", "epicsEventTrigger"); } status = pthread_mutex_unlock(&pevent->mutex); checkStatusReturn(status, "pthread_mutex_unlock", "epicsEventTrigger"); return epicsEventOK; } epicsShareFunc epicsEventStatus epicsEventWait(epicsEventId pevent) { epicsEventStatus result = epicsEventOK; int status = pthread_mutex_lock(&pevent->mutex); checkStatusReturn(status, "pthread_mutex_lock", "epicsEventWait"); while (!pevent->isFull) { status = pthread_cond_wait(&pevent->cond, &pevent->mutex); if (status) { printStatus(status, "pthread_cond_wait", "epicsEventWait"); result = epicsEventError; goto release; } } pevent->isFull = 0; result = epicsEventOK; release: status = pthread_mutex_unlock(&pevent->mutex); checkStatusReturn(status, "pthread_mutex_unlock", "epicsEventWait"); return result; } epicsShareFunc epicsEventStatus epicsEventWaitWithTimeout(epicsEventId pevent, double timeout) { epicsEventStatus result = epicsEventOK; int status = pthread_mutex_lock(&pevent->mutex); checkStatusReturn(status, "pthread_mutex_lock", "epicsEventWaitWithTimeout"); if (!pevent->isFull) { struct timespec wakeTime; convertDoubleToWakeTime(timeout, &wakeTime); while (!status && !pevent->isFull) { status = pthread_cond_timedwait(&pevent->cond, &pevent->mutex, &wakeTime); } if (status) { result = (status == ETIMEDOUT) ? epicsEventWaitTimeout : epicsEventError; goto release; } } pevent->isFull = 0; release: status = pthread_mutex_unlock(&pevent->mutex); checkStatusReturn(status, "pthread_mutex_unlock", "epicsEventWaitWithTimeout"); return result; } epicsShareFunc epicsEventStatus epicsEventTryWait(epicsEventId id) { return epicsEventWaitWithTimeout(id, 0.0); } epicsShareFunc void epicsEventShow(epicsEventId pevent, unsigned int level) { printf("epicsEvent %p: %s\n", pevent, pevent->isFull ? "full" : "empty"); if (level > 0) printf(" pthread_mutex = %p, pthread_cond = %p\n", &pevent->mutex, &pevent->cond); } base-7.0.3.1/modules/libcom/src/osi/os/posix/osdEvent.h0000664000577000060420000000113713557101274021436 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* for a pure posix implementation no osdEvent.h definitions are needed*/ base-7.0.3.1/modules/libcom/src/osi/os/posix/osdExecinfoBackTrace.cpp0000664000577000060420000000141613557101274024210 0ustar anjaesctl/* * Copyright: Stanford University / SLAC National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * Author: Till Straumann , 2011, 2014 */ // pull in libc feature test macros #include // execinfo.h may not be present if uclibc is configured to omit backtrace() #if !defined(__UCLIBC_MAJOR__) || defined(__UCLIBC_HAS_EXECINFO__) # define HAS_EXECINFO 1 #else # define HAS_EXECINFO 0 #endif #if HAS_EXECINFO #include #endif #define epicsExportSharedSymbols #include "epicsStackTracePvt.h" int epicsBackTrace(void **buf, int buf_sz) { #if HAS_EXECINFO return backtrace(buf, buf_sz); #else return -1; #endif } base-7.0.3.1/modules/libcom/src/osi/os/posix/osdFindSymbol.c0000664000577000060420000000142213557101274022413 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osi/os/posix/osdFindSymbol.c */ #include #define epicsExportSharedSymbols #include "epicsFindSymbol.h" epicsShareFunc void * epicsLoadLibrary(const char *name) { return dlopen(name, RTLD_LAZY | RTLD_GLOBAL); } epicsShareFunc const char *epicsLoadError(void) { return dlerror(); } epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name) { return dlsym(0, name); } base-7.0.3.1/modules/libcom/src/osi/os/posix/osdMonotonic.c0000664000577000060420000000364213557101274022320 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Michael Davidsaver * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #define epicsExportSharedSymbols #define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "dbDefs.h" #include "errlog.h" #include "epicsTime.h" #include "generalTimeSup.h" static clockid_t osdMonotonicID; static epicsUInt64 osdMonotonicResolution; void osdMonotonicInit(void) { unsigned i; clockid_t ids[] = { #ifdef CLOCK_HIGHRES CLOCK_HIGHRES, /* solaris specific */ #endif #ifdef CLOCK_MONOTONIC CLOCK_MONOTONIC, /* Linux, RTEMS, and probably others */ #endif /* fallback and vxWorks, not actually monotonic, but always available */ CLOCK_REALTIME }; for(i=0; i #include #include #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsMutex.h" #include "cantProceed.h" #include "epicsTime.h" #include "errlog.h" #include "epicsAssert.h" #define checkStatus(status,message) \ if((status)) { \ errlogPrintf("epicsMutex %s failed: error %s\n", \ (message), strerror((status))); \ } #define checkStatusQuit(status,message,method) \ if(status) { \ errlogPrintf("epicsMutex %s failed: error %s\n", \ (message), strerror((status))); \ cantProceed((method)); \ } static int mutexLock(pthread_mutex_t *id) { int status; while ((status = pthread_mutex_lock(id)) == EINTR) { errlogPrintf("pthread_mutex_lock returned EINTR. Violates SUSv3\n"); } return status; } /* Until these can be demonstrated to work leave them undefined*/ /* On solaris 8 _POSIX_THREAD_PRIO_INHERIT fails*/ #undef _POSIX_THREAD_PROCESS_SHARED #undef _POSIX_THREAD_PRIO_INHERIT /* Two completely different implementations are provided below * If support is available for PTHREAD_MUTEX_RECURSIVE then * only pthread_mutex is used. * If support is not available for PTHREAD_MUTEX_RECURSIVE then * a much more complicated solution is required */ #if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 typedef struct epicsMutexOSD { pthread_mutex_t lock; pthread_mutexattr_t mutexAttr; } epicsMutexOSD; epicsMutexOSD * epicsMutexOsdCreate(void) { epicsMutexOSD *pmutex; int status; pmutex = calloc(1, sizeof(*pmutex)); if(!pmutex) goto fail; status = pthread_mutexattr_init(&pmutex->mutexAttr); if (status) goto fail; #if defined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT > 0 status = pthread_mutexattr_setprotocol(&pmutex->mutexAttr, PTHREAD_PRIO_INHERIT); if (errVerbose) checkStatus(status, "pthread_mutexattr_setprotocal"); #endif /*_POSIX_THREAD_PRIO_INHERIT*/ status = pthread_mutexattr_settype(&pmutex->mutexAttr, PTHREAD_MUTEX_RECURSIVE); checkStatus(status, "pthread_mutexattr_settype"); if (status) goto fail; status = pthread_mutex_init(&pmutex->lock, &pmutex->mutexAttr); if (status) goto dattr; return pmutex; dattr: pthread_mutexattr_destroy(&pmutex->mutexAttr); fail: free(pmutex); return NULL; } void epicsMutexOsdDestroy(struct epicsMutexOSD * pmutex) { int status; status = pthread_mutex_destroy(&pmutex->lock); checkStatus(status, "pthread_mutex_destroy"); status = pthread_mutexattr_destroy(&pmutex->mutexAttr); checkStatus(status, "pthread_mutexattr_destroy"); free(pmutex); } void epicsMutexOsdUnlock(struct epicsMutexOSD * pmutex) { int status; status = pthread_mutex_unlock(&pmutex->lock); checkStatus(status, "pthread_mutex_unlock epicsMutexOsdUnlock"); } epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * pmutex) { int status; status = mutexLock(&pmutex->lock); if (status == EINVAL) return epicsMutexLockError; if(status) { errlogMessage("epicsMutex pthread_mutex_lock failed: error epicsMutexOsdLock\n"); return epicsMutexLockError; } return epicsMutexLockOK; } epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * pmutex) { int status; if (!pmutex) return epicsMutexLockError; status = pthread_mutex_trylock(&pmutex->lock); if (status == EINVAL) return epicsMutexLockError; if (status == EBUSY) return epicsMutexLockTimeout; if(status) { errlogMessage("epicsMutex pthread_mutex_trylock failed: error epicsMutexOsdTryLock"); return epicsMutexLockError; } return epicsMutexLockOK; } void epicsMutexOsdShow(struct epicsMutexOSD * pmutex, unsigned int 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); } #else /*defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 */ typedef struct epicsMutexOSD { pthread_mutex_t lock; pthread_mutexattr_t mutexAttr; pthread_cond_t waitToBeOwner; #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED > 0 pthread_condattr_t condAttr; #endif /*_POSIX_THREAD_PROCESS_SHARED*/ int count; int owned; /* TRUE | FALSE */ pthread_t ownerTid; } epicsMutexOSD; epicsMutexOSD * epicsMutexOsdCreate(void) { epicsMutexOSD *pmutex; int status; pmutex = calloc(1, sizeof(*pmutex)); if(!pmutex) return NULL; status = pthread_mutexattr_init(&pmutex->mutexAttr); if(status) goto fail; #if defined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT > 0 status = pthread_mutexattr_setprotocol( &pmutex->mutexAttr,PTHREAD_PRIO_INHERIT); if (errVerbose) checkStatus(status, "pthread_mutexattr_setprotocal"); #endif /*_POSIX_THREAD_PRIO_INHERIT*/ status = pthread_mutex_init(&pmutex->lock, &pmutex->mutexAttr); if(status) goto dattr; #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED > 0 status = pthread_condattr_init(&pmutex->condAttr); checkStatus(status, "pthread_condattr_init"); status = pthread_condattr_setpshared(&pmutex->condAttr, PTHREAD_PROCESS_PRIVATE); checkStatus(status, "pthread_condattr_setpshared"); status = pthread_cond_init(&pmutex->waitToBeOwner, &pmutex->condAttr); #else status = pthread_cond_init(&pmutex->waitToBeOwner, 0); #endif /*_POSIX_THREAD_PROCESS_SHARED*/ if(status) goto dmutex; return pmutex; dmutex: pthread_mutex_destroy(&pmutex->lock); dattr: pthread_mutexattr_destroy(&pmutex->mutexAttr); fail: free(pmutex); return NULL; } void epicsMutexOsdDestroy(struct epicsMutexOSD * pmutex) { int status; status = pthread_cond_destroy(&pmutex->waitToBeOwner); checkStatus(status, "pthread_cond_destroy"); #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED > 0 status = pthread_condattr_destroy(&pmutex->condAttr); #endif /*_POSIX_THREAD_PROCESS_SHARED*/ status = pthread_mutex_destroy(&pmutex->lock); checkStatus(status, "pthread_mutex_destroy"); status = pthread_mutexattr_destroy(&pmutex->mutexAttr); checkStatus(status, "pthread_mutexattr_destroy"); free(pmutex); } void epicsMutexOsdUnlock(struct epicsMutexOSD * pmutex) { int status; status = mutexLock(&pmutex->lock); checkStatus(status, "pthread_mutex_lock epicsMutexOsdUnlock"); if(status) return; if ((pmutex->count <= 0) || (pmutex->ownerTid != pthread_self())) { pthread_mutex_unlock(&pmutex->lock); checkStatus(status, "pthread_mutex_unlock epicsMutexOsdUnlock"); errlogPrintf("epicsMutexOsdUnlock but caller is not owner\n"); cantProceed("epicsMutexOsdUnlock but caller is not owner"); return; } pmutex->count--; if (pmutex->count == 0) { pmutex->owned = 0; pmutex->ownerTid = 0; status = pthread_cond_signal(&pmutex->waitToBeOwner); checkStatusQuit(status, "pthread_cond_signal epicsMutexOsdUnlock", "epicsMutexOsdUnlock"); } status = pthread_mutex_unlock(&pmutex->lock); checkStatus(status, "pthread_mutex_unlock epicsMutexOsdUnlock"); } static int condWait(pthread_cond_t *condId, pthread_mutex_t *mutexId) { int status; while ((status = pthread_cond_wait(condId, mutexId)) == EINTR) { errlogPrintf("pthread_cond_wait returned EINTR. Violates SUSv3\n"); } return status; } epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * pmutex) { pthread_t tid = pthread_self(); int status; if (!pmutex || !tid) return epicsMutexLockError; status = mutexLock(&pmutex->lock); if (status == EINVAL) return epicsMutexLockError; checkStatus(status, "pthread_mutex_lock epicsMutexOsdLock"); if(status) return epicsMutexLockError; while (pmutex->owned && !pthread_equal(pmutex->ownerTid, tid)) condWait(&pmutex->waitToBeOwner, &pmutex->lock); pmutex->ownerTid = tid; pmutex->owned = 1; pmutex->count++; status = pthread_mutex_unlock(&pmutex->lock); checkStatus(status, "pthread_mutex_unlock epicsMutexOsdLock"); if(status) return epicsMutexLockError; return epicsMutexLockOK; } epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * pmutex) { pthread_t tid = pthread_self(); epicsMutexLockStatus result; int status; status = mutexLock(&pmutex->lock); if (status == EINVAL) return epicsMutexLockError; checkStatus(status, "pthread_mutex_lock epicsMutexOsdTryLock"); if(status) return epicsMutexLockError; if (!pmutex->owned || pthread_equal(pmutex->ownerTid, tid)) { pmutex->ownerTid = tid; pmutex->owned = 1; pmutex->count++; result = epicsMutexLockOK; } else { result = epicsMutexLockTimeout; } status = pthread_mutex_unlock(&pmutex->lock); checkStatus(status, "pthread_mutex_unlock epicsMutexOsdTryLock"); if(status) return epicsMutexLockError; return result; } void epicsMutexOsdShow(struct epicsMutexOSD *pmutex,unsigned int level) { printf("ownerTid %p count %d owned %d\n", (void *)pmutex->ownerTid, pmutex->count, pmutex->owned); } #endif /*defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 */ base-7.0.3.1/modules/libcom/src/osi/os/posix/osdMutex.h0000664000577000060420000000113713557101274021457 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* for a pure posix implementation no osdMutex.h definitions are needed*/ base-7.0.3.1/modules/libcom/src/osi/os/posix/osdProcess.c0000664000577000060420000000650613557101274021773 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Operating System Dependent Implementation of osiProcess.h * * Author: Jeff Hill * */ #include #include #include #include #include #include #include #include #include #include #define epicsExportSharedSymbols #include "osiProcess.h" #include "errlog.h" #include "epicsAssert.h" epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn) { struct passwd *p; p = getpwuid ( getuid () ); if ( p && p->pw_name ) { size_t len = strlen ( p->pw_name ); unsigned uiLength; if ( len > UINT_MAX || len <= 0 ) { return osiGetUserNameFail; } uiLength = (unsigned) len; if ( uiLength + 1 >= bufSizeIn ) { return osiGetUserNameFail; } strncpy ( pBuf, p->pw_name, (size_t) bufSizeIn ); return osiGetUserNameSuccess; } else { return osiGetUserNameFail; } } epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess (const char *pProcessName, const char *pBaseExecutableName) { int status; /* * create a duplicate process */ status = fork (); if (status < 0) { return osiSpawnDetachedProcessFail; } /* * return to the caller * in the initiating (parent) process */ if (status) { return osiSpawnDetachedProcessSuccess; } /* * This is executed only by the new child process. * Close all open files except for STDIO, so they will not * be inherited by the new program. */ { int fd, maxfd = sysconf ( _SC_OPEN_MAX ); for ( fd = 0; fd <= maxfd; fd++ ) { if (fd==STDIN_FILENO) continue; if (fd==STDOUT_FILENO) continue; if (fd==STDERR_FILENO) continue; close (fd); } } #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 /* * Drop real-time SCHED_FIFO priority */ { struct sched_param p; p.sched_priority = 0; status = sched_setscheduler(0, SCHED_OTHER, &p); } #endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ /* * Run the specified executable */ status = execlp (pBaseExecutableName, pBaseExecutableName, (char *)NULL); if ( status < 0 ) { fprintf ( stderr, "**** The executable \"%s\" couldn't be located\n", pBaseExecutableName ); fprintf ( stderr, "**** because of errno = \"%s\".\n", strerror (errno) ); fprintf ( stderr, "**** You may need to modify your PATH environment variable.\n" ); fprintf ( stderr, "**** Unable to start \"%s\" process.\n", pProcessName); } /* Don't run our parent's atexit() handlers */ _exit ( -1 ); } base-7.0.3.1/modules/libcom/src/osi/os/posix/osdSignal.cpp0000664000577000060420000000323413557101274022125 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Authors: J. Hill, A. Johnson */ #include #include #include #include #define epicsExportSharedSymbols #include "epicsSignal.h" static void ignoreIfDefault(int signum, const char *name) { struct sigaction curAction; int status = sigaction(signum, NULL, &curAction); if (status >= 0 && curAction.sa_handler == SIG_DFL) { curAction.sa_handler = SIG_IGN; status = sigaction(signum, &curAction, NULL); } if (status < 0) { fprintf(stderr, "%s: sigaction failed for %s, %s\n", __FILE__, name, strerror(errno)); } } /* * epicsSignalInstallSigHupIgnore () */ epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore (void) { ignoreIfDefault(SIGHUP, "SIGHUP"); } /* * epicsSignalInstallSigPipeIgnore () */ epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore (void) { ignoreIfDefault(SIGPIPE, "SIGPIPE"); } /* Disabled */ epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * /* threadId */ ) {} base-7.0.3.1/modules/libcom/src/osi/os/posix/osdSock.c0000664000577000060420000001057513557101274021255 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* osdSock.c */ /* * Author: Jeff Hill * Date: 04-05-94 * */ #include #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsThread.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "osiSock.h" #include "epicsAssert.h" #include "errlog.h" /* * Protect some routines which are not thread-safe */ static epicsMutexId infoMutex; static void createInfoMutex (void *unused) { infoMutex = epicsMutexMustCreate (); } static void lockInfo (void) { static epicsThreadOnceId infoMutexOnceFlag = EPICS_THREAD_ONCE_INIT; epicsThreadOnce (&infoMutexOnceFlag, createInfoMutex, NULL); epicsMutexMustLock (infoMutex); } static void unlockInfo (void) { epicsMutexUnlock (infoMutex); } /* * NOOP */ int osiSockAttach() { return 1; } /* * NOOP */ void osiSockRelease() { } /* * this version sets the file control flags so that * the socket will be closed if the user uses exec() * as is the case with third party tools such as TCL/TK */ epicsShareFunc SOCKET epicsShareAPI epicsSocketCreate ( int domain, int type, int protocol ) { SOCKET sock = socket ( domain, type, protocol ); if ( sock < 0 ) { sock = INVALID_SOCKET; } else { int status = fcntl ( sock, F_SETFD, FD_CLOEXEC ); if ( status < 0 ) { char buf [ 64 ]; epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); errlogPrintf ( "epicsSocketCreate: failed to " "fcntl FD_CLOEXEC because \"%s\"\n", buf ); close ( sock ); sock = INVALID_SOCKET; } } return sock; } epicsShareFunc int epicsShareAPI epicsSocketAccept ( int sock, struct sockaddr * pAddr, osiSocklen_t * addrlen ) { int newSock = accept ( sock, pAddr, addrlen ); if ( newSock < 0 ) { newSock = INVALID_SOCKET; } else { int status = fcntl ( newSock, F_SETFD, FD_CLOEXEC ); if ( status < 0 ) { char buf [ 64 ]; epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); errlogPrintf ( "epicsSocketCreate: failed to " "fcntl FD_CLOEXEC because \"%s\"\n", buf ); close ( newSock ); newSock = INVALID_SOCKET; } } return newSock; } epicsShareFunc void epicsShareAPI epicsSocketDestroy ( SOCKET s ) { int status = close ( s ); if ( status < 0 ) { char buf [ 64 ]; epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); errlogPrintf ( "epicsSocketDestroy: failed to " "close a socket because \"%s\"\n", buf ); } } /* * ipAddrToHostName * On many systems, gethostbyaddr must be protected by a * mutex since the routine is not thread-safe. */ epicsShareFunc unsigned epicsShareAPI ipAddrToHostName (const struct in_addr *pAddr, char *pBuf, unsigned bufSize) { struct hostent *ent; int ret = 0; if (bufSize<1) { return 0; } lockInfo (); ent = gethostbyaddr((const char *) pAddr, sizeof (*pAddr), AF_INET); if (ent) { strncpy (pBuf, ent->h_name, bufSize); pBuf[bufSize-1] = '\0'; ret = strlen (pBuf); } unlockInfo (); return ret; } /* * hostToIPAddr () * On many systems, gethostbyname must be protected by a * mutex since the routine is not thread-safe. */ epicsShareFunc int epicsShareAPI hostToIPAddr (const char *pHostName, struct in_addr *pIPA) { struct hostent *phe; int ret = -1; lockInfo (); phe = gethostbyname (pHostName); if (phe && phe->h_addr_list[0]) { if (phe->h_addrtype==AF_INET && phe->h_length<=sizeof(struct in_addr)) { struct in_addr *pInAddrIn = (struct in_addr *) phe->h_addr_list[0]; *pIPA = *pInAddrIn; ret = 0; } } unlockInfo (); return ret; } base-7.0.3.1/modules/libcom/src/osi/os/posix/osdSockAddrReuse.cpp0000664000577000060420000000270013557101274023403 0ustar anjaesctl /*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeff Hill */ #define epicsExportSharedSymbols #include "osiSock.h" #include "errlog.h" epicsShareFunc void epicsShareAPI epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) { int yes = true; int status; status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, (char *) & yes, sizeof ( yes ) ); if ( status < 0 ) { errlogPrintf ( "epicsSocketEnableAddressReuseDuringTimeWaitState: " "unable to set SO_REUSEADDR?\n"); } } /* * SO_REUSEPORT is not in POSIX */ epicsShareFunc void epicsShareAPI epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) { int yes = true; int status; status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, (char *) & yes, sizeof ( yes ) ); if ( status < 0 ) { errlogPrintf ( "epicsSocketEnablePortUseForDatagramFanout: " "unable to set SO_REUSEADDR?\n"); } } base-7.0.3.1/modules/libcom/src/osi/os/posix/osdSpin.c0000664000577000060420000000741113557101274021262 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * Copyright (c) 2012 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange */ #include #include #include #include #include #define epicsExportSharedSymbols #include "errlog.h" #include "cantProceed.h" #include "epicsSpin.h" /* POSIX spinlocks may be subject to priority inversion * and so can't be guaranteed safe in situations where * threads have different priorities, and thread * preemption can't be disabled. */ #if defined(DONT_USE_POSIX_THREAD_PRIORITY_SCHEDULING) #if defined(_POSIX_SPIN_LOCKS) && (_POSIX_SPIN_LOCKS > 0) # define USE_PSPIN #endif #endif #define checkStatus(status,message) \ if ((status)) { \ errlogPrintf("epicsSpin %s failed: error %s\n", \ (message), strerror((status))); \ } #ifdef USE_PSPIN /* * POSIX SPIN LOCKS IMPLEMENTATION */ typedef struct epicsSpin { pthread_spinlock_t lock; } epicsSpin; epicsSpinId epicsSpinCreate(void) { epicsSpin *spin; int status; spin = calloc(1, sizeof(*spin)); if (!spin) goto fail; status = pthread_spin_init(&spin->lock, PTHREAD_PROCESS_PRIVATE); checkStatus(status, "pthread_spin_init"); if (status) goto fail; return spin; fail: free(spin); return NULL; } void epicsSpinDestroy(epicsSpinId spin) { int status; status = pthread_spin_destroy(&spin->lock); checkStatus(status, "pthread_spin_destroy"); free(spin); } void epicsSpinLock(epicsSpinId spin) { int status; status = pthread_spin_lock(&spin->lock); checkStatus(status, "pthread_spin_lock"); if (status) cantProceed(NULL); } int epicsSpinTryLock(epicsSpinId spin) { int status; status = pthread_spin_trylock(&spin->lock); if (status == EBUSY) return 1; checkStatus(status, "pthread_spin_trylock"); return status; } void epicsSpinUnlock(epicsSpinId spin) { int status; status = pthread_spin_unlock(&spin->lock); checkStatus(status, "pthread_spin_unlock"); } #else /* USE_PSPIN */ /* * POSIX MUTEX IMPLEMENTATION */ typedef struct epicsSpin { pthread_mutex_t lock; } epicsSpin; epicsSpinId epicsSpinCreate(void) { epicsSpin *spin; int status; spin = calloc(1, sizeof(*spin)); if (!spin) goto fail; status = pthread_mutex_init(&spin->lock, NULL); checkStatus(status, "pthread_mutex_init"); if (status) goto fail; return spin; fail: free(spin); return NULL; } void epicsSpinDestroy(epicsSpinId spin) { int status; status = pthread_mutex_destroy(&spin->lock); checkStatus(status, "pthread_mutex_destroy"); free(spin); } void epicsSpinLock(epicsSpinId spin) { int status; status = pthread_mutex_lock(&spin->lock); checkStatus(status, "pthread_mutex_lock"); if (status) cantProceed(NULL); } int epicsSpinTryLock(epicsSpinId spin) { int status; status = pthread_mutex_trylock(&spin->lock); if (status == EBUSY) return 1; checkStatus(status, "pthread_mutex_trylock"); return status; } void epicsSpinUnlock(epicsSpinId spin) { int status; status = pthread_mutex_unlock(&spin->lock); checkStatus(status, "pthread_mutex_unlock"); } #endif /* USE_PSPIN */ epicsSpinId epicsSpinMustCreate(void) { epicsSpinId ret = epicsSpinCreate(); if(!ret) cantProceed("epicsSpinMustCreate: epicsSpinCreate failed."); return ret; } base-7.0.3.1/modules/libcom/src/osi/os/posix/osdStdio.c0000664000577000060420000000201213557101274021423 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #include #define epicsExportSharedSymbols #include epicsShareFunc int epicsShareAPI epicsSnprintf( char *str, size_t size, const char *format, ...) { int nchars; va_list pvar; va_start(pvar,format); nchars = epicsVsnprintf(str,size,format,pvar); va_end (pvar); return(nchars); } epicsShareFunc int epicsShareAPI epicsVsnprintf( char *str, size_t size, const char *format, va_list ap) { return vsnprintf ( str, size, format, ap ); } base-7.0.3.1/modules/libcom/src/osi/os/posix/osdStrtod.h0000664000577000060420000000104213557101274021627 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * This header fragment is intended to be included as part of epicsString.h */ /* * epicsStrtod() for systems with working strtod() routine */ #define epicsStrtod strtod base-7.0.3.1/modules/libcom/src/osi/os/posix/osdThread.c0000664000577000060420000007777713557101274021606 0ustar anjaesctl/*************************************************************************\ * 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) 2013 ITER Organization. * 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 */ /* This is a posix implementation of epicsThread */ #include #include #include #include #include #include #include #include #include #include #include #if defined(_POSIX_MEMLOCK) && _POSIX_MEMLOCK > 0 #include #endif #define epicsExportSharedSymbols #include "epicsStdio.h" #include "ellLib.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "epicsString.h" #include "epicsThread.h" #include "cantProceed.h" #include "errlog.h" #include "epicsAssert.h" #include "epicsExit.h" #include "epicsAtomic.h" epicsShareFunc void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level); epicsShareFunc void osdThreadHooksRun(epicsThreadId id); epicsShareFunc void osdThreadHooksRunMain(epicsThreadId id); static int mutexLock(pthread_mutex_t *id) { int status; while(1) { status = pthread_mutex_lock(id); if(status!=EINTR) return status; fprintf(stderr,"pthread_mutex_lock returned EINTR. Violates SUSv3\n"); } } #if defined DONT_USE_POSIX_THREAD_PRIORITY_SCHEDULING #undef _POSIX_THREAD_PRIORITY_SCHEDULING #endif typedef struct commonAttr{ pthread_attr_t attr; struct sched_param schedParam; int maxPriority; int minPriority; int schedPolicy; int usePolicy; } commonAttr; #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 typedef struct { int min_pri, max_pri; int policy; int ok; } priAvailable; #endif static pthread_key_t getpthreadInfo; static pthread_mutex_t onceLock; static pthread_mutex_t listLock; static ELLLIST pthreadList = ELLLIST_INIT; static commonAttr *pcommonAttr = 0; static int epicsThreadOnceCalled = 0; static epicsThreadOSD *createImplicit(void); #define checkStatus(status,message) \ if((status)) {\ errlogPrintf("%s error %s\n",(message),strerror((status))); \ } #define checkStatusQuit(status,message,method) \ if(status) { \ errlogPrintf("%s error %s\n",(message),strerror((status))); \ cantProceed((method)); \ } /* The following are for use by once, which is only invoked from epicsThreadInit*/ /* Until epicsThreadInit completes errlogInit will not work */ /* It must also be used by init_threadInfo otherwise errlogInit could get */ /* called recursively */ #define checkStatusOnce(status,message) \ if((status)) {\ fprintf(stderr,"%s error %s\n",(message),strerror((status))); } #define checkStatusOnceQuit(status,message,method) \ if(status) { \ fprintf(stderr,"%s error %s",(message),strerror((status))); \ fprintf(stderr," %s\n",method); \ fprintf(stderr,"epicsThreadInit cant proceed. Program exiting\n"); \ exit(-1);\ } epicsShareFunc int epicsThreadGetPosixPriority(epicsThreadId pthreadInfo) { #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 double maxPriority,minPriority,slope,oss; if(pcommonAttr->maxPriority==pcommonAttr->minPriority) return(pcommonAttr->maxPriority); maxPriority = (double)pcommonAttr->maxPriority; minPriority = (double)pcommonAttr->minPriority; slope = (maxPriority - minPriority)/100.0; oss = (double)pthreadInfo->osiPriority * slope + minPriority; return((int)oss); #else return 0; #endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ } static void setSchedulingPolicy(epicsThreadOSD *pthreadInfo,int policy) { #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 int status; if(!pcommonAttr->usePolicy) return; status = pthread_attr_getschedparam( &pthreadInfo->attr,&pthreadInfo->schedParam); checkStatusOnce(status,"pthread_attr_getschedparam"); pthreadInfo->schedParam.sched_priority = epicsThreadGetPosixPriority(pthreadInfo); pthreadInfo->schedPolicy = policy; status = pthread_attr_setschedpolicy( &pthreadInfo->attr,policy); checkStatusOnce(status,"pthread_attr_setschedpolicy"); status = pthread_attr_setschedparam( &pthreadInfo->attr,&pthreadInfo->schedParam); checkStatusOnce(status,"pthread_attr_setschedparam"); status = pthread_attr_setinheritsched( &pthreadInfo->attr,PTHREAD_EXPLICIT_SCHED); checkStatusOnce(status,"pthread_attr_setinheritsched"); #endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ } static epicsThreadOSD * create_threadInfo(const char *name) { epicsThreadOSD *pthreadInfo; /* sizeof(epicsThreadOSD) includes one byte for the '\0' */ pthreadInfo = calloc(1,sizeof(*pthreadInfo) + strlen(name)); if(!pthreadInfo) return NULL; pthreadInfo->suspendEvent = epicsEventCreate(epicsEventEmpty); if(!pthreadInfo->suspendEvent){ free(pthreadInfo); return NULL; } strcpy(pthreadInfo->name, name); epicsAtomicIncrIntT(&pthreadInfo->refcnt); /* initial ref for the thread itself */ return pthreadInfo; } static epicsThreadOSD * init_threadInfo(const char *name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr,void *parm, unsigned joinable) { epicsThreadOSD *pthreadInfo; int status; pthreadInfo = create_threadInfo(name); if(!pthreadInfo) return NULL; pthreadInfo->createFunc = funptr; pthreadInfo->createArg = parm; pthreadInfo->joinable = joinable; status = pthread_attr_init(&pthreadInfo->attr); checkStatusOnce(status,"pthread_attr_init"); if(status) return 0; if(!joinable){ status = pthread_attr_setdetachstate( &pthreadInfo->attr, PTHREAD_CREATE_DETACHED); checkStatusOnce(status,"pthread_attr_setdetachstate"); } #if defined (_POSIX_THREAD_ATTR_STACKSIZE) #if ! defined (OSITHREAD_USE_DEFAULT_STACK) status = pthread_attr_setstacksize( &pthreadInfo->attr,(size_t)stackSize); checkStatusOnce(status,"pthread_attr_setstacksize"); #endif /*OSITHREAD_USE_DEFAULT_STACK*/ #endif /*_POSIX_THREAD_ATTR_STACKSIZE*/ status = pthread_attr_setscope(&pthreadInfo->attr,PTHREAD_SCOPE_PROCESS); if(errVerbose) checkStatusOnce(status,"pthread_attr_setscope"); pthreadInfo->osiPriority = priority; return(pthreadInfo); } static void free_threadInfo(epicsThreadOSD *pthreadInfo) { int status; if(epicsAtomicDecrIntT(&pthreadInfo->refcnt) > 0) return; status = mutexLock(&listLock); checkStatusQuit(status,"pthread_mutex_lock","free_threadInfo"); if(pthreadInfo->isOnThreadList) ellDelete(&pthreadList,&pthreadInfo->node); status = pthread_mutex_unlock(&listLock); checkStatusQuit(status,"pthread_mutex_unlock","free_threadInfo"); epicsEventDestroy(pthreadInfo->suspendEvent); status = pthread_attr_destroy(&pthreadInfo->attr); checkStatusQuit(status,"pthread_attr_destroy","free_threadInfo"); free(pthreadInfo); } #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 /* * The actually available range priority range (at least under linux) * may be restricted by resource limitations (but that is ignored * by sched_get_priority_max()). See bug #835138 which is fixed by * this code. */ static int try_pri(int pri, int policy) { struct sched_param schedp; schedp.sched_priority = pri; return pthread_setschedparam(pthread_self(), policy, &schedp); } static void* find_pri_range(void *arg) { priAvailable *prm = arg; int min = sched_get_priority_min(prm->policy); int max = sched_get_priority_max(prm->policy); int low, try; if ( -1 == min || -1 == max ) { /* something is very wrong; maintain old behavior * (warning message if sched_get_priority_xxx() fails * and use default policy's sched_priority [even if * that is likely to cause epicsThreadCreate to fail * because that priority is not suitable for SCHED_FIFO]). */ prm->min_pri = prm->max_pri = -1; return 0; } if ( try_pri(min, prm->policy) ) { /* cannot create thread at minimum priority; * probably no permission to use SCHED_FIFO * at all. However, we still must return * a priority range accepted by the SCHED_FIFO * policy. Otherwise, epicsThreadCreate() cannot * detect the unsufficient permission (EPERM) * and fall back to a non-RT thread (because * pthread_attr_setschedparam would fail with * EINVAL due to the bad priority). */ prm->min_pri = prm->max_pri = min; return 0; } /* Binary search through available priorities. * The actually available range may be restricted * by resource limitations (but that is ignored * by sched_get_priority_max() [linux]). */ low = min; while ( low < max ) { try = (max+low)/2; if ( try_pri(try, prm->policy) ) { max = try; } else { low = try + 1; } } prm->min_pri = min; prm->max_pri = try_pri(max, prm->policy) ? max-1 : max; prm->ok = 1; return 0; } static void findPriorityRange(commonAttr *a_p) { priAvailable arg; pthread_t id; void *dummy; int status; arg.policy = a_p->schedPolicy; arg.ok = 0; status = pthread_create(&id, 0, find_pri_range, &arg); checkStatusQuit(status, "pthread_create","epicsThreadInit"); status = pthread_join(id, &dummy); checkStatusQuit(status, "pthread_join","epicsThreadInit"); a_p->minPriority = arg.min_pri; a_p->maxPriority = arg.max_pri; a_p->usePolicy = arg.ok; } #endif static void once(void) { epicsThreadOSD *pthreadInfo; int status; pthread_key_create(&getpthreadInfo,0); status = pthread_mutex_init(&onceLock,0); checkStatusQuit(status,"pthread_mutex_init","epicsThreadInit"); status = pthread_mutex_init(&listLock,0); checkStatusQuit(status,"pthread_mutex_init","epicsThreadInit"); pcommonAttr = calloc(1,sizeof(commonAttr)); if(!pcommonAttr) checkStatusOnceQuit(errno,"calloc","epicsThreadInit"); status = pthread_attr_init(&pcommonAttr->attr); checkStatusOnceQuit(status,"pthread_attr_init","epicsThreadInit"); status = pthread_attr_setdetachstate( &pcommonAttr->attr, PTHREAD_CREATE_DETACHED); checkStatusOnce(status,"pthread_attr_setdetachstate"); status = pthread_attr_setscope(&pcommonAttr->attr,PTHREAD_SCOPE_PROCESS); if(errVerbose) checkStatusOnce(status,"pthread_attr_setscope"); #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 status = pthread_attr_setschedpolicy( &pcommonAttr->attr,SCHED_FIFO); checkStatusOnce(status,"pthread_attr_setschedpolicy"); status = pthread_attr_getschedpolicy( &pcommonAttr->attr,&pcommonAttr->schedPolicy); checkStatusOnce(status,"pthread_attr_getschedpolicy"); status = pthread_attr_getschedparam( &pcommonAttr->attr,&pcommonAttr->schedParam); checkStatusOnce(status,"pthread_attr_getschedparam"); findPriorityRange(pcommonAttr); if(pcommonAttr->maxPriority == -1) { pcommonAttr->maxPriority = pcommonAttr->schedParam.sched_priority; fprintf(stderr,"sched_get_priority_max failed set to %d\n", pcommonAttr->maxPriority); } if(pcommonAttr->minPriority == -1) { pcommonAttr->minPriority = pcommonAttr->schedParam.sched_priority; fprintf(stderr,"sched_get_priority_min failed set to %d\n", pcommonAttr->maxPriority); } if (errVerbose) { fprintf(stderr, "LRT: min priority: %d max priority %d\n", pcommonAttr->minPriority, pcommonAttr->maxPriority); } #else if(errVerbose) fprintf(stderr,"task priorities are not implemented\n"); #endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ pthreadInfo = init_threadInfo("_main_",0,epicsThreadGetStackSize(epicsThreadStackSmall),0,0,0); assert(pthreadInfo!=NULL); status = pthread_setspecific(getpthreadInfo,(void *)pthreadInfo); checkStatusOnceQuit(status,"pthread_setspecific","epicsThreadInit"); status = mutexLock(&listLock); checkStatusQuit(status,"pthread_mutex_lock","epicsThreadInit"); ellAdd(&pthreadList,&pthreadInfo->node); pthreadInfo->isOnThreadList = 1; status = pthread_mutex_unlock(&listLock); checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadInit"); status = atexit(epicsExitCallAtExits); checkStatusOnce(status,"atexit"); osdThreadHooksRunMain(pthreadInfo); epicsThreadOnceCalled = 1; } static void * start_routine(void *arg) { epicsThreadOSD *pthreadInfo = (epicsThreadOSD *)arg; int status; sigset_t blockAllSig; sigfillset(&blockAllSig); pthread_sigmask(SIG_SETMASK,&blockAllSig,NULL); status = pthread_setspecific(getpthreadInfo,arg); checkStatusQuit(status,"pthread_setspecific","start_routine"); status = mutexLock(&listLock); checkStatusQuit(status,"pthread_mutex_lock","start_routine"); ellAdd(&pthreadList,&pthreadInfo->node); pthreadInfo->isOnThreadList = 1; status = pthread_mutex_unlock(&listLock); checkStatusQuit(status,"pthread_mutex_unlock","start_routine"); osdThreadHooksRun(pthreadInfo); (*pthreadInfo->createFunc)(pthreadInfo->createArg); epicsExitCallAtThreadExits (); free_threadInfo(pthreadInfo); return(0); } static void epicsThreadInit(void) { static pthread_once_t once_control = PTHREAD_ONCE_INIT; int status = pthread_once(&once_control,once); checkStatusQuit(status,"pthread_once","epicsThreadInit"); } epicsShareFunc void epicsThreadRealtimeLock(void) { #if defined(_POSIX_MEMLOCK) && _POSIX_MEMLOCK > 0 if (pcommonAttr->maxPriority > pcommonAttr->minPriority) { int status = mlockall(MCL_CURRENT | MCL_FUTURE); if (status) { const int err = errno; switch(err) { #ifdef __linux__ case ENOMEM: fprintf(stderr, "epicsThreadRealtimeLock " "Warning: unable to lock memory. RLIMIT_MEMLOCK is too small or missing CAP_IPC_LOCK\n"); break; case EPERM: fprintf(stderr, "epicsThreadRealtimeLock " "Warning: unable to lock memory. missing CAP_IPC_LOCK\n"); break; #endif default: fprintf(stderr, "epicsThreadRealtimeLock " "Warning: Unable to lock the virtual address space.\n" "VM page faults may harm real-time performance. errno=%d\n", err); } } } #endif } #if defined (OSITHREAD_USE_DEFAULT_STACK) #define STACK_SIZE(f) (0) #elif defined(_POSIX_THREAD_ATTR_STACKSIZE) && _POSIX_THREAD_ATTR_STACKSIZE > 0 #define STACK_SIZE(f) (f * 0x10000 * sizeof(void *)) static const unsigned stackSizeTable[epicsThreadStackBig+1] = { STACK_SIZE(1), STACK_SIZE(2), STACK_SIZE(4) }; #else #define STACK_SIZE(f) (0) #endif /*_POSIX_THREAD_ATTR_STACKSIZE*/ epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize (epicsThreadStackSizeClass stackSizeClass) { #if defined (OSITHREAD_USE_DEFAULT_STACK) return 0; #elif defined(_POSIX_THREAD_ATTR_STACKSIZE) && _POSIX_THREAD_ATTR_STACKSIZE > 0 if (stackSizeClassepicsThreadStackBig) { errlogPrintf("epicsThreadGetStackSize illegal argument (too large)"); return stackSizeTable[epicsThreadStackBig]; } return stackSizeTable[stackSizeClass]; #else return 0; #endif /*_POSIX_THREAD_ATTR_STACKSIZE*/ } epicsShareFunc void epicsShareAPI epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg) { static struct epicsThreadOSD threadOnceComplete; #define EPICS_THREAD_ONCE_DONE &threadOnceComplete int status; epicsThreadInit(); status = mutexLock(&onceLock); if(status) { fprintf(stderr,"epicsThreadOnce: pthread_mutex_lock returned %s.\n", strerror(status)); exit(-1); } if (*id != EPICS_THREAD_ONCE_DONE) { if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */ *id = epicsThreadGetIdSelf(); /* mark active */ status = pthread_mutex_unlock(&onceLock); checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce"); func(arg); status = mutexLock(&onceLock); checkStatusQuit(status,"pthread_mutex_lock", "epicsThreadOnce"); *id = EPICS_THREAD_ONCE_DONE; /* mark done */ } else if (*id == epicsThreadGetIdSelf()) { status = pthread_mutex_unlock(&onceLock); checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce"); cantProceed("Recursive epicsThreadOnce() initialization\n"); } else while (*id != EPICS_THREAD_ONCE_DONE) { /* Another thread is in the above func(arg) call. */ status = pthread_mutex_unlock(&onceLock); checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce"); epicsThreadSleep(epicsThreadSleepQuantum()); status = mutexLock(&onceLock); checkStatusQuit(status,"pthread_mutex_lock", "epicsThreadOnce"); } } status = pthread_mutex_unlock(&onceLock); checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnce"); } epicsThreadId epicsThreadCreateOpt(const char * name, EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts ) { unsigned int stackSize; epicsThreadOSD *pthreadInfo; int status; sigset_t blockAllSig, oldSig; epicsThreadInit(); assert(pcommonAttr); if (!opts) { static const epicsThreadOpts opts_default = EPICS_THREAD_OPTS_INIT; opts = &opts_default; } stackSize = opts->stackSize; if (stackSize <= epicsThreadStackBig) stackSize = epicsThreadGetStackSize(stackSize); sigfillset(&blockAllSig); pthread_sigmask(SIG_SETMASK, &blockAllSig, &oldSig); pthreadInfo = init_threadInfo(name, opts->priority, stackSize, funptr, parm, opts->joinable); if (pthreadInfo==0) return 0; pthreadInfo->isEpicsThread = 1; setSchedulingPolicy(pthreadInfo, SCHED_FIFO); pthreadInfo->isRealTimeScheduled = 1; status = pthread_create(&pthreadInfo->tid, &pthreadInfo->attr, start_routine, pthreadInfo); if (status==EPERM) { /* Try again without SCHED_FIFO*/ free_threadInfo(pthreadInfo); pthreadInfo = init_threadInfo(name, opts->priority, stackSize, funptr, parm, opts->joinable); if (pthreadInfo==0) return 0; pthreadInfo->isEpicsThread = 1; status = pthread_create(&pthreadInfo->tid, &pthreadInfo->attr, start_routine, pthreadInfo); } checkStatusOnce(status, "pthread_create"); if (status) { free_threadInfo(pthreadInfo); return 0; } status = pthread_sigmask(SIG_SETMASK, &oldSig, NULL); checkStatusOnce(status, "pthread_sigmask"); if (pthreadInfo->joinable) { /* extra ref for epicsThreadMustJoin() */ epicsAtomicIncrIntT(&pthreadInfo->refcnt); } return pthreadInfo; } /* * Create dummy context for threads not created by epicsThreadCreate(). */ static epicsThreadOSD *createImplicit(void) { epicsThreadOSD *pthreadInfo; char name[64]; pthread_t tid; int status; tid = pthread_self(); sprintf(name, "non-EPICS_%ld", (long)tid); pthreadInfo = create_threadInfo(name); assert(pthreadInfo); pthreadInfo->tid = tid; pthreadInfo->osiPriority = 0; #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 if(pthread_getschedparam(tid,&pthreadInfo->schedPolicy,&pthreadInfo->schedParam) == 0) { if ( pcommonAttr->usePolicy && pthreadInfo->schedPolicy == pcommonAttr->schedPolicy ) { pthreadInfo->osiPriority = (pthreadInfo->schedParam.sched_priority - pcommonAttr->minPriority) * 100.0 / (pcommonAttr->maxPriority - pcommonAttr->minPriority + 1); } } #endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ status = pthread_setspecific(getpthreadInfo,(void *)pthreadInfo); checkStatus(status,"pthread_setspecific createImplicit"); if(status){ free_threadInfo(pthreadInfo); return NULL; } return pthreadInfo; } void epicsThreadMustJoin(epicsThreadId id) { void *ret = NULL; int status; if(!id) { return; } else if(!id->joinable) { if(epicsThreadGetIdSelf()==id) { errlogPrintf("Warning: %s thread self-join of unjoinable\n", id->name); } else { /* try to error nicely, however in all likelyhood 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); } return; } status = pthread_join(id->tid, &ret); if(status == EDEADLK) { /* Thread can't join itself (directly or indirectly) * so we detach instead. */ status = pthread_detach(id->tid); checkStatusOnce(status, "pthread_detach"); } else checkStatusOnce(status, "pthread_join"); id->joinable = 0; free_threadInfo(id); } epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf(void) { epicsThreadOSD *pthreadInfo; epicsThreadInit(); pthreadInfo = (epicsThreadOSD *)pthread_getspecific(getpthreadInfo); if(pthreadInfo==NULL) pthreadInfo = createImplicit(); pthreadInfo->isSuspended = 1; epicsEventWait(pthreadInfo->suspendEvent); } epicsShareFunc void epicsShareAPI epicsThreadResume(epicsThreadOSD *pthreadInfo) { assert(epicsThreadOnceCalled); pthreadInfo->isSuspended = 0; epicsEventSignal(pthreadInfo->suspendEvent); } epicsShareFunc void epicsShareAPI epicsThreadExitMain(void) { epicsThreadOSD *pthreadInfo; epicsThreadInit(); pthreadInfo = (epicsThreadOSD *)pthread_getspecific(getpthreadInfo); if(pthreadInfo==NULL) pthreadInfo = createImplicit(); if(pthreadInfo->createFunc) { errlogPrintf("called from non-main thread\n"); cantProceed("epicsThreadExitMain"); } else { free_threadInfo(pthreadInfo); pthread_exit(0); } } epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPriority(epicsThreadId pthreadInfo) { assert(epicsThreadOnceCalled); return(pthreadInfo->osiPriority); } epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPrioritySelf(void) { epicsThreadInit(); return(epicsThreadGetPriority(epicsThreadGetIdSelf())); } epicsShareFunc void epicsShareAPI epicsThreadSetPriority(epicsThreadId pthreadInfo,unsigned int priority) { #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 int status; #endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ assert(epicsThreadOnceCalled); assert(pthreadInfo); if(!pthreadInfo->isEpicsThread) { fprintf(stderr,"epicsThreadSetPriority called by non epics thread\n"); return; } pthreadInfo->osiPriority = priority; if(!pthreadInfo->isRealTimeScheduled) return; #if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 if(!pcommonAttr->usePolicy) return; pthreadInfo->schedParam.sched_priority = epicsThreadGetPosixPriority(pthreadInfo); status = pthread_attr_setschedparam( &pthreadInfo->attr,&pthreadInfo->schedParam); if(errVerbose) checkStatus(status,"pthread_attr_setschedparam"); status = pthread_setschedparam( pthreadInfo->tid, pthreadInfo->schedPolicy, &pthreadInfo->schedParam); if(errVerbose) checkStatus(status,"pthread_setschedparam"); #endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ } epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadHighestPriorityLevelBelow( unsigned int priority, unsigned *pPriorityJustBelow) { unsigned newPriority = priority - 1; #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 int diff; diff = pcommonAttr->maxPriority - pcommonAttr->minPriority; if(diff<0) diff = -diff; if(diff>1 && diff <100) newPriority -= 100/(diff+1); #endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ if (newPriority <= 99) { *pPriorityJustBelow = newPriority; return epicsThreadBooleanStatusSuccess; } return epicsThreadBooleanStatusFail; } epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadLowestPriorityLevelAbove( unsigned int priority, unsigned *pPriorityJustAbove) { unsigned newPriority = priority + 1; #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 int diff; diff = pcommonAttr->maxPriority - pcommonAttr->minPriority; if(diff<0) diff = -diff; if(diff>1 && diff <100) newPriority += 100/(diff+1); #endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ if (newPriority <= 99) { *pPriorityJustAbove = newPriority; return epicsThreadBooleanStatusSuccess; } return epicsThreadBooleanStatusFail; } epicsShareFunc int epicsShareAPI epicsThreadIsEqual(epicsThreadId p1, epicsThreadId p2) { assert(epicsThreadOnceCalled); assert(p1); assert(p2); return(pthread_equal(p1->tid,p2->tid)); } epicsShareFunc int epicsShareAPI epicsThreadIsSuspended(epicsThreadId pthreadInfo) { assert(epicsThreadOnceCalled); assert(pthreadInfo); return(pthreadInfo->isSuspended ? 1 : 0); } epicsShareFunc void epicsShareAPI epicsThreadSleep(double seconds) { struct timespec delayTime; struct timespec remainingTime; double nanoseconds; if (seconds > 0) { delayTime.tv_sec = seconds; nanoseconds = (seconds - delayTime.tv_sec) *1e9; delayTime.tv_nsec = nanoseconds; } else { delayTime.tv_sec = 0; delayTime.tv_nsec = 0; } while (nanosleep(&delayTime, &remainingTime) == -1 && errno == EINTR) delayTime = remainingTime; } epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetIdSelf(void) { epicsThreadOSD *pthreadInfo; epicsThreadInit(); pthreadInfo = (epicsThreadOSD *)pthread_getspecific(getpthreadInfo); if(pthreadInfo==NULL) pthreadInfo = createImplicit(); assert ( pthreadInfo ); return(pthreadInfo); } epicsShareFunc pthread_t epicsThreadGetPosixThreadId ( epicsThreadId threadId ) { return threadId->tid; } epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetId(const char *name) { epicsThreadOSD *pthreadInfo; int status; assert(epicsThreadOnceCalled); status = mutexLock(&listLock); checkStatus(status,"pthread_mutex_lock epicsThreadGetId"); if(status) return NULL; pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); while(pthreadInfo) { if(strcmp(name,pthreadInfo->name) == 0) break; pthreadInfo=(epicsThreadOSD *)ellNext(&pthreadInfo->node); } status = pthread_mutex_unlock(&listLock); checkStatus(status,"pthread_mutex_unlock epicsThreadGetId"); return(pthreadInfo); } epicsShareFunc const char epicsShareAPI *epicsThreadGetNameSelf() { epicsThreadOSD *pthreadInfo; epicsThreadInit(); pthreadInfo = (epicsThreadOSD *)pthread_getspecific(getpthreadInfo); if(pthreadInfo==NULL) pthreadInfo = createImplicit(); return(pthreadInfo->name); } epicsShareFunc void epicsShareAPI epicsThreadGetName(epicsThreadId pthreadInfo, char *name, size_t size) { assert(epicsThreadOnceCalled); strncpy(name, pthreadInfo->name, size-1); name[size-1] = '\0'; } epicsShareFunc void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func) { epicsThreadOSD *pthreadInfo; int status; epicsThreadInit(); status = mutexLock(&listLock); checkStatus(status, "pthread_mutex_lock epicsThreadMap"); if (status) return; pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); while (pthreadInfo) { func(pthreadInfo); pthreadInfo = (epicsThreadOSD *)ellNext(&pthreadInfo->node); } status = pthread_mutex_unlock(&listLock); checkStatus(status, "pthread_mutex_unlock epicsThreadMap"); } epicsShareFunc void epicsShareAPI epicsThreadShowAll(unsigned int level) { epicsThreadOSD *pthreadInfo; int status; epicsThreadInit(); epicsThreadShow(0,level); status = mutexLock(&listLock); checkStatus(status,"pthread_mutex_lock epicsThreadShowAll"); if(status) return; pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); while(pthreadInfo) { epicsThreadShowInfo(pthreadInfo,level); pthreadInfo=(epicsThreadOSD *)ellNext(&pthreadInfo->node); } status = pthread_mutex_unlock(&listLock); checkStatus(status,"pthread_mutex_unlock epicsThreadShowAll"); } epicsShareFunc void epicsShareAPI epicsThreadShow(epicsThreadId showThread, unsigned int level) { epicsThreadOSD *pthreadInfo; int status; int found = 0; epicsThreadInit(); if(!showThread) { epicsThreadShowInfo(0,level); return; } status = mutexLock(&listLock); checkStatus(status,"pthread_mutex_lock epicsThreadShowAll"); if(status) return; pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); while(pthreadInfo) { if (((epicsThreadId)pthreadInfo == showThread) || ((epicsThreadId)pthreadInfo->tid == showThread)) { found = 1; epicsThreadShowInfo(pthreadInfo,level); } pthreadInfo=(epicsThreadOSD *)ellNext(&pthreadInfo->node); } status = pthread_mutex_unlock(&listLock); checkStatus(status,"pthread_mutex_unlock epicsThreadShowAll"); if(status) return; if (!found) printf("Thread %#lx (%lu) not found.\n", (unsigned long)showThread, (unsigned long)showThread); } epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate(void) { pthread_key_t *key; int status; epicsThreadInit(); key = calloc(1,sizeof(pthread_key_t)); if(!key) return NULL; status = pthread_key_create(key,0); checkStatus(status,"pthread_key_create epicsThreadPrivateCreate"); if(status) return NULL; return((epicsThreadPrivateId)key); } epicsShareFunc void epicsShareAPI epicsThreadPrivateDelete(epicsThreadPrivateId id) { pthread_key_t *key = (pthread_key_t *)id; int status; assert(epicsThreadOnceCalled); status = pthread_key_delete(*key); checkStatusQuit(status,"pthread_key_delete","epicsThreadPrivateDelete"); free((void *)key); } epicsShareFunc void epicsShareAPI epicsThreadPrivateSet (epicsThreadPrivateId id, void *value) { pthread_key_t *key = (pthread_key_t *)id; int status; assert(epicsThreadOnceCalled); if(errVerbose && !value) errlogPrintf("epicsThreadPrivateSet: setting value of 0\n"); status = pthread_setspecific(*key,value); checkStatusQuit(status,"pthread_setspecific","epicsThreadPrivateSet"); } epicsShareFunc void epicsShareAPI *epicsThreadPrivateGet(epicsThreadPrivateId id) { pthread_key_t *key = (pthread_key_t *)id; assert(epicsThreadOnceCalled); return pthread_getspecific(*key); } epicsShareFunc double epicsShareAPI epicsThreadSleepQuantum () { double hz; hz = sysconf ( _SC_CLK_TCK ); if(hz<=0) return 0.0; return 1.0 / hz; } epicsShareFunc int epicsThreadGetCPUs(void) { long ret; #ifdef _SC_NPROCESSORS_ONLN ret = sysconf(_SC_NPROCESSORS_ONLN); if (ret > 0) return ret; #endif #ifdef _SC_NPROCESSORS_CONF ret = sysconf(_SC_NPROCESSORS_CONF); if (ret > 0) return ret; #endif return 1; } base-7.0.3.1/modules/libcom/src/osi/os/posix/osdThread.h0000664000577000060420000000272313557101274021566 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef osdThreadh #define osdThreadh #include #include "shareLib.h" #include "ellLib.h" #include "epicsEvent.h" #ifdef __cplusplus extern "C" { #endif typedef struct epicsThreadOSD { ELLNODE node; int refcnt; pthread_t tid; pthread_attr_t attr; struct sched_param schedParam; int schedPolicy; EPICSTHREADFUNC createFunc; void *createArg; epicsEventId suspendEvent; int isSuspended; int isEpicsThread; int isRealTimeScheduled; int isOnThreadList; unsigned int osiPriority; int joinable; char name[1]; /* actually larger */ } epicsThreadOSD; epicsShareFunc pthread_t epicsThreadGetPosixThreadId(epicsThreadId id); epicsShareFunc int epicsThreadGetPosixPriority(epicsThreadId id); #ifdef __cplusplus } #endif #endif /* osdThreadh */ base-7.0.3.1/modules/libcom/src/osi/os/posix/osdThreadExtra.c0000664000577000060420000000320213557101274022556 0ustar anjaesctl/*************************************************************************\ * 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. * 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 */ /* This is part of the posix implementation of epicsThread */ #define epicsExportSharedSymbols #include "epicsStdio.h" #include "ellLib.h" #include "epicsEvent.h" #include "epicsThread.h" epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; 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"); } } base-7.0.3.1/modules/libcom/src/osi/os/posix/osdTime.cpp0000664000577000060420000000603613557101274021611 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include #include #include #define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "osiSock.h" #define epicsExportSharedSymbols #include "cantProceed.h" #include "epicsTime.h" #include "generalTimeSup.h" #ifdef CLOCK_REALTIME #include "osiClockTime.h" #define TIME_INIT ClockTime_Init(CLOCKTIME_NOSYNC) #else /* Some posix systems may not have CLOCK_REALTIME */ #define TIME_INIT generalTimeCurrentTpRegister("GetTimeOfDay", \ LAST_RESORT_PRIORITY, osdTimeGetCurrent) extern "C" { int osdTimeGetCurrent (epicsTimeStamp *pDest) { struct timeval tv; struct timezone tz; if (gettimeofday (&tv, &tz)) return errno; *pDest = epicsTime(tv); return epicsTimeOK; } } // extern "C" #endif #ifdef __CYGWIN__ int clock_settime(clockid_t clock, const timespec *tp) { return -EFAULT; } #endif static int timeRegister(void) { TIME_INIT; osdMonotonicInit(); return 1; } static int done = timeRegister(); int epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM ) { struct tm * pRet = gmtime_r ( pAnsiTime, pTM ); if ( pRet ) { return epicsTimeOK; } else { return errno; } } int epicsTime_localtime ( const time_t *clock, struct tm *result ) { struct tm * pRet = localtime_r ( clock, result ); if ( pRet ) { return epicsTimeOK; } else { return errno; } } extern "C" epicsShareFunc void convertDoubleToWakeTime(double timeout,struct timespec *wakeTime) { struct timespec now, wait; int status; if (timeout < 0.0) timeout = 0.0; else if (timeout > 60 * 60 * 24 * 3652.5) timeout = 60 * 60 * 24 * 3652.5; /* 10 years */ #ifdef CLOCK_REALTIME status = clock_gettime(CLOCK_REALTIME, &now); #else { struct timeval tv; struct timezone tz; status = gettimeofday(&tv, &tz); now.tv_sec = tv.tv_sec; now.tv_nsec = tv.tv_usec * 1000; } #endif if (status) { perror("convertDoubleToWakeTime"); cantProceed("convertDoubleToWakeTime"); } wait.tv_sec = static_cast< time_t >(timeout); wait.tv_nsec = static_cast< long >((timeout - (double)wait.tv_sec) * 1e9); wakeTime->tv_sec = now.tv_sec + wait.tv_sec; wakeTime->tv_nsec = now.tv_nsec + wait.tv_nsec; if (wakeTime->tv_nsec >= 1000000000L) { wakeTime->tv_nsec -= 1000000000L; ++wakeTime->tv_sec; } } base-7.0.3.1/modules/libcom/src/osi/os/posix/osdTime.h0000664000577000060420000000204513557101274021252 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeff Hill */ #ifndef osdTimeh #define osdTimeh #include #if !defined(_POSIX_TIMERS) || _POSIX_TIMERS < 0 struct timespec { time_t tv_sec; /* seconds since some epoch */ long tv_nsec; /* nanoseconds within the second */ }; #endif /* !_POSIX_TIMERS */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ epicsShareFunc void epicsShareAPI convertDoubleToWakeTime(double timeout,struct timespec *wakeTime); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* ifndef osdTimeh */ base-7.0.3.1/modules/libcom/src/osi/os/posix/osiUnistd.h0000664000577000060420000000116013557101274021624 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include base-7.0.3.1/modules/libcom/src/osi/os/posix/systemCallIntMech.cpp0000664000577000060420000000142413557101274023571 0ustar anjaesctl /*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author: Jeff Hill */ #define epicsExportSharedSymbols #include "osiSock.h" enum epicsSocketSystemCallInterruptMechanismQueryInfo epicsSocketSystemCallInterruptMechanismQuery () { return esscimqi_socketBothShutdownRequired; } base-7.0.3.1/modules/libcom/src/osi/os/solaris/epicsAtomicOSD.h0000664000577000060420000001234613557101274022773 0ustar anjaesctl /*************************************************************************\ * Copyright (c) 2011 LANS LLC, as Operator of * Los Alamos National Laboratory. * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #include "shareLib.h" #ifndef epicsAtomicOSD_h #define epicsAtomicOSD_h #define EPICS_ATOMIC_OS_NAME "Solaris" #if defined ( __SunOS_5_10 ) /* * atomic.h exists only in Solaris 10 or higher */ #include #include "epicsAssert.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER #define EPICS_ATOMIC_READ_MEMORY_BARRIER EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) { membar_consumer (); } #endif #ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER #define EPICS_ATOMIC_WRITE_MEMORY_BARRIER EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) { membar_producer (); } #endif #ifndef EPICS_ATOMIC_CAS_INTT #define EPICS_ATOMIC_CAS_INTT EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, int oldVal, int newVal ) { STATIC_ASSERT ( sizeof ( int ) == sizeof ( unsigned ) ); unsigned * const pTarg = ( unsigned * ) pTarget; return ( int ) atomic_cas_uint ( pTarg, ( unsigned ) oldVal, ( unsigned ) newVal ); } #endif #ifndef EPICS_ATOMIC_CAS_SIZET #define EPICS_ATOMIC_CAS_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, size_t oldVal, size_t newVal ) { STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); ulong_t * const pTarg = ( ulong_t * ) pTarget; return ( size_t ) atomic_cas_ulong ( pTarg, oldVal, newVal ); } #endif #ifndef EPICS_ATOMIC_CAS_PTRT #define EPICS_ATOMIC_CAS_PTRT EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) { return atomic_cas_ptr ( pTarget, oldVal, newVal ); } #endif #ifndef EPICS_ATOMIC_INCR_INTT #define EPICS_ATOMIC_INCR_INTT EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) { STATIC_ASSERT ( sizeof ( unsigned ) == sizeof ( int ) ); unsigned * const pTarg = ( unsigned * ) ( pTarget ); return ( int ) atomic_inc_uint_nv ( pTarg ); } #endif #ifndef EPICS_ATOMIC_INCR_SIZET #define EPICS_ATOMIC_INCR_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) { STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); ulong_t * const pTarg = ( ulong_t * ) pTarget; return ( size_t ) atomic_inc_ulong_nv ( pTarg ); } #endif #ifndef EPICS_ATOMIC_DECR_INTT #define EPICS_ATOMIC_DECR_INTT EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ) { STATIC_ASSERT ( sizeof ( unsigned ) == sizeof ( int ) ); unsigned * const pTarg = ( unsigned * ) ( pTarget ); return ( int ) atomic_dec_uint_nv ( pTarg ); } #endif #ifndef EPICS_ATOMIC_DECR_SIZET #define EPICS_ATOMIC_DECR_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) { STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); ulong_t * const pTarg = ( ulong_t * ) pTarget; return ( size_t ) atomic_dec_ulong_nv ( pTarg ); } #endif #ifndef EPICS_ATOMIC_ADD_INTT #define EPICS_ATOMIC_ADD_INTT EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ) { STATIC_ASSERT ( sizeof ( unsigned ) == sizeof ( int ) ); unsigned * const pTarg = ( unsigned * ) ( pTarget ); return ( int ) atomic_add_int_nv ( pTarg, delta ); } #endif #ifndef EPICS_ATOMIC_ADD_SIZET #define EPICS_ATOMIC_ADD_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta ) { STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); ulong_t * const pTarg = ( ulong_t * ) pTarget; return ( size_t ) atomic_add_long_nv ( pTarg, ( long ) delta ); } #endif #ifndef EPICS_ATOMIC_SUB_SIZET #define EPICS_ATOMIC_SUB_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ) { STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); ulong_t * const pTarg = ( ulong_t * ) pTarget; long sdelta = ( long ) delta; return ( size_t ) atomic_add_long_nv ( pTarg, -sdelta ); } #endif #ifdef __cplusplus } /* end of extern "C" */ #endif /* __cplusplus */ #endif /* ifdef __SunOS_5_10 */ typedef struct EpicsAtomicLockKey { char dummy; } EpicsAtomicLockKey; #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ epicsShareFunc void epicsAtomicLock ( struct EpicsAtomicLockKey * ); epicsShareFunc void epicsAtomicUnlock ( struct EpicsAtomicLockKey * ); #ifdef __cplusplus } /* end of extern "C" */ #endif /* __cplusplus */ #include "epicsAtomicDefault.h" #endif /* epicsAtomicOSD_h */ base-7.0.3.1/modules/libcom/src/osi/os/solaris/epicsMath.h0000664000577000060420000000146313557101274022100 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne, LLC as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_epicsMath_H #define INC_epicsMath_H #include #include #include #ifndef isinf # define isinf(x) (((x)==(x)) && !finite((x))) /* same as (!isnan(x) && !finite(x)) */ #endif #ifndef isnan # define isnan(x) ((x) != (x)) #endif #ifdef __cplusplus extern "C" { #endif epicsShareExtern float epicsNAN; epicsShareExtern float epicsINF; #ifdef __cplusplus } #endif #endif /* INC_epicsMath_H */ base-7.0.3.1/modules/libcom/src/osi/os/solaris/osdBackTrace.cpp0000664000577000060420000000145613557101274023045 0ustar anjaesctl/* * Copyright: Stanford University / SLAC National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * Author: Till Straumann , 2014 */ #include #define epicsExportSharedSymbols #include "epicsStackTracePvt.h" struct wlk { void **buf; int max; int cur; }; extern "C" { static int walker(uintptr_t addr, int sig, void *arg) { struct wlk *w_p = (struct wlk *)arg; if ( w_p->cur < w_p->max ) w_p->buf[w_p->cur++] = (void*)addr; return 0; } } int epicsBackTrace(void **buf, int buf_sz) { ucontext_t u; struct wlk d; d.buf = buf; d.max = buf_sz; d.cur = 0; if ( getcontext(&u) ) return -1; walkcontext( &u, walker, &d ); return d.cur; } base-7.0.3.1/modules/libcom/src/osi/os/solaris/osdEnv.c0000664000577000060420000000276413557101274021421 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osdEnv.c */ /* * Author: Eric Norum * Date: May 7, 2001 * * Routines to modify/display environment variables and EPICS parameters * */ #include #include #define epicsExportSharedSymbols #include "epicsStdio.h" #include "envDefs.h" #include "osiUnistd.h" #include "iocsh.h" /* * Set the value of an environment variable */ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) { iocshEnvClear(name); setenv(name, value, 1); } /* * Unset an environment variable */ epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name) { iocshEnvClear(name); unsetenv(name); } /* * Show the value of the specified, or all, environment variables */ epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) { if (name == NULL) { extern char **environ; char **sp; for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) printf ("%s\n", *sp); } else { const char *cp = getenv (name); if (cp == NULL) printf ("%s is not an environment variable.\n", name); else printf ("%s=%s\n", name, cp); } } base-7.0.3.1/modules/libcom/src/osi/os/solaris/osdFindAddr.c0000664000577000060420000000046513557101274022340 0ustar anjaesctl/* * Copyright: Stanford University / SLAC National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * Author: Till Straumann , 2011, 2014 */ #include "osdElfFindAddr.c" base-7.0.3.1/modules/libcom/src/osi/os/solaris/osdSock.h0000664000577000060420000000433113557101274021565 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Solaris specific socket include */ #ifndef osdSockH #define osdSockH #include #include #include /* for MAXHOSTNAMELEN */ #include #include #include #include #include #include #include #include #include #include #include /* close() and others */ typedef int SOCKET; #define INVALID_SOCKET (-1) #define SOCKERRNO errno #define socket_ioctl(A,B,C) ioctl(A,B,C) typedef int osiSockIoctl_t; #if SOLARIS > 6 || defined ( _SOCKLEN_T ) typedef uint32_t osiSocklen_t; #else typedef int osiSocklen_t; #endif typedef char osiSockOptMcastLoop_t; typedef unsigned char osiSockOptMcastTTL_t; #define DOES_NOT_ACCEPT_ZERO_LENGTH_UDP #define FD_IN_FDSET(FD) ((FD)=0) #define SOCK_EWOULDBLOCK EWOULDBLOCK #define SOCK_ENOBUFS ENOBUFS #define SOCK_ECONNRESET ECONNRESET #define SOCK_ETIMEDOUT ETIMEDOUT #define SOCK_EACCES EACCES #define SOCK_EADDRINUSE EADDRINUSE #define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL #define SOCK_ECONNREFUSED ECONNREFUSED #define SOCK_ECONNABORTED ECONNABORTED #define SOCK_EINPROGRESS EINPROGRESS #define SOCK_EISCONN EISCONN #define SOCK_EALREADY EALREADY #define SOCK_EINVAL EINVAL #define SOCK_EINTR EINTR #define SOCK_EPIPE EPIPE #define SOCK_EMFILE EMFILE #define SOCK_SHUTDOWN ESHUTDOWN #define SOCK_ENOTSOCK ENOTSOCK #define SOCK_EBADF EBADF #ifndef SHUT_RD # define SHUT_RD 0 #endif #ifndef SHUT_WR # define SHUT_WR 1 #endif #ifndef SHUT_RDWR # define SHUT_RDWR 2 #endif #ifndef INADDR_NONE # define INADDR_NONE (0xffffffff) #endif #define ifreq_size(pifreq) (sizeof(pifreq->ifr_name)) #endif /*osdSockH*/ base-7.0.3.1/modules/libcom/src/osi/os/solaris/osdStrtod.h0000664000577000060420000000121513557101274022143 0ustar anjaesctl/*************************************************************************\ * 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. \*************************************************************************/ /* * This header fragment is intended to be included as part of epicsString.h */ #ifdef __cplusplus extern "C" { #endif /* * epicsStrtod() for systems with broken strtod() routine */ epicsShareFunc double epicsStrtod(const char *str, char **endp); #ifdef __cplusplus } #endif base-7.0.3.1/modules/libcom/src/osi/os/solaris/osdWireConfig.h0000664000577000060420000000111713557101274022721 0ustar anjaesctl/* * Solaris version of * osdWireConfig.h * * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef osdWireConfig_h #define osdWireConfig_h #include #if defined ( _LITTLE_ENDIAN ) # define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE #elif defined ( _BIG_ENDIAN ) # define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG #else # error EPICS hasnt been ported to byte order specified by on Solaris #endif /* for now, assume that Solaris doesnt run on weird arch like ARM NWFP */ #define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER #endif /* ifdef osdWireConfig_h */ base-7.0.3.1/modules/libcom/src/osi/os/solaris/osdgetexec.c0000664000577000060420000000113513557101274022304 0ustar anjaesctl #include #include #define epicsExportSharedSymbols #include char *epicsGetExecName(void) { const char *raw = getexecname(); char *ret = NULL; /* manpage says getexecname() might return a relative path. we treat this as an error */ if(raw[0]=='/') { ret = strdup(raw); } return ret; } char *epicsGetExecDir(void) { char *ret = epicsGetExecName(); if(ret) { char *sep = strrchr(ret, '/'); if(sep) { /* nil the charactor after the / */ sep[1] = '\0'; } } return ret; } base-7.0.3.1/modules/libcom/src/osi/os/solaris/osiFileName.h0000664000577000060420000000125013557101274022350 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * osiFileName.h * Author: Jeff Hill * * */ #ifndef osiFileNameH #define osiFileNameH #include "unixFileName.h" #endif /* osiFileNameH */ base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/atReboot.cpp0000664000577000060420000000232413557101274022301 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* atReboot.cpp */ /* Author: Marty Kraimer Date: 30AUG2003 */ #include #include "epicsFindSymbol.h" #include "epicsExit.h" extern "C" { typedef int (*sysAtReboot_t)(void(func)(void)); void atRebootRegister(void) { sysAtReboot_t sysAtReboot = (sysAtReboot_t) epicsFindSymbol("_sysAtReboot"); if (sysAtReboot) { STATUS status = sysAtReboot(epicsExitCallAtExits); if (status) { printf("atReboot: sysAtReboot returned error %d\n", status); } } else { printf("BSP routine sysAtReboot() not found, epicsExit() will not be\n" "called by reboot. For reduced functionality, call\n" " rebootHookAdd(epicsExitCallAtExits)\n"); } } } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/camacLib.h0000664000577000060420000000353013557101274021662 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* camacLib.h -- Prototypes for camacLib.o * * Marty Wise * 10/11/93 * */ /********************************/ /* GLOBAL DATA */ /********************************/ extern int debug_hook; extern struct glob_dat { int total; int read_error[5]; int write_error[5]; int cmd_error[5]; int total_err; int lam_count[12]; } debug_dat; /********************************/ /* FUNCTION PROTOTYPES */ /********************************/ void cdreg(int *ext, int b, int c, int n, int a); void cfsa(int f, int ext, int *dat, int *q); void cssa(int f, int ext, short *dat, int *q); void ccci(int ext, int l); void cccz(int ext); void cccc(int ext); void ccinit(int b); void ctci(int ext, int *l); void cgreg(int ext, int *b, int *c, int *n, int *a); void cfmad(int f, int extb[2], int *intc, int cb[4]); void cfubc(int f, int ext, int *intc, int cb[4]); void cfubc(int f, int ext, int *intc, int cb[4]); void csmad(int f, int extb[2], short *intc, int cb[4]); void ctcd(int ext, int *l); void cccd(int ext, int l); void csga(int fa[], int exta[], unsigned short intc[], int qa[], int cb[4]); void cfga(int fa[], int exta[], int intc[], int qa[], int cb[4]); void cfubr(int f, int ext, int intc[], int cb[4]); void csubc(int f, int ext, unsigned short *intc, int cb[4]); void csubr(int f, int ext, int intc[], int cb[4]); void print_reg(int ext); base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/devLibVMEOSD.c0000664000577000060420000003014313557101274022305 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Archictecture dependent support for common device driver resources * * Author: Jeff Hill * Date: 10-30-98 */ #include #include #include #include #include #include #include #include #include #include #include "epicsFindSymbol.h" #include "devLibVME.h" #include "errlog.h" typedef void myISR (void *pParam); #if CPU_FAMILY != PPC /* * A list of the names of the unexpected interrupt handlers * ( some of these are provided by wrs ) */ static char *defaultHandlerNames[] = { "excStub", "excIntStub", "unsolicitedHandlerEPICS"}; static myISR *defaultHandlerAddr[NELEMENTS(defaultHandlerNames)]; #endif static myISR *isrFetch(unsigned vectorNumber); /* * this routine needs to be in the symbol table * (i.e. not static) for this code to work correctly */ void unsolicitedHandlerEPICS(int vectorNumber); /* * this is in veclist.c */ int cISRTest(void (*)(), void (**)(), void **); /* * Make sure that the CR/CSR addressing mode is defined. * (it may not be in older versions of vxWorks) */ #ifndef VME_AM_CSR # define VME_AM_CSR (0x2f) #endif /* * we use a translation between an EPICS encoding * and a vxWorks encoding here * to reduce dependency of drivers on vxWorks * * we assume that the BSP are configured to use these * address modes by default */ #define EPICSAddrTypeNoConvert -1 int EPICStovxWorksAddrType[] = { VME_AM_SUP_SHORT_IO, VME_AM_STD_SUP_DATA, VME_AM_EXT_SUP_DATA, EPICSAddrTypeNoConvert, VME_AM_CSR }; #if CPU_FAMILY != PPC static void initHandlerAddrList(void); #endif /* * maps logical address to physical address, but does not detect * two device drivers that are using the same address range */ static long vxDevMapAddr (epicsAddressType addrType, unsigned options, size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress); /* * a bus error safe "wordSize" read at the specified address which returns * unsuccessful status if the device isnt present */ static long vxDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue); /* * a bus error safe "wordSize" write at the specified address which returns * unsuccessful status if the device isnt present */ static long vxDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue); static void *devA24Malloc(size_t size); static void devA24Free(void *pBlock); /* We don't know which functions are implemented in the BSP */ static int (*sysIntEnableFunc)(int) = NULL; static int (*sysIntDisableFunc)(int) = NULL; static int (*sysIntEnablePICFunc)(int) = NULL; static int (*sysIntDisablePICFunc)(int) = NULL; static long devInit(void) { sysIntEnableFunc = epicsFindSymbol ("sysIntEnable"); sysIntDisableFunc = epicsFindSymbol ("sysIntDisable"); sysIntDisablePICFunc = epicsFindSymbol ("sysIntDisablePIC"); sysIntEnablePICFunc = epicsFindSymbol ("sysIntEnablePIC"); return 0; } static long vxDevConnectInterruptVME ( unsigned vectorNumber, void (*pFunction)(), void *parameter); static long vxDevDisconnectInterruptVME ( unsigned vectorNumber, void (*pFunction)() ); static long vxDevEnableInterruptLevelVME (unsigned level); static long vxDevDisableInterruptLevelVME (unsigned level); static int vxDevInterruptInUseVME (unsigned vectorNumber); /* * used by dynamic bind in devLib.c */ static devLibVME vxVirtualOS = { vxDevMapAddr, vxDevReadProbe, vxDevWriteProbe, vxDevConnectInterruptVME, vxDevDisconnectInterruptVME, vxDevEnableInterruptLevelVME, vxDevDisableInterruptLevelVME, devA24Malloc,devA24Free,devInit,vxDevInterruptInUseVME }; devLibVME *pdevLibVME = &vxVirtualOS; /* * devConnectInterruptVME * * wrapper to minimize driver dependency on vxWorks */ static long vxDevConnectInterruptVME ( unsigned vectorNumber, void (*pFunction)(), void *parameter) { int status; if (devInterruptInUseVME(vectorNumber)) { return S_dev_vectorInUse; } status = intConnect( (void *)INUM_TO_IVEC(vectorNumber), pFunction, (int) parameter); if (status<0) { return S_dev_vecInstlFail; } return 0; } /* * * vxDevDisconnectInterruptVME() * * wrapper to minimize driver dependency on vxWorks * * The parameter pFunction should be set to the C function pointer that * was connected. It is used as a key to prevent a driver from removing * an interrupt handler that was installed by another driver * */ static long vxDevDisconnectInterruptVME ( unsigned vectorNumber, void (*pFunction)() ) { void (*psub)(); int status; # if CPU_FAMILY == PPC return S_dev_vecInstlFail; # endif /* * If pFunction not connected to this vector * then they are probably disconnecting from the wrong vector */ psub = isrFetch(vectorNumber); if(psub != pFunction){ return S_dev_vectorNotInUse; } status = intConnect( (void *)INUM_TO_IVEC(vectorNumber), unsolicitedHandlerEPICS, (int) vectorNumber); if(status<0){ return S_dev_vecInstlFail; } return 0; } /* * enable VME interrupt level */ static long vxDevEnableInterruptLevelVME (unsigned level) { if (sysIntEnableFunc) { int s; s = sysIntEnableFunc (level); if (s!=OK) { return S_dev_intEnFail; } return 0; } else { return S_dev_intEnFail; } } /* * enable ISA interrupt level */ long devEnableInterruptLevelISA (unsigned level) { if (sysIntEnablePICFunc) { int s; s = sysIntEnablePICFunc (level); if (s!=OK) { return S_dev_intEnFail; } return 0; } else { return S_dev_intEnFail; } } /* * disable ISA interrupt level */ long devDisableInterruptLevelISA (unsigned level) { if (sysIntDisablePICFunc) { int s; s = sysIntDisablePICFunc (level); if (s!=OK) { return S_dev_intEnFail; } } else { return S_dev_intEnFail; } return 0; } /* * disable VME interrupt level */ static long vxDevDisableInterruptLevelVME (unsigned level) { if (sysIntDisableFunc) { int s; s = sysIntDisableFunc (level); if (s!=OK) { return S_dev_intDissFail; } return 0; } else { return S_dev_intEnFail; } } /* * vxDevMapAddr () */ static long vxDevMapAddr (epicsAddressType addrType, unsigned options, size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress) { long status; if (ppPhysicalAddress==NULL) { return S_dev_badArgument; } if (EPICStovxWorksAddrType[addrType] == EPICSAddrTypeNoConvert) { *ppPhysicalAddress = (void *) logicalAddress; } else { status = sysBusToLocalAdrs (EPICStovxWorksAddrType[addrType], (char *) logicalAddress, (char **)ppPhysicalAddress); if (status) { return S_dev_addrMapFail; } } return 0; } /* * a bus error safe "wordSize" read at the specified address which returns * unsuccessful status if the device isn't present */ static long vxDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue) { long status; status = vxMemProbe ((char *)ptr, VX_READ, wordSize, (char *) pValue); if (status!=OK) { return S_dev_noDevice; } return 0; } /* * a bus error safe "wordSize" write at the specified address which returns * unsuccessful status if the device isn't present */ static long vxDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue) { long status; status = vxMemProbe ((char *)ptr, VX_WRITE, wordSize, (char *) pValue); if (status!=OK) { return S_dev_noDevice; } return 0; } /* * isrFetch() */ static myISR *isrFetch(unsigned vectorNumber) { myISR *psub; myISR *pCISR; void *pParam; int s; /* * fetch the handler or C stub attached at this vector */ psub = (myISR *) intVecGet((FUNCPTR *)INUM_TO_IVEC(vectorNumber)); if ( psub ) { /* * from libvxWorks/veclist.c * * checks to see if it is a C ISR * and if so finds the function pointer and * the parameter passed */ s = cISRTest(psub, &pCISR, &pParam); if(!s){ psub = pCISR; } } return psub; } /* * determine if a VME interrupt vector is in use */ static int vxDevInterruptInUseVME (unsigned vectorNumber) { #if CPU_FAMILY == PPC return FALSE; #else { static int init; int i; myISR *psub; if (!init) { initHandlerAddrList(); init = TRUE; } psub = isrFetch (vectorNumber); /* * its a C routine. Does it match a default handler? */ for (i=0; i #endif #include "vxWorks.h" /* obtain the version of vxWorks */ #include "epicsAssert.h" /* * With vxWorks 6.6 and later we need to use vxAtomicLib * to implement this functionality correctly on SMP systems */ #if _WRS_VXWORKS_MAJOR * 100 + _WRS_VXWORKS_MINOR >= 606 #include #include #define EPICS_ATOMIC_OS_NAME "VX-ATOMICLIB" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER #define EPICS_ATOMIC_READ_MEMORY_BARRIER EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) { VX_MEM_BARRIER_R (); } #endif #ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER #define EPICS_ATOMIC_WRITE_MEMORY_BARRIER EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) { VX_MEM_BARRIER_W (); } #endif /* * we make the probably correct guess that if ULONG_MAX * is the same as UINT_MAX then sizeof ( atomic_t ) * will be the same as sizeof ( size_t ) * * if ULONG_MAX != UINT_MAX then its 64 bit vxWorks and * WRS doesnt not supply at this time the atomic interface * for 8 byte integers that is needed - so that architecture * receives the lock synchronized version */ #if ULONG_MAX == UINT_MAX STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( size_t ) ); STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( EpicsAtomicPtrT ) ); #ifndef EPICS_ATOMIC_INCR_SIZET #define EPICS_ATOMIC_INCR_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) { atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); const atomic_t oldVal = vxAtomicInc ( pTarg ); return 1 + ( size_t ) ( oldVal ); } #endif #ifndef EPICS_ATOMIC_DECR_SIZET #define EPICS_ATOMIC_DECR_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) { atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); const atomic_t oldVal = vxAtomicDec ( pTarg ); return ( ( size_t ) oldVal ) - 1u; } #endif #ifndef EPICS_ATOMIC_ADD_SIZET #define EPICS_ATOMIC_ADD_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta ) { /* * vxAtomicLib doc indicates that vxAtomicAdd is * implemented using signed arithmetic, but it * does not change the end result because twos * complement addition is used in either case */ atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); const atomic_t oldVal = vxAtomicAdd ( pTarg, (atomic_t) delta ); return delta + ( size_t ) oldVal; } #endif #ifndef EPICS_ATOMIC_SUB_SIZET #define EPICS_ATOMIC_SUB_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ) { /* * vxAtomicLib doc indicates that vxAtomicSub is * implemented using signed arithmetic, but it * does not change the end result because twos * complement subtraction is used in either case */ atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); const atomic_t oldVal = vxAtomicSub ( pTarg, (atomic_t) delta ); return ( ( size_t ) oldVal ) - delta; } #endif #ifndef EPICS_ATOMIC_CAS_SIZET #define EPICS_ATOMIC_CAS_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, size_t oldVal, size_t newVal ) { atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); return ( size_t ) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal ); } #endif #ifndef EPICS_ATOMIC_CAS_PTRT #define EPICS_ATOMIC_CAS_PTRT EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) { atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); return (EpicsAtomicPtrT) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal ); } #endif #else /* ULONG_MAX == UINT_MAX */ /* * if its 64 bit SMP vxWorks and the compiler doesnt * have an intrinsic then maybe there isnt any way to * implement these without using a global lock because * size_t is maybe bigger than atomic_t * * I dont yet have access to vxWorks manuals for * 64 bit systems so this is still undecided, but is * defaulting now to a global lock */ #endif /* ULONG_MAX == UINT_MAX */ STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( int ) ); #ifndef EPICS_ATOMIC_INCR_INTT #define EPICS_ATOMIC_INCR_INTT EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) { atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); const atomic_t oldVal = vxAtomicInc ( pTarg ); return 1 + ( int ) oldVal; } #endif #ifndef EPICS_ATOMIC_DECR_INTT #define EPICS_ATOMIC_DECR_INTT EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ) { atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); const atomic_t oldVal = vxAtomicDec ( pTarg ); return ( ( int ) oldVal ) - 1; } #endif #ifndef EPICS_ATOMIC_ADD_INTT #define EPICS_ATOMIC_ADD_INTT EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ) { atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); const atomic_t oldVal = vxAtomicAdd ( pTarg, (atomic_t) delta ); return delta + ( int ) oldVal; } #endif #ifndef EPICS_ATOMIC_CAS_INTT #define EPICS_ATOMIC_CAS_INTT EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, int oldVal, int newVal ) { atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); return ( int ) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal ); } #endif #ifdef __cplusplus } /* end of extern "C" */ #endif /* __cplusplus */ #else /* _WRS_VXWORKS_MAJOR * 100 + _WRS_VXWORKS_MINOR >= 606 */ #include "vxLib.h" #include "intLib.h" #define EPICS_ATOMIC_OS_NAME "VX-INTLIB" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #ifndef EPICS_ATOMIC_LOCK #define EPICS_ATOMIC_LOCK typedef struct EpicsAtomicLockKey { int m_key; } EpicsAtomicLockKey; EPICS_ATOMIC_INLINE void epicsAtomicLock ( EpicsAtomicLockKey * pKey ) { pKey->m_key = intLock (); } EPICS_ATOMIC_INLINE void epicsAtomicUnlock ( EpicsAtomicLockKey * pKey ) { intUnlock ( pKey->m_key ); } #endif #ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER #define EPICS_ATOMIC_READ_MEMORY_BARRIER /* * no need for memory barrior since prior to vxWorks 6.6 it is a single cpu system * (we are not protecting against multiple access to memory mapped IO) */ EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) {} #endif #ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER #define EPICS_ATOMIC_WRITE_MEMORY_BARRIER /* * no need for memory barrior since prior to vxWorks 6.6 it is a single cpu system * (we are not protecting against multiple access to memory mapped IO) */ EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) {} #endif #ifdef __cplusplus } /* end of extern "C" */ #endif /* __cplusplus */ #endif /* _WRS_VXWORKS_MAJOR * 100 + _WRS_VXWORKS_MINOR >= 606 */ #include "epicsAtomicDefault.h" #endif /* epicsAtomicOSD_h */ base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/epicsDynLink.c0000664000577000060420000000524713557101274022565 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * On 68K targets, all symbols have an underscore prepended to their name. * This code permits both standards to work, as long as you're not looking * for a symbol name that actually begins with an underscore. */ #include #include "epicsDynLink.h" #if _WRS_VXWORKS_MAJOR < 6 || _WRS_VXWORKS_MINOR < 9 static int symNoUnderscore(SYMTAB_ID symTblId) { static int init = 0; static int noUnderscore = 0; if (!init) { char name[] = "symFindByNameEPICS"; char *pSymValue; SYM_TYPE type; if (symFindByName(symTblId, name, &pSymValue, &type) == OK) noUnderscore = 1; init = 1; } return noUnderscore; } STATUS symFindByNameEPICS(SYMTAB_ID symTblId, char *name, char **ppvalue, SYM_TYPE *pType) { if (name[0] == '_' && symNoUnderscore(symTblId)) name++; return symFindByName(symTblId, name, ppvalue, pType); } STATUS symFindByNameAndTypeEPICS(SYMTAB_ID symTblId, char *name, char **ppvalue, SYM_TYPE *pType, SYM_TYPE sType, SYM_TYPE mask) { if (name[0] == '_' && symNoUnderscore(symTblId)) name++; return symFindByNameAndType(symTblId, name, ppvalue, pType, sType, mask); } #else /* VxWorks 6.9 deprecated the symFindBy routines */ STATUS symFindByNameEPICS(SYMTAB_ID symTblId, char *name, char **ppvalue, SYM_TYPE *pType) { SYMBOL_DESC symDesc; STATUS status; memset(&symDesc, 0, sizeof(SYMBOL_DESC)); symDesc.mask = SYM_FIND_BY_NAME; symDesc.name = name + (name[0] == '_'); status = symFind(sysSymTbl, &symDesc); if (!status) { *ppvalue = symDesc.value; *pType = symDesc.type; } return status; } STATUS symFindByNameAndTypeEPICS(SYMTAB_ID symTblId, char *name, char **ppvalue, SYM_TYPE *pType, SYM_TYPE sType, SYM_TYPE mask) { SYMBOL_DESC symDesc; STATUS status; memset(&symDesc, 0, sizeof(SYMBOL_DESC)); symDesc.mask = SYM_FIND_BY_NAME | SYM_FIND_BY_TYPE; symDesc.name = name + (name[0] == '_'); symDesc.type = sType; symDesc.typeMask = mask; status = symFind(sysSymTbl, &symDesc); if (!status) { *ppvalue = symDesc.value; *pType = symDesc.type; } return status; } #endif base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/epicsDynLink.h0000664000577000060420000000222013557101274022556 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * These routines will eventually need to be made OS independent * (currently this is vxWorks specific) */ #ifndef epicsDynLinkh #define epicsDynLinkh #include "vxWorks.h" #include "symLib.h" #include "sysSymTbl.h" #include "compilerDependencies.h" #ifdef __cplusplus extern "C" { #endif /* Use epicsFindSymbol() instead of these */ STATUS symFindByNameEPICS(SYMTAB_ID symTblId, char *name, char **pvalue, SYM_TYPE *pType) EPICS_DEPRECATED; STATUS symFindByNameAndTypeEPICS(SYMTAB_ID symTblId, char *name, char **pvalue, SYM_TYPE *pType, SYM_TYPE sType, SYM_TYPE mask) EPICS_DEPRECATED; #ifdef __cplusplus } #endif #endif /* ifdef epicsDynLinkh */ base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/epicsMMIO.h0000664000577000060420000001515213557101274021757 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2006 The Regents of the University of California, * as Operator of Los Alamos National Laboratory. * Copyright (c) 2006 The Board of Trustees of the Leland Stanford Junior * University, as Operator of the Stanford Linear Accelerator Center. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Author: Eric Bjorklund (was called mrfSyncIO.h) * Author: Michael Davidsaver */ #ifndef EPICSMMIO_H #define EPICSMMIO_H #if (CPU_FAMILY != PPC) && (CPU_FAMILY != I80X86) # include "epicsMMIODef.h" #else /**************************************************************************************************/ /* Required Header Files */ /**************************************************************************************************/ /* This is needed on vxWorks 6.8 */ #ifndef _VSB_CONFIG_FILE # define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> #endif #include /* vxWorks common definitions */ #include /* vxWorks System Library Definitions */ #include /* vxWorks Version Definitions */ #include /* EPICS Common Type Definitions */ #include /* EPICS Byte Order Definitions */ #include /*===================== * vxAtomicLib.h (which defines the memory barrier macros) * is available on vxWorks 6.6 and above. */ #if _WRS_VXWORKS_MAJOR > 6 # include #elif _WRS_VXWORKS_MAJOR == 6 && _WRS_VXWORKS_MINOR >= 6 # include #endif static EPICS_ALWAYS_INLINE epicsUInt16 bswap16(epicsUInt16 value) { return (((epicsUInt16)(value) & 0x00ff) << 8) | (((epicsUInt16)(value) & 0xff00) >> 8); } static EPICS_ALWAYS_INLINE epicsUInt32 bswap32(epicsUInt32 value) { return (((epicsUInt32)(value) & 0x000000ff) << 24) | (((epicsUInt32)(value) & 0x0000ff00) << 8) | (((epicsUInt32)(value) & 0x00ff0000) >> 8) | (((epicsUInt32)(value) & 0xff000000) >> 24); } #if EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG # define be16_to_cpu(X) (epicsUInt16)(X) # define be32_to_cpu(X) (epicsUInt32)(X) # define le16_to_cpu(X) bswap16(X) # define le32_to_cpu(X) bswap32(X) #elif EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE # define be16_to_cpu(X) bswap16(X) # define be32_to_cpu(X) bswap32(X) # define le16_to_cpu(X) (epicsUInt16)(X) # define le32_to_cpu(X) (epicsUInt32)(X) #else # error Unable to determine native byte order #endif #if CPU_FAMILY == PPC /* All PowerPC BSPs that I have studied implement these functions * with the same definition, byte-swapping the data and adding a * sync and/or eieio instruction as necessary on that CPU board. * They do *not* all implement the sys{In/Out}{Byte/Word/Long} * functions to do the same thing though, so we can't use them. */ #ifdef __cplusplus extern "C" { #endif UINT8 sysPciInByte(UINT8 *addr); void sysPciOutByte(UINT8 *addr, UINT8 data); UINT16 sysPciInWord(UINT16 *addr); void sysPciOutWord(UINT16 *addr, UINT16 data); UINT32 sysPciInLong (UINT32 *addr); void sysPciOutLong (UINT32 *addr, UINT32 data); #ifdef __cplusplus } #endif #define ioread8(address) sysPciInByte((UINT8 *)(address)) #define iowrite8(address,data) sysPciOutByte((UINT8 *)(address), (epicsUInt8)(data)) #define nat_ioread16(address) bswap16(sysPciInWord((UINT16 *)(address))) #define nat_ioread32(address) bswap32(sysPciInLong((UINT32 *)(address))) #define nat_iowrite16(address,data) sysPciOutWord((UINT16 *)(address), bswap16(data)) #define nat_iowrite32(address,data) sysPciOutLong((UINT32 *)(address), bswap32(data)) #define be_ioread16(address) bswap16(sysPciInWord((UINT16 *)(address))) #define be_ioread32(address) bswap32(sysPciInLong((UINT32 *)(address))) #define be_iowrite16(address,data) sysPciOutWord((UINT16 *)(address), bswap16(data)) #define be_iowrite32(address,data) sysPciOutLong((UINT32 *)(address), bswap32(data)) #define le_ioread16(address) sysPciInWord((UINT16 *)(address)) #define le_ioread32(address) sysPciInLong((UINT32 *)(address)) #define le_iowrite16(address,data) sysPciOutWord((UINT16 *)(address), (data)) #define le_iowrite32(address,data) sysPciOutLong((UINT32 *)(address), (data)) #else /* CPU_FAMILY == I80X86 */ /* All Intel BSPs should implement the sys{In/Out}{Byte/Word/Long} * functions, which are declared in the sysLib.h header. */ #define ioread8(address) sysInByte ((epicsUInt32)(address)) #define iowrite8(address,data) sysOutByte ((epicsUInt32)(address), (epicsUInt8)(data)) #define nat_ioread16(address) sysInWord ((epicsUInt32)(address)) #define nat_ioread32(address) sysInLong ((epicsUInt32)(address)) #define nat_iowrite16(address,data) sysOutWord((epicsUInt32)(address),(data)) #define nat_iowrite32(address,data) sysOutLong((epicsUInt32)(address),(data)) #define be_ioread16(address) be16_to_cpu (sysInWord ((epicsUInt32)(address))) #define be_ioread32(address) be32_to_cpu (sysInLong ((epicsUInt32)(address))) #define be_iowrite16(address,data) sysOutWord ((epicsUInt32)(address), be16_to_cpu((epicsUInt16)(data))) #define be_iowrite32(address,data) sysOutLong ((epicsUInt32)(address), be32_to_cpu((epicsUInt32)(data))) #define le_ioread16(address) le16_to_cpu (sysInWord ((epicsUInt32)(address))) #define le_ioread32(address) le32_to_cpu (sysInLong ((epicsUInt32)(address))) #define le_iowrite16(address,data) sysOutWord ((epicsUInt32)(address), le16_to_cpu((epicsUInt16)(data))) #define le_iowrite32(address,data) sysOutLong ((epicsUInt32)(address), le32_to_cpu((epicsUInt32)(data))) #endif /* I80X86 */ #ifndef VX_MEM_BARRIER_R # define VX_MEM_BARRIER_R() do{}while(0) #endif #ifndef VX_MEM_BARRIER_W # define VX_MEM_BARRIER_W() do{}while(0) #endif #ifndef VX_MEM_BARRIER_RW # define VX_MEM_BARRIER_RW() do{}while(0) #endif #define rbarr() VX_MEM_BARRIER_R() #define wbarr() VX_MEM_BARRIER_W() #define rwbarr() VX_MEM_BARRIER_RW() #endif /* CPU_FAMILY */ #endif /* EPICSMMIO_H */ base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/epicsMath.h0000664000577000060420000000216313557101274022105 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef epicsMathh #define epicsMathh #include #include #include /* private/mathP.h defines NAN as 4, and uses its value in the * isNan() macro. We need mathP.h for isInf(), but can create * our own isnan() test. epicsMath.cpp requires that NAN either * be undef or yield the NaN value, so this solves the issue. */ #undef NAN #define isnan(D) (!(D == D)) #define isinf(D) isInf(D) #define finite(D) (!isnan(D) && !isInf(D)) #ifdef __cplusplus extern "C" { #endif epicsShareExtern float epicsNAN; epicsShareExtern float epicsINF; #ifdef __cplusplus } #endif #endif /* epicsMathh */ base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/logMsgToErrlog.cpp0000664000577000060420000000612213557101274023430 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * route vxWorks logMsg messages into the EPICS logging system * * Author: Jeff Hill * */ #include #include #include #include #include #include "errlog.h" // vxCommonLibrary calls logMsgToErrlog so that logMsgToErrlog gets loaded extern "C" { int logMsgToErrlog(); } int logMsgToErrlog() { return 0;} static class errlogDevTimeInit { public: errlogDevTimeInit (); } errlogDevInstance; static int errlogOpen ( DEV_HDR *, const char *, int ) { return OK; } static int errlogWrite ( DEV_HDR *, const char * pInBuf, int nbytes ) { errlogPrintfNoConsole ( "%.*s", nbytes, pInBuf ); return nbytes; } errlogDevTimeInit::errlogDevTimeInit () { int errlogNo = iosDrvInstall ( 0, // create not supported 0, // remove not supported reinterpret_cast < FUNCPTR > ( errlogOpen ), 0, // close is a noop 0, // read not supported reinterpret_cast < FUNCPTR > ( errlogWrite ), 0 // ioctl not supported ); if ( errlogNo == ERROR ) { errlogPrintf ( "Unable to install driver routing the vxWorks " "logging system to the EPICS logging system because \"%s\"\n", strerror ( errno ) ); return; } DEV_HDR * pDev = static_cast < DEV_HDR * > ( calloc ( 1, sizeof ( *pDev ) ) ); if ( ! pDev ) { errlogPrintf ( "Unable to create driver data structure for routing the vxWorks " "logging system to the EPICS logging system because \"%s\"\n", strerror ( errno ) ); return; } int status = iosDevAdd ( pDev, "/errlog/", errlogNo ); if ( status < 0 ) { errlogPrintf ( "Unable to install device routing the vxWorks " "logging system to the EPICS logging system because \"%s\"\n", strerror ( errno ) ); free ( pDev ); return; } int fd = open ( "/errlog/any", O_WRONLY, 0 ); if ( fd < 0 ) { errlogPrintf ( "Unable to open fd routing the vxWorks " "logging system to the EPICS logging system because \"%s\"\n", strerror ( errno ) ); return; } status = logFdAdd ( fd ); if ( status != OK) { errlogPrintf ( "Unable to install fd routing the vxWorks " "logging system to the EPICS logging system because \"%s\"\n", strerror ( errno ) ); close ( fd ); return; } } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/module_types.h0000664000577000060420000003715713557101274022714 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* module_types.h */ /* * Author: Bob Dalesio * Date: 12-07-88 */ #ifndef INCLmodule_typesh #define INCLmodule_typesh /* Device module types */ /* * all devices have corresponding entries in ~operator/db/src/menus.c * changes must be made in both areas to keep the database and drivers in sync */ /* & in comment indicates tested with card 0 */ /* % in comment indicates tested with card other than card 0 */ /* # in comment indicates that the Nth card has been tested */ /* !! never been tested */ /* * @# If any changes are made to this file, check the procedures * ab_card, and vme_card in signallist.c, and get_address in sigmenu.c. */ #ifdef MODULE_TYPES_INIT #define MODULE_TYPES_DEF(MT_DEF_PARM) MT_DEF_PARM #else #define MODULE_TYPES_DEF(MT_DEF_PARM) extern MT_DEF_PARM; #endif /* Number of columns used in io_report. */ #define IOR_MAX_COLS 4 /* I/O types */ #define IO_AI 0 #define IO_AO 1 #define IO_BI 2 #define IO_BO 3 #define IO_SM 4 #define IO_WF 5 #define IO_TIMER 6 #define MAX_IO_TYPE IO_TIMER /* bus types */ /* must correspond to the values in link types */ /* these defines are in ~gta/dbcon/h/link.h */ /* equates for the Allen-Bradley cards. */ #define AB_BASE_ADDR 0xc00000 /* base addr of first AB6008SV */ #define AB_MAX_LINKS 2 /* number of serial links from VME */ #define AB_MAX_ADAPTERS 8 /* number of physical adapters on a link */ #define AB_MAX_CARDS 16 /* max number of IO cards per adapter */ #define AB_CARD_ADAPTER 16 /* cards per logical adapter */ #define AB_CHAN_CARD 16 /* max channels per card */ /* analog inputs */ #define AB1771IL 0 /* &% Allen-Bradley low level analog input */ #define AB1771IFE 1 /* &% Allen-Bradley low level analog input */ #define AB1771IXE 2 /* &% Allen-Bradley millivolt input */ #define XY566SE 3 /* & Xycom 12-bit Single Ended Scanned*/ #define XY566DI 4 /* &% Xycom 12-bit Differential Scanned */ #define XY566DIL 5 /* &% Xycom 12-bit Differential Latched */ #define VXI_AT5_AI 6 /* % AT-5 VXI module's Analog Inputs */ #define AB1771IFE_SE 7 /* % A-B IFE in 16 single-ended input mode */ #define AB1771IFE_4to20MA 8 /* % A-B IFE in 8 double-ended 4to20Ma */ #define DVX2502 9 /* &% DVX_2502 128 chan 16 bit differential */ #define AB1771IFE_0to5V 10 /* % A-B IFE in 8 double-ended 4to20Ma */ #define KSCV215 11 /* % KSC V215 VXI 16 bit differential */ #define AB1771IrPlatinum 12 /* % A-B RTD Platinum */ #define AB1771IrCopper 13 /* % A-B RTD Copper */ #define MAX_AI_TYPES AB1771IrCopper MODULE_TYPES_DEF(short ai_num_cards[MAX_AI_TYPES+1]) #ifdef MODULE_TYPES_INIT ={12,12,12, 4, 4, 6,32,12,12, 1, 12, 32, 12,12}; #endif MODULE_TYPES_DEF(short ai_num_channels[MAX_AI_TYPES+1]) #ifdef MODULE_TYPES_INIT ={ 8, 8, 8,32,16,16, 8,16, 8, 127, 8, 32,6,6}; #endif MODULE_TYPES_DEF(short ai_interruptable[MAX_AI_TYPES+1]) #ifdef MODULE_TYPES_INIT ={0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,0,0}; #endif MODULE_TYPES_DEF(short ai_bus[MAX_AI_TYPES+1]) #ifdef MODULE_TYPES_INIT ={ 4, 4, 4, 2, 2, 2, 2, 4, 4, 2, 4, 2,4,4}; #endif MODULE_TYPES_DEF(unsigned short ai_addrs[MAX_AI_TYPES+1]) #ifdef MODULE_TYPES_INIT ={ 0,0,0,0x6000,0x7000,0xe000, 0xc014,0,0, 0xff00, 0, 0,0,0}; #endif MODULE_TYPES_DEF(long ai_memaddrs[MAX_AI_TYPES+1]) #ifdef MODULE_TYPES_INIT ={0,0,0,0x000000,0x040000,0x0c0000, 0,0,0, 0x100000, 0, 0,0,0}; #endif /* analog outputs */ #define AB1771OFE 0 /* &% Allen-Bradley 12 bit Analog Output */ #define VMI4100 1 /* & VMIC VMIVME 4100 */ #define ZIO085 2 /* & Ziomek 085 */ #define VXI_AT5_AO 3 /* !! AT-5 VXI modules analog outputs */ #define MAX_AO_TYPES VXI_AT5_AO MODULE_TYPES_DEF(short ao_num_cards[MAX_AO_TYPES+1]) #ifdef MODULE_TYPES_INIT = {12, 4, 1, 32}; #endif MODULE_TYPES_DEF(short ao_num_channels[MAX_AO_TYPES+1]) #ifdef MODULE_TYPES_INIT = { 4, 16, 32, 16}; #endif MODULE_TYPES_DEF(short ao_interruptable[MAX_AO_TYPES+1] ) #ifdef MODULE_TYPES_INIT = { 0, 0, 0, 1}; #endif MODULE_TYPES_DEF(short ao_bus[MAX_AO_TYPES+1]) #ifdef MODULE_TYPES_INIT ={ 4, 2, 2, 2}; #endif MODULE_TYPES_DEF(unsigned short ao_addrs[MAX_AO_TYPES+1]) #ifdef MODULE_TYPES_INIT ={ 0,0x4100,0x0800, 0xc000}; #endif /* binary inputs */ #define ABBI_08_BIT 0 /* &% Allen-Bradley generic Binary In 8 bit */ #define ABBI_16_BIT 1 /* &% Allen-Bradley generic Binary In 16 bit */ #define BB910 2 /* & BURR BROWN MPV 910 (relay) */ #define XY210 3 /* &% XYcom 32 bit binary in */ #define VXI_AT5_BI 4 /* !! AT-5 VXI modules binary inputs */ #define HPE1368A_BI 5 /* !! HP E1368A video switch */ #define AT8_FP10S_BI 6 /* !! AT8 FP10 slave fast protect */ #define XY240_BI 7 /* !! Xycom 32 bit binary in / 32 bit binary out */ #define MAX_BI_TYPES XY240_BI MODULE_TYPES_DEF(short bi_num_cards[MAX_BI_TYPES+1] ) #ifdef MODULE_TYPES_INIT ={ 12, 12, 4, 4, 32, 32, 8, 2}; #endif MODULE_TYPES_DEF(short bi_num_channels[MAX_BI_TYPES+1] ) #ifdef MODULE_TYPES_INIT ={ 8, 16, 32, 32, 32, 16, 32, 32}; #endif MODULE_TYPES_DEF(short bi_interruptable[MAX_BI_TYPES+1] ) #ifdef MODULE_TYPES_INIT ={ 1, 1, 0, 0, 1, 1, 1, 1}; #endif MODULE_TYPES_DEF(short bi_bus[MAX_BI_TYPES+1]) #ifdef MODULE_TYPES_INIT ={ 4, 4, 2, 2, 2, 2, 2, 2}; #endif MODULE_TYPES_DEF(unsigned short bi_addrs[MAX_BI_TYPES+1]) #ifdef MODULE_TYPES_INIT ={ 0,0,0xb800,0xa000, 0xc000, 0xc000, 0x0e00, 0xd000}; #endif /* binary outputs */ #define ABBO_08_BIT 0 /* &% Allen-Bradley 8 bit binary out */ #define ABBO_16_BIT 1 /* &% Allen-Bradley 16 bit binary out */ #define BB902 2 /* &% BURR BROWN MPV 902 (relay) */ #define XY220 3 /* &% XYcom 32 bit binary out */ #define VXI_AT5_BO 4 /* !! AT-5 VXI modules binary outputs */ #define HPE1368A_BO 5 /* !! HP E1368A video switch */ #define AT8_FP10M_BO 6 /* !! AT8 FP10 master fast protect */ #define XY240_BO 7 /* !! Xycom 32 bit binary in / 32 bit binary out */ #define MAX_BO_TYPES XY240_BO MODULE_TYPES_DEF(short bo_num_cards[MAX_BO_TYPES+1] ) #ifdef MODULE_TYPES_INIT ={12, 12, 4, 1, 32, 32, 2, 2}; #endif MODULE_TYPES_DEF(short bo_num_channels[MAX_BO_TYPES+1] ) #ifdef MODULE_TYPES_INIT ={ 8, 16, 32, 32, 32, 16, 32, 32}; #endif MODULE_TYPES_DEF(short bo_interruptable[MAX_BO_TYPES+1] ) #ifdef MODULE_TYPES_INIT ={ 0, 0, 0, 0, 1, 0, 0, 1 }; #endif MODULE_TYPES_DEF(short bo_bus[MAX_BO_TYPES+1]) #ifdef MODULE_TYPES_INIT ={ 4, 4, 2, 2, 2, 2, 2, 2 }; #endif MODULE_TYPES_DEF(unsigned short bo_addrs[MAX_BO_TYPES+1]) #ifdef MODULE_TYPES_INIT ={ 0,0,0xd800,0xc800, 0xc000, 0xc000, 0x0c00, 0xd000}; #endif /* stepper motor drivers */ #define CM57_83E 0 /* & Compumotor 57-83E motor controller */ #define OMS_6AXIS 1 /* & OMS six axis motor controller */ #define MAX_SM_TYPES OMS_6AXIS MODULE_TYPES_DEF(short sm_num_cards[MAX_SM_TYPES+1] ) #ifdef MODULE_TYPES_INIT ={ 8, 8 }; #endif MODULE_TYPES_DEF(short sm_num_channels[MAX_SM_TYPES+1] ) #ifdef MODULE_TYPES_INIT = { 1, 8}; #endif MODULE_TYPES_DEF(short sm_interruptable[MAX_SM_TYPES+1] ) #ifdef MODULE_TYPES_INIT = { 0, 0 }; #endif MODULE_TYPES_DEF(short sm_bus[MAX_SM_TYPES+1]) #ifdef MODULE_TYPES_INIT ={ 2, 2 }; #endif MODULE_TYPES_DEF(unsigned short sm_addrs[MAX_SM_TYPES+1]) #ifdef MODULE_TYPES_INIT ={ 0x8000, 0xfc00 }; #endif /* waveforms */ #define XY566WF 0 /* & Xycom 566 as a waveform */ #define CAMAC_THING 1 /* !! CAMAC waveform digitizer */ #define JGVTR1 2 /* & Joerger transient recorder */ #define COMET 3 /* !! COMET transient recorder */ #define MAX_WF_TYPES COMET MODULE_TYPES_DEF(short wf_num_cards[MAX_WF_TYPES+1] ) #ifdef MODULE_TYPES_INIT ={4, 4, 8, 4}; #endif MODULE_TYPES_DEF(short wf_num_channels[MAX_WF_TYPES+1] ) #ifdef MODULE_TYPES_INIT ={1, 1, 1, 4}; #endif MODULE_TYPES_DEF(short wf_interruptable[MAX_WF_TYPES+1] ) #ifdef MODULE_TYPES_INIT = {0, 0, 0, 0}; #endif MODULE_TYPES_DEF(short wf_bus[MAX_WF_TYPES+1]) #ifdef MODULE_TYPES_INIT ={2, 3, 2, 2}; #endif MODULE_TYPES_DEF(unsigned short wf_addrs[MAX_WF_TYPES+1]) #ifdef MODULE_TYPES_INIT ={0x9000, 0, 0xB000, 0xbc00}; #endif MODULE_TYPES_DEF(unsigned short wf_armaddrs[MAX_WF_TYPES+1]) #ifdef MODULE_TYPES_INIT = {0x5400, 0, 0, 0}; #endif MODULE_TYPES_DEF(long wf_memaddrs[MAX_WF_TYPES+1]) #ifdef MODULE_TYPES_INIT ={0x080000, 0, 0xb80000, 0xe0000000}; #endif /* timing cards */ #define MZ8310 0 /* &% Mizar Timing Module */ #define DG535 1 /* !! GPIB timing instrument */ #define VXI_AT5_TIME 2 /* !! AT-5 VXI modules timing channels */ #define MAX_TM_TYPES VXI_AT5_TIME MODULE_TYPES_DEF(short tm_num_cards[MAX_TM_TYPES+1] ) #ifdef MODULE_TYPES_INIT ={ 4, 1, 32 }; #endif MODULE_TYPES_DEF(short tm_num_channels[MAX_TM_TYPES+1] ) #ifdef MODULE_TYPES_INIT ={10, 1, 10}; #endif MODULE_TYPES_DEF(short tm_interruptable[MAX_TM_TYPES+1] ) #ifdef MODULE_TYPES_INIT = { 1, 0, 1 }; #endif MODULE_TYPES_DEF(short tm_bus[MAX_TM_TYPES+1]) #ifdef MODULE_TYPES_INIT ={ 2, 5, 2 }; #endif MODULE_TYPES_DEF(unsigned short tm_addrs[MAX_TM_TYPES+1]) #ifdef MODULE_TYPES_INIT ={0xf800, 0, 0xc000 }; #endif /* AT830X clock cards */ MODULE_TYPES_DEF(long AT830X_1_addrs ) #ifdef MODULE_TYPES_INIT = 0x0400; #endif MODULE_TYPES_DEF(short AT830X_1_num_cards ) #ifdef MODULE_TYPES_INIT = 2; #endif MODULE_TYPES_DEF(long AT830X_addrs ) #ifdef MODULE_TYPES_INIT = 0xaa0000; #endif MODULE_TYPES_DEF(short AT830X_num_cards ) #ifdef MODULE_TYPES_INIT = 2; #endif /* * system controller cards. * (driver looks for only one card) */ MODULE_TYPES_DEF(long xy010ScA16Base) #ifdef MODULE_TYPES_INIT = 0x0000; #endif /* * limit the size of the VXI logical address space * * = + 0xc000 * * LA VME address * 0 * EPICS_VXI_LA_COUNT + (EPICS_VXI_LA_COUNT-1)*64 */ MODULE_TYPES_DEF(unsigned char EPICS_VXI_LA_COUNT) #ifdef MODULE_TYPES_INIT = 32; #endif /* * * address ranges for VXI A24 and A32 devices * */ MODULE_TYPES_DEF(char *EPICS_VXI_A24_BASE) #ifdef MODULE_TYPES_INIT = (char *) 0x900000; #endif MODULE_TYPES_DEF(unsigned long EPICS_VXI_A24_SIZE) #ifdef MODULE_TYPES_INIT = 0x100000; #endif MODULE_TYPES_DEF(char *EPICS_VXI_A32_BASE) #ifdef MODULE_TYPES_INIT = (char *) 0x90000000; #endif MODULE_TYPES_DEF(unsigned long EPICS_VXI_A32_SIZE) #ifdef MODULE_TYPES_INIT = 0x10000000; #endif /****************************************************************************** * * Interrupt vector locations used by the MV167 CPU board. * These are defined in mv167.h * * PCC2_INT_VEC_BASE 0x40 PCC interrupt vector base number * any multiple of 0x10 * UTIL_INT_VEC_BASE0 0x50 VMEchip2 utility interrupt * vector base number * any multiple of 0x10 * UTIL_INT_VEC_BASE1 0x60 VMEchip2 utility interrupt * vector base number * any multiple of 0x10 * * INT_VEC_CD2400_A 0x90 int vec for channel A * INT_VEC_CD2400_B 0x94 int vec for channel B * INT_VEC_CD2400_C 0x98 int vec for channel C * INT_VEC_CD2400_D 0x9c int vec for channel D * * LANC_IRQ_LEVEL 3 LNANC IRQ level * MPCC_IRQ_LEVEL 4 serial comm IRQ level * SYS_CLK_LEVEL 6 interrupt level for sysClk * AUX_CLK_LEVEL 5 interrupt level for auxClk * SCSI_IRQ_LEVEL 2 scsi interrupt level * ******************************************************************************/ /* interrupt vector allocation - one for each XY566 DIL card */ MODULE_TYPES_DEF(int AI566_VNUM) #ifdef MODULE_TYPES_INIT =0xf8; /* Xycom 566 Differential Latched */ #endif /* interrupt vector allocation - one for each DVX card */ MODULE_TYPES_DEF(int DVX_IVEC0) #ifdef MODULE_TYPES_INIT =0xd0; #endif /* stepper motor interrupt vector - one for each motor */ MODULE_TYPES_DEF(int MD_INT_BASE) #ifdef MODULE_TYPES_INIT =0xf0; /* base of the motor int vector */ #endif /* I reserve from here up to num_cards * 4 interrupting chans/card - joh */ MODULE_TYPES_DEF(int MZ8310_INT_VEC_BASE) #ifdef MODULE_TYPES_INIT =0xe8; #endif /* Allen-Bradley Serial Driver - MAX_AB_LINKS number of vectors */ MODULE_TYPES_DEF(int AB_VEC_BASE) #ifdef MODULE_TYPES_INIT =0x60; #endif /* only one interrupt vector allocated for all Joerger VTR1 boards joh */ MODULE_TYPES_DEF(int JGVTR1_INT_VEC) #ifdef MODULE_TYPES_INIT =0xe0; #endif /* AT830X_1 cards have 1 intr vector for each AT830X_1_num_cards (presently 2) */ MODULE_TYPES_DEF(int AT830X_1_IVEC0) #ifdef MODULE_TYPES_INIT =0xd4; #endif /* AT830X cards have 1 intr vector for each AT830X_num_cards (presently 2) */ MODULE_TYPES_DEF(int AT830X_IVEC0) #ifdef MODULE_TYPES_INIT =0xd6; #endif /* AT8 fast protect interrupt vector base */ MODULE_TYPES_DEF(int AT8FP_IVEC_BASE) #ifdef MODULE_TYPES_INIT =0xa2; #endif MODULE_TYPES_DEF(int AT8FPM_IVEC_BASE ) #ifdef MODULE_TYPES_INIT =0xaa; #endif /****************************************************************************** * * Addresses and IRQ information used by the XVME402 bitbus cards. * ******************************************************************************/ MODULE_TYPES_DEF(unsigned short BB_SHORT_OFF ) #ifdef MODULE_TYPES_INIT = 0x1800; /* the first address of link 0's region */ #endif #define BB_NUM_LINKS 4 /* max number of BB ports allowed */ MODULE_TYPES_DEF(int BB_IVEC_BASE ) #ifdef MODULE_TYPES_INIT = 0xa0; /* vectored interrupts (2 used for each link) */ #endif MODULE_TYPES_DEF(int BB_IRQ_LEVEL ) #ifdef MODULE_TYPES_INIT = 5; /* IRQ level */ #endif /****************************************************************************** * * Information for the PEP modular Bitbus boards. * ******************************************************************************/ MODULE_TYPES_DEF(unsigned short PEP_BB_SHORT_OFF ) #ifdef MODULE_TYPES_INIT = 0x1c00; #endif MODULE_TYPES_DEF(int PEP_BB_IVEC_BASE ) #ifdef MODULE_TYPES_INIT = 0xe8; #endif /****************************************************************************** * * Addresses and IRQ information used by the NI1014 and NI1014D bitbus cards. * ******************************************************************************/ MODULE_TYPES_DEF(unsigned short NIGPIB_SHORT_OFF) #ifdef MODULE_TYPES_INIT = 0x5000;/* First address of link 0's region */ #endif /* Each link uses 0x0200 bytes */ #define NIGPIB_NUM_LINKS 4 /* Max number of NI GPIB ports allowed */ MODULE_TYPES_DEF(int NIGPIB_IVEC_BASE ) #ifdef MODULE_TYPES_INIT = 100; /* Vectored interrupts (2 used for each link) */ #endif MODULE_TYPES_DEF(int NIGPIB_IRQ_LEVEL ) #ifdef MODULE_TYPES_INIT =5; /* IRQ level */ #endif #if 0 /* JRW */ #define NI1014_LINK_NUM_BASE 0 #endif /* * nothing after this endif */ #endif /*INCLmodule_typesh*/ base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdEnv.c0000664000577000060420000000457713557101274021434 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osdEnv.c */ /* * Author: Eric Norum * Date: May 7, 2001 * * Routines to modify/display environment variables and EPICS parameters * */ /* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ #define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> #include #include #include #include #include #define epicsExportSharedSymbols #include "cantProceed.h" #include "epicsFindSymbol.h" #include "epicsStdio.h" #include "errlog.h" #include "iocsh.h" /* * Set the value of an environment variable * Leaks memory, but the assumption is that this routine won't be * called often enough for the leak to be a problem. */ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) { char *cp; if (!name) { printf ("Usage: epicsEnvSet \"name\", \"value\"\n"); return; } iocshEnvClear(name); cp = mallocMustSucceed (strlen (name) + strlen (value) + 2, "epicsEnvSet"); strcpy (cp, name); strcat (cp, "="); strcat (cp, value); if (putenv (cp) < 0) { errPrintf(-1L, __FILE__, __LINE__, "Failed to set environment parameter \"%s\" to \"%s\": %s\n", name, value, strerror (errno)); free (cp); } } /* * Unset an environment variable * Basically destroy the name of that variable because vxWorks does not * support to really unset an environment variable. */ epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name) { char* var; if (!name) return; iocshEnvClear(name); var = getenv(name); if (!var) return; var -= strlen(name); var --; *var = 0; } /* * Show the value of the specified, or all, environment variables */ epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) { if (name == NULL) { envShow (0); } else { const char *cp = getenv (name); if (cp == NULL) printf ("%s is not an environment variable.\n", name); else printf ("%s=%s\n", name, cp); } } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdEvent.c0000664000577000060420000000377513557101274021764 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* os/vxWorks/osdEvent.c */ /* Author: Marty Kraimer Date: 25AUG99 */ #include #include #include #include #include #include #include "epicsEvent.h" /* The following not defined in any vxWorks header */ int sysClkRateGet(void); epicsEventId epicsEventCreate(epicsEventInitialState initialState) { return (epicsEventId) semBCreate(SEM_Q_FIFO, (initialState == epicsEventEmpty) ? SEM_EMPTY : SEM_FULL); } void epicsEventDestroy(epicsEventId id) { semDelete((SEM_ID)id); } epicsEventStatus epicsEventWaitWithTimeout(epicsEventId id, double timeOut) { int rate = sysClkRateGet(); int status; int ticks; if (timeOut <= 0.0) { ticks = 0; } else if (timeOut >= (double) INT_MAX / rate) { ticks = WAIT_FOREVER; } else { ticks = timeOut * rate; if (ticks <= 0) ticks = 1; } status = semTake((SEM_ID)id, ticks); if (status == OK) return epicsEventOK; if (errno == S_objLib_OBJ_TIMEOUT || (errno == S_objLib_OBJ_UNAVAILABLE && ticks == 0)) return epicsEventWaitTimeout; return epicsEventError; } epicsEventStatus epicsEventTryWait(epicsEventId id) { int status = semTake((SEM_ID)id, NO_WAIT); if (status == OK) return epicsEventOK; if (errno == S_objLib_OBJ_UNAVAILABLE) return epicsEventWaitTimeout; return epicsEventError; } void epicsEventShow(epicsEventId id, unsigned int level) { semShow((SEM_ID)id,level); } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdEvent.h0000664000577000060420000000147213557101274021761 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* os/vxWorks/osdEvent.h */ /* Author: Marty Kraimer Date: 25AUG99 */ #include #include #define epicsEventTrigger(ID) \ (semGive((SEM_ID)(ID)) == OK ? epicsEventOK : epicsEventError) #define epicsEventWait(ID) \ (semTake((SEM_ID)(ID), WAIT_FOREVER) == OK ? epicsEventOK : epicsEventError) base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdFindSymbol.c0000664000577000060420000000525213557101274022741 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osi/os/vxWorks/osdFindSymbol */ /* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ #define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> #include #include #include #include #include #include #include #include #include #include "dbmf.h" #include "epicsString.h" #include "epicsFindSymbol.h" static char *errmsg = NULL; static char *oldmsg = NULL; epicsShareFunc void * epicsLoadLibrary(const char *name) { MODULE_ID m = 0; int fd; if (oldmsg) { free(oldmsg); oldmsg = NULL; } if (errmsg) { free(errmsg); errmsg = NULL; } fd = open(name, O_RDONLY, 0); if (fd != ERROR) { m = loadModule(fd, GLOBAL_SYMBOLS); close(fd); } if (!m) { errmsg = epicsStrDup(strerror(errno)); } return m; } epicsShareFunc const char *epicsLoadError(void) { if (oldmsg) free(oldmsg); oldmsg = errmsg; errmsg = NULL; return oldmsg; } void *epicsFindSymbol(const char *name) { STATUS status; #if _WRS_VXWORKS_MAJOR < 6 || _WRS_VXWORKS_MINOR < 9 char *pvalue; SYM_TYPE type; status = symFindByName(sysSymTbl, (char *)name, &pvalue, &type); if (!status) return pvalue; if (name[0] == '_' ) { status = symFindByName(sysSymTbl, (char *)(name+1), &pvalue, &type); } #if CPU_FAMILY == MC680X0 else { char *pname = dbmfMalloc(strlen(name) + 2); pname[0] = '_'; strcpy(pname + 1, name); status = symFindByName(sysSymTbl, pname, &pvalue, &type); dbmfFree(pname); } #endif if (!status) return pvalue; #else SYMBOL_DESC symDesc; memset(&symDesc, 0, sizeof(SYMBOL_DESC)); symDesc.mask = SYM_FIND_BY_NAME; symDesc.name = (char *) name; status = symFind(sysSymTbl, &symDesc); if (!status) return symDesc.value; if (name[0] == '_') { symDesc.name++; status = symFind(sysSymTbl, &symDesc); if (!status) return symDesc.value; } /* No need to prepend an '_'; 68K-only, no longer supported */ #endif return 0; } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdInterrupt.c0000664000577000060420000000172313557101274022666 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* osi/os/vxWorks/osdInterrupt.c */ /* Author: Marty Kraimer Date: 28JAN2000 */ #include #include #include #include "epicsInterrupt.h" int epicsInterruptLock() {return(intLock());} void epicsInterruptUnlock(int key) {intUnlock(key);} int epicsInterruptIsInterruptContext() {return(intContext());} void epicsInterruptContextMessage(const char *message) { logMsg((char *)message,0,0,0,0,0,0); } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdInterrupt.h0000664000577000060420000000121213557101274022664 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* osi/os/vxWorks/osdInterrupt.h */ /* Author: Marty Kraimer Date: 28JAN2000 */ /*osdInterrupt.h not needed */ base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdMessageQueue.cpp0000664000577000060420000000303213557101274023616 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author W. Eric Norum * norume@aps.anl.gov * 630 252 4793 */ #define epicsExportSharedSymbols #include #include "epicsMessageQueue.h" extern "C" int sysClkRateGet(void); epicsShareFunc int epicsShareAPI epicsMessageQueueSendWithTimeout( epicsMessageQueueId id, void *message, unsigned int messageSize, double timeout) { int ticks; if (timeout<=0.0) { ticks = 0; } else { ticks = (int)(timeout*sysClkRateGet()); if(ticks<=0) ticks = 1; } return msgQSend((MSG_Q_ID)id, (char *)message, messageSize, ticks, MSG_PRI_NORMAL); } epicsShareFunc int epicsShareAPI epicsMessageQueueReceiveWithTimeout( epicsMessageQueueId id, void *message, unsigned int size, double timeout) { int ticks; if (timeout<=0.0) { ticks = 0; } else { ticks = (int)(timeout*sysClkRateGet()); if(ticks<=0) ticks = 1; } return msgQReceive((MSG_Q_ID)id, (char *)message, size, ticks); } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdMessageQueue.h0000664000577000060420000000262713557101274023274 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author W. Eric Norum * norume@aps.anl.gov * 630 252 4793 */ /* * Very thin shims around vxWorks routines */ #include #include #define epicsMessageQueueCreate(c,s) ((epicsMessageQueueId)msgQCreate((c),(s),MSG_Q_FIFO)) #define epicsMessageQueueDestroy(q) (msgQDelete((MSG_Q_ID)(q))) #define epicsMessageQueueTrySend(q,m,l) (msgQSend((MSG_Q_ID)(q), (char*)(m), (l), NO_WAIT, MSG_PRI_NORMAL)) #define epicsMessageQueueSend(q,m,l) (msgQSend((MSG_Q_ID)(q), (char*)(m), (l), WAIT_FOREVER, MSG_PRI_NORMAL)) #define epicsMessageQueueTryReceive(q,m,s) (msgQReceive((MSG_Q_ID)(q), (char*)(m), (s), NO_WAIT)) #define epicsMessageQueueReceive(q,m,s) (msgQReceive((MSG_Q_ID)(q), (char*)(m), (s), WAIT_FOREVER)) #define epicsMessageQueuePending(q) (msgQNumMsgs((MSG_Q_ID)(q))) #define epicsMessageQueueShow(q,l) (msgQShow((MSG_Q_ID)(q),(l))) base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdMonotonic.c0000664000577000060420000000623113557101274022636 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> #include #include #include #include #define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTypes.h" #include "epicsTime.h" #include "cantProceed.h" #define NS_PER_SEC 1000000000 union timebase { UINT32 u32[2]; /* vxTimeBaseGet() */ INT64 i64; /* pentiumTscGet64() */ UINT64 u64; /* epicsMonotonicGet() */ }; static void measureTickRate(void); #if CPU_FAMILY == PPC #include #include "epicsFindSymbol.h" /* On PowerPC the timebase counter runs at a rate related to the * bus clock and its frequency should always fit into a UINT32. */ static epicsUInt32 ticksPerSec; #define TIMEBASEGET(TB) \ vxTimeBaseGet(&TB.u32[0], &TB.u32[1]) void osdMonotonicInit(void) { typedef epicsUInt32 (*sysTimeBaseFreq_t)(void); sysTimeBaseFreq_t sysTimeBaseFreq = (sysTimeBaseFreq_t) epicsFindSymbol("_sysTimeBaseFreq"); if (sysTimeBaseFreq) { ticksPerSec = sysTimeBaseFreq(); if (ticksPerSec) return; /* This should never happen */ printf("Warning: sysTimeBaseFreq() present but returned zero.\n"); } /* Fall back to measuring */ measureTickRate(); } #elif CPU_FAMILY == I80X86 #include #include /* On Intel the timebase counter frequency is returned by the OS as a * UINT64. Some CPUs may count at multi-GHz rates so we need 64 bits. */ static epicsUInt64 ticksPerSec; #define TIMEBASEGET(TB) \ pentiumTscGet64(&TB.i64) void osdMonotonicInit(void) { ticksPerSec = vxCpuIdGetFreq(); if (ticksPerSec) return; /* This should never happen */ printf("Warning: vxCpuIdGetFreq() returned zero.\n"); /* Fall back to measuring */ measureTickRate(); } #else #error This CPU family not supported yet! #endif epicsUInt64 epicsMonotonicResolution(void) { if (!ticksPerSec) return 0; return NS_PER_SEC / ticksPerSec; } epicsUInt64 epicsMonotonicGet(void) { union timebase tbNow; if (!ticksPerSec) { cantProceed("Monotonic time source not available.\n"); } TIMEBASEGET(tbNow); /* Using a long double for the calculation below to preserve * as many bits in the mantissa as possible. */ return ((long double) tbNow.u64) * NS_PER_SEC / ticksPerSec; } static void measureTickRate(void) { union timebase start, end; int sysTicks = sysClkRateGet(); /* 1 second */ printf("osdMonotonicInit: Measuring CPU time-base frequency ..."); fflush(stdout); taskDelay(1); TIMEBASEGET(start); taskDelay(sysTicks); TIMEBASEGET(end); ticksPerSec = end.u64 - start.u64; printf(" %llu ticks/sec.\n", (unsigned long long) ticksPerSec); } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdMutex.c0000664000577000060420000000264713557101274022002 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* os/vxWorks/osdMutex.c */ /* Author: Marty Kraimer Date: 25AUG99 */ #include #include #include #include #include /* The following not defined in an vxWorks header */ int sysClkRateGet(void); #include "epicsMutex.h" struct epicsMutexOSD * epicsMutexOsdCreate(void) { return((struct epicsMutexOSD *) semMCreate(SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY)); } void epicsMutexOsdDestroy(struct epicsMutexOSD * id) { semDelete((SEM_ID)id); } epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * id) { int status; status = semTake((SEM_ID)id,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) { semShow((SEM_ID)id,level); } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdMutex.h0000664000577000060420000000172213557101274022000 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* os/vxWorks/osdMutex.h */ /* Author: Marty Kraimer Date: 25AUG99 */ #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) base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdPoolStatus.c0000664000577000060420000000366213557101274023013 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #define epicsExportSharedSymbols #include "epicsThread.h" #include "osiPoolStatus.h" /* * It turns out that memPartInfoGet() and memFindMax() are very CPU intensive on vxWorks * so we must spawn off a thread that periodically polls. Although this isnt 100% safe, I * dont see what else to do. * * It takes about 30 uS to call memPartInfoGet() on a pcPentium I vxWorks system. * * joh */ static epicsThreadOnceId osdMaxBlockOnceler = EPICS_THREAD_ONCE_INIT; static size_t osdMaxBlockSize = 0; static void osdSufficentSpaceInPoolQuery () { osdMaxBlockSize = (size_t) memFindMax (); } static void osdSufficentSpaceInPoolPoll ( void *pArgIn ) { while ( 1 ) { epicsThreadSleep ( 1.0 ); osdSufficentSpaceInPoolQuery (); } } static void osdSufficentSpaceInPoolInit ( void *pArgIn ) { epicsThreadId id; osdSufficentSpaceInPoolQuery (); id = epicsThreadCreate ( "poolPoll", epicsThreadPriorityMedium, epicsThreadGetStackSize ( epicsThreadStackSmall ), osdSufficentSpaceInPoolPoll, 0 ); } /* * osiSufficentSpaceInPool () */ epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ) { epicsThreadOnce ( &osdMaxBlockOnceler, osdSufficentSpaceInPoolInit, 0 ); if ( UINT_MAX - 100000u >= contiguousBlockSize ) { return ( osdMaxBlockSize > 100000 + contiguousBlockSize ); } else { return 0; } } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdProcess.c0000664000577000060420000000305013557101274022303 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Operating System Dependent Implementation of osiProcess.h * * Author: Jeff Hill * */ /* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ #define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> #include #include #include #define epicsExportSharedSymbols #include "osiProcess.h" #include "errlog.h" epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn) { char pName[MAX_IDENTITY_LEN]; unsigned uiLength; size_t len; remCurIdGet ( pName, NULL ); len = strlen ( pName ); if (len>UINT_MAX || len<=0) { return osiGetUserNameFail; } uiLength = (unsigned) len; if ( uiLength + 1 >= bufSizeIn ) { return osiGetUserNameFail; } strncpy ( pBuf, pName, (size_t) bufSizeIn ); return osiGetUserNameSuccess; } epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess (const char *pProcessName, const char *pBaseExecutableName) { return osiSpawnDetachedProcessNoSupport; } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdReadline.c0000664000577000060420000000475413557101274022424 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Eric Norum Date: 12DEC2001 */ /* * This file is included by epicsReadline.c which has already included the * headers stdio.h, stdlib.h, errno.h, envDefs.h and epicsReadline.h */ #include #include /* FIXME: Remove line-lenth limitation */ #define LEDLIB_LINESIZE 1000 #ifndef _WRS_VXWORKS_MAJOR typedef int LED_ID; #endif struct osdContext { LED_ID ledId; char line[LEDLIB_LINESIZE]; }; /* * Create a command-line context */ static void osdReadlineBegin(struct readlineContext *context) { struct osdContext *osd = malloc(sizeof *osd); if (osd != NULL) { osd->ledId = (LED_ID) ERROR; if (context->in == NULL) { long i = 50; envGetLongConfigParam(&IOCSH_HISTSIZE, &i); if (i < 1) i = 1; osd->ledId = ledOpen(fileno(stdin), fileno(stdout), i); if (osd->ledId == (LED_ID) ERROR) { context->in = stdin; printf("Warning -- Unabled to allocate space for command-line history.\n"); printf("Warning -- Command-line editting disabled.\n"); } } context->osd = osd; } } /* * Read a line of input */ static char * osdReadline (const char *prompt, struct readlineContext *context) { struct osdContext *osd = context->osd; int i; if (prompt) { fputs(prompt, stdout); fflush(stdout); } if (osd->ledId != (LED_ID) ERROR) { i = ledRead(osd->ledId, osd->line, LEDLIB_LINESIZE-1); if (i < 0) return NULL; } else { if (fgets(osd->line, LEDLIB_LINESIZE, context->in) == NULL) return NULL; i = strlen(osd->line); } if ((i >= 1) && (osd->line[i-1] == '\n')) osd->line[i-1] = '\0'; else osd->line[i] = '\0'; return osd->line; } /* * Destroy a command-line context */ static void osdReadlineEnd (struct readlineContext *context) { LED_ID ledId = context->osd->ledId; if (ledId != (LED_ID) ERROR) ledClose(ledId); free(context->osd); } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdSignal.cpp0000664000577000060420000000167213557101274022452 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ #define epicsExportSharedSymbols #include "epicsSignal.h" /* * NOOP */ epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ) {} epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ) {} epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * /* threadId */ ) {} base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdSock.c0000664000577000060420000000556613557101274021602 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ #define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> #include #include #ifndef vxWorks #error this is a vxWorks specific source code #endif #include #include #include "errlog.h" #define epicsExportSharedSymbols #include "osiSock.h" int osiSockAttach() { return 1; } void osiSockRelease() { } epicsShareFunc SOCKET epicsShareAPI epicsSocketCreate ( int domain, int type, int protocol ) { SOCKET sock = socket ( domain, type, protocol ); if ( sock < 0 ) { sock = INVALID_SOCKET; } return sock; } epicsShareFunc int epicsShareAPI epicsSocketAccept ( int sock, struct sockaddr * pAddr, osiSocklen_t * addrlen ) { int newSock = accept ( sock, pAddr, addrlen ); if ( newSock < 0 ) { newSock = INVALID_SOCKET; } return newSock; } epicsShareFunc void epicsShareAPI epicsSocketDestroy ( SOCKET s ) { int status = close ( s ); if ( status < 0 ) { char buf [ 64 ]; epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); errlogPrintf ( "epicsSocketDestroy: failed to " "close a socket because \"%s\"\n", buf ); } } /* * ipAddrToHostName */ epicsShareFunc unsigned epicsShareAPI ipAddrToHostName (const struct in_addr *pAddr, char *pBuf, unsigned bufSize) { int status; int errnoCopy = errno; unsigned len; if (bufSize<1) { return 0; } if (bufSize>MAXHOSTNAMELEN) { status = hostGetByAddr ((int)pAddr->s_addr, pBuf); if (status==OK) { pBuf[MAXHOSTNAMELEN] = '\0'; len = strlen (pBuf); } else { len = 0; } } else { char name[MAXHOSTNAMELEN+1]; status = hostGetByAddr (pAddr->s_addr, name); if (status==OK) { strncpy (pBuf, name, bufSize); pBuf[bufSize-1] = '\0'; len = strlen (pBuf); } else { len = 0; } } errno = errnoCopy; return len; } /* * hostToIPAddr () */ epicsShareFunc int epicsShareAPI hostToIPAddr(const char *pHostName, struct in_addr *pIPA) { int addr = hostGetByName ( (char *) pHostName ); if ( addr == ERROR ) { return -1; } pIPA->s_addr = (unsigned long) addr; /* * success */ return 0; } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdSock.h0000664000577000060420000000537313557101274021603 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * vxWorks specific socket include */ #ifndef osdSockH #define osdSockH /* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ #ifndef _VSB_CONFIG_FILE # define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /*This following is not defined in any vxWorks header files*/ int sysClkRateGet(void); #ifdef __cplusplus } #endif typedef int SOCKET; #define INVALID_SOCKET (-1) #define SOCKERRNO errno #ifndef SHUT_RD # define SHUT_RD 0 #endif #ifndef SHUT_WR # define SHUT_WR 1 #endif #ifndef SHUT_RDWR # define SHUT_RDWR 2 #endif /* * it is quite lame on WRS's part to assume that * a ptr is always the same as an int */ #define socket_ioctl(A,B,C) ioctl(A,B,(int)C) typedef int osiSockIoctl_t; typedef int osiSocklen_t; typedef char osiSockOptMcastLoop_t; typedef char osiSockOptMcastTTL_t; #define FD_IN_FDSET(FD) ((FD)=0) #define SOCK_EWOULDBLOCK EWOULDBLOCK #define SOCK_ENOBUFS ENOBUFS #define SOCK_ECONNRESET ECONNRESET #define SOCK_ETIMEDOUT ETIMEDOUT #define SOCK_EACCES EACCES #define SOCK_EADDRINUSE EADDRINUSE #define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL #define SOCK_ECONNREFUSED ECONNREFUSED #define SOCK_ECONNABORTED ECONNABORTED #define SOCK_EINPROGRESS EINPROGRESS #define SOCK_EISCONN EISCONN #define SOCK_EALREADY EALREADY #define SOCK_EINVAL EINVAL #define SOCK_EINTR EINTR #define SOCK_EPIPE EPIPE #define SOCK_EMFILE EMFILE #define SOCK_SHUTDOWN ESHUTDOWN #define SOCK_ENOTSOCK ENOTSOCK #define SOCK_EBADF EBADF #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7F000001 #endif #ifndef INADDR_NONE # define INADDR_NONE (0xffffffff) #endif #if defined(_SIZEOF_ADDR_IFREQ) # define ifreq_size(pifreq) _SIZEOF_ADDR_IFREQ(*pifreq) #elif ( defined (BSD) && ( BSD >= 44 ) ) # define ifreq_size(pifreq) (pifreq->ifr_addr.sa_len + sizeof(pifreq->ifr_name)) #else # define ifreq_size(pifreq) sizeof(*pifreq) #endif #endif /*osdSockH*/ base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdSpin.c0000664000577000060420000000570113557101274021603 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * Copyright (c) 2012 ITER Organization. * Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2013 Brookhaven Science Assoc. as Operator of Brookhaven * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Authors: Ralph Lange * Andrew Johnson * Michael Davidsaver * * Inspired by Linux UP spinlocks implemention * include/linux/spinlock_api_up.h */ /* * vxWorks (single CPU): LOCK INTERRUPT and DISABLE PREEMPTION * * CAVEAT: * This implementation will not compile on vxWorks SMP architectures. * These architectures provide spinlocks, which must be used instead. * */ /* This is needed for vxWorks 6.x to prevent an obnoxious compiler warning */ #define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> #include #include #include #include #include "cantProceed.h" #include "epicsSpin.h" typedef struct epicsSpin { int key; unsigned int locked; } epicsSpin; epicsSpinId epicsSpinCreate(void) { return calloc(1, sizeof(epicsSpin)); } epicsSpinId epicsSpinMustCreate(void) { epicsSpinId ret = epicsSpinCreate(); if (!ret) cantProceed("epicsSpinMustCreate: epicsSpinCreate failed."); return ret; } void epicsSpinDestroy(epicsSpinId spin) { free(spin); } void epicsSpinLock(epicsSpinId spin) { int key = intLock(); if (!intContext()) taskLock(); if (spin->locked) { intUnlock(key); if (!intContext()) { taskUnlock(); logMsg("epicsSpinLock(%p): Deadlock.\n", (int) spin, 0, 0, 0, 0, 0); cantProceed("Recursive lock, missed unlock or block when locked."); } else { logMsg("epicsSpinLock(%p): Deadlock in ISR.\n" "Recursive lock, missed unlock or block when locked.\n", (int) spin, 0, 0, 0, 0, 0); } return; } spin->key = key; spin->locked = 1; } int epicsSpinTryLock(epicsSpinId spin) { int key = intLock(); if (!intContext()) taskLock(); if (spin->locked) { intUnlock(key); if (!intContext()) taskUnlock(); return 1; } spin->key = key; spin->locked = 1; return 0; } void epicsSpinUnlock(epicsSpinId spin) { int key = spin->key; if (!spin->locked) { logMsg("epicsSpinUnlock(%p): not locked\n", (int) spin, 0, 0, 0, 0, 0); return; } spin->key = spin->locked = 0; intUnlock(key); if (!intContext()) taskUnlock(); } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdStdio.c0000664000577000060420000000316013557101274021751 0ustar anjaesctl/* osdStdio.c */ /*************************************************************************\ * Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include #include "epicsStdio.h" #include "dbDefs.h" struct outStr_s { char *str; size_t free; }; static STATUS outRoutine(char *buffer, size_t nchars, int outarg) { struct outStr_s *poutStr = (struct outStr_s *) outarg; size_t free = poutStr->free; size_t len; if (free < 1) { /*let fioFormatV continue to count length*/ return OK; } else if (free > 1) { len = min(free-1, nchars); strncpy(poutStr->str, buffer, len); poutStr->str += len; poutStr->free -= len; } /*make sure final string is null terminated*/ *poutStr->str = 0; return OK; } int epicsVsnprintf(char *str, size_t size, const char *format, va_list ap) { struct outStr_s outStr; outStr.str = str; outStr.free = size; return fioFormatV(format, ap, outRoutine, (int) &outStr); } int epicsSnprintf(char *str, size_t size, const char *format, ...) { size_t nchars; va_list pvar; va_start(pvar,format); nchars = epicsVsnprintf(str,size,format,pvar); va_end (pvar); return(nchars); } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdStrtod.h0000664000577000060420000000144313557101274022155 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * This header is included by epicsString.h and epicsStdlib.h */ #ifdef __cplusplus extern "C" { #endif /* * epicsStrtod() for systems with broken strtod() routine */ double epicsStrtod(const char *str, char **endp); /* * VxWorks doesn't provide these routines, so for now we do */ long long int strtoll(const char *nptr, char **endptr, int base); unsigned long long int strtoull(const char *nptr, char **endptr, int base); #ifdef __cplusplus } #endif base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdThread.c0000664000577000060420000003711513557101274022105 0ustar anjaesctl/*************************************************************************\ * 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) 2012 ITER Organization * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osi/os/vxWorks/epicsThread.c */ /* Author: Marty Kraimer Date: 25AUG99 */ /* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ #define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> #include #include #include #include #include #include #include #include #include #include #include "errlog.h" #include "ellLib.h" #include "epicsThread.h" #include "cantProceed.h" #include "epicsAssert.h" #include "vxLib.h" #include "epicsExit.h" #ifdef EPICS_THREAD_CAN_JOIN /* The implementation of epicsThreadMustJoin() here uses 2 features * of VxWorks that were first introduced in VxWorks 6.9: taskWait(), * and the taskSpareFieldGet/Set routines in taskUtilLib. */ #include #define JOIN_WARNING_TIMEOUT (60 * sysClkRateGet()) static SPARE_NUM joinField; #define ALLOT_JOIN(tid) taskSpareNumAllot(tid, &joinField) #else #define ALLOT_JOIN(tid) #endif epicsShareFunc void osdThreadHooksRun(epicsThreadId id); #if CPU_FAMILY == MC680X0 #define ARCH_STACK_FACTOR 1 #elif CPU_FAMILY == SPARC #define ARCH_STACK_FACTOR 2 #else #define ARCH_STACK_FACTOR 2 #endif static const unsigned stackSizeTable[epicsThreadStackBig+1] = {4000*ARCH_STACK_FACTOR, 6000*ARCH_STACK_FACTOR, 11000*ARCH_STACK_FACTOR}; /* Table and lock for epicsThreadMap() */ #define ID_LIST_CHUNK 512 static int *taskIdList = 0; int taskIdListSize = 0; static SEM_ID epicsThreadListMutex = 0; /* This routine is found in atReboot.cpp */ extern void atRebootRegister(void); /* definitions for implementation of epicsThreadPrivate */ static void **papTSD = 0; static int nepicsThreadPrivate = 0; static SEM_ID epicsThreadOnceMutex = 0; /* Just map osi 0 to 99 into vx 100 to 199 */ /* remember that for vxWorks lower number means higher priority */ /* vx = 100 + (99 -osi) = 199 - osi*/ /* osi = 199 - vx */ static unsigned int getOsiPriorityValue(int ossPriority) { if ( ossPriority < 100 ) { return epicsThreadPriorityMax; } else if ( ossPriority > 199 ) { return epicsThreadPriorityMin; } else { return ( 199u - (unsigned int) ossPriority ); } } static int getOssPriorityValue(unsigned int osiPriority) { if ( osiPriority > 99 ) { return 100; } else { return ( 199 - (signed int) osiPriority ); } } #define THREAD_SEM_FLAGS (SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY) static void epicsThreadInit(void) { static int lock = 0; static int done = 0; if (done) return; while(!vxTas(&lock)) taskDelay(1); if (!done) { epicsThreadOnceMutex = semMCreate(THREAD_SEM_FLAGS); assert(epicsThreadOnceMutex); epicsThreadListMutex = semMCreate(THREAD_SEM_FLAGS); assert(epicsThreadListMutex); taskIdList = calloc(ID_LIST_CHUNK, sizeof(int)); assert(taskIdList); taskIdListSize = ID_LIST_CHUNK; atRebootRegister(); ALLOT_JOIN(0); done = 1; } lock = 0; } void epicsThreadRealtimeLock(void) {} unsigned int epicsThreadGetStackSize (epicsThreadStackSizeClass stackSizeClass) { if (stackSizeClassepicsThreadStackBig) { errlogPrintf("epicsThreadGetStackSize illegal argument (too large)"); return stackSizeTable[epicsThreadStackBig]; } return stackSizeTable[stackSizeClass]; } struct epicsThreadOSD {}; /* Strictly speaking this should be a WIND_TCB, but we only need it to * be able to create an epicsThreadId that is guaranteed never to be * the same as any current TID, and since TIDs are pointers this works. */ void epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg) { static struct epicsThreadOSD threadOnceComplete; #define EPICS_THREAD_ONCE_DONE &threadOnceComplete int result; epicsThreadInit(); result = semTake(epicsThreadOnceMutex, WAIT_FOREVER); assert(result == OK); if (*id != EPICS_THREAD_ONCE_DONE) { if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */ *id = epicsThreadGetIdSelf(); /* mark active */ semGive(epicsThreadOnceMutex); func(arg); result = semTake(epicsThreadOnceMutex, WAIT_FOREVER); assert(result == OK); *id = EPICS_THREAD_ONCE_DONE; /* mark done */ } else if (*id == epicsThreadGetIdSelf()) { semGive(epicsThreadOnceMutex); cantProceed("Recursive epicsThreadOnce() initialization\n"); } else while (*id != EPICS_THREAD_ONCE_DONE) { /* Another thread is in the above func(arg) call. */ semGive(epicsThreadOnceMutex); epicsThreadSleep(epicsThreadSleepQuantum()); result = semTake(epicsThreadOnceMutex, WAIT_FOREVER); assert(result == OK); } } semGive(epicsThreadOnceMutex); } #ifdef EPICS_THREAD_CAN_JOIN /* This routine is not static so it appears in the back-trace * of a thread that is waiting to be joined. */ void epicsThreadAwaitingJoin(int tid) { SEM_ID joinSem = (SEM_ID) taskSpareFieldGet(tid, joinField); STATUS status; if (!joinSem || (int) joinSem == ERROR) return; /* Wait for our supervisor */ status = semTake(joinSem, JOIN_WARNING_TIMEOUT); if (status && errno == S_objLib_OBJ_TIMEOUT) { errlogPrintf("Warning: epicsThread '%s' still awaiting join\n", epicsThreadGetNameSelf()); status = semTake(joinSem, WAIT_FOREVER); } if (status) perror("epicsThreadAwaitingJoin"); semDelete(joinSem); taskSpareFieldSet(tid, joinField, 0); } #define PREPARE_JOIN(tid, joinable) \ taskSpareFieldSet(tid, joinField, \ joinable ? (int) semBCreate(SEM_Q_FIFO, SEM_EMPTY) : 0) #define AWAIT_JOIN(tid) epicsThreadAwaitingJoin(tid) #else #define PREPARE_JOIN(tid, joinable) #define AWAIT_JOIN(tid) #endif static void createFunction(EPICSTHREADFUNC func, void *parm) { int tid = taskIdSelf(); taskVarAdd(tid,(int *)(char *)&papTSD); papTSD = NULL; /* Initialize for this thread */ osdThreadHooksRun((epicsThreadId)tid); (*func)(parm); epicsExitCallAtThreadExits (); free(papTSD); taskVarDelete(tid,(int *)(char *)&papTSD); AWAIT_JOIN(tid); } #ifdef ALTIVEC #define TASK_FLAGS (VX_FP_TASK | VX_ALTIVEC_TASK) #else #define TASK_FLAGS (VX_FP_TASK) #endif epicsThreadId epicsThreadCreateOpt(const char * name, EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts ) { unsigned int stackSize; int tid; epicsThreadInit(); if (!opts) { static const epicsThreadOpts opts_default = EPICS_THREAD_OPTS_INIT; opts = &opts_default; } stackSize = opts->stackSize; if (stackSize <= epicsThreadStackBig) stackSize = epicsThreadGetStackSize(stackSize); if (stackSize < 100) { errlogPrintf("epicsThreadCreate %s illegal stackSize %d\n", name, stackSize); return 0; } tid = taskCreate((char *)name,getOssPriorityValue(opts->priority), TASK_FLAGS, stackSize, (FUNCPTR)createFunction, (int)funptr, (int)parm, 0,0,0,0,0,0,0,0); if (tid == ERROR) { errlogPrintf("epicsThreadCreate %s failure %s\n", name, strerror(errno)); return 0; } PREPARE_JOIN(tid, opts->joinable); taskActivate(tid); return (epicsThreadId)tid; } void epicsThreadMustJoin(epicsThreadId id) { #ifdef EPICS_THREAD_CAN_JOIN const char *fn = "epicsThreadMustJoin"; int tid = (int) id; SEM_ID joinSem; STATUS status; if (!tid) return; joinSem = (SEM_ID) taskSpareFieldGet(tid, joinField); if ((int) joinSem == ERROR) { errlogPrintf("%s: Thread '%s' no longer exists.\n", fn, taskName(tid)); return; } if (tid == taskIdSelf()) { if (joinSem) { semDelete(joinSem); taskSpareFieldSet(tid, joinField, 0); } else { errlogPrintf("%s: Self-join of unjoinable thread '%s'\n", fn, taskName(tid)); } return; } if (!joinSem) { cantProceed("%s: Thread '%s' is not joinable.\n", fn, taskName(tid)); return; } semGive(joinSem); /* Rendezvous with thread */ status = taskWait(tid, JOIN_WARNING_TIMEOUT); if (status && errno == S_objLib_OBJ_TIMEOUT) { errlogPrintf("Warning: %s still waiting for thread '%s'\n", fn, taskName(tid)); status = taskWait(tid, WAIT_FOREVER); } if (status) { if (errno == S_taskLib_ILLEGAL_OPERATION) { errlogPrintf("%s: This shouldn't happen!\n", fn); } else if (errno == S_objLib_OBJ_ID_ERROR) { errlogPrintf("%s: %x is not a known thread\n", fn, tid); } else { perror(fn); } cantProceed(fn); } #endif } void epicsThreadSuspendSelf() { STATUS status; status = taskSuspend(taskIdSelf()); if(status) errlogPrintf("epicsThreadSuspendSelf failed\n"); } void epicsThreadResume(epicsThreadId id) { int tid = (int)id; STATUS status; status = taskResume(tid); if(status) errlogPrintf("epicsThreadResume failed\n"); } void epicsThreadExitMain(void) { errlogPrintf("epicsThreadExitMain was called for vxWorks. Why?\n"); } unsigned int epicsThreadGetPriority(epicsThreadId id) { int tid = (int)id; STATUS status; int priority = 0; status = taskPriorityGet(tid,&priority); if(status) errlogPrintf("epicsThreadGetPriority failed\n"); return(getOsiPriorityValue(priority)); } unsigned int epicsThreadGetPrioritySelf(void) { return(epicsThreadGetPriority(epicsThreadGetIdSelf())); } void epicsThreadSetPriority(epicsThreadId id,unsigned int osip) { int tid = (int)id; STATUS status; int priority = 0; priority = getOssPriorityValue(osip); status = taskPrioritySet(tid,priority); if(status) errlogPrintf("epicsThreadSetPriority failed\n"); } epicsThreadBooleanStatus epicsThreadHighestPriorityLevelBelow( unsigned int priority, unsigned *pPriorityJustBelow) { unsigned newPriority = priority - 1; if (newPriority <= 99) { *pPriorityJustBelow = newPriority; return epicsThreadBooleanStatusSuccess; } return epicsThreadBooleanStatusFail; } epicsThreadBooleanStatus epicsThreadLowestPriorityLevelAbove( unsigned int priority, unsigned *pPriorityJustAbove) { unsigned newPriority = priority + 1; newPriority = priority + 1; if (newPriority <= 99) { *pPriorityJustAbove = newPriority; return epicsThreadBooleanStatusSuccess; } return epicsThreadBooleanStatusFail; } int epicsThreadIsEqual(epicsThreadId id1, epicsThreadId id2) { return((id1==id2) ? 1 : 0); } int epicsThreadIsSuspended(epicsThreadId id) { int tid = (int)id; return((int)taskIsSuspended(tid)); } void epicsThreadSleep(double seconds) { STATUS status; int ticks; if (seconds > 0.0) { seconds *= sysClkRateGet(); seconds += 0.99999999; /* 8 9s here is optimal */ ticks = (seconds >= INT_MAX) ? INT_MAX : (int) seconds; } else { /* seconds <= 0 or NAN */ ticks = 0; } status = taskDelay(ticks); if(status) errlogPrintf("epicsThreadSleep\n"); } epicsThreadId epicsThreadGetIdSelf(void) { return((epicsThreadId)taskIdSelf()); } epicsThreadId epicsThreadGetId(const char *name) { int tid = taskNameToId((char *)name); return((epicsThreadId)(tid==ERROR?0:tid)); } const char *epicsThreadGetNameSelf(void) { return taskName(taskIdSelf()); } void epicsThreadGetName (epicsThreadId id, char *name, size_t size) { int tid = (int)id; strncpy(name,taskName(tid),size-1); name[size-1] = '\0'; } epicsShareFunc void epicsThreadMap ( EPICS_THREAD_HOOK_ROUTINE func ) { int noTasks = 0; int i; int result; result = semTake(epicsThreadListMutex, WAIT_FOREVER); assert(result == OK); while (noTasks == 0) { noTasks = taskIdListGet(taskIdList, taskIdListSize); if (noTasks == taskIdListSize) { taskIdList = realloc(taskIdList, (taskIdListSize+ID_LIST_CHUNK)*sizeof(int)); assert(taskIdList); taskIdListSize += ID_LIST_CHUNK; noTasks = 0; } } for (i = 0; i < noTasks; i++) { func ((epicsThreadId)taskIdList[i]); } semGive(epicsThreadListMutex); } void epicsThreadShowAll(unsigned int level) { taskShow(0,2); } void epicsThreadShow(epicsThreadId id, unsigned int level) { int tid = (int)id; if (level > 1) level = 1; if (tid) taskShow(tid, level); } /* The following algorithm was thought of by Andrew Johnson APS/ASD . * The basic idea is to use a single vxWorks task variable. * The task variable is papTSD, which is an array of pointers to the TSD * The array size is equal to the number of epicsThreadPrivateIds created + 1 * when epicsThreadPrivateSet is called. * Until the first call to epicsThreadPrivateCreate by a application papTSD=0 * After first call papTSD[0] is value of nepicsThreadPrivate when * epicsThreadPrivateSet was last called by the thread. This is also * the value of epicsThreadPrivateId. * The algorithm allows for epicsThreadPrivateCreate being called after * the first call to epicsThreadPrivateSet. */ epicsThreadPrivateId epicsThreadPrivateCreate() { static int lock = 0; epicsThreadPrivateId id; epicsThreadInit(); /*lock is necessary because ++nepicsThreadPrivate may not be indivisible*/ while(!vxTas(&lock)) taskDelay(1); id = (epicsThreadPrivateId)++nepicsThreadPrivate; lock = 0; return(id); } void epicsThreadPrivateDelete(epicsThreadPrivateId id) { /*nothing to delete */ return; } /* * No mutex is necessary here because by definition * thread variables are local to a single thread. */ void epicsThreadPrivateSet (epicsThreadPrivateId id, void *pvt) { int int_id = (int)id; int nepicsThreadPrivateOld = 0; if (papTSD) nepicsThreadPrivateOld = (int)papTSD[0]; if (!papTSD || nepicsThreadPrivateOld < int_id) { void **papTSDold = papTSD; int i; papTSD = callocMustSucceed(int_id + 1,sizeof(void *), "epicsThreadPrivateSet"); papTSD[0] = (void *)(int_id); for (i = 1; i <= nepicsThreadPrivateOld; i++) { papTSD[i] = papTSDold[i]; } free (papTSDold); } papTSD[int_id] = pvt; } void *epicsThreadPrivateGet(epicsThreadPrivateId id) { int int_id = (int)id; /* * If epicsThreadPrivateGet() is called before epicsThreadPrivateSet() * for any particular thread, the value returned is always NULL. */ if ( papTSD && int_id <= (int) papTSD[0] ) { return papTSD[int_id]; } return NULL; } double epicsThreadSleepQuantum () { double HZ = sysClkRateGet (); return 1.0 / HZ; } epicsShareFunc int epicsThreadGetCPUs(void) { return 1; } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdThread.h0000664000577000060420000000131413557101274022102 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef osdThreadh #define osdThreadh /* VxWorks 6.9 and later can support joining threads */ #if (_WRS_VXWORKS_MAJOR == 6 && _WRS_VXWORKS_MINOR < 9) #undef EPICS_THREAD_CAN_JOIN #endif #endif /* osdThreadh */ base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdThreadExtra.c0000664000577000060420000000120313557101274023076 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 ITER Organization * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Ralph Lange Date: 26 Jun 2012 */ /* Null default thread hooks for all platforms that do not do anything special */ #define epicsExportSharedSymbols #include "epicsThread.h" epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdTime.cpp0000664000577000060420000000733213557101274022132 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> #include #include #include #include #include #include #define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTime.h" #include "osiNTPTime.h" #include "osiClockTime.h" #include "generalTimeSup.h" #include "envDefs.h" #define NTP_REQUEST_TIMEOUT 4 /* seconds */ extern "C" { int tz2timezone(void); } static char sntp_sync_task[] = "ipsntps"; static char ntp_daemon[] = "ipntpd"; static const char *pserverAddr = NULL; static CLOCKTIME_SYNCHOOK prevHook; extern char* sysBootLine; static void timeSync(int synchronized) { if (!tz2timezone()) ClockTime_syncHook = prevHook; /* Don't call me again */ } static int timeRegister(void) { /* If TZ not defined, set it from EPICS_TZ */ if (getenv("TZ") == NULL) { const char *tz = envGetConfigParamPtr(&EPICS_TZ); if (tz && *tz) { epicsEnvSet("TZ", tz); /* Call tz2timezone() once we know what year it is */ prevHook = ClockTime_syncHook; ClockTime_syncHook = timeSync; } else if (getenv("TIMEZONE") == NULL) printf("timeRegister: No Time Zone Information available\n"); } // Define EPICS_TS_FORCE_NTPTIME to force use of NTPTime provider bool useNTP = getenv("EPICS_TS_FORCE_NTPTIME") != NULL; if (!useNTP && (taskNameToId(sntp_sync_task) != ERROR || taskNameToId(ntp_daemon) != ERROR)) { // A VxWorks 6 SNTP/NTP sync task is running struct timespec clockNow; useNTP = clock_gettime(CLOCK_REALTIME, &clockNow) != OK || clockNow.tv_sec < BUILD_TIME; // Assumes VxWorks and the host OS have the same epoch } if (useNTP) { // Start NTP first so it can be used to sync ClockTime NTPTime_Init(100); ClockTime_Init(CLOCKTIME_SYNC); } else { ClockTime_Init(CLOCKTIME_NOSYNC); } osdMonotonicInit(); return 1; } static int done = timeRegister(); // Routines for NTPTime provider int osdNTPGet(struct timespec *ts) { return sntpcTimeGet(const_cast(pserverAddr), NTP_REQUEST_TIMEOUT * sysClkRateGet(), ts); } void osdNTPInit(void) { pserverAddr = envGetConfigParamPtr(&EPICS_TS_NTP_INET); if (!pserverAddr) { /* use the boot host */ BOOT_PARAMS bootParms; static char host_addr[BOOT_ADDR_LEN]; bootStringToStruct(sysBootLine, &bootParms); /* bootParms.had = host IP address */ strncpy(host_addr, bootParms.had, BOOT_ADDR_LEN); pserverAddr = host_addr; } } void osdNTPReport(void) { if (pserverAddr) printf("NTP Server = %s\n", pserverAddr); } // Other Time Routines // vxWorks localtime_r returns different things in different versions. // It can't fail though, so we just ignore the return value. int epicsTime_localtime(const time_t *clock, struct tm *result) { localtime_r(clock, result); return epicsTimeOK; } // vxWorks gmtime_r returns different things in different versions. // It can't fail though, so we just ignore the return value. int epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM ) { gmtime_r(pAnsiTime, pTM); return epicsTimeOK; } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdTime.h0000664000577000060420000000163213557101274021574 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* Following needed for struct timeval */ #include #ifndef osdTimeh #define osdTimeh #include #include #ifdef __cplusplus extern "C" { #endif void osdNTPInit(void); int osdNTPGet(struct timespec *); void osdNTPReport(void); #define osdTickRateGet sysClkRateGet #define osdTickGet tickGet #ifdef __cplusplus } #endif #endif /* ifndef osdTimeh */ base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdVME.h0000664000577000060420000000111213557101274021316 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * OS-dependent VME support */ #include base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osdWireConfig.h0000664000577000060420000000114713557101274022733 0ustar anjaesctl/* * vxWorks version of * osdWireConfig.h * * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef osdWireConfig_h #define osdWireConfig_h #include #include #if _BYTE_ORDER == _LITTLE_ENDIAN # define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE #elif _BYTE_ORDER == _BIG_ENDIAN # define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG #else # error EPICS hasnt been ported to _BYTE_ORDER specified by vxWorks #endif /* for now, assume that vxWorks doesnt run on weird arch like ARM NWFP */ #define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER #endif /* ifdef osdWireConfig_h */ base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/osiFileName.h0000664000577000060420000000124213557101274022360 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * osiFileName.h * Author: Jeff Hill */ #ifndef osiFileNameH #define osiFileNameH #include "unixFileName.h" #endif /* osiFileNameH */ base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/strtoll.c0000664000577000060420000001042013557101274021661 0ustar anjaesctl/*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #ifndef LLONG_MAX #define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL #define LLONG_MIN (-0x7FFFFFFFFFFFFFFFLL - 1) #endif /* * Convert a string to a long long integer. * * Assumes that the upper and lower case * alphabets and digits are each contiguous. */ long long strtoll(const char * __restrict nptr, char ** __restrict endptr, int base) { const char *s; unsigned long long acc; char c; unsigned long long cutoff; int neg, any, cutlim; /* * Skip white space and pick up leading +/- sign if any. * If base is 0, allow 0x for hex and 0 for octal, else * assume decimal; if base is already 16, allow 0x. */ s = nptr; do { c = *s++; } while (isspace((unsigned char)c)); if (c == '-') { neg = 1; c = *s++; } else { neg = 0; if (c == '+') c = *s++; } if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X') && ((s[1] >= '0' && s[1] <= '9') || (s[1] >= 'A' && s[1] <= 'F') || (s[1] >= 'a' && s[1] <= 'f'))) { c = s[1]; s += 2; base = 16; } if (base == 0) base = c == '0' ? 8 : 10; acc = any = 0; if (base < 2 || base > 36) goto noconv; /* * Compute the cutoff value between legal numbers and illegal * numbers. That is the largest legal value, divided by the * base. An input number that is greater than this value, if * followed by a legal input character, is too big. One that * is equal to this value may be valid or not; the limit * between valid and invalid numbers is then based on the last * digit. For instance, if the range for quads is * [-9223372036854775808..9223372036854775807] and the input base * is 10, cutoff will be set to 922337203685477580 and cutlim to * either 7 (neg==0) or 8 (neg==1), meaning that if we have * accumulated a value > 922337203685477580, or equal but the * next digit is > 7 (or 8), the number is too big, and we will * return a range error. * * Set 'any' if any `digits' consumed; make it negative to indicate * overflow. */ cutoff = neg ? (unsigned long long)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX : LLONG_MAX; cutlim = cutoff % base; cutoff /= base; for ( ; ; c = *s++) { if (c >= '0' && c <= '9') c -= '0'; else if (c >= 'A' && c <= 'Z') c -= 'A' - 10; else if (c >= 'a' && c <= 'z') c -= 'a' - 10; else break; if (c >= base) break; if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) any = -1; else { any = 1; acc *= base; acc += c; } } if (any < 0) { acc = neg ? LLONG_MIN : LLONG_MAX; errno = ERANGE; } else if (!any) { noconv: errno = EINVAL; } else if (neg) acc = -acc; if (endptr != NULL) *endptr = (char *)(any ? s - 1 : nptr); return (acc); } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/strtoull.c0000664000577000060420000000627213557101274022060 0ustar anjaesctl/*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #ifndef ULLONG_MAX #define ULLONG_MAX 0xFFFFFFFFFFFFFFFFULL #endif /* * Convert a string to an unsigned long long integer. * * Assumes that the upper and lower case * alphabets and digits are each contiguous. */ unsigned long long strtoull(const char * __restrict nptr, char ** __restrict endptr, int base) { const char *s; unsigned long long acc; char c; unsigned long long cutoff; int neg, any, cutlim; /* * See strtoq for comments as to the logic used. */ s = nptr; do { c = *s++; } while (isspace((unsigned char)c)); if (c == '-') { neg = 1; c = *s++; } else { neg = 0; if (c == '+') c = *s++; } if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X') && ((s[1] >= '0' && s[1] <= '9') || (s[1] >= 'A' && s[1] <= 'F') || (s[1] >= 'a' && s[1] <= 'f'))) { c = s[1]; s += 2; base = 16; } if (base == 0) base = c == '0' ? 8 : 10; acc = any = 0; if (base < 2 || base > 36) goto noconv; cutoff = ULLONG_MAX / base; cutlim = ULLONG_MAX % base; for ( ; ; c = *s++) { if (c >= '0' && c <= '9') c -= '0'; else if (c >= 'A' && c <= 'Z') c -= 'A' - 10; else if (c >= 'a' && c <= 'z') c -= 'a' - 10; else break; if (c >= base) break; if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) any = -1; else { any = 1; acc *= base; acc += c; } } if (any < 0) { acc = ULLONG_MAX; errno = ERANGE; } else if (!any) { noconv: errno = EINVAL; } else if (neg) acc = -acc; if (endptr != NULL) *endptr = (char *)(any ? s - 1 : nptr); return (acc); } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/task_params.h0000664000577000060420000001650413557101274022501 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* Parameters for tasks on IOC */ /* * Authors: Andy Kozubal, Jeff Hill, and Bob Dalesio * Date: 2-24-89 */ #ifndef INCtaskLibh #include #endif #define VXTASKIDSELF 0 /* Task Names */ #define EVENTSCAN_NAME "scanEvent" #define SCANONCE_NAME "scanOnce" #define SMCMD_NAME "smCommand" #define SMRESP_NAME "smResponse" #define ABDONE_NAME "abDone" #define ABSCAN_NAME "abScan" #define ABCOS_NAME "abBiCosScanner" #define MOMENTARY_NAME "momentary" #define WFDONE_NAME "wfDone" #define SEQUENCER_NAME "sequencer" #define BKPT_CONT_NAME "bkptCont" #define SCANNER_NAME "scanner" #define REQ_SRVR_NAME "CA_TCP" #define CA_CLIENT_NAME "CA_client" #define CA_EVENT_NAME "CA_event" #define CAST_SRVR_NAME "CA_UDP" #define CA_REPEATER_NAME "CA_repeater" #define CA_ONLINE_NAME "CA_online" #define TASKWD_NAME "taskwd" #define SMIOTEST_NAME "smInout" #define SMROTTEST_NAME "smRotate" #define EVENT_PEND_NAME "event_task" #define XY240_NAME "xy_240_scan" #define GPIBLINK_NAME "gpibLink" #define BBLINK_NAME "bbLinkTask" #define BBTXLINK_NAME "bbTx" #define BBRXLINK_NAME "bbRx" #define BBWDTASK_NAME "bbwd" #define ERRLOG_NAME "errlog" #define LOG_RESTART_NAME "logRestart" /* Task priorities */ #define SCANONCE_PRI 129 /* scan one time */ /*DO NOT RUN ANY RECORD PROCESSING TASKS AT HIGHER PRIORITY THAN _netTask=50*/ #define CALLBACK_PRI_LOW 140 /* callback task - generall callback task */ #define CALLBACK_PRI_MEDIUM 135 /* callback task - generall callback task */ #define CALLBACK_PRI_HIGH 128 /* callback task - generall callback task */ #define EVENTSCAN_PRI 129 /* Event Scanner - Runs on a global event */ #define SMCMD_PRI 120 /* Stepper Motor Command Task - Waits for cmds */ #define SMRESP_PRI 121 /* Stepper Motor Resp Task - Waits for resps */ #define ABCOS_PRI 121 /* Allen-Bradley Binary Input COS io_event wakeup */ #define ABDONE_PRI 122 /* Allen-Bradley Resp Task - Interrupt Driven */ #define ABSCAN_PRI 123 /* Allen-Bradley Scan Task - Base Rate .1 secs */ #define BBLINK_PRI 124 #define BBWDTASK_PRI 123 /* BitBus watchdog task */ #define BBRXLINK_PRI 124 /* BitBus link task */ #define BBTXLINK_PRI 125 /* BitBus link task */ #define GPIBLINK_PRI 125 /* GPIB link task */ #define MOMENTARY_PRI 126 /* Momentary output - posted from watchdog */ #define WFDONE_PRI 127 /* Waveform Task - Base Rate of .1 second */ #define PERIODSCAN_PRI 139 /* Periodic Scanners - Slowest rate */ #define DB_CA_PRI 149 /*database to channel access*/ #define SEQUENCER_PRI 151 #define XY240_PRI 160 /* xy 240 dio scanner */ #define SCANNER_PRI 170 #define REQ_SRVR_PRI 181 /* Channel Access TCP request server*/ #define CA_CLIENT_PRI 180 /* Channel Access clients */ #define CA_REPEATER_PRI 181 /* Channel Access repeater */ #define ERRLOG_PRI CA_REPEATER_PRI /*error logger task*/ #define CAST_SRVR_PRI 182 /* Channel Access broadcast server */ #define CA_ONLINE_PRI 183 /* Channel Access server online notify */ #define TASKWD_PRI 200 /* Watchdog Scan Task - runs every 6 seconds */ #define SMIOTEST_PRI 205 /* Stepper Mtr in/out test - runs every .1 sec */ #define SMROTTEST_PRI 205 /* Stepper Mtr rotate test - runs every .1 sec */ #define LOG_RESTART_PRI 200 /* Log server connection watch dog */ /* Task delay times (seconds) */ #define TASKWD_DELAY 6 /* Task delay times (tics) */ #define ABSCAN_RATE (sysClkRateGet()/6) #define SEQUENCER_DELAY (sysClkRateGet()/5) #define SCANNER_DELAY (sysClkRateGet()/5) #define CA_ONLINE_DELAY (sysClkRateGet()*15) #define LOG_RESTART_DELAY (sysClkRateGet()*30) /* Task creation options */ #define ERRLOG_OPT VX_FP_TASK #define EVENTSCAN_OPT VX_FP_TASK #define SCANONCE_OPT VX_FP_TASK #define CALLBACK_OPT VX_FP_TASK #define SMCMD_OPT VX_FP_TASK #define SMRESP_OPT VX_FP_TASK #define ABDONE_OPT VX_FP_TASK #define ABCOS_OPT VX_FP_TASK #define ABSCAN_OPT VX_FP_TASK #define MOMENTARY_OPT VX_FP_TASK #define PERIODSCAN_OPT VX_FP_TASK #define WFDONE_OPT VX_FP_TASK #define SEQUENCER_OPT VX_FP_TASK | VX_STDIO #define BKPT_CONT_OPT VX_FP_TASK #define SCANNER_OPT VX_FP_TASK #define REQ_SRVR_OPT VX_FP_TASK #define CAST_SRVR_OPT VX_FP_TASK #define CA_CLIENT_OPT VX_FP_TASK #define CA_REPEATER_OPT VX_FP_TASK #define CA_ONLINE_OPT VX_FP_TASK #define TASKWD_OPT VX_FP_TASK #define SMIOTEST_OPT VX_FP_TASK #define SMROTTEST_OPT VX_FP_TASK #define EVENT_PEND_OPT VX_FP_TASK #define GPIBLINK_OPT VX_FP_TASK|VX_STDIO #define BBLINK_OPT VX_FP_TASK|VX_STDIO #define BBTXLINK_OPT VX_FP_TASK|VX_STDIO #define BBRXLINK_OPT VX_FP_TASK|VX_STDIO #define BBWDTASK_OPT VX_FP_TASK|VX_STDIO #define DB_CA_OPT (VX_FP_TASK | VX_STDIO) #define XY_240_OPT (0) /* none */ #define LOG_RESTART_OPT (VX_FP_TASK) /* * Task stack sizes * * (original stack sizes are appropriate for the 68k) * ARCH_STACK_FACTOR allows scaling the stacks on particular * processor architectures */ #if CPU_FAMILY == MC680X0 #define ARCH_STACK_FACTOR 1 #elif CPU_FAMILY == SPARC #define ARCH_STACK_FACTOR 2 #else #define ARCH_STACK_FACTOR 2 #endif #define ERRLOG_STACK (4000*ARCH_STACK_FACTOR) #define EVENTSCAN_STACK (11000*ARCH_STACK_FACTOR) #define SCANONCE_STACK (11000*ARCH_STACK_FACTOR) #define CALLBACK_STACK (11000*ARCH_STACK_FACTOR) #define SMCMD_STACK (3000*ARCH_STACK_FACTOR) #define SMRESP_STACK (3000*ARCH_STACK_FACTOR) #define ABCOS_STACK (3000*ARCH_STACK_FACTOR) #define ABDONE_STACK (3000*ARCH_STACK_FACTOR) #define ABSCAN_STACK (3000*ARCH_STACK_FACTOR) #define MOMENTARY_STACK (2000*ARCH_STACK_FACTOR) #define PERIODSCAN_STACK (11000*ARCH_STACK_FACTOR) #define WFDONE_STACK (9000*ARCH_STACK_FACTOR) #define SEQUENCER_STACK (5500*ARCH_STACK_FACTOR) #define BKPT_CONT_STACK (11000*ARCH_STACK_FACTOR) #define SCANNER_STACK (3048*ARCH_STACK_FACTOR) #define RSP_SRVR_STACK (5096*ARCH_STACK_FACTOR) #define REQ_SRVR_STACK (5096*ARCH_STACK_FACTOR) #define CAST_SRVR_STACK (5096*ARCH_STACK_FACTOR) #define CA_CLIENT_STACK (11000*ARCH_STACK_FACTOR) #define CA_REPEATER_STACK (5096*ARCH_STACK_FACTOR) #define CA_ONLINE_STACK (3048*ARCH_STACK_FACTOR) #define TASKWD_STACK (2000*ARCH_STACK_FACTOR) #define SMIOTEST_STACK (2000*ARCH_STACK_FACTOR) #define SMROTTEST_STACK (2000*ARCH_STACK_FACTOR) #define EVENT_PEND_STACK (5096*ARCH_STACK_FACTOR) #define TIMESTAMP_STACK (4000*ARCH_STACK_FACTOR) #define GPIBLINK_STACK (5000*ARCH_STACK_FACTOR) #define BBLINK_STACK (5000*ARCH_STACK_FACTOR) #define BBRXLINK_STACK (5000*ARCH_STACK_FACTOR) #define BBTXLINK_STACK (5000*ARCH_STACK_FACTOR) #define BBWDTASK_STACK (5000*ARCH_STACK_FACTOR) #define DB_CA_STACK (11000*ARCH_STACK_FACTOR) #define XY_240_STACK (4096*ARCH_STACK_FACTOR) #define LOG_RESTART_STACK (0x1000*ARCH_STACK_FACTOR) base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/tz2timezone.c0000664000577000060420000002135013557101274022454 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2009 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * Copyright (c) 2019 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Authors: Larry Hoff, Andrew Johnson */ /* * This file exports a single function "int tz2timezone(void)" which reads * the TZ environment variable (defined by POSIX) and converts it to the * TIMEZONE environment variable defined by ANSI. The latter is used by * VxWorks "time" functions, is largely deprecated in other computing * environments, has limitations, and is difficult to maintain. This holds * out the possibility of "pretending" that VxWorks supports "TZ" - until * such time as it actually does. * * For simplicity, only the "POSIX standard form" of TZ will be supported. * Even that is complicated enough (see following spec). Furthermore, * only the "M" form of DST start and stop dates are supported. * * TZ = zone[-]offset[dst[offset],start[/time],end[/time]] * * zone * A three or more letter name for the timezone in normal (winter) time. * * [-]offset * A signed time giving the offset of the time zone westwards from * Greenwich. The time has the form hh[:mm[:ss]] with a one of two * digit hour, and optional two digit minutes and seconds. * * dst * The name of the time zone when daylight saving is in effect. It may * be followed by an offset giving how big the adjustment is, required * if different than the default of 1 hour. * * start/time,end/time * Specify the start and end of the daylight saving period. The start * and end fields indicate on what day the changeover occurs, and must * be in this format: * * Mm.n.d * This indicates month m, the n-th occurrence of day d, where * 1 <= m <= 12, 1 <= n <= 5, 0 <= d <= 6, 0=Sunday * The 5th occurrence means the last occurrence of that day in a * month. So M4.1.0 is the first Sunday in April, M9.5.0 is the * last Sunday in September. * * The time field indicates what hour the changeover occurs on the given * day (TIMEZONE only supports switching on the hour). * */ #include #include /* getenv() */ #include /* printf() */ #include /* strchr() */ #include /* isalpha() */ #include /* for reference: TZ syntax, example, and TIMEZONE example * std offset dst [offset],start[/time],end[/time] * CST6CDT5,M3.2.0,M11.1.0 * EST+5EDT,M4.1.0/2,M10.5.0/2 * * std offset start stop * TIMEZONE=EST::300:030802:110102 */ static int extractDate(const char *tz, struct tm *current, char *s) { static const int startdays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; static const int molengths[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int month, week, weekday, hour=2; /* default=2AM */ int jan1wday, wday, mday; /* Require 'M' format */ if (*++tz != 'M') { printf("tz2timezone: Unsupported date type, need 'M' format\n"); return ERROR; } tz++; if (sscanf(tz, "%d.%d.%d/%d", &month, &week, &weekday, &hour) < 3) return ERROR; /* something important missing */ if (month == 0 || month>12 || week < 1 || week > 5 || weekday < 0 || weekday > 6 || hour < 0 || hour > 23) return ERROR; /* Now for some brute-force calendar calculations... */ /* start month is in "month", and the day is "weekday", but we need to know when that weekday first occurs in that month */ /* Let's start with weekday on Jan. 1 */ jan1wday = (7 + current->tm_wday - (current->tm_yday % 7)) % 7; /* We need to know if it is a leap year (and if it matters) */ /* Let's assume that we're working with a date between 1901 and 2099, that way we don't have to think about the "century exception". If this code is still running (unchanged) in 2100, I'll be stunned (and 139 years old) */ wday = (jan1wday + startdays[month-1] + ((month > 2 && (current->tm_year % 4 == 0)) ? 1 : 0)) % 7; /* Let's see on what day-of-the-month the first target weekday occurs (counting from 1). The result is a number between 1 and 7, inclusive. */ mday = 1 + ((7 + weekday - wday) % 7); /* Next, we add the week offset. If we overflow the month, we subtract one week */ mday += 7 * (week - 1); if (mday > molengths[month-1]) mday -= 7; /* Should I handle Feb 29? I'm willing to gamble that no one in their right mind would schedule a time change to occur on Feb. 29. If so, we'll be a week early */ sprintf(s, "%02d%02d%02d", month, mday, hour); return OK; } static const char *getTime(const char *s, int *time) { /* Time format is [+/-]hh[:mm][:ss] followed by the next zone name */ *time = 0; if (!isdigit((int) s[0])) return s; /* no number here... */ if (!isdigit((int) s[1])) { /* single digit form */ *time = s[0] - '0'; return s + 1; } if (isdigit((int) s[1])) { /* two digit form */ *time = 10 * (s[0] - '0') + (s[1] - '0'); return s + 2; } return s; /* does not follow supported form */ } int tz2timezone(void) { const char *tz = getenv("TZ"); /* Spec. says that zone names must be at least 3 chars. * I've never seen a longer zone name, but I'll allocate * 40 chars. If you live in a zone with a longer name, * you may want to think about the benefits of relocation. */ char zone[40]; char start[10], stop[10]; /* only really need 7 bytes now */ int hours = 0, minutes = 0, sign = 1; /* This is more than enough, even with a 40-char zone * name, and 4-char offset. */ char timezone[100]; int i = 0; /* You *always need an "i" :-) */ epicsTimeStamp now; struct tm current; /* First let's get the current time. We need the year to * compute the start/stop dates for DST. */ if (epicsTimeGetCurrent(&now) || epicsTimeToTM(¤t, NULL, &now)) return ERROR; /* Make sure TZ exists. * Spec. says that ZONE must be at least 3 chars. */ if ((!tz) || (strlen(tz) < 3)) return ERROR; /* OK, now a bunch of brute-force parsing. My brain hurts if * I try to think of an elegant regular expression for the * string. */ /* Start extracting zone name, must be alpha */ while ((i < sizeof(zone) - 1) && isalpha((int) *tz)) { zone[i++] = *tz++; } if (i < 3) return ERROR; /* Too short, not a real zone name? */ zone[i] = 0; /* Nil-terminate (for now) */ /* Now extract offset time. The format is [+/-]hh[:mm[:ss]] * Recall that TIMEZONE doesn't support seconds.... */ if (*tz == '-') { sign = -1; tz++; } else if (*tz == '+') { tz++; } /* Need a digit now */ if (!isdigit((int) *tz)) return ERROR; /* First get the hours */ tz = getTime(tz, &hours); if (hours > 24) return ERROR; if (*tz == ':') { /* There is a minutes part */ /* Need another digit now */ if (!isdigit((int) *++tz)) return ERROR; /* Extract the minutes */ tz = getTime(tz, &minutes); if (minutes > 60) return ERROR; /* Skip any seconds part */ if (*tz == ':') { int seconds; tz = getTime(tz + 1, &seconds); } } /* Extract any DST zone name, must be alpha */ if (isalpha((int) *tz)) { zone[i++] = '/'; /* Separate the names */ while ((i < sizeof(zone) - 1) && isalpha((int) *tz)) { zone[i++] = *tz++; } zone[i] = 0; /* Nil-terminate */ } minutes += hours * 60; minutes *= sign; /* Look for start/stop dates - require neither or both */ tz = strchr(tz, ','); if (!tz) { /* No daylight savings time here */ /* Format the env. variable */ sprintf(timezone, "TIMEZONE=%s::%d", zone, minutes); } else { if (extractDate(tz, ¤t, start) != OK) return ERROR; tz = strchr(tz + 1, ','); if (!tz) return ERROR; if (extractDate(tz, ¤t, stop) != OK) return ERROR; /* Format the env. variable */ sprintf(timezone, "TIMEZONE=%s::%d:%s:%s", zone, minutes, start, stop); } /* Make it live! */ putenv(timezone); return OK; } base-7.0.3.1/modules/libcom/src/osi/os/vxWorks/veclist.c0000664000577000060420000000753013557101274021637 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * list fuctions attached to the interrupt vector table * * Created 28Mar89 Jeffrey O. Hill * johill@lanl.gov * (505) 665 1831 * */ #include "vxWorks.h" #include "stdio.h" #include "string.h" #include "intLib.h" #include "vxLib.h" #include "iv.h" #include "ctype.h" #include "sysSymTbl.h" /* * * VME bus dependent * */ #define NVEC 0x100 static char *ignore_list[] = {"_excStub","_excIntStub"}; int veclist(int); int cISRTest(FUNCPTR proutine, FUNCPTR *ppisr, void **pparam); static void *fetch_pointer(unsigned char *); /* * * veclist() * */ int veclist(int all) { int vec; int value; SYM_TYPE type; char name[MAX_SYS_SYM_LEN]; char function_type[10]; FUNCPTR proutine; FUNCPTR pCISR; int cRoutine; void *pparam; int status; unsigned i; for(vec=0; vec #include #include #include #define epicsExportSharedSymbols #include "epicsEvent.h" #include "epicsExit.h" #include "epicsMutex.h" #include "epicsThread.h" #include "errlog.h" #include "epicsGeneralTime.h" #include "generalTimeSup.h" #include "iocsh.h" #include "osiClockTime.h" #include "taskwd.h" #define NSEC_PER_SEC 1000000000 #define ClockTimeSyncInterval_initial 1.0 #define ClockTimeSyncInterval_normal 60.0 static struct { int synchronize; int synchronized; epicsEventId loopEvent; epicsTimeStamp startTime; epicsTimeStamp syncTime; double ClockTimeSyncInterval; int syncFromPriority; epicsMutexId lock; } ClockTimePvt; static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; #if defined(CLOCK_REALTIME) && !defined(_WIN32) && !defined(__APPLE__) /* This code is not used on systems without Posix CLOCK_REALTIME, * but the only way to detect that is from the OS headers, so the * Makefile can't exclude compiling this file on those systems. */ /* Forward references */ int osdTimeGetCurrent(epicsTimeStamp *pDest); #if defined(vxWorks) || defined(__rtems__) static void ClockTimeSync(void *dummy); #endif /* ClockTime_Report iocsh command */ static const iocshArg ReportArg0 = { "interest_level", iocshArgArgv}; static const iocshArg * const ReportArgs[1] = { &ReportArg0 }; static const iocshFuncDef ReportFuncDef = {"ClockTime_Report", 1, ReportArgs}; static void ReportCallFunc(const iocshArgBuf *args) { ClockTime_Report(args[0].ival); } /* ClockTime_Shutdown iocsh command */ static const iocshFuncDef ShutdownFuncDef = {"ClockTime_Shutdown", 0, NULL}; static void ShutdownCallFunc(const iocshArgBuf *args) { ClockTime_Shutdown(NULL); } /* Initialization */ static void ClockTime_InitOnce(void *pfirst) { *(int *) pfirst = 1; ClockTimePvt.loopEvent = epicsEventMustCreate(epicsEventEmpty); ClockTimePvt.lock = epicsMutexCreate(); ClockTimePvt.ClockTimeSyncInterval = ClockTimeSyncInterval_initial; epicsAtExit(ClockTime_Shutdown, NULL); /* Register the iocsh commands */ iocshRegister(&ReportFuncDef, ReportCallFunc); iocshRegister(&ShutdownFuncDef, ShutdownCallFunc); /* Register as a time provider */ generalTimeRegisterCurrentProvider("OS Clock", LAST_RESORT_PRIORITY, osdTimeGetCurrent); } void ClockTime_Init(int synchronize) { int firstTime = 0; epicsThreadOnce(&onceId, ClockTime_InitOnce, &firstTime); if (synchronize == CLOCKTIME_SYNC) { if (ClockTimePvt.synchronize == CLOCKTIME_NOSYNC) { #if defined(vxWorks) || defined(__rtems__) /* Start synchronizing */ ClockTimePvt.synchronize = synchronize; epicsThreadCreate("ClockTimeSync", epicsThreadPriorityHigh, epicsThreadGetStackSize(epicsThreadStackSmall), ClockTimeSync, NULL); #else errlogPrintf("Clock synchronization must be performed by the OS\n"); #endif } else { /* No change, sync thread should already be running */ } } else { if (ClockTimePvt.synchronize == CLOCKTIME_SYNC) { /* Turn off synchronization thread */ ClockTime_Shutdown(NULL); } else { /* No synchronization thread */ if (firstTime) osdTimeGetCurrent(&ClockTimePvt.startTime); } } } /* Shutdown */ void ClockTime_Shutdown(void *dummy) { ClockTimePvt.synchronize = CLOCKTIME_NOSYNC; epicsEventSignal(ClockTimePvt.loopEvent); } void ClockTime_GetProgramStart(epicsTimeStamp *pDest) { *pDest = ClockTimePvt.startTime; } /* Synchronization thread */ #if defined(vxWorks) || defined(__rtems__) CLOCKTIME_SYNCHOOK ClockTime_syncHook = NULL; static void ClockTimeSync(void *dummy) { taskwdInsert(0, NULL, NULL); for (epicsEventWaitWithTimeout(ClockTimePvt.loopEvent, ClockTimePvt.ClockTimeSyncInterval); ClockTimePvt.synchronize == CLOCKTIME_SYNC; epicsEventWaitWithTimeout(ClockTimePvt.loopEvent, ClockTimePvt.ClockTimeSyncInterval)) { epicsTimeStamp timeNow; int priority; if (generalTimeGetExceptPriority(&timeNow, &priority, LAST_RESORT_PRIORITY) == epicsTimeOK) { struct timespec clockNow; epicsTimeToTimespec(&clockNow, &timeNow); if (clock_settime(CLOCK_REALTIME, &clockNow)) { errlogPrintf("ClockTimeSync: clock_settime failed\n"); continue; } epicsMutexMustLock(ClockTimePvt.lock); if (!ClockTimePvt.synchronized) { ClockTimePvt.startTime = timeNow; ClockTimePvt.synchronized = 1; } ClockTimePvt.syncFromPriority = priority; ClockTimePvt.syncTime = timeNow; epicsMutexUnlock(ClockTimePvt.lock); if (ClockTime_syncHook) ClockTime_syncHook(1); ClockTimePvt.ClockTimeSyncInterval = ClockTimeSyncInterval_normal; } } ClockTimePvt.synchronized = 0; if (ClockTime_syncHook) ClockTime_syncHook(0); taskwdRemove(0); } #endif /* Time Provider Routine */ int osdTimeGetCurrent(epicsTimeStamp *pDest) { struct timespec clockNow; /* If a Hi-Res clock is available and works, use it */ #ifdef CLOCK_REALTIME_HR clock_gettime(CLOCK_REALTIME_HR, &clockNow) && /* Note: Uses the lo-res clock below if the above call fails */ #endif clock_gettime(CLOCK_REALTIME, &clockNow); if (!ClockTimePvt.synchronized && clockNow.tv_sec < POSIX_TIME_AT_EPICS_EPOCH) { clockNow.tv_sec = POSIX_TIME_AT_EPICS_EPOCH + 86400; clockNow.tv_nsec = 0; #if defined(vxWorks) || defined(__rtems__) clock_settime(CLOCK_REALTIME, &clockNow); errlogPrintf("WARNING: OS Clock time was read before being set.\n" "Using 1990-01-02 00:00:00.000000 UTC\n"); #else errlogPrintf("WARNING: OS Clock pre-dates the EPICS epoch!\n" "Using 1990-01-02 00:00:00.000000 UTC\n"); #endif } epicsTimeFromTimespec(pDest, &clockNow); return 0; } #endif /* CLOCK_REALTIME */ /* Allow the following report routine to be compiled anyway * to avoid getting a build warning from ranlib. */ /* Status Report */ int ClockTime_Report(int level) { char timebuf[32]; if (onceId == EPICS_THREAD_ONCE_INIT) { printf("OS Clock driver not %s.\n", #ifdef CLOCK_REALTIME "initialized" #else "available" #endif /* CLOCK_REALTIME */ ); } else if (ClockTimePvt.synchronize == CLOCKTIME_SYNC) { int synchronized, syncFromPriority; epicsTimeStamp startTime, syncTime; epicsMutexMustLock(ClockTimePvt.lock); synchronized = ClockTimePvt.synchronized; syncFromPriority = ClockTimePvt.syncFromPriority; startTime = ClockTimePvt.startTime; syncTime = ClockTimePvt.syncTime; epicsMutexUnlock(ClockTimePvt.lock); if (synchronized) { printf("OS Clock driver is synchronized to a priority=%d provider\n", syncFromPriority); if (level) { epicsTimeToStrftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S.%06f", &startTime); printf("Initial sync was at %s\n", timebuf); epicsTimeToStrftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S.%06f", &syncTime); printf("Last successful sync was at %s\n", timebuf); } printf("Syncronization interval = %.0f seconds\n", ClockTimePvt.ClockTimeSyncInterval); } else printf("OS Clock driver is *not* synchronized\n"); } else { epicsTimeToStrftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S.%06f", &ClockTimePvt.startTime); printf("Program started at %s\n", timebuf); printf("OS Clock synchronization thread not running.\n"); } return 0; } base-7.0.3.1/modules/libcom/src/osi/osiClockTime.h0000664000577000060420000000147413557101274020455 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_osiClockTime_H #define INC_osiClockTime_H #define CLOCKTIME_NOSYNC 0 #define CLOCKTIME_SYNC 1 #ifdef __cplusplus extern "C" { #endif void ClockTime_Init(int synchronize); void ClockTime_Shutdown(void *dummy); int ClockTime_Report(int level); #if defined(vxWorks) || defined(__rtems__) typedef void (* CLOCKTIME_SYNCHOOK)(int synchronized); extern CLOCKTIME_SYNCHOOK ClockTime_syncHook; #endif #ifdef __cplusplus } #endif #endif base-7.0.3.1/modules/libcom/src/osi/osiNTPTime.c0000664000577000060420000002044013557101274020050 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Original Author: Marty Kraimer * Date: 16JUN2000 */ #include #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsEvent.h" #include "epicsExit.h" #include "epicsTypes.h" #include "cantProceed.h" #include "epicsThread.h" #include "epicsMutex.h" #include "errlog.h" #include "epicsGeneralTime.h" #include "generalTimeSup.h" #include "iocsh.h" #include "osdTime.h" #include "osiNTPTime.h" #include "taskwd.h" #define NSEC_PER_SEC 1000000000 #define NTPTimeSyncInterval 60.0 #define NTPTimeSyncRetries 4 static struct { int synchronize; int synchronized; epicsEventId loopEvent; int syncsFailed; epicsMutexId lock; epicsTimeStamp syncTime; epicsUInt32 syncTick; epicsTimeStamp clockTime; epicsUInt32 clockTick; epicsUInt32 ticksToSkip; double tickRate; } NTPTimePvt; static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; /* Forward references */ static int NTPTimeGetCurrent(epicsTimeStamp *pDest); static void NTPTimeSync(void *dummy); /* NTPTime_Report iocsh command */ static const iocshArg ReportArg0 = { "interest_level", iocshArgArgv}; static const iocshArg * const ReportArgs[1] = { &ReportArg0 }; static const iocshFuncDef ReportFuncDef = {"NTPTime_Report", 1, ReportArgs}; static void ReportCallFunc(const iocshArgBuf *args) { NTPTime_Report(args[0].ival); } /* NTPTime_Shutdown iocsh command */ static const iocshFuncDef ShutdownFuncDef = {"NTPTime_Shutdown", 0, NULL}; static void ShutdownCallFunc(const iocshArgBuf *args) { NTPTime_Shutdown(NULL); } /* Initialization */ static void NTPTime_InitOnce(void *pprio) { struct timespec timespecNow; NTPTimePvt.synchronize = 1; NTPTimePvt.synchronized = 0; NTPTimePvt.loopEvent = epicsEventMustCreate(epicsEventEmpty); NTPTimePvt.syncsFailed = 0; NTPTimePvt.lock = epicsMutexCreate(); /* Initialize OS-dependent code */ osdNTPInit(); /* Try to sync with NTP server */ if (!osdNTPGet(×pecNow)) { NTPTimePvt.syncTick = osdTickGet(); if (timespecNow.tv_sec > POSIX_TIME_AT_EPICS_EPOCH && epicsTimeOK == epicsTimeFromTimespec(&NTPTimePvt.syncTime, ×pecNow)) { NTPTimePvt.clockTick = NTPTimePvt.syncTick; NTPTimePvt.clockTime = NTPTimePvt.syncTime; NTPTimePvt.synchronized = 1; } } /* Start the sync thread */ epicsThreadCreate("NTPTimeSync", epicsThreadPriorityHigh, epicsThreadGetStackSize(epicsThreadStackSmall), NTPTimeSync, NULL); epicsAtExit(NTPTime_Shutdown, NULL); /* Register the iocsh commands */ iocshRegister(&ReportFuncDef, ReportCallFunc); iocshRegister(&ShutdownFuncDef, ShutdownCallFunc); /* Finally register as a time provider */ generalTimeRegisterCurrentProvider("NTP", *(int *)pprio, NTPTimeGetCurrent); } void NTPTime_Init(int priority) { epicsThreadOnce(&onceId, NTPTime_InitOnce, &priority); } /* Shutdown */ void NTPTime_Shutdown(void *dummy) { NTPTimePvt.synchronize = 0; epicsEventSignal(NTPTimePvt.loopEvent); } /* Synchronization thread */ static void NTPTimeSync(void *dummy) { taskwdInsert(0, NULL, NULL); for (epicsEventWaitWithTimeout(NTPTimePvt.loopEvent, NTPTimeSyncInterval); NTPTimePvt.synchronize; epicsEventWaitWithTimeout(NTPTimePvt.loopEvent, NTPTimeSyncInterval)) { int status; struct timespec timespecNow; epicsTimeStamp timeNow; epicsUInt32 tickNow; double diff; double ntpDelta; status = osdNTPGet(×pecNow); tickNow = osdTickGet(); if (status) { if (++NTPTimePvt.syncsFailed > NTPTimeSyncRetries && NTPTimePvt.synchronized) { errlogPrintf("NTPTimeSync: NTP requests failing - %s\n", strerror(errno)); NTPTimePvt.synchronized = 0; } continue; } if (timespecNow.tv_sec <= POSIX_TIME_AT_EPICS_EPOCH || epicsTimeFromTimespec(&timeNow, ×pecNow) != epicsTimeOK) { errlogPrintf("NTPTimeSync: Bad time received from NTP server\n"); NTPTimePvt.synchronized = 0; continue; } ntpDelta = epicsTimeDiffInSeconds(&timeNow, &NTPTimePvt.syncTime); if (ntpDelta <= 0.0 && NTPTimePvt.synchronized) { errlogPrintf("NTPTimeSync: NTP time not increasing, delta = %g\n", ntpDelta); NTPTimePvt.synchronized = 0; continue; } NTPTimePvt.syncsFailed = 0; if (!NTPTimePvt.synchronized) { errlogPrintf("NTPTimeSync: Sync recovered.\n"); } epicsMutexMustLock(NTPTimePvt.lock); diff = epicsTimeDiffInSeconds(&timeNow, &NTPTimePvt.clockTime); if (diff >= 0.0) { NTPTimePvt.ticksToSkip = 0; } else { /* dont go back in time */ NTPTimePvt.ticksToSkip = -diff * osdTickRateGet(); } NTPTimePvt.clockTick = tickNow; NTPTimePvt.clockTime = timeNow; NTPTimePvt.synchronized = 1; epicsMutexUnlock(NTPTimePvt.lock); NTPTimePvt.tickRate = (tickNow - NTPTimePvt.syncTick) / ntpDelta; NTPTimePvt.syncTick = tickNow; NTPTimePvt.syncTime = timeNow; } NTPTimePvt.synchronized = 0; taskwdRemove(0); } /* Time Provider Routine */ static int NTPTimeGetCurrent(epicsTimeStamp *pDest) { epicsUInt32 tickNow; epicsUInt32 ticksSince; if (!NTPTimePvt.synchronized) return S_time_unsynchronized; epicsMutexMustLock(NTPTimePvt.lock); tickNow = osdTickGet(); ticksSince = tickNow - NTPTimePvt.clockTick; if (NTPTimePvt.ticksToSkip <= ticksSince) { if (NTPTimePvt.ticksToSkip) { ticksSince -= NTPTimePvt.ticksToSkip; NTPTimePvt.ticksToSkip = 0; } if (ticksSince) { epicsUInt32 ticksPerSecond = osdTickRateGet(); epicsUInt32 nsecsPerTick = NSEC_PER_SEC / ticksPerSecond; epicsUInt32 secsSince = ticksSince / ticksPerSecond; ticksSince -= secsSince * ticksPerSecond; NTPTimePvt.clockTime.nsec += ticksSince * nsecsPerTick; if (NTPTimePvt.clockTime.nsec >= NSEC_PER_SEC) { secsSince++; NTPTimePvt.clockTime.nsec -= NSEC_PER_SEC; } NTPTimePvt.clockTime.secPastEpoch += secsSince; NTPTimePvt.clockTick = tickNow; } } *pDest = NTPTimePvt.clockTime; epicsMutexUnlock(NTPTimePvt.lock); return 0; } /* Status Report */ int NTPTime_Report(int level) { if (onceId == EPICS_THREAD_ONCE_INIT) { printf("NTP driver not initialized\n"); } else if (NTPTimePvt.synchronize) { printf("NTP driver %s synchronized with server\n", NTPTimePvt.synchronized ? "is" : "is *not*"); if (NTPTimePvt.syncsFailed) { printf("Last successful sync was %.1f minutes ago\n", NTPTimePvt.syncsFailed * NTPTimeSyncInterval / 60.0); } if (level) { char lastSync[32]; epicsTimeToStrftime(lastSync, sizeof(lastSync), "%Y-%m-%d %H:%M:%S.%06f", &NTPTimePvt.syncTime); printf("Syncronization interval = %.1f seconds\n", NTPTimeSyncInterval); printf("Last synchronized at %s\n", lastSync); printf("Current OS tick rate = %u Hz\n", osdTickRateGet()); printf("Measured tick rate = %.3f Hz\n", NTPTimePvt.tickRate); osdNTPReport(); } } else { printf("NTP synchronization thread not running.\n"); } return 0; } base-7.0.3.1/modules/libcom/src/osi/osiNTPTime.h0000664000577000060420000000114113557101274020052 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_osiNTPTime_H #define INC_osiNTPTime_H #ifdef __cplusplus extern "C" { #endif void NTPTime_Init(int priority); void NTPTime_Shutdown(void *dummy); int NTPTime_Report(int level); #ifdef __cplusplus } #endif #endif base-7.0.3.1/modules/libcom/src/osi/osiPoolStatus.h0000664000577000060420000000225313557101274020714 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_osiPoolStatus_H #define INC_osiPoolStatus_H /* * Author: Jeff Hill * * Functions which interrogate the state of the system wide pool * */ #include #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /* * tests to see if there is sufficent space for a block of the requested size * along with whatever additional free space is necessary to keep the system running * reliably * * this routine is called quite frequently so an efficent implementation is important */ epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ); #ifdef __cplusplus } #endif #include "osdPoolStatus.h" #endif /* INC_osiPoolStatus_H */ base-7.0.3.1/modules/libcom/src/osi/osiProcess.h0000664000577000060420000000300713557101274020213 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef INC_osiProcess_H #define INC_osiProcess_H /* * Operating System Independent Interface to Process Environment * * Author: Jeff Hill * */ #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif typedef enum osiGetUserNameReturn { osiGetUserNameFail, osiGetUserNameSuccess} osiGetUserNameReturn; epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSize); /* * Spawn detached process with named executable, but return * osiSpawnDetachedProcessNoSupport if the local OS does not * support heavy weight processes. */ typedef enum osiSpawnDetachedProcessReturn { osiSpawnDetachedProcessFail, osiSpawnDetachedProcessSuccess, osiSpawnDetachedProcessNoSupport} osiSpawnDetachedProcessReturn; epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess (const char *pProcessName, const char *pBaseExecutableName); #ifdef __cplusplus } #endif #endif /* INC_osiProcess_H */ base-7.0.3.1/modules/libcom/src/osi/osiSock.c0000664000577000060420000001152113557101274017467 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * socket support library generic code * * 7-1-97 -joh- */ #include #include #define epicsExportSharedSymbols #include "epicsAssert.h" #include "epicsSignal.h" #include "epicsStdio.h" #include "osiSock.h" #define nDigitsDottedIP 4u #define chunkSize 8u #define makeMask(NBITS) ( ( 1u << ( (unsigned) NBITS) ) - 1u ) /* * sockAddrAreIdentical() * (returns true if addresses are identical) */ int epicsShareAPI sockAddrAreIdentical ( const osiSockAddr *plhs, const osiSockAddr *prhs ) { int match; if ( plhs->sa.sa_family != prhs->sa.sa_family ) { match = 0; } else if ( plhs->sa.sa_family != AF_INET ) { match = 0; } else if ( plhs->ia.sin_addr.s_addr != prhs->ia.sin_addr.s_addr ) { match = 0; } else if ( plhs->ia.sin_port != prhs->ia.sin_port ) { match = 0; } else { match = 1; } return match; } /* * sockAddrToA() * (convert socket address to ASCII host name) */ unsigned epicsShareAPI sockAddrToA ( const struct sockaddr * paddr, char * pBuf, unsigned bufSize ) { if ( bufSize < 1 ) { return 0; } if ( paddr->sa_family != AF_INET ) { static const char * pErrStr = ""; unsigned len = strlen ( pErrStr ); if ( len < bufSize ) { strcpy ( pBuf, pErrStr ); return len; } else { strncpy ( pBuf, "", bufSize-1 ); pBuf[bufSize-1] = '\0'; return bufSize-1; } } else { const struct sockaddr_in * paddr_in = (const struct sockaddr_in *) paddr; return ipAddrToA ( paddr_in, pBuf, bufSize ); } } /* * ipAddrToA() * (convert IP address to ASCII host name) */ unsigned epicsShareAPI ipAddrToA ( const struct sockaddr_in * paddr, char * pBuf, unsigned bufSize ) { unsigned len = ipAddrToHostName ( & paddr->sin_addr, pBuf, bufSize ); if ( len == 0 ) { len = ipAddrToDottedIP ( paddr, pBuf, bufSize ); } else { unsigned reducedSize = bufSize - len; int status = epicsSnprintf ( &pBuf[len], reducedSize, ":%hu", ntohs (paddr->sin_port) ); if ( status > 0 ) { unsigned portSize = (unsigned) status; if ( portSize < reducedSize ) { len += portSize; } } } return len; } /* * sockAddrToDottedIP () */ unsigned epicsShareAPI sockAddrToDottedIP ( const struct sockaddr * paddr, char * pBuf, unsigned bufSize ) { if ( paddr->sa_family != AF_INET ) { const char * pErrStr = ""; unsigned errStrLen = strlen ( pErrStr ); if ( errStrLen < bufSize ) { strcpy ( pBuf, pErrStr ); return errStrLen; } else { unsigned reducedSize = bufSize - 1u; strncpy ( pBuf, pErrStr, reducedSize ); pBuf[reducedSize] = '\0'; return reducedSize; } } else { const struct sockaddr_in *paddr_in = ( const struct sockaddr_in * ) paddr; return ipAddrToDottedIP ( paddr_in, pBuf, bufSize ); } } /* * ipAddrToDottedIP () */ unsigned epicsShareAPI ipAddrToDottedIP ( const struct sockaddr_in *paddr, char *pBuf, unsigned bufSize ) { static const char * pErrStr = ""; unsigned chunk[nDigitsDottedIP]; unsigned addr = ntohl ( paddr->sin_addr.s_addr ); unsigned strLen; unsigned i; int status; if ( bufSize == 0u ) { return 0u; } for ( i = 0; i < nDigitsDottedIP; i++ ) { chunk[i] = addr & makeMask ( chunkSize ); addr >>= chunkSize; } /* * inet_ntoa() isnt used because it isnt thread safe * (and the replacements are not standardized) */ status = epicsSnprintf ( pBuf, bufSize, "%u.%u.%u.%u:%hu", chunk[3], chunk[2], chunk[1], chunk[0], ntohs ( paddr->sin_port ) ); if ( status > 0 ) { strLen = ( unsigned ) status; if ( strLen < bufSize - 1 ) { return strLen; } } strLen = strlen ( pErrStr ); if ( strLen < bufSize ) { strcpy ( pBuf, pErrStr ); return strLen; } else { strncpy ( pBuf, pErrStr, bufSize ); pBuf[bufSize-1] = '\0'; return bufSize - 1u; } } base-7.0.3.1/modules/libcom/src/osi/osiSock.h0000664000577000060420000001673113557101274017504 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * socket support library API def * * 7-1-97 -joh- */ #ifndef osiSockh #define osiSockh #include "shareLib.h" #include "osdSock.h" #include "ellLib.h" #ifdef __cplusplus extern "C" { #endif struct sockaddr; struct sockaddr_in; struct in_addr; epicsShareFunc SOCKET epicsShareAPI epicsSocketCreate ( int domain, int type, int protocol ); epicsShareFunc int epicsShareAPI epicsSocketAccept ( int sock, struct sockaddr * pAddr, osiSocklen_t * addrlen ); epicsShareFunc void epicsShareAPI epicsSocketDestroy ( SOCKET ); epicsShareFunc void epicsShareAPI epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ); epicsShareFunc void epicsShareAPI epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ); /* * Fortunately, on most systems the combination of a shutdown of both * directions and or a signal is sufficent to interrupt a blocking send, * receive, or connect call. For odd ball systems this is stubbed out in the * osi area. */ enum epicsSocketSystemCallInterruptMechanismQueryInfo { esscimqi_socketCloseRequired, esscimqi_socketBothShutdownRequired, esscimqi_socketSigAlarmRequired /* NO LONGER USED/SUPPORTED */ }; epicsShareFunc enum epicsSocketSystemCallInterruptMechanismQueryInfo epicsSocketSystemCallInterruptMechanismQuery (); #ifdef EPICS_PRIVATE_API /* * Some systems (e.g Linux and Windows 10) allow to check the amount * of unsent data in the output queue. * Returns -1 if the information is not available. */ epicsShareFunc int epicsSocketUnsentCount(SOCKET sock); #endif /* * convert socket address to ASCII in this order * 1) look for matching host name and typically add trailing IP port * 2) failing that, convert to raw ascii address (typically this is a * dotted IP address with trailing port) * 3) failing that, writes "" into pBuf * * returns the number of character elements stored in buffer not * including the null termination, but always writes at least a * null ternminater in the string (if bufSize >= 1) */ epicsShareFunc unsigned epicsShareAPI sockAddrToA ( const struct sockaddr * paddr, char * pBuf, unsigned bufSize ); /* * convert IP address to ASCII in this order * 1) look for matching host name and add trailing port * 2) convert to raw dotted IP address with trailing port * * returns the number of character elements stored in buffer not * including the null termination, but always writes at least a * null ternminater in the string (if bufSize >= 1) */ epicsShareFunc unsigned epicsShareAPI ipAddrToA ( const struct sockaddr_in * pInetAddr, char * pBuf, unsigned bufSize ); /* * sockAddrToDottedIP () * typically convert to raw dotted IP address with trailing port * * returns the number of character elements stored in buffer not * including the null termination, but always writes at least a * null ternminater in the string (if bufSize >= 1) */ epicsShareFunc unsigned epicsShareAPI sockAddrToDottedIP ( const struct sockaddr * paddr, char * pBuf, unsigned bufSize ); /* * ipAddrToDottedIP () * convert to raw dotted IP address with trailing port * * returns the number of character elements stored in buffer not * including the null termination, but always writes at least a * null ternminater in the string (if bufSize >= 1) */ epicsShareFunc unsigned epicsShareAPI ipAddrToDottedIP ( const struct sockaddr_in * paddr, char * pBuf, unsigned bufSize ); /* * convert inet address to a host name string * * returns the number of character elements stored in buffer not * including the null termination. This will be zero if a matching * host name cant be found. * * there are many OS specific implementation stubs for this routine */ epicsShareFunc unsigned epicsShareAPI ipAddrToHostName ( const struct in_addr * pAddr, char * pBuf, unsigned bufSize ); /* * attempt to convert ASCII string to an IP address in this order * 1) look for traditional doted ip with optional port * 2) look for raw number form of ip address with optional port * 3) look for valid host name with optional port */ epicsShareFunc int epicsShareAPI aToIPAddr ( const char * pAddrString, unsigned short defaultPort, struct sockaddr_in * pIP); /* * attempt to convert ASCII host name string with optional port to an IP address */ epicsShareFunc int epicsShareAPI hostToIPAddr (const char *pHostName, struct in_addr *pIPA); /* * attach to BSD socket library */ epicsShareFunc int epicsShareAPI osiSockAttach (void); /* returns T if success, else F */ /* * release BSD socket library */ epicsShareFunc void epicsShareAPI osiSockRelease (void); /* * convert socket error numbers to a string */ epicsShareFunc void epicsSocketConvertErrorToString ( char * pBuf, unsigned bufSize, int error ); epicsShareFunc void epicsSocketConvertErrnoToString ( char * pBuf, unsigned bufSize ); typedef union osiSockAddr { struct sockaddr_in ia; struct sockaddr sa; } osiSockAddr; typedef struct osiSockAddrNode { ELLNODE node; osiSockAddr addr; } osiSockAddrNode; /* * sockAddrAreIdentical() * (returns true if addresses are identical) */ epicsShareFunc int epicsShareAPI sockAddrAreIdentical ( const osiSockAddr * plhs, const osiSockAddr * prhs ); /* * osiSockDiscoverBroadcastAddresses () * Returns the broadcast addresses of each network interface found. * * This routine is provided with the address of an ELLLIST, a socket, * a destination port number, and a match address. When the * routine returns there will be one additional entry * (an osiSockAddrNode) in the list for each network interface found that * is up and isnt a loop back interface (match addr is INADDR_ANY), * or only the interfaces that match the specified addresses (match addr * is other than INADDR_ANY). If the interface supports broadcasting * then add its broadcast address to the list. If the interface is a * point to point link then add the destination address of the point to * point link to the list. * * Any mutex locking required to protect pList is applied externally. * */ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses (ELLLIST *pList, SOCKET socket, const osiSockAddr *pMatchAddr); /* * osiLocalAddr () * Returns the osiSockAddr of the first non-loopback interface found * that is operational (up flag is set). If no valid address can be * located then return an osiSockAddr with the address family set to * unspecified (AF_UNSPEC). * * Unfortunately in EPICS 3.13 beta 11 and before the CA * repeater would not always allow the loopback address * as a local client address so current clients alternate * between the address of the first non-loopback interface * found and the loopback addresss when subscribing with * the CA repeater until all CA repeaters have been updated * to current code. After all CA repeaters have been restarted * this osi interface can be eliminated. */ epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket); #ifdef __cplusplus } #endif #endif /* ifndef osiSockh */ base-7.0.3.1/modules/libcom/src/osi/osiWireFormat.h0000664000577000060420000001542313557101274020661 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov */ #ifndef osiWireFormat #define osiWireFormat #include "epicsTypes.h" // // With future CA protocols user defined payload composition will be // supported and we will need to move away from a naturally aligned // protocol (because pad byte overhead will probably be excessive when // maintaining 8 byte natural alignment if the user isnt thinking about // placing like sized elements together). // // Nevertheless, the R3.14 protocol continues to be naturally aligned, // and all of the fields within the DBR_XXXX types are naturally aligned. // Therefore we support here two wire transfer interfaces (naturally // aligned and otherwise) because there are important optimizations // specific to each of them. // // At some point in the future the naturally aligned interfaces might // be eliminated (or unbundled from base) should they be no-longer needed. // template < class T > void WireGet ( const epicsUInt8 * pWireSrc, T & ); template < class T > void WireSet ( const T &, epicsUInt8 * pWireDst ); template < class T > void AlignedWireGet ( const T &, T & ); template < class T > void AlignedWireSet ( const T &, T & ); template < class T > class AlignedWireRef { public: AlignedWireRef ( T & ref ); operator T () const; AlignedWireRef < T > & operator = ( const T & ); private: T & _ref; AlignedWireRef ( const AlignedWireRef & ); AlignedWireRef & operator = ( const AlignedWireRef & ); }; template < class T > class AlignedWireRef < const T > { public: AlignedWireRef ( const T & ref ); operator T () const; private: const T & _ref; AlignedWireRef ( const AlignedWireRef & ); AlignedWireRef & operator = ( const AlignedWireRef & ); }; template < class T > inline AlignedWireRef < T > :: AlignedWireRef ( T & ref ) : _ref ( ref ) { } template < class T > inline AlignedWireRef < T > :: operator T () const { T tmp; AlignedWireGet ( _ref, tmp ); return tmp; } template < class T > inline AlignedWireRef < T > & AlignedWireRef < T > :: operator = ( const T & src ) { AlignedWireSet ( src, _ref ); return *this; } template < class T > inline AlignedWireRef < const T > :: AlignedWireRef ( const T & ref ) : _ref ( ref ) { } template < class T > inline AlignedWireRef < const T > :: operator T () const { T tmp; AlignedWireGet ( _ref, tmp ); return tmp; } // may be useful when creating support for little endian inline epicsUInt16 byteSwap ( const epicsUInt16 & src ) { return static_cast < epicsUInt16 > ( ( src << 8u ) | ( src >> 8u ) ); } // may be useful when creating support for little endian inline epicsUInt32 byteSwap ( const epicsUInt32 & src ) { epicsUInt32 tmp0 = byteSwap ( static_cast < epicsUInt16 > ( src >> 16u ) ); epicsUInt32 tmp1 = byteSwap ( static_cast < epicsUInt16 > ( src ) ); return static_cast < epicsUInt32 > ( ( tmp1 << 16u ) | tmp0 ); } template < class T > union WireAlias; template <> union WireAlias < epicsInt8 > { epicsUInt8 _u; epicsInt8 _o; }; template <> union WireAlias < epicsInt16 > { epicsUInt16 _u; epicsInt16 _o; }; template <> union WireAlias < epicsInt32 > { epicsUInt32 _u; epicsInt32 _o; }; template <> union WireAlias < epicsFloat32 > { epicsUInt32 _u; epicsFloat32 _o; }; // // Missaligned unsigned wire format get/set can be implemented generically // w/o performance penalty. Attempts to improve this on architectures that // dont have alignement requirements will probably get into trouble with // over-aggressive optimization under strict aliasing rules. // template < class T > inline void WireGet ( const epicsUInt8 * pWireSrc, T & dst ) { // copy through union here // a) prevents over-aggressive optimization under strict aliasing rules // b) doesnt preclude extra copy operation being optimized away WireAlias < T > tmp; WireGet ( pWireSrc, tmp._u ); dst = tmp._o; } template <> inline void WireGet < epicsUInt8 > ( const epicsUInt8 * pWireSrc, epicsUInt8 & dst ) { dst = pWireSrc[0]; } template <> inline void WireGet < epicsUInt16 > ( const epicsUInt8 * pWireSrc, epicsUInt16 & dst ) { dst = static_cast < epicsUInt16 > ( ( pWireSrc[0] << 8u ) | pWireSrc[1] ); } template <> inline void WireGet < epicsUInt32 > ( const epicsUInt8 * pWireSrc, epicsUInt32 & dst ) { dst = static_cast < epicsUInt32 > ( ( pWireSrc[0] << 24u ) | ( pWireSrc[1] << 16u ) | ( pWireSrc[2] << 8u ) | pWireSrc[3] ); } template < class T > inline void WireSet ( const T & src, epicsUInt8 * pWireDst ) { // copy through union here // a) prevents over-aggressive optimization under strict aliasing rules // b) doesnt preclude extra copy operation being optimized away WireAlias < T > tmp; tmp._o = src; WireSet ( tmp._u, pWireDst ); } template <> inline void WireSet < epicsUInt8 > ( const epicsUInt8 & src, epicsUInt8 * pWireDst ) { pWireDst[0] = src; } template <> inline void WireSet < epicsUInt16 > ( const epicsUInt16 & src, epicsUInt8 * pWireDst ) { pWireDst[0] = static_cast < epicsUInt8 > ( src >> 8u ); pWireDst[1] = static_cast < epicsUInt8 > ( src ); } template <> inline void WireSet < epicsUInt32 > ( const epicsUInt32 & src, epicsUInt8 * pWireDst ) { pWireDst[0] = static_cast < epicsUInt8 > ( src >> 24u ); pWireDst[1] = static_cast < epicsUInt8 > ( src >> 16u ); pWireDst[2] = static_cast < epicsUInt8 > ( src >> 8u ); pWireDst[3] = static_cast < epicsUInt8 > ( src ); } template < class T > inline void AlignedWireGet ( const T & src, T & dst ) { // copy through union here // a) prevents over-aggressive optimization under strict aliasing rules // b) doesnt preclude extra copy operation being optimized away WireAlias < T > srcu, dstu; srcu._o = src; AlignedWireGet ( srcu._u, dstu._u ); dst = dstu._o; } template < class T > inline void AlignedWireSet ( const T & src, T & dst ) { // copy through union here // a) prevents over-aggressive optimization under strict aliasing rules // b) doesnt preclude extra copy operation being optimized away WireAlias < T > srcu, dstu; srcu._o = src; AlignedWireSet ( srcu._u, dstu._u ); dst = dstu._o; } #include "osdWireFormat.h" #endif // osiWireFormat base-7.0.3.1/modules/libcom/src/pool/Makefile0000664000577000060420000000103113557101274017523 0ustar anjaesctl#************************************************************************* # Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/pool INC += epicsThreadPool.h Com_SRCS += poolJob.c Com_SRCS += threadPool.c base-7.0.3.1/modules/libcom/src/pool/epicsThreadPool.h0000664000577000060420000001263013557101274021330 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* General purpose worker thread pool manager * mdavidsaver@bnl.gov */ #ifndef EPICSTHREADPOOL_H #define EPICSTHREADPOOL_H #include #include #include "shareLib.h" #include "errMdef.h" #define S_pool_jobBusy (M_pool| 1) /*Job already queued or running*/ #define S_pool_jobIdle (M_pool| 2) /*Job was not queued or running*/ #define S_pool_noPool (M_pool| 3) /*Job not associated with a pool*/ #define S_pool_paused (M_pool| 4) /*Pool not currently accepting jobs*/ #define S_pool_noThreads (M_pool| 5) /*Can't create worker thread*/ #define S_pool_timeout (M_pool| 6) /*Pool still busy after timeout*/ #ifdef __cplusplus extern "C" { #endif typedef struct { unsigned int initialThreads; unsigned int maxThreads; unsigned int workerStack; unsigned int workerPriority; } epicsThreadPoolConfig; typedef struct epicsThreadPool epicsThreadPool; /* Job function call modes */ typedef enum { /* Normal run of job */ epicsJobModeRun, /* Thread pool is being destroyed. * A chance to cleanup the job immediately with epicsJobDestroy(). * If ignored, the job is orphaned (dissociated from the thread pool) * and epicsJobDestroy() must be called later. */ epicsJobModeCleanup } epicsJobMode; typedef void (*epicsJobFunction)(void* arg, epicsJobMode mode); typedef struct epicsJob epicsJob; /* Pool operations */ /* Initialize a pool config with default values. * This much be done to preserve future compatibility * when new options are added. */ epicsShareFunc void epicsThreadPoolConfigDefaults(epicsThreadPoolConfig *); /* fetch or create a thread pool which can be shared with other users. * may return NULL for allocation failures */ epicsShareFunc epicsThreadPool* epicsThreadPoolGetShared(epicsThreadPoolConfig *opts); epicsShareFunc void epicsThreadPoolReleaseShared(epicsThreadPool *pool); /* If opts is NULL then defaults are used. * The opts pointer is not stored by this call, and may exist on the stack. */ epicsShareFunc epicsThreadPool* epicsThreadPoolCreate(epicsThreadPoolConfig *opts); /* Blocks until all worker threads have stopped. * Any jobs still attached to this pool receive a callback with EPICSJOB_CLEANUP * and are then orphaned. */ epicsShareFunc void epicsThreadPoolDestroy(epicsThreadPool *); /* pool control options */ typedef enum { epicsThreadPoolQueueAdd, /* val==0 causes epicsJobQueue to fail, 1 is default */ epicsThreadPoolQueueRun /* val==0 prevents workers from running jobs, 1 is default */ } epicsThreadPoolOption; epicsShareFunc void epicsThreadPoolControl(epicsThreadPool* pool, epicsThreadPoolOption opt, unsigned int val); /* Block until job queue is emptied and no jobs are running. * Useful after calling epicsThreadPoolControl() with option epicsThreadPoolQueueAdd=0 * * timeout<0 waits forever, timeout==0 polls, timeout>0 waits at most one timeout period * Returns 0 for success or non-zero on error (timeout is ETIMEOUT) */ epicsShareFunc int epicsThreadPoolWait(epicsThreadPool* pool, double timeout); /* Per job operations */ /* Special flag for epicsJobCreate(). * When passed as the third argument "user" * the argument passed to the job callback * will be the epicsJob* */ #define EPICSJOB_SELF epicsJobArgSelfMagic epicsShareExtern void* epicsJobArgSelfMagic; /* Creates, but does not add, a new job. * If pool is NULL then the job is not associated with any pool and * epicsJobMove() must be called before epicsJobQueue(). * Safe to call from a running job function. * Returns a new job pointer, or NULL on error. */ epicsShareFunc epicsJob* epicsJobCreate(epicsThreadPool* pool, epicsJobFunction cb, void* user); /* Cancel and free a job structure. Does not block. * Job may not be immediately free'd. * Safe to call from a running job function. */ epicsShareFunc void epicsJobDestroy(epicsJob*); /* Move the job to a different pool. * If pool is NULL then the job will no longer be associated * with any pool. * Not thread safe. Job must not be running or queued. * returns 0 on success, non-zero on error. */ epicsShareFunc int epicsJobMove(epicsJob* job, epicsThreadPool* pool); /* Adds the job to the run queue * Safe to call from a running job function. * returns 0 for success, non-zero on error. */ epicsShareFunc int epicsJobQueue(epicsJob*); /* Remove a job from the run queue if it is queued. * Safe to call from a running job function. * returns 0 if job was queued and now is not. * 1 if job already ran, is running, or was not queued before, * Other non-zero on error */ epicsShareFunc int epicsJobUnqueue(epicsJob*); /* Mostly useful for debugging */ epicsShareFunc void epicsThreadPoolReport(epicsThreadPool *pool, FILE *fd); /* Current number of active workers. May be less than the maximum */ epicsShareFunc unsigned int epicsThreadPoolNThreads(epicsThreadPool *); #ifdef __cplusplus } #endif #endif // EPICSTHREADPOOL_H base-7.0.3.1/modules/libcom/src/pool/poolJob.c0000664000577000060420000001742113557101274017645 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include #define epicsExportSharedSymbols #include "dbDefs.h" #include "errlog.h" #include "ellLib.h" #include "epicsThread.h" #include "epicsMutex.h" #include "epicsEvent.h" #include "epicsInterrupt.h" #include "epicsThreadPool.h" #include "poolPriv.h" void *epicsJobArgSelfMagic = &epicsJobArgSelfMagic; static void workerMain(void *arg) { epicsThreadPool *pool = arg; unsigned int nrun, ocnt; /* workers are created with counts * in the running, sleeping, and (possibly) waking counters */ epicsMutexMustLock(pool->guard); pool->threadsAreAwake++; pool->threadsSleeping--; while (1) { ELLNODE *cur; pool->threadsAreAwake--; pool->threadsSleeping++; epicsMutexUnlock(pool->guard); epicsEventMustWait(pool->workerWakeup); epicsMutexMustLock(pool->guard); pool->threadsSleeping--; pool->threadsAreAwake++; if (pool->threadsWaking==0) continue; pool->threadsWaking--; CHECKCOUNT(pool); if (pool->shutdown) break; if (pool->pauserun) continue; /* more threads to wakeup */ if (pool->threadsWaking) { epicsEventSignal(pool->workerWakeup); } while ((cur=ellGet(&pool->jobs)) != NULL) { epicsJob *job = CONTAINER(cur, epicsJob, jobnode); assert(job->queued && !job->running); job->queued=0; job->running=1; epicsMutexUnlock(pool->guard); (*job->func)(job->arg, epicsJobModeRun); epicsMutexMustLock(pool->guard); if (job->freewhendone) { job->dead=1; free(job); } else { job->running=0; /* job may be re-queued from within callback */ if (job->queued) ellAdd(&pool->jobs, &job->jobnode); else ellAdd(&pool->owned, &job->jobnode); } } if (pool->observerCount) epicsEventSignal(pool->observerWakeup); } pool->threadsAreAwake--; pool->threadsRunning--; nrun = pool->threadsRunning; ocnt = pool->observerCount; epicsMutexUnlock(pool->guard); if (ocnt) epicsEventSignal(pool->observerWakeup); if (nrun) epicsEventSignal(pool->workerWakeup); /* pass along */ else epicsEventSignal(pool->shutdownEvent); } int createPoolThread(epicsThreadPool *pool) { epicsThreadId tid; tid = epicsThreadCreate("PoolWorker", pool->conf.workerPriority, pool->conf.workerStack, &workerMain, pool); if (!tid) return S_pool_noThreads; pool->threadsRunning++; pool->threadsSleeping++; return 0; } epicsJob* epicsJobCreate(epicsThreadPool *pool, epicsJobFunction func, void *arg) { epicsJob *job = calloc(1, sizeof(*job)); if (!job) return NULL; if (arg == &epicsJobArgSelfMagic) arg = job; job->pool = NULL; job->func = func; job->arg = arg; epicsJobMove(job, pool); return job; } void epicsJobDestroy(epicsJob *job) { epicsThreadPool *pool; if (!job || !job->pool) { free(job); return; } pool = job->pool; epicsMutexMustLock(pool->guard); assert(!job->dead); epicsJobUnqueue(job); if (job->running || job->freewhendone) { job->freewhendone = 1; } else { ellDelete(&pool->owned, &job->jobnode); job->dead = 1; free(job); } epicsMutexUnlock(pool->guard); } int epicsJobMove(epicsJob *job, epicsThreadPool *newpool) { epicsThreadPool *pool = job->pool; /* remove from current pool */ if (pool) { epicsMutexMustLock(pool->guard); if (job->queued || job->running) { epicsMutexUnlock(pool->guard); return S_pool_jobBusy; } ellDelete(&pool->owned, &job->jobnode); epicsMutexUnlock(pool->guard); } pool = job->pool = newpool; /* add to new pool */ if (pool) { epicsMutexMustLock(pool->guard); ellAdd(&pool->owned, &job->jobnode); epicsMutexUnlock(pool->guard); } return 0; } int epicsJobQueue(epicsJob *job) { int ret = 0; epicsThreadPool *pool = job->pool; if (!pool) return S_pool_noPool; epicsMutexMustLock(pool->guard); assert(!job->dead); if (pool->pauseadd) { ret = S_pool_paused; goto done; } else if (job->freewhendone) { ret = S_pool_jobBusy; goto done; } else if (job->queued) { goto done; } job->queued = 1; /* Job may be queued from within a callback */ if (!job->running) { ellDelete(&pool->owned, &job->jobnode); ellAdd(&pool->jobs, &job->jobnode); } else { /* some worker will find it again before sleeping */ goto done; } /* Since we hold the lock, we can be certain that all awake worker are * executing work functions. The current thread may be a worker. * We prefer to wakeup a new worker rather then wait for a busy worker to * finish. However, after we initiate a wakeup there will be a race * between the worker waking up, and a busy worker finishing. * Thus we can't avoid spurious wakeups. */ if (pool->threadsRunning >= pool->conf.maxThreads) { /* all workers created... */ /* ... but some are sleeping, so wake one up */ if (pool->threadsWaking < pool->threadsSleeping) { pool->threadsWaking++; epicsEventSignal(pool->workerWakeup); } /*else one of the running workers will find this job before sleeping */ CHECKCOUNT(pool); } else { /* could create more workers so * will either create a new worker, or wakeup an existing worker */ if (pool->threadsWaking >= pool->threadsSleeping) { /* all sleeping workers have already been woken. * start a new worker for this job */ if (createPoolThread(pool) && pool->threadsRunning == 0) { /* oops, we couldn't lazy create our first worker * so this job would never run! */ ret = S_pool_noThreads; job->queued = 0; /* if threadsRunning==0 then no jobs can be running */ assert(!job->running); ellDelete(&pool->jobs, &job->jobnode); ellAdd(&pool->owned, &job->jobnode); } } if (ret == 0) { pool->threadsWaking++; epicsEventSignal(pool->workerWakeup); } CHECKCOUNT(pool); } done: epicsMutexUnlock(pool->guard); return ret; } int epicsJobUnqueue(epicsJob *job) { int ret = S_pool_jobIdle; epicsThreadPool *pool = job->pool; if (!pool) return S_pool_noPool; epicsMutexMustLock(pool->guard); assert(!job->dead); if (job->queued) { if (!job->running) { ellDelete(&pool->jobs, &job->jobnode); ellAdd(&pool->owned, &job->jobnode); } job->queued = 0; ret = 0; } epicsMutexUnlock(pool->guard); return ret; } base-7.0.3.1/modules/libcom/src/pool/poolPriv.h0000664000577000060420000000576113557101274020064 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef POOLPRIV_H #define POOLPRIV_H #include "epicsThreadPool.h" #include "ellLib.h" #include "epicsThread.h" #include "epicsEvent.h" #include "epicsMutex.h" struct epicsThreadPool { ELLNODE sharedNode; size_t sharedCount; ELLLIST jobs; /* run queue */ ELLLIST owned; /* unqueued jobs. */ /* Worker state counters. * The life cycle of a worker is * Wakeup -> Awake -> Sleeping * Newly created workers go into the wakeup state */ /* # of running workers which are not waiting for a wakeup event */ unsigned int threadsAreAwake; /* # of sleeping workers which need to be awakened */ unsigned int threadsWaking; /* # of workers waiting on the workerWakeup event */ unsigned int threadsSleeping; /* # of threads started and not stopped */ unsigned int threadsRunning; /* # of observers waiting on pool events */ unsigned int observerCount; epicsEventId workerWakeup; epicsEventId shutdownEvent; epicsEventId observerWakeup; /* Disallow epicsJobQueue */ unsigned int pauseadd:1; /* Prevent workers from running new jobs */ unsigned int pauserun:1; /* Prevent further changes to pool options */ unsigned int freezeopt:1; /* tell workers to exit */ unsigned int shutdown:1; epicsMutexId guard; /* copy of config passed when created */ epicsThreadPoolConfig conf; }; /* Called after manipulating counters to check that invariants are preserved */ #define CHECKCOUNT(pPool) do { \ if (!(pPool)->shutdown) { \ assert((pPool)->threadsAreAwake + (pPool)->threadsSleeping == (pPool)->threadsRunning); \ assert((pPool)->threadsWaking <= (pPool)->threadsSleeping); \ } \ } while(0) /* When created a job is idle. queued and running are false * and jobnode is in the thread pool's owned list. * * When the job is added, the queued flag is set and jobnode * is in the jobs list. * * When the job starts running the queued flag is cleared and * the running flag is set. jobnode is not in any list * (held locally by worker). * * When the job has finished running, the running flag is cleared. * The queued flag may be set if the job re-added itself. * Based on the queued flag jobnode is added to the appropriate * list. */ struct epicsJob { ELLNODE jobnode; epicsJobFunction func; void *arg; epicsThreadPool *pool; unsigned int queued:1; unsigned int running:1; unsigned int freewhendone:1; /* lazy delete of running job */ unsigned int dead:1; /* flag to catch use of freed objects */ }; int createPoolThread(epicsThreadPool *pool); #endif // POOLPRIV_H base-7.0.3.1/modules/libcom/src/pool/threadPool.c0000664000577000060420000002550413557101274020343 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include #define epicsExportSharedSymbols #include "dbDefs.h" #include "errlog.h" #include "ellLib.h" #include "epicsThread.h" #include "epicsMutex.h" #include "epicsEvent.h" #include "epicsInterrupt.h" #include "cantProceed.h" #include "epicsThreadPool.h" #include "poolPriv.h" void epicsThreadPoolConfigDefaults(epicsThreadPoolConfig *opts) { memset(opts, 0, sizeof(*opts)); opts->maxThreads = epicsThreadGetCPUs(); opts->workerStack = epicsThreadGetStackSize(epicsThreadStackSmall); if (epicsThreadLowestPriorityLevelAbove(epicsThreadPriorityCAServerHigh, &opts->workerPriority) != epicsThreadBooleanStatusSuccess) opts->workerPriority = epicsThreadPriorityMedium; } epicsThreadPool* epicsThreadPoolCreate(epicsThreadPoolConfig *opts) { size_t i; epicsThreadPool *pool; /* caller likely didn't initialize the options structure */ if (opts && opts->maxThreads == 0) { errlogMessage("Error: epicsThreadPoolCreate() options provided, but not initialized"); return NULL; } pool = calloc(1, sizeof(*pool)); if (!pool) return NULL; if (opts) memcpy(&pool->conf, opts, sizeof(*opts)); else epicsThreadPoolConfigDefaults(&pool->conf); if (pool->conf.initialThreads > pool->conf.maxThreads) pool->conf.initialThreads = pool->conf.maxThreads; pool->workerWakeup = epicsEventCreate(epicsEventEmpty); pool->shutdownEvent = epicsEventCreate(epicsEventEmpty); pool->observerWakeup = epicsEventCreate(epicsEventEmpty); pool->guard = epicsMutexCreate(); if (!pool->workerWakeup || !pool->shutdownEvent || !pool->observerWakeup || !pool->guard) goto cleanup; ellInit(&pool->jobs); ellInit(&pool->owned); epicsMutexMustLock(pool->guard); for (i = 0; i < pool->conf.initialThreads; i++) { createPoolThread(pool); } if (pool->threadsRunning == 0 && pool->conf.initialThreads != 0) { epicsMutexUnlock(pool->guard); errlogPrintf("Error: Unable to create any threads for thread pool\n"); goto cleanup; } else if (pool->threadsRunning < pool->conf.initialThreads) { errlogPrintf("Warning: Unable to create all threads for thread pool (%u/%u)\n", pool->threadsRunning, pool->conf.initialThreads); } epicsMutexUnlock(pool->guard); return pool; cleanup: if (pool->workerWakeup) epicsEventDestroy(pool->workerWakeup); if (pool->shutdownEvent) epicsEventDestroy(pool->shutdownEvent); if (pool->observerWakeup) epicsEventDestroy(pool->observerWakeup); if (pool->guard) epicsMutexDestroy(pool->guard); free(pool); return NULL; } static void epicsThreadPoolControlImpl(epicsThreadPool *pool, epicsThreadPoolOption opt, unsigned int val) { if (pool->freezeopt) return; if (opt == epicsThreadPoolQueueAdd) { pool->pauseadd = !val; } else if (opt == epicsThreadPoolQueueRun) { if (!val && !pool->pauserun) pool->pauserun = 1; else if (val && pool->pauserun) { int jobs = ellCount(&pool->jobs); pool->pauserun = 0; if (jobs) { int wakeable = pool->threadsSleeping - pool->threadsWaking; /* first try to give jobs to sleeping workers */ if (wakeable) { int wakeup = jobs > wakeable ? wakeable : jobs; assert(wakeup > 0); jobs -= wakeup; pool->threadsWaking += wakeup; epicsEventSignal(pool->workerWakeup); CHECKCOUNT(pool); } } while (jobs-- && pool->threadsRunning < pool->conf.maxThreads) { if (createPoolThread(pool) == 0) { pool->threadsWaking++; epicsEventSignal(pool->workerWakeup); } else break; /* oops, couldn't create worker */ } CHECKCOUNT(pool); } } /* unknown options ignored */ } void epicsThreadPoolControl(epicsThreadPool *pool, epicsThreadPoolOption opt, unsigned int val) { epicsMutexMustLock(pool->guard); epicsThreadPoolControlImpl(pool, opt, val); epicsMutexUnlock(pool->guard); } int epicsThreadPoolWait(epicsThreadPool *pool, double timeout) { int ret = 0; epicsMutexMustLock(pool->guard); while (ellCount(&pool->jobs) > 0 || pool->threadsAreAwake > 0) { pool->observerCount++; epicsMutexUnlock(pool->guard); if (timeout < 0.0) { epicsEventMustWait(pool->observerWakeup); } else { switch (epicsEventWaitWithTimeout(pool->observerWakeup, timeout)) { case epicsEventWaitError: cantProceed("epicsThreadPoolWait: failed to wait for Event"); break; case epicsEventWaitTimeout: ret = S_pool_timeout; break; case epicsEventWaitOK: ret = 0; break; } } epicsMutexMustLock(pool->guard); pool->observerCount--; if (pool->observerCount) epicsEventSignal(pool->observerWakeup); if (ret != 0) break; } epicsMutexUnlock(pool->guard); return ret; } void epicsThreadPoolDestroy(epicsThreadPool *pool) { unsigned int nThr; ELLLIST notify; ELLNODE *cur; if (!pool) return; ellInit(¬ify); epicsMutexMustLock(pool->guard); /* run remaining queued jobs */ epicsThreadPoolControlImpl(pool, epicsThreadPoolQueueAdd, 0); epicsThreadPoolControlImpl(pool, epicsThreadPoolQueueRun, 1); nThr = pool->threadsRunning; pool->freezeopt = 1; epicsMutexUnlock(pool->guard); epicsThreadPoolWait(pool, -1.0); /* At this point all queued jobs have run */ epicsMutexMustLock(pool->guard); pool->shutdown = 1; /* wakeup all */ if (pool->threadsWaking < pool->threadsSleeping) { pool->threadsWaking = pool->threadsSleeping; epicsEventSignal(pool->workerWakeup); } ellConcat(¬ify, &pool->owned); ellConcat(¬ify, &pool->jobs); epicsMutexUnlock(pool->guard); if (nThr && epicsEventWait(pool->shutdownEvent) != epicsEventWaitOK){ errlogMessage("epicsThreadPoolDestroy: wait error"); return; } /* all workers are now shutdown */ /* notify remaining jobs that pool is being destroyed */ while ((cur = ellGet(¬ify)) != NULL) { epicsJob *job = CONTAINER(cur, epicsJob, jobnode); job->running = 1; job->func(job->arg, epicsJobModeCleanup); job->running = 0; if (job->freewhendone) free(job); else job->pool = NULL; /* orphan */ } epicsEventDestroy(pool->workerWakeup); epicsEventDestroy(pool->shutdownEvent); epicsEventDestroy(pool->observerWakeup); epicsMutexDestroy(pool->guard); free(pool); } void epicsThreadPoolReport(epicsThreadPool *pool, FILE *fd) { ELLNODE *cur; epicsMutexMustLock(pool->guard); fprintf(fd, "Thread Pool with %u/%u threads\n" " running %d jobs with %u threads\n", pool->threadsRunning, pool->conf.maxThreads, ellCount(&pool->jobs), pool->threadsAreAwake); if (pool->pauseadd) fprintf(fd, " Inhibit queueing\n"); if (pool->pauserun) fprintf(fd, " Pause workers\n"); if (pool->shutdown) fprintf(fd, " Shutdown in progress\n"); for (cur = ellFirst(&pool->jobs); cur; cur = ellNext(cur)) { epicsJob *job = CONTAINER(cur, epicsJob, jobnode); fprintf(fd, " job %p func: %p, arg: %p ", job, job->func, job->arg); if (job->queued) fprintf(fd, "Queued "); if (job->running) fprintf(fd, "Running "); if (job->freewhendone) fprintf(fd, "Free "); fprintf(fd, "\n"); } epicsMutexUnlock(pool->guard); } unsigned int epicsThreadPoolNThreads(epicsThreadPool *pool) { unsigned int ret; epicsMutexMustLock(pool->guard); ret = pool->threadsRunning; epicsMutexUnlock(pool->guard); return ret; } static ELLLIST sharedPools = ELLLIST_INIT; static epicsMutexId sharedPoolsGuard; static epicsThreadOnceId sharedPoolsOnce = EPICS_THREAD_ONCE_INIT; static void sharedPoolsInit(void* unused) { sharedPoolsGuard = epicsMutexMustCreate(); } epicsShareFunc epicsThreadPool* epicsThreadPoolGetShared(epicsThreadPoolConfig *opts) { ELLNODE *node; epicsThreadPool *cur; epicsThreadPoolConfig defopts; size_t N = epicsThreadGetCPUs(); if (!opts) { epicsThreadPoolConfigDefaults(&defopts); opts = &defopts; } /* shared pools must have a minimum allowed number of workers. * Use the number of CPU cores */ if (opts->maxThreads < N) opts->maxThreads = N; epicsThreadOnce(&sharedPoolsOnce, &sharedPoolsInit, NULL); epicsMutexMustLock(sharedPoolsGuard); for (node = ellFirst(&sharedPools); node; node = ellNext(node)) { cur = CONTAINER(node, epicsThreadPool, sharedNode); /* Must have exactly the requested priority * At least the requested max workers * and at least the requested stack size */ if (cur->conf.workerPriority != opts->workerPriority) continue; if (cur->conf.maxThreads < opts->maxThreads) continue; if (cur->conf.workerStack < opts->workerStack) continue; cur->sharedCount++; assert(cur->sharedCount > 0); epicsMutexUnlock(sharedPoolsGuard); epicsMutexMustLock(cur->guard); *opts = cur->conf; epicsMutexUnlock(cur->guard); return cur; } cur = epicsThreadPoolCreate(opts); if (!cur) { epicsMutexUnlock(sharedPoolsGuard); return NULL; } cur->sharedCount = 1; ellAdd(&sharedPools, &cur->sharedNode); epicsMutexUnlock(sharedPoolsGuard); return cur; } epicsShareFunc void epicsThreadPoolReleaseShared(epicsThreadPool *pool) { if (!pool) return; epicsMutexMustLock(sharedPoolsGuard); assert(pool->sharedCount > 0); pool->sharedCount--; if (pool->sharedCount == 0) { ellDelete(&sharedPools, &pool->sharedNode); epicsThreadPoolDestroy(pool); } epicsMutexUnlock(sharedPoolsGuard); } base-7.0.3.1/modules/libcom/src/ring/Makefile0000664000577000060420000000120513557101274017514 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/ring #following needed for locating epicsRingPointer.h and epicsRingBytes.h INC += epicsRingPointer.h INC += epicsRingBytes.h Com_SRCS += epicsRingPointer.cpp Com_SRCS += epicsRingBytes.c base-7.0.3.1/modules/libcom/src/ring/epicsRingBytes.c0000664000577000060420000001572013557101274021161 0ustar anjaesctl/*************************************************************************\ * 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) 2012 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Marty Kraimer Date: 15JUL99 * Eric Norum * Ralph Lange */ #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsSpin.h" #include "dbDefs.h" #include "epicsRingBytes.h" /* * Need at least one extra byte to be able to distinguish a completely * full buffer from a completely empty one. Allow for a little extra * space to try and keep good alignment and avoid multiple calls to * memcpy for a single put/get operation. */ #define SLOP 16 typedef struct ringPvt { epicsSpinId lock; volatile int nextPut; volatile int nextGet; int size; int highWaterMark; volatile char buffer[1]; /* actually larger */ }ringPvt; epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int size) { ringPvt *pring = malloc(sizeof(ringPvt) + size + SLOP); if(!pring) return NULL; pring->size = size + SLOP; pring->highWaterMark = 0; pring->nextGet = 0; pring->nextPut = 0; pring->lock = 0; return((void *)pring); } epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesLockedCreate(int size) { ringPvt *pring = (ringPvt *)epicsRingBytesCreate(size); if(!pring) return NULL; pring->lock = epicsSpinCreate(); return((void *)pring); } epicsShareFunc void epicsShareAPI epicsRingBytesDelete(epicsRingBytesId id) { ringPvt *pring = (ringPvt *)id; if (pring->lock) epicsSpinDestroy(pring->lock); free((void *)pring); } epicsShareFunc int epicsShareAPI epicsRingBytesGet( epicsRingBytesId id, char *value,int nbytes) { ringPvt *pring = (ringPvt *)id; int nextGet, nextPut, size; int count; if (pring->lock) epicsSpinLock(pring->lock); nextGet = pring->nextGet; nextPut = pring->nextPut; size = pring->size; if (nextGet <= nextPut) { count = nextPut - nextGet; if (count < nbytes) nbytes = count; if (nbytes) memcpy (value, (void *)&pring->buffer[nextGet], nbytes); nextGet += nbytes; } else { count = size - nextGet; if (count > nbytes) count = nbytes; memcpy (value, (void *)&pring->buffer[nextGet], count); nextGet += count; if (nextGet == size) { int nLeft = nbytes - count; if (nLeft > nextPut) nLeft = nextPut; memcpy (value+count, (void *)&pring->buffer[0], nLeft); nextGet = nLeft; nbytes = count + nLeft; } else { nbytes = count; } } pring->nextGet = nextGet; if (pring->lock) epicsSpinUnlock(pring->lock); return nbytes; } epicsShareFunc int epicsShareAPI epicsRingBytesPut( epicsRingBytesId id, char *value,int nbytes) { ringPvt *pring = (ringPvt *)id; int nextGet, nextPut, size; int freeCount, copyCount, topCount, used; if (pring->lock) epicsSpinLock(pring->lock); nextGet = pring->nextGet; nextPut = pring->nextPut; size = pring->size; if (nextPut < nextGet) { freeCount = nextGet - nextPut - SLOP; if (nbytes > freeCount) { if (pring->lock) epicsSpinUnlock(pring->lock); return 0; } if (nbytes) { memcpy ((void *)&pring->buffer[nextPut], value, nbytes); } nextPut += nbytes; } else { freeCount = size - nextPut + nextGet - SLOP; if (nbytes > freeCount) { if (pring->lock) epicsSpinUnlock(pring->lock); return 0; } topCount = size - nextPut; copyCount = (nbytes > topCount) ? topCount : nbytes; if (copyCount) { memcpy ((void *)&pring->buffer[nextPut], value, copyCount); } nextPut += copyCount; if (nextPut == size) { int nLeft = nbytes - copyCount; if (nLeft) memcpy ((void *)&pring->buffer[0], value+copyCount, nLeft); nextPut = nLeft; } } pring->nextPut = nextPut; used = nextPut - nextGet; if (used < 0) used += pring->size; if (used > pring->highWaterMark) pring->highWaterMark = used; if (pring->lock) epicsSpinUnlock(pring->lock); return nbytes; } epicsShareFunc void epicsShareAPI epicsRingBytesFlush(epicsRingBytesId id) { ringPvt *pring = (ringPvt *)id; if (pring->lock) epicsSpinLock(pring->lock); pring->nextGet = pring->nextPut; if (pring->lock) epicsSpinUnlock(pring->lock); } epicsShareFunc int epicsShareAPI epicsRingBytesFreeBytes(epicsRingBytesId id) { ringPvt *pring = (ringPvt *)id; int nextGet, nextPut; if (pring->lock) epicsSpinLock(pring->lock); nextGet = pring->nextGet; nextPut = pring->nextPut; if (pring->lock) epicsSpinUnlock(pring->lock); if (nextPut < nextGet) return nextGet - nextPut - SLOP; else return pring->size - nextPut + nextGet - SLOP; } epicsShareFunc int epicsShareAPI epicsRingBytesUsedBytes(epicsRingBytesId id) { ringPvt *pring = (ringPvt *)id; int nextGet, nextPut; int used; if (pring->lock) epicsSpinLock(pring->lock); nextGet = pring->nextGet; nextPut = pring->nextPut; if (pring->lock) epicsSpinUnlock(pring->lock); used = nextPut - nextGet; if (used < 0) used += pring->size; return used; } epicsShareFunc int epicsShareAPI epicsRingBytesSize(epicsRingBytesId id) { ringPvt *pring = (ringPvt *)id; return pring->size - SLOP; } epicsShareFunc int epicsShareAPI epicsRingBytesIsEmpty(epicsRingBytesId id) { ringPvt *pring = (ringPvt *)id; int isEmpty; if (pring->lock) epicsSpinLock(pring->lock); isEmpty = (pring->nextPut == pring->nextGet); if (pring->lock) epicsSpinUnlock(pring->lock); return isEmpty; } epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id) { return (epicsRingBytesFreeBytes(id) <= 0); } epicsShareFunc int epicsShareAPI epicsRingBytesHighWaterMark(epicsRingBytesIdConst id) { ringPvt *pring = (ringPvt *)id; return pring->highWaterMark; } epicsShareFunc void epicsShareAPI epicsRingBytesResetHighWaterMark(epicsRingBytesId id) { ringPvt *pring = (ringPvt *)id; int used; if (pring->lock) epicsSpinLock(pring->lock); used = pring->nextGet - pring->nextPut; if (used < 0) used += pring->size; pring->highWaterMark = used; if (pring->lock) epicsSpinUnlock(pring->lock); } base-7.0.3.1/modules/libcom/src/ring/epicsRingBytes.h0000664000577000060420000000430413557101274021162 0ustar anjaesctl/*************************************************************************\ * 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) 2012 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Marty Kraimer Date: 15JUL99 * Eric Norum * Ralph Lange */ #ifndef INCepicsRingBytesh #define INCepicsRingBytesh #ifdef __cplusplus extern "C" { #endif #include "shareLib.h" typedef void *epicsRingBytesId; typedef void const *epicsRingBytesIdConst; epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int nbytes); /* Same, but secured by a spinlock */ epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesLockedCreate(int nbytes); epicsShareFunc void epicsShareAPI epicsRingBytesDelete(epicsRingBytesId id); epicsShareFunc int epicsShareAPI epicsRingBytesGet( epicsRingBytesId id, char *value,int nbytes); epicsShareFunc int epicsShareAPI epicsRingBytesPut( epicsRingBytesId id, char *value,int nbytes); epicsShareFunc void epicsShareAPI epicsRingBytesFlush(epicsRingBytesId id); epicsShareFunc int epicsShareAPI epicsRingBytesFreeBytes(epicsRingBytesId id); epicsShareFunc int epicsShareAPI epicsRingBytesUsedBytes(epicsRingBytesId id); epicsShareFunc int epicsShareAPI epicsRingBytesSize(epicsRingBytesId id); epicsShareFunc int epicsShareAPI epicsRingBytesIsEmpty(epicsRingBytesId id); epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id); epicsShareFunc int epicsShareAPI epicsRingBytesHighWaterMark(epicsRingBytesIdConst id); epicsShareFunc void epicsShareAPI epicsRingBytesResetHighWaterMark(epicsRingBytesId id); #ifdef __cplusplus } #endif /* NOTES If there is only one writer it is not necessary to lock for put If there is a single reader it is not necessary to lock for puts epicsRingBytesLocked uses a spinlock. */ #endif /* INCepicsRingBytesh */ base-7.0.3.1/modules/libcom/src/ring/epicsRingPointer.cpp0000664000577000060420000000651013557101274022050 0ustar anjaesctl/*************************************************************************\ * 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) 2012 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Marty Kraimer Date: 13OCT2000 * Ralph Lange */ #include #include #include #include #include #define epicsExportSharedSymbols #include "epicsRingPointer.h" typedef epicsRingPointer voidPointer; epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerCreate(int size) { voidPointer *pvoidPointer = new voidPointer(size, false); return(reinterpret_cast(pvoidPointer)); } epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerLockedCreate(int size) { voidPointer *pvoidPointer = new voidPointer(size, true); return(reinterpret_cast(pvoidPointer)); } epicsShareFunc void epicsShareAPI epicsRingPointerDelete(epicsRingPointerId id) { voidPointer *pvoidPointer = reinterpret_cast(id); delete pvoidPointer; } epicsShareFunc void* epicsShareAPI epicsRingPointerPop(epicsRingPointerId id) { voidPointer *pvoidPointer = reinterpret_cast(id); return pvoidPointer->pop(); } epicsShareFunc int epicsShareAPI epicsRingPointerPush(epicsRingPointerId id, void *p) { voidPointer *pvoidPointer = reinterpret_cast(id); return((pvoidPointer->push(p) ? 1 : 0)); } epicsShareFunc void epicsShareAPI epicsRingPointerFlush(epicsRingPointerId id) { voidPointer *pvoidPointer = reinterpret_cast(id); pvoidPointer->flush(); } epicsShareFunc int epicsShareAPI epicsRingPointerGetFree(epicsRingPointerId id) { voidPointer *pvoidPointer = reinterpret_cast(id); return(pvoidPointer->getFree()); } epicsShareFunc int epicsShareAPI epicsRingPointerGetUsed(epicsRingPointerId id) { voidPointer *pvoidPointer = reinterpret_cast(id); return(pvoidPointer->getUsed()); } epicsShareFunc int epicsShareAPI epicsRingPointerGetSize(epicsRingPointerId id) { voidPointer *pvoidPointer = reinterpret_cast(id); return(pvoidPointer->getSize()); } epicsShareFunc int epicsShareAPI epicsRingPointerIsEmpty(epicsRingPointerId id) { voidPointer *pvoidPointer = reinterpret_cast(id); return((pvoidPointer->isEmpty()) ? 1 : 0); } epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id) { voidPointer *pvoidPointer = reinterpret_cast(id); return((pvoidPointer->isFull()) ? 1 : 0); } epicsShareFunc int epicsShareAPI epicsRingPointerGetHighWaterMark(epicsRingPointerIdConst id) { voidPointer const *pvoidPointer = reinterpret_cast(id); return(pvoidPointer->getHighWaterMark()); } epicsShareFunc void epicsShareAPI epicsRingPointerResetHighWaterMark(epicsRingPointerId id) { voidPointer *pvoidPointer = reinterpret_cast(id); pvoidPointer->resetHighWaterMark(); } base-7.0.3.1/modules/libcom/src/ring/epicsRingPointer.h0000664000577000060420000001447513557101274021526 0ustar anjaesctl/*************************************************************************\ * 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) 2012 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Marty Kraimer Date: 15JUL99 * Ralph Lange */ #ifndef INCepicsRingPointerh #define INCepicsRingPointerh /* NOTES * If there is only one writer it is not necessary to lock push * If there is a single reader it is not necessary to lock pop * * epicsRingPointerLocked uses a spinlock. */ #include "epicsSpin.h" #include "shareLib.h" #ifdef __cplusplus template class epicsRingPointer { public: /* Functions */ epicsRingPointer(int size, bool locked); ~epicsRingPointer(); bool push(T *p); T* pop(); void flush(); int getFree() const; int getUsed() const; int getSize() const; bool isEmpty() const; bool isFull() const; int getHighWaterMark() const; void resetHighWaterMark(); private: /* Prevent compiler-generated member functions */ /* default constructor, copy constructor, assignment operator */ epicsRingPointer(); epicsRingPointer(const epicsRingPointer &); epicsRingPointer& operator=(const epicsRingPointer &); int getUsedNoLock() const; private: /* Data */ epicsSpinId lock; volatile int nextPush; volatile int nextPop; int size; int highWaterMark; T * volatile * buffer; }; extern "C" { #endif /*__cplusplus */ typedef void *epicsRingPointerId; typedef void const *epicsRingPointerIdConst; epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerCreate(int size); /* Same, but secured by a spinlock */ epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerLockedCreate(int size); epicsShareFunc void epicsShareAPI epicsRingPointerDelete(epicsRingPointerId id); /*ringPointerPush returns (0,1) if p (was not, was) put on ring*/ epicsShareFunc int epicsShareAPI epicsRingPointerPush(epicsRingPointerId id,void *p); /*ringPointerPop returns 0 if ring is empty*/ epicsShareFunc void* epicsShareAPI epicsRingPointerPop(epicsRingPointerId id) ; epicsShareFunc void epicsShareAPI epicsRingPointerFlush(epicsRingPointerId id); epicsShareFunc int epicsShareAPI epicsRingPointerGetFree(epicsRingPointerId id); epicsShareFunc int epicsShareAPI epicsRingPointerGetUsed(epicsRingPointerId id); epicsShareFunc int epicsShareAPI epicsRingPointerGetSize(epicsRingPointerId id); epicsShareFunc int epicsShareAPI epicsRingPointerIsEmpty(epicsRingPointerId id); epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id); epicsShareFunc int epicsShareAPI epicsRingPointerGetHighWaterMark(epicsRingPointerIdConst id); epicsShareFunc void epicsShareAPI epicsRingPointerResetHighWaterMark(epicsRingPointerId id); /* This routine was incorrectly named in previous releases */ #define epicsRingPointerSize epicsRingPointerGetSize #ifdef __cplusplus } #endif /* END OF DECLARATIONS */ /* INLINE FUNCTIONS */ /* Algorithm note * Space is allocated for one additional element. * A put request is rejected if the it would cause nextPush to equal nextPop * The algorithm does not require locking puts for a single writer * or locking of gets for a single reader */ #ifdef __cplusplus template inline epicsRingPointer::epicsRingPointer(int sz, bool locked) : lock(0), nextPush(0), nextPop(0), size(sz+1), highWaterMark(0), buffer(new T* [sz+1]) { if (locked) lock = epicsSpinCreate(); } template inline epicsRingPointer::~epicsRingPointer() { if (lock) epicsSpinDestroy(lock); delete [] buffer; } template inline bool epicsRingPointer::push(T *p) { if (lock) epicsSpinLock(lock); int next = nextPush; int newNext = next + 1; if(newNext>=size) newNext=0; if (newNext == nextPop) { if (lock) epicsSpinUnlock(lock); return(false); } buffer[next] = p; nextPush = newNext; int used = getUsedNoLock(); if (used > highWaterMark) highWaterMark = used; if (lock) epicsSpinUnlock(lock); return(true); } template inline T* epicsRingPointer::pop() { if (lock) epicsSpinLock(lock); int next = nextPop; if (next == nextPush) { if (lock) epicsSpinUnlock(lock); return(0); } T*p = buffer[next]; ++next; if(next >=size) next = 0; nextPop = next; if (lock) epicsSpinUnlock(lock); return(p); } template inline void epicsRingPointer::flush() { if (lock) epicsSpinLock(lock); nextPop = 0; nextPush = 0; if (lock) epicsSpinUnlock(lock); } template inline int epicsRingPointer::getFree() const { if (lock) epicsSpinLock(lock); int n = nextPop - nextPush - 1; if (n < 0) n += size; if (lock) epicsSpinUnlock(lock); return n; } template inline int epicsRingPointer::getUsedNoLock() const { int n = nextPush - nextPop; if (n < 0) n += size; return n; } template inline int epicsRingPointer::getUsed() const { if (lock) epicsSpinLock(lock); int n = getUsedNoLock(); if (lock) epicsSpinUnlock(lock); return n; } template inline int epicsRingPointer::getSize() const { return(size-1); } template inline bool epicsRingPointer::isEmpty() const { bool isEmpty; if (lock) epicsSpinLock(lock); isEmpty = (nextPush == nextPop); if (lock) epicsSpinUnlock(lock); return isEmpty; } template inline bool epicsRingPointer::isFull() const { if (lock) epicsSpinLock(lock); int count = nextPush - nextPop +1; if (lock) epicsSpinUnlock(lock); return((count == 0) || (count == size)); } template inline int epicsRingPointer::getHighWaterMark() const { return highWaterMark; } template inline void epicsRingPointer::resetHighWaterMark() { if (lock) epicsSpinLock(lock); highWaterMark = getUsedNoLock(); if (lock) epicsSpinUnlock(lock); } #endif /* __cplusplus */ #endif /* INCepicsRingPointerh */ base-7.0.3.1/modules/libcom/src/taskwd/Makefile0000664000577000060420000000076513557101274020064 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/taskwd INC += taskwd.h Com_SRCS += taskwd.c base-7.0.3.1/modules/libcom/src/taskwd/taskwd.c0000664000577000060420000002446713557101274020072 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* taskwd.c */ /* tasks and subroutines for a general purpose task watchdog */ /* * Original Author: Marty Kraimer * Date: 07-18-91 */ #include #include #define epicsExportSharedSymbols #include "cantProceed.h" #include "dbDefs.h" #include "epicsEvent.h" #include "epicsExit.h" #include "epicsStdioRedirect.h" #include "epicsThread.h" #include "epicsMutex.h" #include "valgrind/valgrind.h" #include "errlog.h" #include "ellLib.h" #include "errMdef.h" #include "taskwd.h" struct tNode { ELLNODE node; epicsThreadId tid; TASKWDFUNC callback; void *usr; int suspended; }; struct mNode { ELLNODE node; const taskwdMonitor *funcs; void *usr; }; struct aNode { void *key; TASKWDANYFUNC callback; void *usr; }; union twdNode { struct tNode t; struct mNode m; struct aNode a; }; /* Registered Tasks */ static epicsMutexId tLock; static ELLLIST tList = ELLLIST_INIT; /* Active Monitors */ static epicsMutexId mLock; static ELLLIST mList = ELLLIST_INIT; /* Free List */ static epicsMutexId fLock; static ELLLIST fList = ELLLIST_INIT; /* Watchdog task control */ static volatile enum { twdctlInit, twdctlRun, twdctlDisable, twdctlExit } twdCtl; static epicsEventId loopEvent; static epicsEventId exitEvent; /* Task delay times (seconds) */ #define TASKWD_DELAY 6.0 /* forward definitions */ static union twdNode *allocNode(void); static void freeNode(union twdNode *); /* Initialization, lazy */ static void twdTask(void *arg) { struct tNode *pt; struct mNode *pm; while (twdCtl != twdctlExit) { if (twdCtl == twdctlRun) { epicsMutexMustLock(tLock); pt = (struct tNode *)ellFirst(&tList); while (pt) { int susp = epicsThreadIsSuspended(pt->tid); if (susp != pt->suspended) { epicsMutexMustLock(mLock); pm = (struct mNode *)ellFirst(&mList); while (pm) { if (pm->funcs->notify) { pm->funcs->notify(pm->usr, pt->tid, susp); } pm = (struct mNode *)ellNext(&pm->node); } epicsMutexUnlock(mLock); if (susp) { char tName[40]; epicsThreadGetName(pt->tid, tName, sizeof(tName)); errlogPrintf("Thread %s (%p) suspended\n", tName, (void *)pt->tid); if (pt->callback) { pt->callback(pt->usr); } } pt->suspended = susp; } pt = (struct tNode *)ellNext(&pt->node); } epicsMutexUnlock(tLock); } epicsEventWaitWithTimeout(loopEvent, TASKWD_DELAY); } epicsEventSignal(exitEvent); } static void twdShutdown(void *arg) { ELLNODE *cur; twdCtl = twdctlExit; epicsEventSignal(loopEvent); epicsEventWait(exitEvent); while ((cur = ellGet(&fList)) != NULL) { VALGRIND_MEMPOOL_FREE(&fList, cur); free(cur); } VALGRIND_DESTROY_MEMPOOL(&fList); } static void twdInitOnce(void *arg) { epicsThreadId tid; tLock = epicsMutexMustCreate(); mLock = epicsMutexMustCreate(); fLock = epicsMutexMustCreate(); ellInit(&fList); VALGRIND_CREATE_MEMPOOL(&fList, 0, 0); twdCtl = twdctlRun; loopEvent = epicsEventMustCreate(epicsEventEmpty); exitEvent = epicsEventMustCreate(epicsEventEmpty); tid = epicsThreadCreate("taskwd", epicsThreadPriorityLow, epicsThreadGetStackSize(epicsThreadStackSmall), twdTask, NULL); if (tid == 0) cantProceed("Failed to spawn task watchdog thread\n"); epicsAtExit(twdShutdown, NULL); } void taskwdInit(void) { static epicsThreadOnceId twdOnceFlag = EPICS_THREAD_ONCE_INIT; epicsThreadOnce(&twdOnceFlag, twdInitOnce, NULL); } /* For tasks to be monitored */ void taskwdInsert(epicsThreadId tid, TASKWDFUNC callback, void *usr) { struct tNode *pt; struct mNode *pm; taskwdInit(); if (tid == 0) tid = epicsThreadGetIdSelf(); pt = &allocNode()->t; pt->tid = tid; pt->callback = callback; pt->usr = usr; pt->suspended = FALSE; epicsMutexMustLock(mLock); pm = (struct mNode *)ellFirst(&mList); while (pm) { if (pm->funcs->insert) { pm->funcs->insert(pm->usr, tid); } pm = (struct mNode *)ellNext(&pm->node); } epicsMutexUnlock(mLock); epicsMutexMustLock(tLock); ellAdd(&tList, (void *)pt); epicsMutexUnlock(tLock); } void taskwdRemove(epicsThreadId tid) { struct tNode *pt; struct mNode *pm; char tName[40]; taskwdInit(); if (tid == 0) tid = epicsThreadGetIdSelf(); epicsMutexMustLock(tLock); pt = (struct tNode *)ellFirst(&tList); while (pt != NULL) { if (tid == pt->tid) { ellDelete(&tList, (void *)pt); epicsMutexUnlock(tLock); freeNode((union twdNode *)pt); epicsMutexMustLock(mLock); pm = (struct mNode *)ellFirst(&mList); while (pm) { if (pm->funcs->remove) { pm->funcs->remove(pm->usr, tid); } pm = (struct mNode *)ellNext(&pm->node); } epicsMutexUnlock(mLock); return; } pt = (struct tNode *)ellNext(&pt->node); } epicsMutexUnlock(tLock); epicsThreadGetName(tid, tName, sizeof(tName)); errlogPrintf("taskwdRemove: Thread %s (%p) not registered!\n", tName, (void *)tid); } /* Monitoring API */ void taskwdMonitorAdd(const taskwdMonitor *funcs, void *usr) { struct mNode *pm; if (funcs == NULL) return; taskwdInit(); pm = &allocNode()->m; pm->funcs = funcs; pm->usr = usr; epicsMutexMustLock(mLock); ellAdd(&mList, (void *)pm); epicsMutexUnlock(mLock); } void taskwdMonitorDel(const taskwdMonitor *funcs, void *usr) { struct mNode *pm; if (funcs == NULL) return; taskwdInit(); epicsMutexMustLock(mLock); pm = (struct mNode *)ellFirst(&mList); while (pm) { if (pm->funcs == funcs && pm->usr == usr) { ellDelete(&mList, (void *)pm); freeNode((union twdNode *)pm); epicsMutexUnlock(mLock); return; } pm = (struct mNode *)ellNext(&pm->node); } epicsMutexUnlock(mLock); errlogPrintf("taskwdMonitorDel: Unregistered!\n"); } /* Support old API for backwards compatibility */ static void anyNotify(void *usr, epicsThreadId tid, int suspended) { struct aNode *pa = (struct aNode *)usr; if (suspended) { pa->callback(pa->usr, tid); } } static taskwdMonitor anyFuncs = { NULL, anyNotify, NULL }; void taskwdAnyInsert(void *key, TASKWDANYFUNC callback, void *usr) { struct mNode *pm; struct aNode *pa; if (callback == NULL) return; taskwdInit(); pa = &allocNode()->a; pa->key = key; pa->callback = callback; pa->usr = usr; pm = &allocNode()->m; pm->funcs = &anyFuncs; pm->usr = pa; epicsMutexMustLock(mLock); ellAdd(&mList, (void *)pm); epicsMutexUnlock(mLock); } void taskwdAnyRemove(void *key) { struct mNode *pm; struct aNode *pa; taskwdInit(); epicsMutexMustLock(mLock); pm = (struct mNode *)ellFirst(&mList); while (pm) { if (pm->funcs == &anyFuncs) { pa = (struct aNode *)pm->usr; if (pa->key == key) { ellDelete(&mList, (void *)pm); freeNode((union twdNode *)pa); freeNode((union twdNode *)pm); epicsMutexUnlock(mLock); return; } } pm = (struct mNode *)ellNext(&pm->node); } epicsMutexUnlock(mLock); errlogPrintf("taskwdAnyRemove: Unregistered key %p\n", key); } /* Report function */ epicsShareFunc void taskwdShow(int level) { struct tNode *pt; int mCount, fCount, tCount; char tName[40]; epicsMutexMustLock(mLock); mCount = ellCount(&mList); epicsMutexUnlock(mLock); epicsMutexMustLock(fLock); fCount = ellCount(&fList); epicsMutexUnlock(fLock); epicsMutexMustLock(tLock); tCount = ellCount(&tList); printf("%d monitors, %d threads registered, %d free nodes\n", mCount, tCount, fCount); if (level) { printf("%16.16s %9s %12s %12s %12s\n", "THREAD NAME", "STATE", "EPICS TID", "epicsCallback", "USR ARG"); pt = (struct tNode *)ellFirst(&tList); while (pt != NULL) { epicsThreadGetName(pt->tid, tName, sizeof(tName)); printf("%16.16s %9s %12p %12p %12p\n", tName, pt->suspended ? "Suspended" : "Ok ", (void *)pt->tid, (void *)pt->callback, pt->usr); pt = (struct tNode *)ellNext(&pt->node); } } epicsMutexUnlock(tLock); } /* Free list management */ static union twdNode *newNode(void) { union twdNode *pn; epicsMutexMustLock(fLock); pn = (union twdNode *)ellGet(&fList); if (pn) { VALGRIND_MEMPOOL_FREE(&fList, pn); } epicsMutexUnlock(fLock); if (!pn) pn = calloc(1, sizeof(union twdNode)); if (pn) VALGRIND_MEMPOOL_ALLOC(&fList, pn, sizeof(*pn)); return pn; } static union twdNode *allocNode(void) { union twdNode *pn = newNode(); while (!pn) { errlogPrintf("Thread taskwd suspending: out of memory\n"); epicsThreadSuspendSelf(); pn = newNode(); } return pn; } static void freeNode(union twdNode *pn) { VALGRIND_MEMPOOL_FREE(&fList, pn); VALGRIND_MEMPOOL_ALLOC(&fList, pn, sizeof(ELLNODE)); epicsMutexMustLock(fLock); ellAdd(&fList, (void *)pn); epicsMutexUnlock(fLock); } base-7.0.3.1/modules/libcom/src/taskwd/taskwd.h0000664000577000060420000000336013557101274020064 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* General purpose task watchdog */ /* * Original Author: Marty Kraimer * Date: 07-18-91 */ #ifndef INC_taskwd_H #define INC_taskwd_H #include "epicsThread.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif /* Initialization, optional */ epicsShareFunc void taskwdInit(void); /* For tasks to be monitored */ typedef void (*TASKWDFUNC)(void *usr); epicsShareFunc void taskwdInsert(epicsThreadId tid, TASKWDFUNC callback, void *usr); epicsShareFunc void taskwdRemove(epicsThreadId tid); /* Monitoring API */ typedef struct { void (*insert)(void *usr, epicsThreadId tid); void (*notify)(void *usr, epicsThreadId tid, int suspended); void (*remove)(void *usr, epicsThreadId tid); } taskwdMonitor; epicsShareFunc void taskwdMonitorAdd(const taskwdMonitor *funcs, void *usr); epicsShareFunc void taskwdMonitorDel(const taskwdMonitor *funcs, void *usr); /* Old monitoring API, deprecated */ typedef void (*TASKWDANYFUNC)(void *usr, epicsThreadId tid); epicsShareFunc void taskwdAnyInsert(void *key, TASKWDANYFUNC callback, void *usr); epicsShareFunc void taskwdAnyRemove(void *key); /* Report function */ epicsShareFunc void taskwdShow(int level); #ifdef __cplusplus } #endif #endif /* INC_taskwd_H */ base-7.0.3.1/modules/libcom/src/timer/Makefile0000664000577000060420000000122613557101274017700 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/timer INC += epicsTimer.h Com_SRCS += epicsTimer.cpp Com_SRCS += timer.cpp Com_SRCS += timerQueue.cpp Com_SRCS += timerQueueActive.cpp Com_SRCS += timerQueueActiveMgr.cpp Com_SRCS += timerQueuePassive.cpp base-7.0.3.1/modules/libcom/src/timer/epicsTimer.cpp0000664000577000060420000001575413557101274021063 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include #define epicsExportSharedSymbols #include "epicsMath.h" #include "epicsTimer.h" #include "epicsGuard.h" #include "timerPrivate.h" #ifdef _MSC_VER # pragma warning ( push ) # pragma warning ( disable:4660 ) #endif #ifdef _MSC_VER # pragma warning ( pop ) #endif template class tsFreeList < epicsTimerForC, 0x20 >; epicsTimer::~epicsTimer () {} epicsTimerQueueNotify::~epicsTimerQueueNotify () {} epicsTimerNotify::~epicsTimerNotify () {} void epicsTimerNotify::show ( unsigned /* level */ ) const {} epicsTimerForC::epicsTimerForC ( timerQueue &queue, epicsTimerCallback pCBIn, void *pPrivateIn ) : timer ( queue ), pCallBack ( pCBIn ), pPrivate ( pPrivateIn ) { } epicsTimerForC::~epicsTimerForC () { } void epicsTimerForC::destroy () { timerQueue & queueTmp = this->queue; this->~epicsTimerForC (); queueTmp.timerForCFreeList.release ( this ); } epicsTimerNotify::expireStatus epicsTimerForC::expire ( const epicsTime & ) { ( *this->pCallBack ) ( this->pPrivate ); return noRestart; } epicsTimerQueueActiveForC :: epicsTimerQueueActiveForC ( RefMgr & refMgr, bool okToShare, unsigned priority ) : timerQueueActive ( refMgr, okToShare, priority ) { timerQueueActive::start(); } epicsTimerQueueActiveForC::~epicsTimerQueueActiveForC () { } void epicsTimerQueueActiveForC::release () { _refMgr->release ( *this ); } epicsTimerQueuePassiveForC::epicsTimerQueuePassiveForC ( epicsTimerQueueNotifyReschedule pRescheduleCallbackIn, epicsTimerQueueNotifyQuantum pSleepQuantumCallbackIn, void * pPrivateIn ) : timerQueuePassive ( * static_cast < epicsTimerQueueNotify * > ( this ) ), pRescheduleCallback ( pRescheduleCallbackIn ), pSleepQuantumCallback ( pSleepQuantumCallbackIn ), pPrivate ( pPrivateIn ) { } epicsTimerQueuePassiveForC::~epicsTimerQueuePassiveForC () { } void epicsTimerQueuePassiveForC::reschedule () { (*this->pRescheduleCallback) ( this->pPrivate ); } double epicsTimerQueuePassiveForC::quantum () { return (*this->pSleepQuantumCallback) ( this->pPrivate ); } void epicsTimerQueuePassiveForC::destroy () { delete this; } epicsShareFunc epicsTimerNotify::expireStatus::expireStatus ( restart_t restart ) : delay ( - DBL_MAX ) { if ( restart != noRestart ) { throw std::logic_error ( "timer restart was requested without specifying a delay?" ); } } epicsShareFunc epicsTimerNotify::expireStatus::expireStatus ( restart_t restartIn, const double & expireDelaySec ) : delay ( expireDelaySec ) { if ( restartIn != epicsTimerNotify::restart ) { throw std::logic_error ( "no timer restart was requested, but a delay was specified?" ); } if ( this->delay < 0.0 || !finite(this->delay) ) { throw std::logic_error ( "timer restart was requested, but a negative delay was specified?" ); } } epicsShareFunc bool epicsTimerNotify::expireStatus::restart () const { return this->delay >= 0.0 && finite(this->delay); } epicsShareFunc double epicsTimerNotify::expireStatus::expirationDelay () const { if ( this->delay < 0.0 || !finite(this->delay) ) { throw std::logic_error ( "no timer restart was requested, but you are asking for a restart delay?" ); } return this->delay; } extern "C" epicsTimerQueuePassiveId epicsShareAPI epicsTimerQueuePassiveCreate ( epicsTimerQueueNotifyReschedule pRescheduleCallbackIn, epicsTimerQueueNotifyQuantum pSleepQuantumCallbackIn, void * pPrivateIn ) { try { return new epicsTimerQueuePassiveForC ( pRescheduleCallbackIn, pSleepQuantumCallbackIn, pPrivateIn ); } catch ( ... ) { return 0; } } extern "C" void epicsShareAPI epicsTimerQueuePassiveDestroy ( epicsTimerQueuePassiveId pQueue ) { pQueue->destroy (); } extern "C" double epicsShareAPI epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId pQueue ) { try { return pQueue->process ( epicsTime::getMonotonic() ); } catch ( ... ) { return 1.0; } } extern "C" epicsTimerId epicsShareAPI epicsTimerQueuePassiveCreateTimer ( epicsTimerQueuePassiveId pQueue, epicsTimerCallback pCallback, void *pArg ) { try { return & pQueue->createTimerForC ( pCallback, pArg ); } catch ( ... ) { return 0; } } extern "C" epicsShareFunc void epicsShareAPI epicsTimerQueuePassiveDestroyTimer ( epicsTimerQueuePassiveId /* pQueue */, epicsTimerId pTmr ) { pTmr->destroy (); } extern "C" void epicsShareAPI epicsTimerQueuePassiveShow ( epicsTimerQueuePassiveId pQueue, unsigned int level ) { pQueue->show ( level ); } extern "C" epicsTimerQueueId epicsShareAPI epicsTimerQueueAllocate ( int okToShare, unsigned int threadPriority ) { try { epicsSingleton < timerQueueActiveMgr > :: reference ref = timerQueueMgrEPICS.getReference (); epicsTimerQueueActiveForC & tmr = ref->allocate ( ref, okToShare ? true : false, threadPriority ); return &tmr; } catch ( ... ) { return 0; } } extern "C" void epicsShareAPI epicsTimerQueueRelease ( epicsTimerQueueId pQueue ) { pQueue->release (); } extern "C" epicsTimerId epicsShareAPI epicsTimerQueueCreateTimer ( epicsTimerQueueId pQueue, epicsTimerCallback pCallback, void *pArg ) { try { return & pQueue->createTimerForC ( pCallback, pArg ); } catch ( ... ) { return 0; } } extern "C" void epicsShareAPI epicsTimerQueueShow ( epicsTimerQueueId pQueue, unsigned int level ) { pQueue->show ( level ); } extern "C" void epicsShareAPI epicsTimerQueueDestroyTimer ( epicsTimerQueueId /* pQueue */, epicsTimerId pTmr ) { pTmr->destroy (); } extern "C" void epicsShareAPI epicsTimerStartTime ( epicsTimerId pTmr, const epicsTimeStamp *pTime ) { pTmr->start ( *pTmr, *pTime ); } extern "C" void epicsShareAPI epicsTimerStartDelay ( epicsTimerId pTmr, double delaySeconds ) { pTmr->start ( *pTmr, delaySeconds ); } extern "C" void epicsShareAPI epicsTimerCancel ( epicsTimerId pTmr ) { pTmr->cancel (); } extern "C" double epicsShareAPI epicsTimerGetExpireDelay ( epicsTimerId pTmr ) { return pTmr->getExpireDelay (); } extern "C" void epicsShareAPI epicsTimerShow ( epicsTimerId pTmr, unsigned int level ) { pTmr->timer::show ( level ); } base-7.0.3.1/modules/libcom/src/timer/epicsTimer.h0000664000577000060420000001466013557101274020523 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* epicsTimer.h */ /* Authors: Marty Kraimer, Jeff Hill */ #ifndef epicsTimerH #define epicsTimerH #include #include "shareLib.h" #include "epicsTime.h" #include "epicsThread.h" #ifdef __cplusplus /* * Notes: * 1) epicsTimer does not hold its lock when calling callbacks. */ /* code using a timer must implement epicsTimerNotify */ class epicsShareClass epicsTimerNotify { public: enum restart_t { noRestart, restart }; class expireStatus { public: epicsShareFunc expireStatus ( restart_t ); epicsShareFunc expireStatus ( restart_t, const double & expireDelaySec ); epicsShareFunc bool restart () const; epicsShareFunc double expirationDelay () const; private: double delay; }; virtual ~epicsTimerNotify () = 0; /* return "noRestart" or "expireStatus ( restart, 30.0 )" */ virtual expireStatus expire ( const epicsTime & currentTime ) = 0; virtual void show ( unsigned int level ) const; }; class epicsShareClass epicsTimer { public: /* calls cancel (see warning below) and then destroys the timer */ virtual void destroy () = 0; virtual void start ( epicsTimerNotify &, const epicsTime & ) = 0; virtual void start ( epicsTimerNotify &, double delaySeconds ) = 0; /* WARNING: A deadlock will occur if you hold a lock while * calling this function that you also take within the timer * expiration callback. */ virtual void cancel () = 0; struct expireInfo { expireInfo ( bool active, const epicsTime & expireTime ); bool active; epicsTime expireTime; }; virtual expireInfo getExpireInfo () const = 0; double getExpireDelay (); virtual void show ( unsigned int level ) const = 0; protected: virtual ~epicsTimer () = 0; /* protected => delete() must not be called */ }; class epicsTimerQueue { public: virtual epicsTimer & createTimer () = 0; virtual void show ( unsigned int level ) const = 0; protected: epicsShareFunc virtual ~epicsTimerQueue () = 0; }; class epicsTimerQueueActive : public epicsTimerQueue { public: static epicsShareFunc epicsTimerQueueActive & allocate ( bool okToShare, unsigned threadPriority = epicsThreadPriorityMin + 10 ); virtual void release () = 0; protected: epicsShareFunc virtual ~epicsTimerQueueActive () = 0; }; class epicsTimerQueueNotify { public: /* called when a new timer is inserted into the queue and the */ /* delay to the next expire has changed */ virtual void reschedule () = 0; /* if there is a quantum in the scheduling of timer intervals */ /* return this quantum in seconds. If unknown then return zero. */ virtual double quantum () = 0; protected: epicsShareFunc virtual ~epicsTimerQueueNotify () = 0; }; class epicsTimerQueuePassive : public epicsTimerQueue { public: static epicsShareFunc epicsTimerQueuePassive & create ( epicsTimerQueueNotify & ); epicsShareFunc virtual ~epicsTimerQueuePassive () = 0; /* ok to call delete */ virtual double process ( const epicsTime & currentTime ) = 0; /* returns delay to next expire */ }; inline epicsTimer::expireInfo::expireInfo ( bool activeIn, const epicsTime & expireTimeIn ) : active ( activeIn ), expireTime ( expireTimeIn ) { } inline double epicsTimer::getExpireDelay () { epicsTimer::expireInfo info = this->getExpireInfo (); if ( info.active ) { double delay = info.expireTime - epicsTime::getMonotonic (); if ( delay < 0.0 ) { delay = 0.0; } return delay; } return - DBL_MAX; } extern "C" { #endif /* __cplusplus */ typedef struct epicsTimerForC * epicsTimerId; typedef void ( *epicsTimerCallback ) ( void *pPrivate ); /* thread managed timer queue */ typedef struct epicsTimerQueueActiveForC * epicsTimerQueueId; epicsShareFunc epicsTimerQueueId epicsShareAPI epicsTimerQueueAllocate ( int okToShare, unsigned int threadPriority ); epicsShareFunc void epicsShareAPI epicsTimerQueueRelease ( epicsTimerQueueId ); epicsShareFunc epicsTimerId epicsShareAPI epicsTimerQueueCreateTimer ( epicsTimerQueueId queueid, epicsTimerCallback callback, void *arg ); epicsShareFunc void epicsShareAPI epicsTimerQueueDestroyTimer ( epicsTimerQueueId queueid, epicsTimerId id ); epicsShareFunc void epicsShareAPI epicsTimerQueueShow ( epicsTimerQueueId id, unsigned int level ); /* passive timer queue */ typedef struct epicsTimerQueuePassiveForC * epicsTimerQueuePassiveId; typedef void ( * epicsTimerQueueNotifyReschedule ) ( void * pPrivate ); typedef double ( * epicsTimerQueueNotifyQuantum ) ( void * pPrivate ); epicsShareFunc epicsTimerQueuePassiveId epicsShareAPI epicsTimerQueuePassiveCreate ( epicsTimerQueueNotifyReschedule, epicsTimerQueueNotifyQuantum, void *pPrivate ); epicsShareFunc void epicsShareAPI epicsTimerQueuePassiveDestroy ( epicsTimerQueuePassiveId ); epicsShareFunc epicsTimerId epicsShareAPI epicsTimerQueuePassiveCreateTimer ( epicsTimerQueuePassiveId queueid, epicsTimerCallback pCallback, void *pArg ); epicsShareFunc void epicsShareAPI epicsTimerQueuePassiveDestroyTimer ( epicsTimerQueuePassiveId queueid, epicsTimerId id ); epicsShareFunc double epicsShareAPI epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId ); epicsShareFunc void epicsShareAPI epicsTimerQueuePassiveShow ( epicsTimerQueuePassiveId id, unsigned int level ); /* timer */ epicsShareFunc void epicsShareAPI epicsTimerStartTime ( epicsTimerId id, const epicsTimeStamp *pTime ); epicsShareFunc void epicsShareAPI epicsTimerStartDelay ( epicsTimerId id, double delaySeconds ); epicsShareFunc void epicsShareAPI epicsTimerCancel ( epicsTimerId id ); epicsShareFunc double epicsShareAPI epicsTimerGetExpireDelay ( epicsTimerId id ); epicsShareFunc void epicsShareAPI epicsTimerShow ( epicsTimerId id, unsigned int level ); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* epicsTimerH */ base-7.0.3.1/modules/libcom/src/timer/timer.cpp0000664000577000060420000001623013557101274020065 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include #include #include #define epicsExportSharedSymbols #include "epicsGuard.h" #include "timerPrivate.h" #include "errlog.h" #ifdef _MSC_VER # pragma warning ( push ) # pragma warning ( disable:4660 ) #endif template class tsFreeList < timer, 0x20 >; #ifdef _MSC_VER # pragma warning ( pop ) #endif timer::timer ( timerQueue & queueIn ) : queue ( queueIn ), curState ( stateLimbo ), pNotify ( 0 ) { } timer::~timer () { this->cancel (); } void timer::destroy () { timerQueue & queueTmp = this->queue; this->~timer (); queueTmp.timerFreeList.release ( this ); } void timer::start ( epicsTimerNotify & notify, double delaySeconds ) { this->start ( notify, epicsTime::getMonotonic () + delaySeconds ); } void timer::start ( epicsTimerNotify & notify, const epicsTime & expire ) { epicsGuard < epicsMutex > locker ( this->queue.mutex ); this->privateStart ( notify, expire ); } void timer::privateStart ( epicsTimerNotify & notify, const epicsTime & expire ) { this->pNotify = & notify; this->exp = expire - ( this->queue.notify.quantum () / 2.0 ); bool reschedualNeeded = false; if ( this->curState == stateActive ) { // above expire time and notify will override any restart parameters // that may be returned from the timer expire callback return; } else if ( this->curState == statePending ) { this->queue.timerList.remove ( *this ); if ( this->queue.timerList.first() == this && this->queue.timerList.count() > 0 ) { reschedualNeeded = true; } } # ifdef DEBUG unsigned preemptCount=0u; # endif // // insert into the pending queue // // Finds proper time sorted location using a linear search. // // **** this should use a binary tree ???? // tsDLIter < timer > pTmr = this->queue.timerList.lastIter (); while ( true ) { if ( ! pTmr.valid () ) { // // add to the beginning of the list // this->queue.timerList.push ( *this ); reschedualNeeded = true; break; } if ( pTmr->exp <= this->exp ) { // // add after the item found that expires earlier // this->queue.timerList.insertAfter ( *this, *pTmr ); break; } # ifdef DEBUG preemptCount++; # endif --pTmr; } this->curState = timer::statePending; if ( reschedualNeeded ) { this->queue.notify.reschedule (); } # if defined(DEBUG) && 0 this->show ( 10u ); this->queue.show ( 10u ); # endif debugPrintf ( ("Start of \"%s\" with delay %f at %p preempting %u\n", typeid ( this->notify ).name (), expire - epicsTime::getMonotonic (), this, preemptCount ) ); } void timer::cancel () { bool reschedual = false; bool wakeupCancelBlockingThreads = false; { epicsGuard < epicsMutex > locker ( this->queue.mutex ); this->pNotify = 0; if ( this->curState == statePending ) { this->queue.timerList.remove ( *this ); this->curState = stateLimbo; if ( this->queue.timerList.first() == this && this->queue.timerList.count() > 0 ) { reschedual = true; } } else if ( this->curState == stateActive ) { this->queue.cancelPending = true; this->curState = timer::stateLimbo; if ( this->queue.processThread != epicsThreadGetIdSelf() ) { // make certain timer expire() does not run after cancel () returns, // but dont require that lock is applied while calling expire() while ( this->queue.cancelPending && this->queue.pExpireTmr == this ) { epicsGuardRelease < epicsMutex > autoRelease ( locker ); this->queue.cancelBlockingEvent.wait (); } // in case other threads are waiting wakeupCancelBlockingThreads = true; } } } if ( reschedual ) { this->queue.notify.reschedule (); } if ( wakeupCancelBlockingThreads ) { this->queue.cancelBlockingEvent.signal (); } } epicsTimer::expireInfo timer::getExpireInfo () const { // taking a lock here guarantees that users will not // see brief intervals when a timer isnt active because // it is is canceled when start is called epicsGuard < epicsMutex > locker ( this->queue.mutex ); if ( this->curState == statePending || this->curState == stateActive ) { return expireInfo ( true, this->exp ); } return expireInfo ( false, epicsTime() ); } void timer::show ( unsigned int level ) const { epicsGuard < epicsMutex > locker ( this->queue.mutex ); double delay; if ( this->curState == statePending || this->curState == stateActive ) { try { delay = this->exp - epicsTime::getMonotonic(); } catch ( ... ) { delay = - DBL_MAX; } } else { delay = -DBL_MAX; } const char *pStateName; if ( this->curState == statePending ) { pStateName = "pending"; } else if ( this->curState == stateActive ) { pStateName = "active"; } else if ( this->curState == stateLimbo ) { pStateName = "limbo"; } else { pStateName = "corrupt"; } printf ( "timer, state = %s, delay = %f\n", pStateName, delay ); if ( level >= 1u && this->pNotify ) { this->pNotify->show ( level - 1u ); } } void timer::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } void epicsTimerForC::operator delete ( void * ) { // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", __FILE__, __LINE__ ); } base-7.0.3.1/modules/libcom/src/timer/timerPrivate.h0000664000577000060420000002146313557101274021071 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #ifndef epicsTimerPrivate_h #define epicsTimerPrivate_h #include #include "tsFreeList.h" #include "epicsSingleton.h" #include "tsDLList.h" #include "epicsTimer.h" #include "compilerDependencies.h" #ifdef DEBUG # define debugPrintf(ARGSINPAREN) printf ARGSINPAREN #else # define debugPrintf(ARGSINPAREN) #endif template < class T > class epicsGuard; class timer : public epicsTimer, public tsDLNode < timer > { public: void destroy (); void start ( class epicsTimerNotify &, const epicsTime & ); void start ( class epicsTimerNotify &, double delaySeconds ); void cancel (); expireInfo getExpireInfo () const; void show ( unsigned int level ) const; void * operator new ( size_t size, tsFreeList < timer, 0x20 > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < timer, 0x20 > & )) protected: timer ( class timerQueue & ); ~timer (); timerQueue & queue; private: enum state { statePending = 45, stateActive = 56, stateLimbo = 78 }; epicsTime exp; // experation time state curState; // current state epicsTimerNotify * pNotify; // callback void privateStart ( epicsTimerNotify & notify, const epicsTime & ); timer & operator = ( const timer & ); // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. void operator delete ( void * ); friend class timerQueue; }; struct epicsTimerForC : public epicsTimerNotify, public timer { public: void destroy (); protected: epicsTimerForC ( timerQueue &, epicsTimerCallback, void *pPrivateIn ); ~epicsTimerForC (); void * operator new ( size_t size, tsFreeList < epicsTimerForC, 0x20 > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < epicsTimerForC, 0x20 > & )) private: epicsTimerCallback pCallBack; void * pPrivate; expireStatus expire ( const epicsTime & currentTime ); epicsTimerForC & operator = ( const epicsTimerForC & ); // Visual C++ .net appears to require operator delete if // placement operator delete is defined? I smell a ms rat // because if I declare placement new and delete, but // comment out the placement delete definition there are // no undefined symbols. void operator delete ( void * ); friend class timerQueue; }; using std :: type_info; class timerQueue : public epicsTimerQueue { public: timerQueue ( epicsTimerQueueNotify ¬ify ); virtual ~timerQueue (); epicsTimer & createTimer (); epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); double process ( const epicsTime & currentTime ); void show ( unsigned int level ) const; private: tsFreeList < timer, 0x20 > timerFreeList; tsFreeList < epicsTimerForC, 0x20 > timerForCFreeList; mutable epicsMutex mutex; epicsEvent cancelBlockingEvent; tsDLList < timer > timerList; epicsTimerQueueNotify & notify; timer * pExpireTmr; epicsThreadId processThread; epicsTime exceptMsgTimeStamp; bool cancelPending; static const double exceptMsgMinPeriod; void printExceptMsg ( const char * pName, const type_info & type ); timerQueue ( const timerQueue & ); timerQueue & operator = ( const timerQueue & ); friend class timer; friend struct epicsTimerForC; }; class timerQueueActiveMgrPrivate { public: timerQueueActiveMgrPrivate (); protected: virtual ~timerQueueActiveMgrPrivate () = 0; private: unsigned referenceCount; friend class timerQueueActiveMgr; }; class timerQueueActiveMgr; class timerQueueActive : public epicsTimerQueueActive, public epicsThreadRunable, public epicsTimerQueueNotify, public timerQueueActiveMgrPrivate { public: typedef epicsSingleton < timerQueueActiveMgr > :: reference RefMgr; timerQueueActive ( RefMgr &, bool okToShare, unsigned priority ); void start (); epicsTimer & createTimer (); epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); void show ( unsigned int level ) const; bool sharingOK () const; unsigned threadPriority () const; protected: ~timerQueueActive (); RefMgr _refMgr; private: timerQueue queue; epicsEvent rescheduleEvent; epicsEvent exitEvent; epicsThread thread; const double sleepQuantum; bool okToShare; int exitFlag; // use atomic ops bool terminateFlag; void run (); void reschedule (); double quantum (); void _printLastChanceExceptionMessage ( const char * pExceptionTypeName, const char * pExceptionContext ); epicsTimerQueue & getEpicsTimerQueue (); timerQueueActive ( const timerQueueActive & ); timerQueueActive & operator = ( const timerQueueActive & ); }; class timerQueueActiveMgr { public: typedef epicsSingleton < timerQueueActiveMgr > :: reference RefThis; timerQueueActiveMgr (); ~timerQueueActiveMgr (); epicsTimerQueueActiveForC & allocate ( RefThis &, bool okToShare, unsigned threadPriority = epicsThreadPriorityMin + 10 ); void release ( epicsTimerQueueActiveForC & ); private: epicsMutex mutex; tsDLList < epicsTimerQueueActiveForC > sharedQueueList; timerQueueActiveMgr ( const timerQueueActiveMgr & ); timerQueueActiveMgr & operator = ( const timerQueueActiveMgr & ); }; extern epicsSingleton < timerQueueActiveMgr > timerQueueMgrEPICS; class timerQueuePassive : public epicsTimerQueuePassive { public: timerQueuePassive ( epicsTimerQueueNotify & ); epicsTimer & createTimer (); epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); void show ( unsigned int level ) const; double process ( const epicsTime & currentTime ); protected: timerQueue queue; ~timerQueuePassive (); epicsTimerQueue & getEpicsTimerQueue (); timerQueuePassive ( const timerQueuePassive & ); timerQueuePassive & operator = ( const timerQueuePassive & ); }; struct epicsTimerQueuePassiveForC : public epicsTimerQueueNotify, public timerQueuePassive { public: epicsTimerQueuePassiveForC ( epicsTimerQueueNotifyReschedule, epicsTimerQueueNotifyQuantum, void * pPrivate ); void destroy (); protected: ~epicsTimerQueuePassiveForC (); private: epicsTimerQueueNotifyReschedule pRescheduleCallback; epicsTimerQueueNotifyQuantum pSleepQuantumCallback; void * pPrivate; static epicsSingleton < tsFreeList < epicsTimerQueuePassiveForC, 0x10 > > pFreeList; void reschedule (); double quantum (); }; struct epicsTimerQueueActiveForC : public timerQueueActive, public tsDLNode < epicsTimerQueueActiveForC > { public: epicsTimerQueueActiveForC ( RefMgr &, bool okToShare, unsigned priority ); void release (); void * operator new ( size_t ); void operator delete ( void * ); protected: virtual ~epicsTimerQueueActiveForC (); private: epicsTimerQueueActiveForC ( const epicsTimerQueueActiveForC & ); epicsTimerQueueActiveForC & operator = ( const epicsTimerQueueActiveForC & ); }; inline bool timerQueueActive::sharingOK () const { return this->okToShare; } inline unsigned timerQueueActive::threadPriority () const { return thread.getPriority (); } inline void * timer::operator new ( size_t size, tsFreeList < timer, 0x20 > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void timer::operator delete ( void * pCadaver, tsFreeList < timer, 0x20 > & freeList ) { freeList.release ( pCadaver ); } #endif inline void * epicsTimerForC::operator new ( size_t size, tsFreeList < epicsTimerForC, 0x20 > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void epicsTimerForC::operator delete ( void * pCadaver, tsFreeList < epicsTimerForC, 0x20 > & freeList ) { freeList.release ( pCadaver ); } #endif inline void * epicsTimerQueueActiveForC::operator new ( size_t size ) { return ::operator new ( size ); } inline void epicsTimerQueueActiveForC::operator delete ( void * pCadaver ) { ::operator delete ( pCadaver ); } #endif // epicsTimerPrivate_h base-7.0.3.1/modules/libcom/src/timer/timerQueue.cpp0000664000577000060420000001625613557101274021102 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include #define epicsExportSharedSymbols #include "epicsGuard.h" #include "timerPrivate.h" #include "errlog.h" const double timerQueue :: exceptMsgMinPeriod = 60.0 * 5.0; // seconds epicsTimerQueue::~epicsTimerQueue () {} timerQueue::timerQueue ( epicsTimerQueueNotify & notifyIn ) : mutex(__FILE__, __LINE__), notify ( notifyIn ), pExpireTmr ( 0 ), processThread ( 0 ), exceptMsgTimeStamp ( epicsTime :: getMonotonic () - exceptMsgMinPeriod ), cancelPending ( false ) { } timerQueue::~timerQueue () { timer *pTmr; while ( ( pTmr = this->timerList.get () ) ) { pTmr->curState = timer::stateLimbo; } } void timerQueue :: printExceptMsg ( const char * pName, const type_info & type ) { char date[64]; double delay; try { epicsTime cur = epicsTime :: getMonotonic (); delay = cur - this->exceptMsgTimeStamp; cur.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f" ); if ( delay >= exceptMsgMinPeriod ) { this->exceptMsgTimeStamp = cur; } } catch ( ... ) { delay = DBL_MAX; strcpy ( date, "UKN DATE" ); } if ( delay >= exceptMsgMinPeriod ) { // we dont touch the typeid for the timer expiration // notify interface here because they might have // destroyed the timer during its callback errlogPrintf ( "timerQueue: Unexpected C++ exception \"%s\" " "with type \"%s\" during timer expiration " "callback at %s\n", pName, type.name (), date ); errlogFlush (); } } double timerQueue::process ( const epicsTime & currentTime ) { epicsGuard < epicsMutex > guard ( this->mutex ); if ( this->pExpireTmr ) { // if some other thread is processing the queue // (or if this is a recursive call) timer * pTmr = this->timerList.first (); if ( pTmr ) { double delay = pTmr->exp - currentTime; if ( delay < 0.0 ) { delay = 0.0; } return delay; } else { return DBL_MAX; } } // // Tag current epired tmr so that we can detect if call back // is in progress when canceling the timer. // if ( this->timerList.first () ) { if ( currentTime >= this->timerList.first ()->exp ) { this->pExpireTmr = this->timerList.first (); this->timerList.remove ( *this->pExpireTmr ); this->pExpireTmr->curState = timer::stateActive; this->processThread = epicsThreadGetIdSelf (); # ifdef DEBUG this->pExpireTmr->show ( 0u ); # endif } else { double delay = this->timerList.first ()->exp - currentTime; debugPrintf ( ( "no activity process %f to next\n", delay ) ); return delay; } } else { return DBL_MAX; } # ifdef DEBUG unsigned N = 0u; # endif double delay = DBL_MAX; while ( true ) { epicsTimerNotify *pTmpNotify = this->pExpireTmr->pNotify; this->pExpireTmr->pNotify = 0; epicsTimerNotify::expireStatus expStat ( epicsTimerNotify::noRestart ); { epicsGuardRelease < epicsMutex > unguard ( guard ); debugPrintf ( ( "%5u expired \"%s\" with error %f sec\n", N++, typeid ( this->pExpireTmr->notify ).name (), currentTime - this->pExpireTmr->exp ) ); try { expStat = pTmpNotify->expire ( currentTime ); } catch ( std::exception & except ) { printExceptMsg ( except.what (), typeid ( except ) ); } catch ( ... ) { printExceptMsg ( "non-standard exception", typeid ( void ) ); } } // // only restart if they didnt cancel() the timer // while the call back was running // if ( this->cancelPending ) { // 1) if another thread is canceling then cancel() waits for // the event below // 2) if this thread is canceling in the timer callback then // dont touch timer or notify here because the cancel might // have occurred because they destroyed the timer in the // callback this->cancelPending = false; this->cancelBlockingEvent.signal (); } else { this->pExpireTmr->curState = timer::stateLimbo; if ( this->pExpireTmr->pNotify ) { // pNotify was cleared above so if it is valid now we know that // someone has started the timer from another thread and that // predominates over the restart parameters from expire. this->pExpireTmr->privateStart ( *this->pExpireTmr->pNotify, this->pExpireTmr->exp ); } else if ( expStat.restart() ) { // restart as nec this->pExpireTmr->privateStart ( *pTmpNotify, currentTime + expStat.expirationDelay() ); } } this->pExpireTmr = 0; if ( this->timerList.first () ) { if ( currentTime >= this->timerList.first ()->exp ) { this->pExpireTmr = this->timerList.first (); this->timerList.remove ( *this->pExpireTmr ); this->pExpireTmr->curState = timer::stateActive; # ifdef DEBUG this->pExpireTmr->show ( 0u ); # endif } else { delay = this->timerList.first ()->exp - currentTime; this->processThread = 0; break; } } else { this->processThread = 0; delay = DBL_MAX; break; } } return delay; } epicsTimer & timerQueue::createTimer () { return * new ( this->timerFreeList ) timer ( * this ); } epicsTimerForC & timerQueue::createTimerForC ( epicsTimerCallback pCallback, void *pArg ) { return * new ( this->timerForCFreeList ) epicsTimerForC ( *this, pCallback, pArg ); } void timerQueue::show ( unsigned level ) const { epicsGuard < epicsMutex > locker ( this->mutex ); printf ( "epicsTimerQueue with %u items pending\n", this->timerList.count () ); if ( level >= 1u ) { tsDLIterConst < timer > iter = this->timerList.firstIter (); while ( iter.valid () ) { iter->show ( level - 1u ); ++iter; } } } base-7.0.3.1/modules/libcom/src/timer/timerQueueActive.cpp0000664000577000060420000001111113557101274022217 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #define epicsExportSharedSymbols #include "epicsAtomic.h" #include "timerPrivate.h" #include "errlog.h" #ifdef _MSC_VER # pragma warning ( push ) # pragma warning ( disable:4660 ) #endif template class epicsSingleton < timerQueueActiveMgr >; #ifdef _MSC_VER # pragma warning ( pop ) #endif epicsSingleton < timerQueueActiveMgr > timerQueueMgrEPICS; epicsTimerQueueActive::~epicsTimerQueueActive () {} epicsTimerQueueActive & epicsTimerQueueActive::allocate ( bool okToShare, unsigned threadPriority ) { epicsSingleton < timerQueueActiveMgr >::reference pMgr = timerQueueMgrEPICS.getReference (); return pMgr->allocate ( pMgr, okToShare, threadPriority ); } timerQueueActive :: timerQueueActive ( RefMgr & refMgr, bool okToShareIn, unsigned priority ) : _refMgr ( refMgr ), queue ( *this ), thread ( *this, "timerQueue", epicsThreadGetStackSize ( epicsThreadStackMedium ), priority ), sleepQuantum ( epicsThreadSleepQuantum() ), okToShare ( okToShareIn ), exitFlag ( 0 ), terminateFlag ( false ) { } void timerQueueActive::start () { this->thread.start (); } timerQueueActive::~timerQueueActive () { this->terminateFlag = true; this->rescheduleEvent.signal (); while ( ! epics::atomic::get(this->exitFlag) ) { this->exitEvent.wait ( 1.0 ); } // in case other threads are waiting here also this->exitEvent.signal (); } void timerQueueActive :: _printLastChanceExceptionMessage ( const char * pExceptionTypeName, const char * pExceptionContext ) { char date[64]; try { epicsTime cur = epicsTime :: getMonotonic (); cur.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); } catch ( ... ) { strcpy ( date, "" ); } errlogPrintf ( "timerQueueActive: Unexpected C++ exception \"%s\" with type \"%s\" " "while processing timer queue, at %s\n", pExceptionContext, pExceptionTypeName, date ); } void timerQueueActive :: run () { epics::atomic::set(this->exitFlag, 0); while ( ! this->terminateFlag ) { try { double delay = this->queue.process ( epicsTime::getMonotonic() ); debugPrintf ( ( "timer thread sleeping for %g sec (max)\n", delay ) ); this->rescheduleEvent.wait ( delay ); } catch ( std :: exception & except ) { _printLastChanceExceptionMessage ( typeid ( except ).name (), except.what () ); epicsThreadSleep ( 10.0 ); } catch ( ... ) { _printLastChanceExceptionMessage ( "catch ( ... )", "Non-standard C++ exception" ); epicsThreadSleep ( 10.0 ); } } epics::atomic::set(this->exitFlag, 1); this->exitEvent.signal (); // no access to queue after exitEvent signal } epicsTimer & timerQueueActive::createTimer () { return this->queue.createTimer(); } epicsTimerForC & timerQueueActive::createTimerForC ( epicsTimerCallback pCallback, void * pArg ) { return this->queue.createTimerForC ( pCallback, pArg ); } void timerQueueActive::reschedule () { this->rescheduleEvent.signal (); } double timerQueueActive::quantum () { return this->sleepQuantum; } void timerQueueActive::show ( unsigned int level ) const { printf ( "EPICS threaded timer queue at %p\n", static_cast ( this ) ); if ( level > 0u ) { // specifying level one here avoids recursive // show callback this->thread.show ( 1u ); this->queue.show ( level - 1u ); printf ( "reschedule event\n" ); this->rescheduleEvent.show ( level - 1u ); printf ( "exit event\n" ); this->exitEvent.show ( level - 1u ); printf ( "exitFlag = %c, terminateFlag = %c\n", epics::atomic::get(this->exitFlag) ? 'T' : 'F', this->terminateFlag ? 'T' : 'F' ); } } epicsTimerQueue & timerQueueActive::getEpicsTimerQueue () { return static_cast < epicsTimerQueue &> ( * this ); } base-7.0.3.1/modules/libcom/src/timer/timerQueueActiveMgr.cpp0000664000577000060420000000521413557101274022674 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #define epicsExportSharedSymbols #include "epicsGuard.h" #include "timerPrivate.h" timerQueueActiveMgr::timerQueueActiveMgr () :mutex(__FILE__, __LINE__) { } timerQueueActiveMgr::~timerQueueActiveMgr () { epicsGuard < epicsMutex > locker ( this->mutex ); } epicsTimerQueueActiveForC & timerQueueActiveMgr :: allocate ( RefThis & refThis, bool okToShare, unsigned threadPriority ) { epicsGuard < epicsMutex > locker ( this->mutex ); if ( okToShare ) { tsDLIter < epicsTimerQueueActiveForC > iter = this->sharedQueueList.firstIter (); while ( iter.valid () ) { if ( iter->threadPriority () == threadPriority ) { assert ( iter->timerQueueActiveMgrPrivate::referenceCount < UINT_MAX ); iter->timerQueueActiveMgrPrivate::referenceCount++; return *iter; } iter++; } } epicsTimerQueueActiveForC & queue = * new epicsTimerQueueActiveForC ( refThis, okToShare, threadPriority ); queue.timerQueueActiveMgrPrivate::referenceCount = 1u; if ( okToShare ) { this->sharedQueueList.add ( queue ); } return queue; } void timerQueueActiveMgr :: release ( epicsTimerQueueActiveForC & queue ) { { epicsGuard < epicsMutex > locker ( this->mutex ); assert ( queue.timerQueueActiveMgrPrivate::referenceCount > 0u ); queue.timerQueueActiveMgrPrivate::referenceCount--; if ( queue.timerQueueActiveMgrPrivate::referenceCount > 0u ) { return; } else if ( queue.sharingOK () ) { this->sharedQueueList.remove ( queue ); } } // delete only after we release the guard in case the embedded // reference is the last one and this object is destroyed // as a side effect timerQueueActiveMgrPrivate * pPriv = & queue; delete pPriv; } timerQueueActiveMgrPrivate::timerQueueActiveMgrPrivate () : referenceCount ( 0u ) { } timerQueueActiveMgrPrivate::~timerQueueActiveMgrPrivate () { } base-7.0.3.1/modules/libcom/src/timer/timerQueuePassive.cpp0000664000577000060420000000403313557101274022423 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ // // Note, a free list for this class is not currently used because of // entanglements between the file scope free list destructor and a // file scope fdManager destructor which is trying to call a // destructor for a passive timer queue which is no longer valid // in pool. // #include #define epicsExportSharedSymbols #include "timerPrivate.h" epicsTimerQueuePassive::~epicsTimerQueuePassive () {} epicsTimerQueuePassive & epicsTimerQueuePassive::create ( epicsTimerQueueNotify ¬ify ) { return * new timerQueuePassive ( notify ); } timerQueuePassive::timerQueuePassive ( epicsTimerQueueNotify ¬ifyIn ) : queue ( notifyIn ) {} timerQueuePassive::~timerQueuePassive () {} epicsTimer & timerQueuePassive::createTimer () { return this->queue.createTimer (); } epicsTimerForC & timerQueuePassive::createTimerForC ( epicsTimerCallback pCallback, void * pArg ) { return this->queue.createTimerForC ( pCallback, pArg ); } double timerQueuePassive::process ( const epicsTime & currentTime ) { return this->queue.process ( currentTime ); } void timerQueuePassive::show ( unsigned int level ) const { printf ( "EPICS non-threaded timer queue at %p\n", static_cast ( this ) ); if ( level >=1u ) { this->queue.show ( level - 1u ); } } epicsTimerQueue & timerQueuePassive::getEpicsTimerQueue () { return static_cast < epicsTimerQueue &> ( * this ); } base-7.0.3.1/modules/libcom/src/valgrind/valgrind.h0000775000577000060420000136731313557101274020725 0ustar anjaesctl/* -*- c -*- ---------------------------------------------------------------- Notice that the following BSD-style license applies to this one file (valgrind.h) only. The rest of Valgrind is licensed under the terms of the GNU General Public License, version 2, unless otherwise indicated. See the COPYING file in the source distribution for details. ---------------------------------------------------------------- This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2000-2013 Julian Seward. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 4. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------- Notice that the above BSD-style license applies to this one file (valgrind.h) only. The entire rest of Valgrind is licensed under the terms of the GNU General Public License, version 2. See the COPYING file in the source distribution for details. ---------------------------------------------------------------- */ /* This file is for inclusion into client (your!) code. You can use these macros to manipulate and query Valgrind's execution inside your own programs. The resulting executables will still run without Valgrind, just a little bit more slowly than they otherwise would, but otherwise unchanged. When not running on valgrind, each client request consumes very few (eg. 7) instructions, so the resulting performance loss is negligible unless you plan to execute client requests millions of times per second. Nevertheless, if that is still a problem, you can compile with the NVALGRIND symbol defined (gcc -DNVALGRIND) so that client requests are not even compiled in. */ #ifndef __VALGRIND_H #define __VALGRIND_H /* ------------------------------------------------------------------ */ /* VERSION NUMBER OF VALGRIND */ /* ------------------------------------------------------------------ */ /* Specify Valgrind's version number, so that user code can conditionally compile based on our version number. Note that these were introduced at version 3.6 and so do not exist in version 3.5 or earlier. The recommended way to use them to check for "version X.Y or later" is (eg) #if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__) \ && (__VALGRIND_MAJOR__ > 3 \ || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6)) */ #define __VALGRIND_MAJOR__ 3 #define __VALGRIND_MINOR__ 10 #include /* Nb: this file might be included in a file compiled with -ansi. So we can't use C++ style "//" comments nor the "asm" keyword (instead use "__asm__"). */ /* Derive some tags indicating what the target platform is. Note that in this file we're using the compiler's CPP symbols for identifying architectures, which are different to the ones we use within the rest of Valgrind. Note, __powerpc__ is active for both 32 and 64-bit PPC, whereas __powerpc64__ is only active for the latter (on Linux, that is). Misc note: how to find out what's predefined in gcc by default: gcc -Wp,-dM somefile.c */ #undef PLAT_x86_darwin #undef PLAT_amd64_darwin #undef PLAT_x86_win32 #undef PLAT_amd64_win64 #undef PLAT_x86_linux #undef PLAT_amd64_linux #undef PLAT_ppc32_linux #undef PLAT_ppc64be_linux #undef PLAT_ppc64le_linux #undef PLAT_arm_linux #undef PLAT_arm64_linux #undef PLAT_s390x_linux #undef PLAT_mips32_linux #undef PLAT_mips64_linux #if defined(__APPLE__) && defined(__i386__) # define PLAT_x86_darwin 1 #elif defined(__APPLE__) && defined(__x86_64__) # define PLAT_amd64_darwin 1 #elif (defined(__MINGW32__) && !defined(__MINGW64__)) \ || defined(__CYGWIN32__) \ || (defined(_WIN32) && defined(_M_IX86) && defined(__GNUC__)) # define PLAT_x86_win32 1 #elif defined(__MINGW64__) \ || (defined(_WIN64) && defined(_M_X64) && defined(__GNUC__)) # define PLAT_amd64_win64 1 #elif defined(__linux__) && defined(__i386__) # define PLAT_x86_linux 1 #elif defined(__linux__) && defined(__x86_64__) # define PLAT_amd64_linux 1 #elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__) # define PLAT_ppc32_linux 1 #elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF != 2 /* Big Endian uses ELF version 1 */ # define PLAT_ppc64be_linux 1 #elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF == 2 /* Little Endian uses ELF version 2 */ # define PLAT_ppc64le_linux 1 #elif defined(__linux__) && defined(__arm__) && !defined(__aarch64__) # define PLAT_arm_linux 1 #elif defined(__linux__) && defined(__aarch64__) && !defined(__arm__) # define PLAT_arm64_linux 1 #elif defined(__linux__) && defined(__s390__) && defined(__s390x__) # define PLAT_s390x_linux 1 #elif defined(__linux__) && defined(__mips__) && (__mips==64) # define PLAT_mips64_linux 1 #elif defined(__linux__) && defined(__mips__) && (__mips!=64) # define PLAT_mips32_linux 1 #else /* If we're not compiling for our target platform, don't generate any inline asms. */ # if !defined(NVALGRIND) # define NVALGRIND 1 # endif #endif /* ------------------------------------------------------------------ */ /* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */ /* in here of use to end-users -- skip to the next section. */ /* ------------------------------------------------------------------ */ /* * VALGRIND_DO_CLIENT_REQUEST(): a statement that invokes a Valgrind client * request. Accepts both pointers and integers as arguments. * * VALGRIND_DO_CLIENT_REQUEST_STMT(): a statement that invokes a Valgrind * client request that does not return a value. * VALGRIND_DO_CLIENT_REQUEST_EXPR(): a C expression that invokes a Valgrind * client request and whose value equals the client request result. Accepts * both pointers and integers as arguments. Note that such calls are not * necessarily pure functions -- they may have side effects. */ #define VALGRIND_DO_CLIENT_REQUEST(_zzq_rlval, _zzq_default, \ _zzq_request, _zzq_arg1, _zzq_arg2, \ _zzq_arg3, _zzq_arg4, _zzq_arg5) \ do { (_zzq_rlval) = VALGRIND_DO_CLIENT_REQUEST_EXPR((_zzq_default), \ (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) #define VALGRIND_DO_CLIENT_REQUEST_STMT(_zzq_request, _zzq_arg1, \ _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ do { (void) VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) #if defined(NVALGRIND) /* Define NVALGRIND to completely remove the Valgrind magic sequence from the compiled code (analogous to NDEBUG's effects on assert()) */ #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ (_zzq_default) #else /* ! NVALGRIND */ /* The following defines the magic code sequences which the JITter spots and handles magically. Don't look too closely at them as they will rot your brain. The assembly code sequences for all architectures is in this one file. This is because this file must be stand-alone, and we don't want to have multiple files. For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default value gets put in the return slot, so that everything works when this is executed not under Valgrind. Args are passed in a memory block, and so there's no intrinsic limit to the number that could be passed, but it's currently five. The macro args are: _zzq_rlval result lvalue _zzq_default default value (result returned when running on real CPU) _zzq_request request code _zzq_arg1..5 request params The other two macros are used to support function wrapping, and are a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the guest's NRADDR pseudo-register and whatever other information is needed to safely run the call original from the wrapper: on ppc64-linux, the R2 value at the divert point is also needed. This information is abstracted into a user-visible type, OrigFn. VALGRIND_CALL_NOREDIR_* behaves the same as the following on the guest, but guarantees that the branch instruction will not be redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64: branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a complete inline asm, since it needs to be combined with more magic inline asm stuff to be useful. */ /* ------------------------- x86-{linux,darwin} ---------------- */ #if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ || (defined(PLAT_x86_win32) && defined(__GNUC__)) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "roll $3, %%edi ; roll $13, %%edi\n\t" \ "roll $29, %%edi ; roll $19, %%edi\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({volatile unsigned int _zzq_args[6]; \ volatile unsigned int _zzq_result; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %EDX = client_request ( %EAX ) */ \ "xchgl %%ebx,%%ebx" \ : "=d" (_zzq_result) \ : "a" (&_zzq_args[0]), "0" (_zzq_default) \ : "cc", "memory" \ ); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %EAX = guest_NRADDR */ \ "xchgl %%ecx,%%ecx" \ : "=a" (__addr) \ : \ : "cc", "memory" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_EAX \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir *%EAX */ \ "xchgl %%edx,%%edx\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "xchgl %%edi,%%edi\n\t" \ : : : "cc", "memory" \ ); \ } while (0) #endif /* PLAT_x86_linux || PLAT_x86_darwin || (PLAT_x86_win32 && __GNUC__) */ /* ------------------------- x86-Win32 ------------------------- */ #if defined(PLAT_x86_win32) && !defined(__GNUC__) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #if defined(_MSC_VER) #define __SPECIAL_INSTRUCTION_PREAMBLE \ __asm rol edi, 3 __asm rol edi, 13 \ __asm rol edi, 29 __asm rol edi, 19 #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ valgrind_do_client_request_expr((uintptr_t)(_zzq_default), \ (uintptr_t)(_zzq_request), (uintptr_t)(_zzq_arg1), \ (uintptr_t)(_zzq_arg2), (uintptr_t)(_zzq_arg3), \ (uintptr_t)(_zzq_arg4), (uintptr_t)(_zzq_arg5)) static __inline uintptr_t valgrind_do_client_request_expr(uintptr_t _zzq_default, uintptr_t _zzq_request, uintptr_t _zzq_arg1, uintptr_t _zzq_arg2, uintptr_t _zzq_arg3, uintptr_t _zzq_arg4, uintptr_t _zzq_arg5) { volatile uintptr_t _zzq_args[6]; volatile unsigned int _zzq_result; _zzq_args[0] = (uintptr_t)(_zzq_request); _zzq_args[1] = (uintptr_t)(_zzq_arg1); _zzq_args[2] = (uintptr_t)(_zzq_arg2); _zzq_args[3] = (uintptr_t)(_zzq_arg3); _zzq_args[4] = (uintptr_t)(_zzq_arg4); _zzq_args[5] = (uintptr_t)(_zzq_arg5); __asm { __asm lea eax, _zzq_args __asm mov edx, _zzq_default __SPECIAL_INSTRUCTION_PREAMBLE /* %EDX = client_request ( %EAX ) */ __asm xchg ebx,ebx __asm mov _zzq_result, edx } return _zzq_result; } #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned int __addr; \ __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ /* %EAX = guest_NRADDR */ \ __asm xchg ecx,ecx \ __asm mov __addr, eax \ } \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_EAX ERROR #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ __asm xchg edi,edi \ } \ } while (0) #else #error Unsupported compiler. #endif #endif /* PLAT_x86_win32 */ /* ------------------------ amd64-{linux,darwin} --------------- */ #if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \ || (defined(PLAT_amd64_win64) && defined(__GNUC__)) typedef struct { unsigned long long int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \ "rolq $61, %%rdi ; rolq $51, %%rdi\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({ volatile unsigned long long int _zzq_args[6]; \ volatile unsigned long long int _zzq_result; \ _zzq_args[0] = (unsigned long long int)(_zzq_request); \ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %RDX = client_request ( %RAX ) */ \ "xchgq %%rbx,%%rbx" \ : "=d" (_zzq_result) \ : "a" (&_zzq_args[0]), "0" (_zzq_default) \ : "cc", "memory" \ ); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned long long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %RAX = guest_NRADDR */ \ "xchgq %%rcx,%%rcx" \ : "=a" (__addr) \ : \ : "cc", "memory" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_RAX \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir *%RAX */ \ "xchgq %%rdx,%%rdx\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "xchgq %%rdi,%%rdi\n\t" \ : : : "cc", "memory" \ ); \ } while (0) #endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ /* ------------------------- amd64-Win64 ------------------------- */ #if defined(PLAT_amd64_win64) && !defined(__GNUC__) #error Unsupported compiler. #endif /* PLAT_amd64_win64 */ /* ------------------------ ppc32-linux ------------------------ */ #if defined(PLAT_ppc32_linux) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rlwinm 0,0,3,0,31 ; rlwinm 0,0,13,0,31\n\t" \ "rlwinm 0,0,29,0,31 ; rlwinm 0,0,19,0,31\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({ unsigned int _zzq_args[6]; \ unsigned int _zzq_result; \ unsigned int* _zzq_ptr; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ _zzq_ptr = _zzq_args; \ __asm__ volatile("mr 3,%1\n\t" /*default*/ \ "mr 4,%2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = client_request ( %R4 ) */ \ "or 1,1,1\n\t" \ "mr %0,3" /*result*/ \ : "=b" (_zzq_result) \ : "b" (_zzq_default), "b" (_zzq_ptr) \ : "cc", "memory", "r3", "r4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR */ \ "or 2,2,2\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R11 */ \ "or 3,3,3\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or 5,5,5\n\t" \ ); \ } while (0) #endif /* PLAT_ppc32_linux */ /* ------------------------ ppc64-linux ------------------------ */ #if defined(PLAT_ppc64be_linux) typedef struct { unsigned long long int nraddr; /* where's the code? */ unsigned long long int r2; /* what tocptr do we need? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ "rotldi 0,0,61 ; rotldi 0,0,51\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({ unsigned long long int _zzq_args[6]; \ unsigned long long int _zzq_result; \ unsigned long long int* _zzq_ptr; \ _zzq_args[0] = (unsigned long long int)(_zzq_request); \ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ _zzq_ptr = _zzq_args; \ __asm__ volatile("mr 3,%1\n\t" /*default*/ \ "mr 4,%2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = client_request ( %R4 ) */ \ "or 1,1,1\n\t" \ "mr %0,3" /*result*/ \ : "=b" (_zzq_result) \ : "b" (_zzq_default), "b" (_zzq_ptr) \ : "cc", "memory", "r3", "r4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned long long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR */ \ "or 2,2,2\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->nraddr = __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR_GPR2 */ \ "or 4,4,4\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->r2 = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R11 */ \ "or 3,3,3\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or 5,5,5\n\t" \ ); \ } while (0) #endif /* PLAT_ppc64be_linux */ #if defined(PLAT_ppc64le_linux) typedef struct { unsigned long long int nraddr; /* where's the code? */ unsigned long long int r2; /* what tocptr do we need? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ "rotldi 0,0,61 ; rotldi 0,0,51\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({ unsigned long long int _zzq_args[6]; \ unsigned long long int _zzq_result; \ unsigned long long int* _zzq_ptr; \ _zzq_args[0] = (unsigned long long int)(_zzq_request); \ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ _zzq_ptr = _zzq_args; \ __asm__ volatile("mr 3,%1\n\t" /*default*/ \ "mr 4,%2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = client_request ( %R4 ) */ \ "or 1,1,1\n\t" \ "mr %0,3" /*result*/ \ : "=b" (_zzq_result) \ : "b" (_zzq_default), "b" (_zzq_ptr) \ : "cc", "memory", "r3", "r4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned long long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR */ \ "or 2,2,2\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->nraddr = __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR_GPR2 */ \ "or 4,4,4\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->r2 = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R12 */ \ "or 3,3,3\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or 5,5,5\n\t" \ ); \ } while (0) #endif /* PLAT_ppc64le_linux */ /* ------------------------- arm-linux ------------------------- */ #if defined(PLAT_arm_linux) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \ "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({volatile unsigned int _zzq_args[6]; \ volatile unsigned int _zzq_result; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ __asm__ volatile("mov r3, %1\n\t" /*default*/ \ "mov r4, %2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* R3 = client_request ( R4 ) */ \ "orr r10, r10, r10\n\t" \ "mov %0, r3" /*result*/ \ : "=r" (_zzq_result) \ : "r" (_zzq_default), "r" (&_zzq_args[0]) \ : "cc","memory", "r3", "r4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* R3 = guest_NRADDR */ \ "orr r11, r11, r11\n\t" \ "mov %0, r3" \ : "=r" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R4 */ \ "orr r12, r12, r12\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "orr r9, r9, r9\n\t" \ : : : "cc", "memory" \ ); \ } while (0) #endif /* PLAT_arm_linux */ /* ------------------------ arm64-linux ------------------------- */ #if defined(PLAT_arm64_linux) typedef struct { unsigned long long int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "ror x12, x12, #3 ; ror x12, x12, #13 \n\t" \ "ror x12, x12, #51 ; ror x12, x12, #61 \n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({volatile unsigned long long int _zzq_args[6]; \ volatile unsigned long long int _zzq_result; \ _zzq_args[0] = (unsigned long long int)(_zzq_request); \ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ __asm__ volatile("mov x3, %1\n\t" /*default*/ \ "mov x4, %2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* X3 = client_request ( X4 ) */ \ "orr x10, x10, x10\n\t" \ "mov %0, x3" /*result*/ \ : "=r" (_zzq_result) \ : "r" (_zzq_default), "r" (&_zzq_args[0]) \ : "cc","memory", "x3", "x4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned long long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* X3 = guest_NRADDR */ \ "orr x11, x11, x11\n\t" \ "mov %0, x3" \ : "=r" (__addr) \ : \ : "cc", "memory", "x3" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir X8 */ \ "orr x12, x12, x12\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "orr x9, x9, x9\n\t" \ : : : "cc", "memory" \ ); \ } while (0) #endif /* PLAT_arm64_linux */ /* ------------------------ s390x-linux ------------------------ */ #if defined(PLAT_s390x_linux) typedef struct { unsigned long long int nraddr; /* where's the code? */ } OrigFn; /* __SPECIAL_INSTRUCTION_PREAMBLE will be used to identify Valgrind specific * code. This detection is implemented in platform specific toIR.c * (e.g. VEX/priv/guest_s390_decoder.c). */ #define __SPECIAL_INSTRUCTION_PREAMBLE \ "lr 15,15\n\t" \ "lr 1,1\n\t" \ "lr 2,2\n\t" \ "lr 3,3\n\t" #define __CLIENT_REQUEST_CODE "lr 2,2\n\t" #define __GET_NR_CONTEXT_CODE "lr 3,3\n\t" #define __CALL_NO_REDIR_CODE "lr 4,4\n\t" #define __VEX_INJECT_IR_CODE "lr 5,5\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({volatile unsigned long long int _zzq_args[6]; \ volatile unsigned long long int _zzq_result; \ _zzq_args[0] = (unsigned long long int)(_zzq_request); \ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ __asm__ volatile(/* r2 = args */ \ "lgr 2,%1\n\t" \ /* r3 = default */ \ "lgr 3,%2\n\t" \ __SPECIAL_INSTRUCTION_PREAMBLE \ __CLIENT_REQUEST_CODE \ /* results = r3 */ \ "lgr %0, 3\n\t" \ : "=d" (_zzq_result) \ : "a" (&_zzq_args[0]), "0" (_zzq_default) \ : "cc", "2", "3", "memory" \ ); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned long long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ __GET_NR_CONTEXT_CODE \ "lgr %0, 3\n\t" \ : "=a" (__addr) \ : \ : "cc", "3", "memory" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_R1 \ __SPECIAL_INSTRUCTION_PREAMBLE \ __CALL_NO_REDIR_CODE #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ __VEX_INJECT_IR_CODE); \ } while (0) #endif /* PLAT_s390x_linux */ /* ------------------------- mips32-linux ---------------- */ #if defined(PLAT_mips32_linux) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; /* .word 0x342 * .word 0x742 * .word 0xC2 * .word 0x4C2*/ #define __SPECIAL_INSTRUCTION_PREAMBLE \ "srl $0, $0, 13\n\t" \ "srl $0, $0, 29\n\t" \ "srl $0, $0, 3\n\t" \ "srl $0, $0, 19\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({ volatile unsigned int _zzq_args[6]; \ volatile unsigned int _zzq_result; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ __asm__ volatile("move $11, %1\n\t" /*default*/ \ "move $12, %2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* T3 = client_request ( T4 ) */ \ "or $13, $13, $13\n\t" \ "move %0, $11\n\t" /*result*/ \ : "=r" (_zzq_result) \ : "r" (_zzq_default), "r" (&_zzq_args[0]) \ : "$11", "$12"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %t9 = guest_NRADDR */ \ "or $14, $14, $14\n\t" \ "move %0, $11" /*result*/ \ : "=r" (__addr) \ : \ : "$11" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_T9 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir *%t9 */ \ "or $15, $15, $15\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or $11, $11, $11\n\t" \ ); \ } while (0) #endif /* PLAT_mips32_linux */ /* ------------------------- mips64-linux ---------------- */ #if defined(PLAT_mips64_linux) typedef struct { unsigned long long nraddr; /* where's the code? */ } OrigFn; /* dsll $0,$0, 3 * dsll $0,$0, 13 * dsll $0,$0, 29 * dsll $0,$0, 19*/ #define __SPECIAL_INSTRUCTION_PREAMBLE \ "dsll $0,$0, 3 ; dsll $0,$0,13\n\t" \ "dsll $0,$0,29 ; dsll $0,$0,19\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({ volatile unsigned long long int _zzq_args[6]; \ volatile unsigned long long int _zzq_result; \ _zzq_args[0] = (unsigned long long int)(_zzq_request); \ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ __asm__ volatile("move $11, %1\n\t" /*default*/ \ "move $12, %2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* $11 = client_request ( $12 ) */ \ "or $13, $13, $13\n\t" \ "move %0, $11\n\t" /*result*/ \ : "=r" (_zzq_result) \ : "r" (_zzq_default), "r" (&_zzq_args[0]) \ : "$11", "$12"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned long long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* $11 = guest_NRADDR */ \ "or $14, $14, $14\n\t" \ "move %0, $11" /*result*/ \ : "=r" (__addr) \ : \ : "$11"); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_T9 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir $25 */ \ "or $15, $15, $15\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or $11, $11, $11\n\t" \ ); \ } while (0) #endif /* PLAT_mips64_linux */ /* Insert assembly code for other platforms here... */ #endif /* NVALGRIND */ /* ------------------------------------------------------------------ */ /* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */ /* ugly. It's the least-worst tradeoff I can think of. */ /* ------------------------------------------------------------------ */ /* This section defines magic (a.k.a appalling-hack) macros for doing guaranteed-no-redirection macros, so as to get from function wrappers to the functions they are wrapping. The whole point is to construct standard call sequences, but to do the call itself with a special no-redirect call pseudo-instruction that the JIT understands and handles specially. This section is long and repetitious, and I can't see a way to make it shorter. The naming scheme is as follows: CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc} 'W' stands for "word" and 'v' for "void". Hence there are different macros for calling arity 0, 1, 2, 3, 4, etc, functions, and for each, the possibility of returning a word-typed result, or no result. */ /* Use these to write the name of your wrapper. NOTE: duplicates VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. NOTE also: inserts the default behaviour equivalance class tag "0000" into the name. See pub_tool_redir.h for details -- normally you don't need to think about this, though. */ /* Use an extra level of macroisation so as to ensure the soname/fnname args are fully macro-expanded before pasting them together. */ #define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd #define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \ VG_CONCAT4(_vgw00000ZU_,soname,_,fnname) #define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \ VG_CONCAT4(_vgw00000ZZ_,soname,_,fnname) /* Use this macro from within a wrapper function to collect the context (address and possibly other info) of the original function. Once you have that you can then use it in one of the CALL_FN_ macros. The type of the argument _lval is OrigFn. */ #define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval) /* Also provide end-user facilities for function replacement, rather than wrapping. A replacement function differs from a wrapper in that it has no way to get hold of the original function being called, and hence no way to call onwards to it. In a replacement function, VALGRIND_GET_ORIG_FN always returns zero. */ #define I_REPLACE_SONAME_FNNAME_ZU(soname,fnname) \ VG_CONCAT4(_vgr00000ZU_,soname,_,fnname) #define I_REPLACE_SONAME_FNNAME_ZZ(soname,fnname) \ VG_CONCAT4(_vgr00000ZZ_,soname,_,fnname) /* Derivatives of the main macros below, for calling functions returning void. */ #define CALL_FN_v_v(fnptr) \ do { volatile unsigned long _junk; \ CALL_FN_W_v(_junk,fnptr); } while (0) #define CALL_FN_v_W(fnptr, arg1) \ do { volatile unsigned long _junk; \ CALL_FN_W_W(_junk,fnptr,arg1); } while (0) #define CALL_FN_v_WW(fnptr, arg1,arg2) \ do { volatile unsigned long _junk; \ CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0) #define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \ do { volatile unsigned long _junk; \ CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0) #define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \ do { volatile unsigned long _junk; \ CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0) #define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \ do { volatile unsigned long _junk; \ CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0) #define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \ do { volatile unsigned long _junk; \ CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0) #define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \ do { volatile unsigned long _junk; \ CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0) /* ------------------------- x86-{linux,darwin} ---------------- */ #if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) /* These regs are trashed by the hidden call. No need to mention eax as gcc can already see that, plus causes gcc to bomb. */ #define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "movl %%esp,%%edi\n\t" \ "andl $0xfffffff0,%%esp\n\t" #define VALGRIND_RESTORE_STACK \ "movl %%edi,%%esp\n\t" /* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $12, %%esp\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $8, %%esp\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $4, %%esp\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $12, %%esp\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $8, %%esp\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $4, %%esp\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $12, %%esp\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $8, %%esp\n\t" \ "pushl 40(%%eax)\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $4, %%esp\n\t" \ "pushl 44(%%eax)\n\t" \ "pushl 40(%%eax)\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "pushl 48(%%eax)\n\t" \ "pushl 44(%%eax)\n\t" \ "pushl 40(%%eax)\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_x86_linux || PLAT_x86_darwin */ /* ------------------------ amd64-{linux,darwin} --------------- */ #if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) /* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \ "rdi", "r8", "r9", "r10", "r11" /* This is all pretty complex. It's so as to make stack unwinding work reliably. See bug 243270. The basic problem is the sub and add of 128 of %rsp in all of the following macros. If gcc believes the CFA is in %rsp, then unwinding may fail, because what's at the CFA is not what gcc "expected" when it constructs the CFIs for the places where the macros are instantiated. But we can't just add a CFI annotation to increase the CFA offset by 128, to match the sub of 128 from %rsp, because we don't know whether gcc has chosen %rsp as the CFA at that point, or whether it has chosen some other register (eg, %rbp). In the latter case, adding a CFI annotation to change the CFA offset is simply wrong. So the solution is to get hold of the CFA using __builtin_dwarf_cfa(), put it in a known register, and add a CFI annotation to say what the register is. We choose %rbp for this (perhaps perversely), because: (1) %rbp is already subject to unwinding. If a new register was chosen then the unwinder would have to unwind it in all stack traces, which is expensive, and (2) %rbp is already subject to precise exception updates in the JIT. If a new register was chosen, we'd have to have precise exceptions for it too, which reduces performance of the generated code. However .. one extra complication. We can't just whack the result of __builtin_dwarf_cfa() into %rbp and then add %rbp to the list of trashed registers at the end of the inline assembly fragments; gcc won't allow %rbp to appear in that list. Hence instead we need to stash %rbp in %r15 for the duration of the asm, and say that %r15 is trashed instead. gcc seems happy to go with that. Oh .. and this all needs to be conditionalised so that it is unchanged from before this commit, when compiled with older gccs that don't support __builtin_dwarf_cfa. Furthermore, since this header file is freestanding, it has to be independent of config.h, and so the following conditionalisation cannot depend on configure time checks. Although it's not clear from 'defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)', this expression excludes Darwin. .cfi directives in Darwin assembly appear to be completely different and I haven't investigated how they work. For even more entertainment value, note we have to use the completely undocumented __builtin_dwarf_cfa(), which appears to really compute the CFA, whereas __builtin_frame_address(0) claims to but actually doesn't. See https://bugs.kde.org/show_bug.cgi?id=243270#c47 */ #if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) # define __FRAME_POINTER \ ,"r"(__builtin_dwarf_cfa()) # define VALGRIND_CFI_PROLOGUE \ "movq %%rbp, %%r15\n\t" \ "movq %2, %%rbp\n\t" \ ".cfi_remember_state\n\t" \ ".cfi_def_cfa rbp, 0\n\t" # define VALGRIND_CFI_EPILOGUE \ "movq %%r15, %%rbp\n\t" \ ".cfi_restore_state\n\t" #else # define __FRAME_POINTER # define VALGRIND_CFI_PROLOGUE # define VALGRIND_CFI_EPILOGUE #endif /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "movq %%rsp,%%r14\n\t" \ "andq $0xfffffffffffffff0,%%rsp\n\t" #define VALGRIND_RESTORE_STACK \ "movq %%r14,%%rsp\n\t" /* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned long) == 8. */ /* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_ macros. In order not to trash the stack redzone, we need to drop %rsp by 128 before the hidden call, and restore afterwards. The nastyness is that it is only by luck that the stack still appears to be unwindable during the hidden call - since then the behaviour of any routine using this macro does not match what the CFI data says. Sigh. Why is this important? Imagine that a wrapper has a stack allocated local, and passes to the hidden call, a pointer to it. Because gcc does not know about the hidden call, it may allocate that local in the redzone. Unfortunately the hidden call may then trash it before it comes to use it. So we must step clear of the redzone, for the duration of the hidden call, to make it safe. Probably the same problem afflicts the other redzone-style ABIs too (ppc64-linux); but for those, the stack is self describing (none of this CFI nonsense) so at least messing with the stack pointer doesn't give a danger of non-unwindable stack. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $136,%%rsp\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $136,%%rsp\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "pushq 80(%%rax)\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $136,%%rsp\n\t" \ "pushq 88(%%rax)\n\t" \ "pushq 80(%%rax)\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "pushq 96(%%rax)\n\t" \ "pushq 88(%%rax)\n\t" \ "pushq 80(%%rax)\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ /* ------------------------ ppc32-linux ------------------------ */ #if defined(PLAT_ppc32_linux) /* This is useful for finding out about the on-stack stuff: extern int f9 ( int,int,int,int,int,int,int,int,int ); extern int f10 ( int,int,int,int,int,int,int,int,int,int ); extern int f11 ( int,int,int,int,int,int,int,int,int,int,int ); extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int ); int g9 ( void ) { return f9(11,22,33,44,55,66,77,88,99); } int g10 ( void ) { return f10(11,22,33,44,55,66,77,88,99,110); } int g11 ( void ) { return f11(11,22,33,44,55,66,77,88,99,110,121); } int g12 ( void ) { return f12(11,22,33,44,55,66,77,88,99,110,121,132); } */ /* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "lr", "ctr", "xer", \ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ "r11", "r12", "r13" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "mr 28,1\n\t" \ "rlwinm 1,1,0,0,27\n\t" #define VALGRIND_RESTORE_STACK \ "mr 1,28\n\t" /* These CALL_FN_ macros assume that on ppc32-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "addi 1,1,-16\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "addi 1,1,-16\n\t" \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,12(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ _argvec[11] = (unsigned long)arg11; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "addi 1,1,-32\n\t" \ /* arg11 */ \ "lwz 3,44(11)\n\t" \ "stw 3,16(1)\n\t" \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,12(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ _argvec[11] = (unsigned long)arg11; \ _argvec[12] = (unsigned long)arg12; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "addi 1,1,-32\n\t" \ /* arg12 */ \ "lwz 3,48(11)\n\t" \ "stw 3,20(1)\n\t" \ /* arg11 */ \ "lwz 3,44(11)\n\t" \ "stw 3,16(1)\n\t" \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,12(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_ppc32_linux */ /* ------------------------ ppc64-linux ------------------------ */ #if defined(PLAT_ppc64be_linux) /* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "lr", "ctr", "xer", \ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ "r11", "r12", "r13" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "mr 28,1\n\t" \ "rldicr 1,1,0,59\n\t" #define VALGRIND_RESTORE_STACK \ "mr 1,28\n\t" /* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned long) == 8. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+0]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+1]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+2]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+3]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+4]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+5]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+6]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+7]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+8]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+9]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-128\n\t" /* expand stack frame */ \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+10]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-128\n\t" /* expand stack frame */ \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+11]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-144\n\t" /* expand stack frame */ \ /* arg11 */ \ "ld 3,88(11)\n\t" \ "std 3,128(1)\n\t" \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+12]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ _argvec[2+12] = (unsigned long)arg12; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-144\n\t" /* expand stack frame */ \ /* arg12 */ \ "ld 3,96(11)\n\t" \ "std 3,136(1)\n\t" \ /* arg11 */ \ "ld 3,88(11)\n\t" \ "std 3,128(1)\n\t" \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_ppc64be_linux */ /* ------------------------- ppc64le-linux ----------------------- */ #if defined(PLAT_ppc64le_linux) /* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "lr", "ctr", "xer", \ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ "r11", "r12", "r13" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "mr 28,1\n\t" \ "rldicr 1,1,0,59\n\t" #define VALGRIND_RESTORE_STACK \ "mr 1,28\n\t" /* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned long) == 8. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+0]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+1]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+2]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+3]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+4]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+5]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+6]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+7]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+8]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+9]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-128\n\t" /* expand stack frame */ \ /* arg9 */ \ "ld 3,72(12)\n\t" \ "std 3,96(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+10]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-128\n\t" /* expand stack frame */ \ /* arg10 */ \ "ld 3,80(12)\n\t" \ "std 3,104(1)\n\t" \ /* arg9 */ \ "ld 3,72(12)\n\t" \ "std 3,96(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+11]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-144\n\t" /* expand stack frame */ \ /* arg11 */ \ "ld 3,88(12)\n\t" \ "std 3,112(1)\n\t" \ /* arg10 */ \ "ld 3,80(12)\n\t" \ "std 3,104(1)\n\t" \ /* arg9 */ \ "ld 3,72(12)\n\t" \ "std 3,96(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+12]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ _argvec[2+12] = (unsigned long)arg12; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-144\n\t" /* expand stack frame */ \ /* arg12 */ \ "ld 3,96(12)\n\t" \ "std 3,120(1)\n\t" \ /* arg11 */ \ "ld 3,88(12)\n\t" \ "std 3,112(1)\n\t" \ /* arg10 */ \ "ld 3,80(12)\n\t" \ "std 3,104(1)\n\t" \ /* arg9 */ \ "ld 3,72(12)\n\t" \ "std 3,96(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_ppc64le_linux */ /* ------------------------- arm-linux ------------------------- */ #if defined(PLAT_arm_linux) /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4","r14" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ /* This is a bit tricky. We store the original stack pointer in r10 as it is callee-saves. gcc doesn't allow the use of r11 for some reason. Also, we can't directly "bic" the stack pointer in thumb mode since r13 isn't an allowed register number in that context. So use r4 as a temporary, since that is about to get trashed anyway, just after each use of this macro. Side effect is we need to be very careful about any future changes, since VALGRIND_ALIGN_STACK simply assumes r4 is usable. */ #define VALGRIND_ALIGN_STACK \ "mov r10, sp\n\t" \ "mov r4, sp\n\t" \ "bic r4, r4, #7\n\t" \ "mov sp, r4\n\t" #define VALGRIND_RESTORE_STACK \ "mov sp, r10\n\t" /* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #4] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #4 \n\t" \ "ldr r0, [%1, #20] \n\t" \ "push {r0} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "push {r0, r1} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #4 \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "push {r0, r1, r2} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "push {r0, r1, r2, r3} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #4 \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #40] \n\t" \ "push {r0} \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #4 \n\t" \ "ldr r0, [%1, #40] \n\t" \ "ldr r1, [%1, #44] \n\t" \ "push {r0, r1} \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #40] \n\t" \ "ldr r1, [%1, #44] \n\t" \ "ldr r2, [%1, #48] \n\t" \ "push {r0, r1, r2} \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_arm_linux */ /* ------------------------ arm64-linux ------------------------ */ #if defined(PLAT_arm64_linux) /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "x0", "x1", "x2", "x3","x4", "x5", "x6", "x7", "x8", "x9", \ "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", \ "x18", "x19", "x20", "x30", \ "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", \ "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17", \ "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", \ "v26", "v27", "v28", "v29", "v30", "v31" /* x21 is callee-saved, so we can use it to save and restore SP around the hidden call. */ #define VALGRIND_ALIGN_STACK \ "mov x21, sp\n\t" \ "bic sp, x21, #15\n\t" #define VALGRIND_RESTORE_STACK \ "mov sp, x21\n\t" /* These CALL_FN_ macros assume that on arm64-linux, sizeof(unsigned long) == 8. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #0x20 \n\t" \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1, #72] \n\t" \ "str x8, [sp, #0] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #0x20 \n\t" \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1, #72] \n\t" \ "str x8, [sp, #0] \n\t" \ "ldr x8, [%1, #80] \n\t" \ "str x8, [sp, #8] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #0x30 \n\t" \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1, #72] \n\t" \ "str x8, [sp, #0] \n\t" \ "ldr x8, [%1, #80] \n\t" \ "str x8, [sp, #8] \n\t" \ "ldr x8, [%1, #88] \n\t" \ "str x8, [sp, #16] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11, \ arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #0x30 \n\t" \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1, #72] \n\t" \ "str x8, [sp, #0] \n\t" \ "ldr x8, [%1, #80] \n\t" \ "str x8, [sp, #8] \n\t" \ "ldr x8, [%1, #88] \n\t" \ "str x8, [sp, #16] \n\t" \ "ldr x8, [%1, #96] \n\t" \ "str x8, [sp, #24] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_arm64_linux */ /* ------------------------- s390x-linux ------------------------- */ #if defined(PLAT_s390x_linux) /* Similar workaround as amd64 (see above), but we use r11 as frame pointer and save the old r11 in r7. r11 might be used for argvec, therefore we copy argvec in r1 since r1 is clobbered after the call anyway. */ #if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) # define __FRAME_POINTER \ ,"d"(__builtin_dwarf_cfa()) # define VALGRIND_CFI_PROLOGUE \ ".cfi_remember_state\n\t" \ "lgr 1,%1\n\t" /* copy the argvec pointer in r1 */ \ "lgr 7,11\n\t" \ "lgr 11,%2\n\t" \ ".cfi_def_cfa r11, 0\n\t" # define VALGRIND_CFI_EPILOGUE \ "lgr 11, 7\n\t" \ ".cfi_restore_state\n\t" #else # define __FRAME_POINTER # define VALGRIND_CFI_PROLOGUE \ "lgr 1,%1\n\t" # define VALGRIND_CFI_EPILOGUE #endif /* Nb: On s390 the stack pointer is properly aligned *at all times* according to the s390 GCC maintainer. (The ABI specification is not precise in this regard.) Therefore, VALGRIND_ALIGN_STACK and VALGRIND_RESTORE_STACK are not defined here. */ /* These regs are trashed by the hidden call. Note that we overwrite r14 in s390_irgen_noredir (VEX/priv/guest_s390_irgen.c) to give the function a proper return address. All others are ABI defined call clobbers. */ #define __CALLER_SAVED_REGS "0","1","2","3","4","5","14", \ "f0","f1","f2","f3","f4","f5","f6","f7" /* Nb: Although r11 is modified in the asm snippets below (inside VALGRIND_CFI_PROLOGUE) it is not listed in the clobber section, for two reasons: (1) r11 is restored in VALGRIND_CFI_EPILOGUE, so effectively it is not modified (2) GCC will complain that r11 cannot appear inside a clobber section, when compiled with -O -fno-omit-frame-pointer */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 1, 0(1)\n\t" /* target->r1 */ \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "d" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) /* The call abi has the arguments in r2-r6 and stack */ #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1, arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1, arg2, arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1, arg2, arg3, arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1, arg2, arg3, arg4, arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-168\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,168\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-176\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,176\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-184\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,184\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8, arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-192\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "mvc 184(8,15), 72(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,192\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8, arg9, arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-200\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "mvc 184(8,15), 72(1)\n\t" \ "mvc 192(8,15), 80(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,200\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8, arg9, arg10, arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ _argvec[11] = (unsigned long)arg11; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-208\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "mvc 184(8,15), 72(1)\n\t" \ "mvc 192(8,15), 80(1)\n\t" \ "mvc 200(8,15), 88(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,208\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8, arg9, arg10, arg11, arg12)\ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ _argvec[11] = (unsigned long)arg11; \ _argvec[12] = (unsigned long)arg12; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-216\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "mvc 184(8,15), 72(1)\n\t" \ "mvc 192(8,15), 80(1)\n\t" \ "mvc 200(8,15), 88(1)\n\t" \ "mvc 208(8,15), 96(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,216\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_s390x_linux */ /* ------------------------- mips32-linux ----------------------- */ #if defined(PLAT_mips32_linux) /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ "$25", "$31" /* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16\n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $4, 4(%1) \n\t" /* arg1*/ \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 24\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 24 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 32\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "nop\n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 32 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 32\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 32 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 40\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 40 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 40\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 36(%1) \n\t" \ "sw $4, 32($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 40 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 48\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 36(%1) \n\t" \ "sw $4, 32($29) \n\t" \ "lw $4, 40(%1) \n\t" \ "sw $4, 36($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 48 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 48\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 36(%1) \n\t" \ "sw $4, 32($29) \n\t" \ "lw $4, 40(%1) \n\t" \ "sw $4, 36($29) \n\t" \ "lw $4, 44(%1) \n\t" \ "sw $4, 40($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 48 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 56\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 36(%1) \n\t" \ "sw $4, 32($29) \n\t" \ "lw $4, 40(%1) \n\t" \ "sw $4, 36($29) \n\t" \ "lw $4, 44(%1) \n\t" \ "sw $4, 40($29) \n\t" \ "lw $4, 48(%1) \n\t" \ "sw $4, 44($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 56 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_mips32_linux */ /* ------------------------- mips64-linux ------------------------- */ #if defined(PLAT_mips64_linux) /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ "$25", "$31" /* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" /* arg1*/ \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ "dsubu $29, $29, 8\n\t" \ "ld $4, 72(%1)\n\t" \ "sd $4, 0($29)\n\t" \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "daddu $29, $29, 8\n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ "dsubu $29, $29, 16\n\t" \ "ld $4, 72(%1)\n\t" \ "sd $4, 0($29)\n\t" \ "ld $4, 80(%1)\n\t" \ "sd $4, 8($29)\n\t" \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "daddu $29, $29, 16\n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ "dsubu $29, $29, 24\n\t" \ "ld $4, 72(%1)\n\t" \ "sd $4, 0($29)\n\t" \ "ld $4, 80(%1)\n\t" \ "sd $4, 8($29)\n\t" \ "ld $4, 88(%1)\n\t" \ "sd $4, 16($29)\n\t" \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "daddu $29, $29, 24\n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ "dsubu $29, $29, 32\n\t" \ "ld $4, 72(%1)\n\t" \ "sd $4, 0($29)\n\t" \ "ld $4, 80(%1)\n\t" \ "sd $4, 8($29)\n\t" \ "ld $4, 88(%1)\n\t" \ "sd $4, 16($29)\n\t" \ "ld $4, 96(%1)\n\t" \ "sd $4, 24($29)\n\t" \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "daddu $29, $29, 32\n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_mips64_linux */ /* ------------------------------------------------------------------ */ /* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */ /* */ /* ------------------------------------------------------------------ */ /* Some request codes. There are many more of these, but most are not exposed to end-user view. These are the public ones, all of the form 0x1000 + small_number. Core ones are in the range 0x00000000--0x0000ffff. The non-public ones start at 0x2000. */ /* These macros are used by tools -- they must be public, but don't embed them into other programs. */ #define VG_USERREQ_TOOL_BASE(a,b) \ ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) #define VG_IS_TOOL_USERREQ(a, b, v) \ (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) /* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! This enum comprises an ABI exported by Valgrind to programs which use client requests. DO NOT CHANGE THE ORDER OF THESE ENTRIES, NOR DELETE ANY -- add new ones at the end. */ typedef enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, /* These allow any function to be called from the simulated CPU but run on the real CPU. Nb: the first arg passed to the function is always the ThreadId of the running thread! So CLIENT_CALL0 actually requires a 1 arg function, etc. */ VG_USERREQ__CLIENT_CALL0 = 0x1101, VG_USERREQ__CLIENT_CALL1 = 0x1102, VG_USERREQ__CLIENT_CALL2 = 0x1103, VG_USERREQ__CLIENT_CALL3 = 0x1104, /* Can be useful in regression testing suites -- eg. can send Valgrind's output to /dev/null and still count errors. */ VG_USERREQ__COUNT_ERRORS = 0x1201, /* Allows the client program and/or gdbserver to execute a monitor command. */ VG_USERREQ__GDB_MONITOR_COMMAND = 0x1202, /* These are useful and can be interpreted by any tool that tracks malloc() et al, by using vg_replace_malloc.c. */ VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, VG_USERREQ__RESIZEINPLACE_BLOCK = 0x130b, VG_USERREQ__FREELIKE_BLOCK = 0x1302, /* Memory pool support. */ VG_USERREQ__CREATE_MEMPOOL = 0x1303, VG_USERREQ__DESTROY_MEMPOOL = 0x1304, VG_USERREQ__MEMPOOL_ALLOC = 0x1305, VG_USERREQ__MEMPOOL_FREE = 0x1306, VG_USERREQ__MEMPOOL_TRIM = 0x1307, VG_USERREQ__MOVE_MEMPOOL = 0x1308, VG_USERREQ__MEMPOOL_CHANGE = 0x1309, VG_USERREQ__MEMPOOL_EXISTS = 0x130a, /* Allow printfs to valgrind log. */ /* The first two pass the va_list argument by value, which assumes it is the same size as or smaller than a UWord, which generally isn't the case. Hence are deprecated. The second two pass the vargs by reference and so are immune to this problem. */ /* both :: char* fmt, va_list vargs (DEPRECATED) */ VG_USERREQ__PRINTF = 0x1401, VG_USERREQ__PRINTF_BACKTRACE = 0x1402, /* both :: char* fmt, va_list* vargs */ VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403, VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404, /* Stack support. */ VG_USERREQ__STACK_REGISTER = 0x1501, VG_USERREQ__STACK_DEREGISTER = 0x1502, VG_USERREQ__STACK_CHANGE = 0x1503, /* Wine support */ VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601, /* Querying of debug info. */ VG_USERREQ__MAP_IP_TO_SRCLOC = 0x1701, /* Disable/enable error reporting level. Takes a single Word arg which is the delta to this thread's error disablement indicator. Hence 1 disables or further disables errors, and -1 moves back towards enablement. Other values are not allowed. */ VG_USERREQ__CHANGE_ERR_DISABLEMENT = 0x1801, /* Initialise IR injection */ VG_USERREQ__VEX_INIT_FOR_IRI = 0x1901 } Vg_ClientRequest; #if !defined(__GNUC__) # define __extension__ /* */ #endif /* Returns the number of Valgrinds this code is running under. That is, 0 if running natively, 1 if running under Valgrind, 2 if running under Valgrind which is running under another Valgrind, etc. */ #define RUNNING_ON_VALGRIND \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* if not */, \ VG_USERREQ__RUNNING_ON_VALGRIND, \ 0, 0, 0, 0, 0) \ /* Discard translation of code in the range [_qzz_addr .. _qzz_addr + _qzz_len - 1]. Useful if you are debugging a JITter or some such, since it provides a way to make sure valgrind will retranslate the invalidated area. Returns no value. */ #define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DISCARD_TRANSLATIONS, \ _qzz_addr, _qzz_len, 0, 0, 0) /* These requests are for getting Valgrind itself to print something. Possibly with a backtrace. This is a really ugly hack. The return value is the number of characters printed, excluding the "**** " part at the start and the backtrace (if present). */ #if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) /* Modern GCC will optimize the static routine out if unused, and unused attribute will shut down warnings about it. */ static int VALGRIND_PRINTF(const char *format, ...) __attribute__((format(__printf__, 1, 2), __unused__)); #endif static int #if defined(_MSC_VER) __inline #endif VALGRIND_PRINTF(const char *format, ...) { #if defined(NVALGRIND) return 0; #else /* NVALGRIND */ #if defined(_MSC_VER) || defined(__MINGW64__) uintptr_t _qzz_res; #else unsigned long _qzz_res; #endif va_list vargs; va_start(vargs, format); #if defined(_MSC_VER) || defined(__MINGW64__) _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__PRINTF_VALIST_BY_REF, (uintptr_t)format, (uintptr_t)&vargs, 0, 0, 0); #else _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__PRINTF_VALIST_BY_REF, (unsigned long)format, (unsigned long)&vargs, 0, 0, 0); #endif va_end(vargs); return (int)_qzz_res; #endif /* NVALGRIND */ } #if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) __attribute__((format(__printf__, 1, 2), __unused__)); #endif static int #if defined(_MSC_VER) __inline #endif VALGRIND_PRINTF_BACKTRACE(const char *format, ...) { #if defined(NVALGRIND) return 0; #else /* NVALGRIND */ #if defined(_MSC_VER) || defined(__MINGW64__) uintptr_t _qzz_res; #else unsigned long _qzz_res; #endif va_list vargs; va_start(vargs, format); #if defined(_MSC_VER) || defined(__MINGW64__) _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, (uintptr_t)format, (uintptr_t)&vargs, 0, 0, 0); #else _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, (unsigned long)format, (unsigned long)&vargs, 0, 0, 0); #endif va_end(vargs); return (int)_qzz_res; #endif /* NVALGRIND */ } /* These requests allow control to move from the simulated CPU to the real CPU, calling an arbitary function. Note that the current ThreadId is inserted as the first argument. So this call: VALGRIND_NON_SIMD_CALL2(f, arg1, arg2) requires f to have this signature: Word f(Word tid, Word arg1, Word arg2) where "Word" is a word-sized type. Note that these client requests are not entirely reliable. For example, if you call a function with them that subsequently calls printf(), there's a high chance Valgrind will crash. Generally, your prospects of these working are made higher if the called function does not refer to any global variables, and does not refer to any libc or other functions (printf et al). Any kind of entanglement with libc or dynamic linking is likely to have a bad outcome, for tricky reasons which we've grappled with a lot in the past. */ #define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__CLIENT_CALL0, \ _qyy_fn, \ 0, 0, 0, 0) #define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__CLIENT_CALL1, \ _qyy_fn, \ _qyy_arg1, 0, 0, 0) #define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__CLIENT_CALL2, \ _qyy_fn, \ _qyy_arg1, _qyy_arg2, 0, 0) #define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__CLIENT_CALL3, \ _qyy_fn, \ _qyy_arg1, _qyy_arg2, \ _qyy_arg3, 0) /* Counts the number of errors that have been recorded by a tool. Nb: the tool must record the errors with VG_(maybe_record_error)() or VG_(unique_error)() for them to be counted. */ #define VALGRIND_COUNT_ERRORS \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR( \ 0 /* default return */, \ VG_USERREQ__COUNT_ERRORS, \ 0, 0, 0, 0, 0) /* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing when heap blocks are allocated in order to give accurate results. This happens automatically for the standard allocator functions such as malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete, delete[], etc. But if your program uses a custom allocator, this doesn't automatically happen, and Valgrind will not do as well. For example, if you allocate superblocks with mmap() and then allocates chunks of the superblocks, all Valgrind's observations will be at the mmap() level and it won't know that the chunks should be considered separate entities. In Memcheck's case, that means you probably won't get heap block overrun detection (because there won't be redzones marked as unaddressable) and you definitely won't get any leak detection. The following client requests allow a custom allocator to be annotated so that it can be handled accurately by Valgrind. VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated by a malloc()-like function. For Memcheck (an illustrative case), this does two things: - It records that the block has been allocated. This means any addresses within the block mentioned in error messages will be identified as belonging to the block. It also means that if the block isn't freed it will be detected by the leak checker. - It marks the block as being addressable and undefined (if 'is_zeroed' is not set), or addressable and defined (if 'is_zeroed' is set). This controls how accesses to the block by the program are handled. 'addr' is the start of the usable block (ie. after any redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator can apply redzones -- these are blocks of padding at the start and end of each block. Adding redzones is recommended as it makes it much more likely Valgrind will spot block overruns. `is_zeroed' indicates if the memory is zeroed (or filled with another predictable value), as is the case for calloc(). VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a heap block -- that will be used by the client program -- is allocated. It's best to put it at the outermost level of the allocator if possible; for example, if you have a function my_alloc() which calls internal_alloc(), and the client request is put inside internal_alloc(), stack traces relating to the heap block will contain entries for both my_alloc() and internal_alloc(), which is probably not what you want. For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out custom blocks from within a heap block, B, that has been allocated with malloc/calloc/new/etc, then block B will be *ignored* during leak-checking -- the custom blocks will take precedence. VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For Memcheck, it does two things: - It records that the block has been deallocated. This assumes that the block was annotated as having been allocated via VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. - It marks the block as being unaddressable. VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a heap block is deallocated. VALGRIND_RESIZEINPLACE_BLOCK informs a tool about reallocation. For Memcheck, it does four things: - It records that the size of a block has been changed. This assumes that the block was annotated as having been allocated via VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. - If the block shrunk, it marks the freed memory as being unaddressable. - If the block grew, it marks the new area as undefined and defines a red zone past the end of the new block. - The V-bits of the overlap between the old and the new block are preserved. VALGRIND_RESIZEINPLACE_BLOCK should be put after allocation of the new block and before deallocation of the old block. In many cases, these three client requests will not be enough to get your allocator working well with Memcheck. More specifically, if your allocator writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call will be necessary to mark the memory as addressable just before the zeroing occurs, otherwise you'll get a lot of invalid write errors. For example, you'll need to do this if your allocator recycles freed blocks, but it zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK). Alternatively, if your allocator reuses freed blocks for allocator-internal data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary. Really, what's happening is a blurring of the lines between the client program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the memory should be considered unaddressable to the client program, but the allocator knows more than the rest of the client program and so may be able to safely access it. Extra client requests are necessary for Valgrind to understand the distinction between the allocator and the rest of the program. Ignored if addr == 0. */ #define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MALLOCLIKE_BLOCK, \ addr, sizeB, rzB, is_zeroed, 0) /* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. Ignored if addr == 0. */ #define VALGRIND_RESIZEINPLACE_BLOCK(addr, oldSizeB, newSizeB, rzB) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__RESIZEINPLACE_BLOCK, \ addr, oldSizeB, newSizeB, rzB, 0) /* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. Ignored if addr == 0. */ #define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__FREELIKE_BLOCK, \ addr, rzB, 0, 0, 0) /* Create a memory pool. */ #define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CREATE_MEMPOOL, \ pool, rzB, is_zeroed, 0, 0) /* Destroy a memory pool. */ #define VALGRIND_DESTROY_MEMPOOL(pool) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DESTROY_MEMPOOL, \ pool, 0, 0, 0, 0) /* Associate a piece of memory with a memory pool. */ #define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_ALLOC, \ pool, addr, size, 0, 0) /* Disassociate a piece of memory from a memory pool. */ #define VALGRIND_MEMPOOL_FREE(pool, addr) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_FREE, \ pool, addr, 0, 0, 0) /* Disassociate any pieces outside a particular range. */ #define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_TRIM, \ pool, addr, size, 0, 0) /* Resize and/or move a piece associated with a memory pool. */ #define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MOVE_MEMPOOL, \ poolA, poolB, 0, 0, 0) /* Resize and/or move a piece associated with a memory pool. */ #define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_CHANGE, \ pool, addrA, addrB, size, 0) /* Return 1 if a mempool exists, else 0. */ #define VALGRIND_MEMPOOL_EXISTS(pool) \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__MEMPOOL_EXISTS, \ pool, 0, 0, 0, 0) /* Mark a piece of memory as being a stack. Returns a stack id. start is the lowest addressable stack byte, end is the highest addressable stack byte. */ #define VALGRIND_STACK_REGISTER(start, end) \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__STACK_REGISTER, \ start, end, 0, 0, 0) /* Unmark the piece of memory associated with a stack id as being a stack. */ #define VALGRIND_STACK_DEREGISTER(id) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_DEREGISTER, \ id, 0, 0, 0, 0) /* Change the start and end address of the stack id. start is the new lowest addressable stack byte, end is the new highest addressable stack byte. */ #define VALGRIND_STACK_CHANGE(id, start, end) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_CHANGE, \ id, start, end, 0, 0) /* Load PDB debug info for Wine PE image_map. */ #define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__LOAD_PDB_DEBUGINFO, \ fd, ptr, total_size, delta, 0) /* Map a code address to a source file name and line number. buf64 must point to a 64-byte buffer in the caller's address space. The result will be dumped in there and is guaranteed to be zero terminated. If no info is found, the first byte is set to zero. */ #define VALGRIND_MAP_IP_TO_SRCLOC(addr, buf64) \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__MAP_IP_TO_SRCLOC, \ addr, buf64, 0, 0, 0) /* Disable error reporting for this thread. Behaves in a stack like way, so you can safely call this multiple times provided that VALGRIND_ENABLE_ERROR_REPORTING is called the same number of times to re-enable reporting. The first call of this macro disables reporting. Subsequent calls have no effect except to increase the number of VALGRIND_ENABLE_ERROR_REPORTING calls needed to re-enable reporting. Child threads do not inherit this setting from their parents -- they are always created with reporting enabled. */ #define VALGRIND_DISABLE_ERROR_REPORTING \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ 1, 0, 0, 0, 0) /* Re-enable error reporting, as per comments on VALGRIND_DISABLE_ERROR_REPORTING. */ #define VALGRIND_ENABLE_ERROR_REPORTING \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ -1, 0, 0, 0, 0) /* Execute a monitor command from the client program. If a connection is opened with GDB, the output will be sent according to the output mode set for vgdb. If no connection is opened, output will go to the log output. Returns 1 if command not recognised, 0 otherwise. */ #define VALGRIND_MONITOR_COMMAND(command) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__GDB_MONITOR_COMMAND, \ command, 0, 0, 0, 0) #undef PLAT_x86_darwin #undef PLAT_amd64_darwin #undef PLAT_x86_win32 #undef PLAT_amd64_win64 #undef PLAT_x86_linux #undef PLAT_amd64_linux #undef PLAT_ppc32_linux #undef PLAT_ppc64be_linux #undef PLAT_ppc64le_linux #undef PLAT_arm_linux #undef PLAT_s390x_linux #undef PLAT_mips32_linux #undef PLAT_mips64_linux #endif /* __VALGRIND_H */ base-7.0.3.1/modules/libcom/src/yacc/ACKNOWLEDGEMENTS0000664000577000060420000000140313557101274020311 0ustar anjaesctl Berkeley Yacc owes much to the unflagging efforts of Keith Bostic. His badgering kept me working on it long after I was ready to quit. Berkeley Yacc is based on the excellent algorithm for computing LALR(1) lookaheads developed by Tom Pennello and Frank DeRemer. The algorithm is described in their almost impenetrable article in TOPLAS 4,4. Finally, much of the credit for the latest version must go to those who pointed out deficiencies of my earlier releases. Among the most prolific contributors were Benson I. Margulies Dave Gentzel Antoine Verheijen Peter S. Housel Dale Smith Ozan Yigit John Campbell Bill Sommerfeld Paul Hilfinger Gary Bridgewater Dave Bakken Dan Lanciani Richard Sargent Parag Patel base-7.0.3.1/modules/libcom/src/yacc/EPICS_READ_THIS0000664000577000060420000000340613557101274020251 0ustar anjaesctl This is the source of the BSD version of yacc that is distributed in the NET2 release of BSD Unix. We chose this over Bison due to the bison license agreement. And if you are scratching you head over that comment, read the addendum in the xxx-info-1 file that comes with bison. Anyway, this BSD-yacc has been hacked on to make it generate an all-static parser C program so that it may be used on an IOC w/o getting things confused. They way we intend to use it is with the epics-hacked version of flex that also generates an all-static source program. The general idea is that your yacc source (xxx.y) file could be formatted like this: ========================================================= %% YOUR YACC PROGRAM %% int MyYaccParser(char *f1) { static int FirstFlag = 1; printf("processing file >%s<\n", f1); yyin = fopen(f1, "r"); if (!FirstFlag) yyrestart(yyin); FirstFlag = 0; yyparse(); fclose(yyin); return(0); } static int yyerror(char *str) { printf("Error: %s\n", str); return(0); } ========================================================= The FirstFlag and yyrestart jazz is the flex-flavored version of how you restart the scanner if you want to parse another file. Without it, the scanner can only be used one time. Even if you think your code will only be used one time, it is desireable to put in the restart stuff anyway, because some day you (or your boss) will change your mind and without it the IOC will crash when you make your second call to your parser. Note also that there is a yyerror function present. This is mandatory for the way we are using flex. Without, results are indeterminate... because you will end up calling some other random yyerror() in the ioc.. probably the one for the console command interpreter. --John base-7.0.3.1/modules/libcom/src/yacc/Makefile0000664000577000060420000000170113557101274017475 0ustar anjaesctl#************************************************************************* # 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/yacc antelope_SRCS += closure.c antelope_SRCS += error.c antelope_SRCS += lalr.c antelope_SRCS += lr0.c antelope_SRCS += antelope.c antelope_SRCS += mkpar.c antelope_SRCS += output.c antelope_SRCS += reader.c antelope_SRCS += skeleton.c antelope_SRCS += symtab.c antelope_SRCS += verbose.c antelope_SRCS += warshall.c antelope_OBJS += epicsTempFile$(OBJ) PROD_HOST += antelope base-7.0.3.1/modules/libcom/src/yacc/NEW_FEATURES0000664000577000060420000000400213557101274017704 0ustar anjaesctl The -r option has been implemented. The -r option tells Yacc to put the read-only tables in y.tab.c and the code and variables in y.code.c. Keith Bostic asked for this option so that :yyfix could be eliminated. The -l and -t options have been implemented. The -l option tells Yacc not to include #line directives in the code it produces. The -t option causes debugging code to be included in the compiled parser. The code for error recovery has been changed to implement the same algorithm as AT&T Yacc. There will still be differences in the way error recovery works because AT&T Yacc uses more default reductions than Berkeley Yacc. The environment variable TMPDIR determines the directory where temporary files will be created. If TMPDIR is defined, temporary files will be created in the directory whose pathname is the value of TMPDIR. By default, temporary files are created in /tmp. The keywords are now case-insensitive. For example, %nonassoc, %NONASSOC, %NonAssoc, and %nOnAsSoC are all equivalent. Commas and semicolons that are not part of C code are treated as commentary. Line-end comments, as in BCPL, are permitted. Line-end comments begin with // and end at the next end-of-line. Line-end comments are permitted in C code; they are converted to C comments on output. The form of y.output files has been changed to look more like those produced by AT&T Yacc. A new kind of declaration has been added. The form of the declaration is %ident string where string is a sequence of characters begining with a double quote and ending with either a double quote or the next end-of-line, whichever comes first. The declaration will cause a #ident directive to be written near the start of the output file. If a parser has been compiled with debugging code, that code can be enabled by setting an environment variable. If the environment variable YYDEBUG is set to 0, debugging output is suppressed. If it is set to 1, debugging output is written to standard output. base-7.0.3.1/modules/libcom/src/yacc/NOTES0000664000577000060420000000077013557101274016655 0ustar anjaesctlBerkeley Yacc reflects its origins. The reason so many routines use exactly six register variables is that Berkeley Yacc was developed on a VAX using PCC. PCC placed at most six variables in registers. I went to considerable effort to find which six variables most belonged in registers. Changes in machines and compilers make that effort worthless, perhaps even harmful. The code contains many instances where address calculations are performed in particular ways to optimize the code for the VAX. base-7.0.3.1/modules/libcom/src/yacc/NO_WARRANTY0000664000577000060420000000023413557101274017623 0ustar anjaesctl Berkeley Yacc is distributed with no warranty whatever. The author and any other contributors take no responsibility for the consequences of its use. base-7.0.3.1/modules/libcom/src/yacc/README0000664000577000060420000000220013557101274016710 0ustar anjaesctl Berkeley Yacc is an LALR(1) parser generator. Berkeley Yacc has been made as compatible as possible with AT&T Yacc. Berkeley Yacc can accept any input specification that conforms to the AT&T Yacc documentation. Specifications that take advantage of undocumented features of AT&T Yacc will probably be rejected. Berkeley Yacc is distributed with no warranty whatever. The code is certain to contain errors. Neither the author nor any contributor takes responsibility for any consequences of its use. Berkeley Yacc is in the public domain. The data structures and algorithms used in Berkeley Yacc are all either taken from documents available to the general public or are inventions of the author. Anyone may freely distribute source or binary forms of Berkeley Yacc whether unchanged or modified. Distributers may charge whatever fees they can obtain for Berkeley Yacc. Programs generated by Berkeley Yacc may be distributed freely. Please report bugs to robert.corbett@eng.Sun.COM Include a small example if possible. Please include the banner string from skeleton.c with the bug report. Do not expect rapid responses. base-7.0.3.1/modules/libcom/src/yacc/antelope.c0000664000577000060420000001400113557101274020005 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include "defs.h" #define epicsExportSharedSymbols #include "epicsTempFile.h" #undef epicsExportSharedSymbols char dflag; char lflag; char rflag; char tflag; char vflag; char *symbol_prefix; char *file_prefix = "y"; char *myname = "yacc"; char *temp_form = "yacc.XXXXXXX"; int lineno; int outline; char *code_file_name; char *defines_file_name; char *input_file_name = ""; char *output_file_name; char *verbose_file_name; FILE *action_file; /* a temp file, used to save actions associated */ /* with rules until the parser is written */ FILE *code_file; /* y.code.c (used when the -r option is specified) */ FILE *defines_file; /* y.tab.h */ FILE *input_file; /* the input file */ FILE *output_file; /* y.tab.c */ FILE *text_file; /* a temp file, used to save text until all */ /* symbols have been defined */ FILE *union_file; /* a temp file, used to save the union */ /* definition until all symbol have been */ /* defined */ FILE *verbose_file; /* y.output */ int nitems; int nrules; int nsyms; int ntokens; int nvars; int start_symbol; char **symbol_name; short *symbol_value; short *symbol_prec; char *symbol_assoc; short *ritem; short *rlhs; short *rrhs; short *rprec; char *rassoc; short **derives; char *nullable; void done(int k) { if (action_file) { fclose(action_file); } if (text_file) { fclose(text_file); } if (union_file) { fclose(union_file); } exit(k); } static void onintr(int StupidInconsistantSignalTypes) { done(1); } static void set_signals(void) { #ifdef SIGINT if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, onintr); #endif #ifdef SIGTERM if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, onintr); #endif #ifdef SIGHUP if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, onintr); #endif } static void usage(void) { fprintf(stderr, "usage: %s [-dlrtv] [-b file_prefix] [-p symbol_prefix] filename\n", myname); exit(1); } static int getargs(int argc, char *argv[]) { int i; char *s; if (argc > 0) { myname = strrchr(argv[0], '/'); if (myname) myname++; else myname = argv[0]; } for (i = 1; i < argc; ++i) { s = argv[i]; if (*s != '-') break; switch (*++s) { case '\0': input_file = stdin; if (i + 1 < argc) usage(); return(0); case '-': ++i; goto no_more_options; case 'b': if (*++s) file_prefix = s; else if (++i < argc) file_prefix = argv[i]; else usage(); continue; case 'd': dflag = 1; break; case 'l': lflag = 1; break; case 'p': if (*++s) symbol_prefix = s; else if (++i < argc) symbol_prefix = argv[i]; else usage(); continue; case 'r': rflag = 1; break; case 't': tflag = 1; break; case 'v': vflag = 1; break; default: usage(); } for (;;) { switch (*++s) { case '\0': goto end_of_option; case 'd': dflag = 1; break; case 'l': lflag = 1; break; case 'r': rflag = 1; break; case 't': tflag = 1; break; case 'v': vflag = 1; break; default: usage(); } } end_of_option:; } no_more_options:; if (i + 1 != argc) usage(); input_file_name = argv[i]; return(0); } char * allocate(unsigned int n) { char *p; p = NULL; if (n) { p = CALLOC(1, n); if (!p) no_space(); } return (p); } static void create_file_names(void) { int len; len = strlen(file_prefix); output_file_name = MALLOC(len + 7); if (output_file_name == 0) no_space(); strcpy(output_file_name, file_prefix); strcpy(output_file_name + len, OUTPUT_SUFFIX); if (rflag) { code_file_name = MALLOC(len + 8); if (code_file_name == 0) no_space(); strcpy(code_file_name, file_prefix); strcpy(code_file_name + len, CODE_SUFFIX); } else code_file_name = output_file_name; if (dflag) { defines_file_name = MALLOC(len + 7); if (defines_file_name == 0) no_space(); strcpy(defines_file_name, file_prefix); strcpy(defines_file_name + len, DEFINES_SUFFIX); } if (vflag) { verbose_file_name = MALLOC(len + 8); if (verbose_file_name == 0) no_space(); strcpy(verbose_file_name, file_prefix); strcpy(verbose_file_name + len, VERBOSE_SUFFIX); } } static void open_files(void) { create_file_names(); if (input_file == 0) { input_file = fopen(input_file_name, "r"); if (input_file == 0) open_error(input_file_name); } action_file = epicsTempFile(); if (action_file == 0) open_error("temp action file"); text_file = epicsTempFile(); if (text_file == 0) open_error("temp text file"); if (vflag) { verbose_file = fopen(verbose_file_name, "w"); if (verbose_file == 0) open_error(verbose_file_name); } if (dflag) { defines_file = fopen(defines_file_name, "w"); if (defines_file == 0) open_error(defines_file_name); union_file = epicsTempFile(); if (union_file == 0) open_error("temp union file"); } output_file = fopen(output_file_name, "w"); if (output_file == 0) open_error(output_file_name); if (rflag) { code_file = fopen(code_file_name, "w"); if (code_file == 0) open_error(code_file_name); } else code_file = output_file; } int main(int argc, char *argv[]) { set_signals(); getargs(argc, argv); open_files(); reader(); lr0(); lalr(); make_parser(); verbose(); output(); done(0); /*NOTREACHED*/ } base-7.0.3.1/modules/libcom/src/yacc/closure.c0000664000577000060420000001115513557101274017661 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "defs.h" short *itemset; short *itemsetend; unsigned *ruleset; static unsigned *first_derives; static unsigned *EFF; #ifdef DEBUG static void print_closure(int n); static void print_EFF(void); static void print_first_derives(void); #endif static void set_EFF(void) { unsigned *row; int symbol; short *sp; int rowsize; int i; int rule; rowsize = WORDSIZE(nvars); EFF = NEW2(nvars * rowsize, unsigned); row = EFF; for (i = start_symbol; i < nsyms; i++) { sp = derives[i]; for (rule = *sp; rule > 0; rule = *++sp) { symbol = ritem[rrhs[rule]]; if (ISVAR(symbol)) { symbol -= start_symbol; SETBIT(row, symbol); } } row += rowsize; } reflexive_transitive_closure(EFF, nvars); #ifdef DEBUG print_EFF(); #endif } void set_first_derives(void) { unsigned *rrow; unsigned *vrow; int j; unsigned k; unsigned cword = 0; short *rp; int rule; int i; int rulesetsize; int varsetsize; rulesetsize = WORDSIZE(nrules); varsetsize = WORDSIZE(nvars); first_derives = NEW2(nvars * rulesetsize, unsigned) - ntokens * rulesetsize; set_EFF(); rrow = first_derives + ntokens * rulesetsize; for (i = start_symbol; i < nsyms; i++) { vrow = EFF + ((i - ntokens) * varsetsize); k = BITS_PER_WORD; for (j = start_symbol; j < nsyms; k++, j++) { if (k >= BITS_PER_WORD) { cword = *vrow++; k = 0; } if (cword & (1 << k)) { rp = derives[j]; while ((rule = *rp++) >= 0) { SETBIT(rrow, rule); } } } vrow += varsetsize; rrow += rulesetsize; } #ifdef DEBUG print_first_derives(); #endif FREE(EFF); } void closure(short int *nucleus, int n) { int ruleno; unsigned word; unsigned i; short *csp; unsigned *dsp; unsigned *rsp; int rulesetsize; short *csend; unsigned *rsend; int symbol; int itemno; rulesetsize = WORDSIZE(nrules); rsp = ruleset; rsend = ruleset + rulesetsize; for (rsp = ruleset; rsp < rsend; rsp++) *rsp = 0; csend = nucleus + n; for (csp = nucleus; csp < csend; ++csp) { symbol = ritem[*csp]; if (ISVAR(symbol)) { dsp = first_derives + symbol * rulesetsize; rsp = ruleset; while (rsp < rsend) *rsp++ |= *dsp++; } } ruleno = 0; itemsetend = itemset; csp = nucleus; for (rsp = ruleset; rsp < rsend; ++rsp) { word = *rsp; if (word) { for (i = 0; i < BITS_PER_WORD; ++i) { if (word & (1 << i)) { itemno = rrhs[ruleno+i]; while (csp < csend && *csp < itemno) *itemsetend++ = *csp++; *itemsetend++ = itemno; while (csp < csend && *csp == itemno) ++csp; } } } ruleno += BITS_PER_WORD; } while (csp < csend) *itemsetend++ = *csp++; #ifdef DEBUG print_closure(n); #endif } void finalize_closure(void) { FREE(itemset); FREE(ruleset); FREE(first_derives + ntokens * WORDSIZE(nrules)); } #ifdef DEBUG static void print_closure(int n) { short *isp; printf("\n\nn = %d\n\n", n); for (isp = itemset; isp < itemsetend; isp++) printf(" %d\n", *isp); } static void print_EFF(void) { int i, j; unsigned *rowp; unsigned word; unsigned k; printf("\n\nEpsilon Free Firsts\n"); for (i = start_symbol; i < nsyms; i++) { printf("\n%s", symbol_name[i]); rowp = EFF + ((i - start_symbol) * WORDSIZE(nvars)); word = *rowp++; k = BITS_PER_WORD; for (j = 0; j < nvars; k++, j++) { if (k >= BITS_PER_WORD) { word = *rowp++; k = 0; } if (word & (1 << k)) printf(" %s", symbol_name[start_symbol + j]); } } } static void print_first_derives(void) { int i; int j; unsigned *rp; unsigned cword; unsigned k; printf("\n\n\nFirst Derives\n"); for (i = start_symbol; i < nsyms; i++) { printf("\n%s derives\n", symbol_name[i]); rp = first_derives + i * WORDSIZE(nrules); k = BITS_PER_WORD; for (j = 0; j <= nrules; k++, j++) { if (k >= BITS_PER_WORD) { cword = *rp++; k = 0; } if (cword & (1 << k)) printf(" %d\n", j); } } fflush(stdout); } #endif base-7.0.3.1/modules/libcom/src/yacc/defs.h0000664000577000060420000002126213557101274017133 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include /* machine-dependent definitions */ /* the following definitions are for the Tahoe */ /* they might have to be changed for other machines */ /* MAXCHAR is the largest unsigned character value */ /* MAXSHORT is the largest value of a C short */ /* MINSHORT is the most negative value of a C short */ /* MAXTABLE is the maximum table size */ /* BITS_PER_WORD is the number of bits in a C unsigned */ /* WORDSIZE computes the number of words needed to */ /* store n bits */ /* BIT returns the value of the n-th bit starting */ /* from r (0-indexed) */ /* SETBIT sets the n-th bit starting from r */ #define MAXCHAR 255 #define MAXSHORT 32767 #define MINSHORT -32768 #define MAXTABLE 32500 #define BITS_PER_WORD 32 #define WORDSIZE(n) (((n)+(BITS_PER_WORD-1))/BITS_PER_WORD) #define BIT(r, n) ((((r)[(n)>>5])>>((n)&31))&1) #define SETBIT(r, n) ((r)[(n)>>5]|=((unsigned)1<<((n)&31))) /* character names */ #define NUL '\0' /* the null character */ #define NEWLINE '\n' /* line feed */ #define SP ' ' /* space */ #define BS '\b' /* backspace */ #define HT '\t' /* horizontal tab */ #define VT '\013' /* vertical tab */ #define CR '\r' /* carriage return */ #define FF '\f' /* form feed */ #define QUOTE '\'' /* single quote */ #define DOUBLE_QUOTE '\"' /* double quote */ #define BACKSLASH '\\' /* backslash */ /* defines for constructing filenames */ #define CODE_SUFFIX ".code.c" #define DEFINES_SUFFIX ".tab.h" #define OUTPUT_SUFFIX ".tab.c" #define VERBOSE_SUFFIX ".output" /* keyword codes */ #define TOKEN 0 #define LEFT 1 #define RIGHT 2 #define NONASSOC 3 #define MARK 4 #define TEXT 5 #define TYPE 6 #define START 7 #define UNION 8 #define IDENT 9 /* symbol classes */ #define UNKNOWN 0 #define TERM 1 #define NONTERM 2 /* the undefined value */ #define UNDEFINED (-1) /* action codes */ #define SHIFT 1 #define REDUCE 2 /* character macros */ #define IS_IDENT(c) (isalnum(c) || (c) == '_' || (c) == '.' || (c) == '$') #define IS_OCTAL(c) ((c) >= '0' && (c) <= '7') #define NUMERIC_VALUE(c) ((c) - '0') /* symbol macros */ #define ISTOKEN(s) ((s) < start_symbol) #define ISVAR(s) ((s) >= start_symbol) /* storage allocation macros */ #define CALLOC(k,n) (calloc((unsigned)(k),(unsigned)(n))) #define FREE(x) (free((char*)(x))) #define MALLOC(n) (malloc((unsigned)(n))) #define NEW(t) ((t*)allocate(sizeof(t))) #define NEW2(n,t) ((t*)allocate((unsigned)((n)*sizeof(t)))) #define REALLOC(p,n) (realloc((char*)(p),(unsigned)(n))) /* the structure of a symbol table entry */ typedef struct bucket bucket; struct bucket { struct bucket *link; struct bucket *next; char *name; char *tag; short value; short index; short prec; char class; char assoc; }; /* the structure of the LR(0) state machine */ typedef struct core core; struct core { struct core *next; struct core *link; short number; short accessing_symbol; short nitems; short items[1]; }; /* the structure used to record shifts */ typedef struct shifts shifts; struct shifts { struct shifts *next; short number; short nshifts; short shift[1]; }; /* the structure used to store reductions */ typedef struct reductions reductions; struct reductions { struct reductions *next; short number; short nreds; short rules[1]; }; /* the structure used to represent parser actions */ typedef struct action action; struct action { struct action *next; short symbol; short number; short prec; char action_code; char assoc; char suppressed; }; /* global variables */ extern char dflag; extern char lflag; extern char rflag; extern char tflag; extern char vflag; extern char *symbol_prefix; extern char *myname; extern char *cptr; extern char *line; extern int lineno; extern int outline; extern char *banner[]; extern char *tables[]; extern char *header[]; extern char *body[]; extern char *trailer[]; extern char *code_file_name; extern char *defines_file_name; extern char *input_file_name; extern char *output_file_name; extern char *verbose_file_name; extern FILE *action_file; extern FILE *code_file; extern FILE *defines_file; extern FILE *input_file; extern FILE *output_file; extern FILE *text_file; extern FILE *union_file; extern FILE *verbose_file; extern int nitems; extern int nrules; extern int nsyms; extern int ntokens; extern int nvars; extern int ntags; extern char unionized; extern char line_format[]; extern int start_symbol; extern char **symbol_name; extern short *symbol_value; extern short *symbol_prec; extern char *symbol_assoc; extern short *ritem; extern short *rlhs; extern short *rrhs; extern short *rprec; extern char *rassoc; extern short **derives; extern char *nullable; extern bucket *first_symbol; extern bucket *last_symbol; extern int nstates; extern core *first_state; extern shifts *first_shift; extern reductions *first_reduction; extern short *accessing_symbol; extern core **state_table; extern shifts **shift_table; extern reductions **reduction_table; extern unsigned *LA; extern short *LAruleno; extern short *lookaheads; extern short *goto_map; extern short *from_state; extern short *to_state; extern action **parser; extern int SRtotal; extern int RRtotal; extern short *SRconflicts; extern short *RRconflicts; extern short *defred; extern short *rules_used; extern short nunused; extern short final_state; /* * global functions */ #ifdef __GNUC__ #define NORETURN __attribute__((noreturn)) #else #define NORETURN #endif /* main.c */ extern void done(int k) NORETURN; extern char *allocate(unsigned int n); /* error.c */ extern void no_space(void) NORETURN; extern void fatal(char *msg) NORETURN; extern void open_error(char *filename) NORETURN; extern void unexpected_EOF(void) NORETURN; extern void syntax_error(int st_lineno, char *st_line, char *st_cptr) NORETURN; extern void unterminated_comment(int c_lineno, char *c_line, char *c_cptr) NORETURN; extern void unterminated_string(int s_lineno, char *s_line, char *s_cptr) NORETURN; extern void unterminated_text(int t_lineno, char *t_line, char *t_cptr) NORETURN; extern void unterminated_union(int u_lineno, char *u_line, char *u_cptr) NORETURN; extern void over_unionized(char *u_cptr) NORETURN; extern void illegal_tag(int t_lineno, char *t_line, char *t_cptr) NORETURN; extern void illegal_character(char *c_cptr) NORETURN; extern void used_reserved(char *s) NORETURN; extern void tokenized_start(char *s) NORETURN; extern void retyped_warning(char *s); extern void reprec_warning(char *s); extern void revalued_warning(char *s); extern void terminal_start(char *s); extern void restarted_warning(void); extern void no_grammar(void) NORETURN; extern void terminal_lhs(int s_lineno) NORETURN; extern void prec_redeclared(void); extern void unterminated_action(int a_lineno, char *a_line, char *a_cptr) NORETURN; extern void dollar_warning(int a_lineno, int i); extern void dollar_error(int a_lineno, char *a_line, char *a_cptr) NORETURN; extern void untyped_lhs(void) NORETURN; extern void untyped_rhs(int i, char *s) NORETURN; extern void unknown_rhs(int i) NORETURN; extern void default_action_warning(void); extern void undefined_goal(char *s) NORETURN; extern void undefined_symbol_warning(char *s); /* symtab.c */ extern bucket *make_bucket(char *name); extern bucket *lookup(char *name); extern void create_symbol_table(void); extern void free_symbol_table(void); extern void free_symbols(void); /* closure.c */ extern void set_first_derives(void); extern void closure(short int *nucleus, int n); extern void finalize_closure(void); /* reader.c */ extern void reader(void); /* lr0.c */ extern void lr0(void); /* lalr.c */ extern void lalr(void); /* mkpar.c */ extern void make_parser(void); extern void free_parser(void); /* warshall.c */ extern void reflexive_transitive_closure(unsigned int *R, int n); /* output.c */ extern void output(void); extern int default_goto(int symbol); extern int matching_vector(int vector); extern int pack_vector(int vector); /* skeleton.c */ extern void write_section(char *section[]); /* verbose.c */ extern void verbose(void); /* system includes */ #include #include #include base-7.0.3.1/modules/libcom/src/yacc/error.c0000664000577000060420000001467013557101274017343 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* routines for printing error messages */ #include "defs.h" void fatal(char *msg) { fprintf(stderr, "%s: fatal - %s\n", myname, msg); done(2); } void no_space(void) { fprintf(stderr, "%s: fatal - out of space\n", myname); done(2); } void open_error(char *filename) { fprintf(stderr, "%s: fatal - cannot open \"%s\"\n", myname, filename); done(2); } void unexpected_EOF(void) { fprintf(stderr, "%s: error - line %d of \"%s\", unexpected end-of-file\n", myname, lineno, input_file_name); done(1); } static int print_pos(char *st_line, char *st_cptr) { char *s; if (st_line == 0) return(0); for (s = st_line; *s != '\n'; ++s) { if (isprint((int) *s) || *s == '\t') putc(*s, stderr); else putc('?', stderr); } putc('\n', stderr); for (s = st_line; s < st_cptr; ++s) { if (*s == '\t') putc('\t', stderr); else putc(' ', stderr); } putc('^', stderr); putc('\n', stderr); return(0); } void syntax_error(int st_lineno, char *st_line, char *st_cptr) { fprintf(stderr, "%s: error - line %d of \"%s\", syntax error\n", myname, st_lineno, input_file_name); print_pos(st_line, st_cptr); done(1); } void unterminated_comment(int c_lineno, char *c_line, char *c_cptr) { fprintf(stderr, "%s: error - line %d of \"%s\", unmatched /*\n", myname, c_lineno, input_file_name); print_pos(c_line, c_cptr); done(1); } void unterminated_string(int s_lineno, char *s_line, char *s_cptr) { fprintf(stderr, "%s: error - line %d of \"%s\", unterminated string\n", myname, s_lineno, input_file_name); print_pos(s_line, s_cptr); done(1); } void unterminated_text(int t_lineno, char *t_line, char *t_cptr) { fprintf(stderr, "%s: error - line %d of \"%s\", unmatched %%{\n", myname, t_lineno, input_file_name); print_pos(t_line, t_cptr); done(1); } void unterminated_union(int u_lineno, char *u_line, char *u_cptr) { fprintf(stderr, "%s: error - line %d of \"%s\", unterminated %%union \ declaration\n", myname, u_lineno, input_file_name); print_pos(u_line, u_cptr); done(1); } void over_unionized(char *u_cptr) { fprintf(stderr, "%s: error - line %d of \"%s\", too many %%union \ declarations\n", myname, lineno, input_file_name); print_pos(line, u_cptr); done(1); } void illegal_tag(int t_lineno, char *t_line, char *t_cptr) { fprintf(stderr, "%s: error - line %d of \"%s\", illegal tag\n", myname, t_lineno, input_file_name); print_pos(t_line, t_cptr); done(1); } void illegal_character(char *c_cptr) { fprintf(stderr, "%s: error - line %d of \"%s\", illegal character\n", myname, lineno, input_file_name); print_pos(line, c_cptr); done(1); } void used_reserved(char *s) { fprintf(stderr, "%s: error - line %d of \"%s\", illegal use of reserved symbol \ %s\n", myname, lineno, input_file_name, s); done(1); } void tokenized_start(char *s) { fprintf(stderr, "%s: error - line %d of \"%s\", the start symbol %s cannot be \ declared to be a token\n", myname, lineno, input_file_name, s); done(1); } void retyped_warning(char *s) { fprintf(stderr, "%s: warning - line %d of \"%s\", the type of %s has been \ redeclared\n", myname, lineno, input_file_name, s); } void reprec_warning(char *s) { fprintf(stderr, "%s: warning - line %d of \"%s\", the precedence of %s has been \ redeclared\n", myname, lineno, input_file_name, s); } void revalued_warning(char *s) { fprintf(stderr, "%s: warning - line %d of \"%s\", the value of %s has been \ redeclared\n", myname, lineno, input_file_name, s); } void terminal_start(char *s) { fprintf(stderr, "%s: error - line %d of \"%s\", the start symbol %s is a \ token\n", myname, lineno, input_file_name, s); done(1); } void restarted_warning(void) { fprintf(stderr, "%s: warning - line %d of \"%s\", the start symbol has been \ redeclared\n", myname, lineno, input_file_name); } void no_grammar(void) { fprintf(stderr, "%s: error - line %d of \"%s\", no grammar has been \ specified\n", myname, lineno, input_file_name); done(1); } void terminal_lhs(int s_lineno) { fprintf(stderr, "%s: error - line %d of \"%s\", a token appears on the lhs \ of a production\n", myname, s_lineno, input_file_name); done(1); } void prec_redeclared(void) { fprintf(stderr, "%s: warning - line %d of \"%s\", conflicting %%prec \ specifiers\n", myname, lineno, input_file_name); } void unterminated_action(int a_lineno, char *a_line, char *a_cptr) { fprintf(stderr, "%s: error - line %d of \"%s\", unterminated action\n", myname, a_lineno, input_file_name); print_pos(a_line, a_cptr); done(1); } void dollar_warning(int a_lineno, int i) { fprintf(stderr, "%s: warning - line %d of \"%s\", $%d references beyond the \ end of the current rule\n", myname, a_lineno, input_file_name, i); } void dollar_error(int a_lineno, char *a_line, char *a_cptr) { fprintf(stderr, "%s: error - line %d of \"%s\", illegal $-name\n", myname, a_lineno, input_file_name); print_pos(a_line, a_cptr); done(1); } void untyped_lhs(void) { fprintf(stderr, "%s: error - line %d of \"%s\", $$ is untyped\n", myname, lineno, input_file_name); done(1); } void untyped_rhs(int i, char *s) { fprintf(stderr, "%s: error - line %d of \"%s\", $%d (%s) is untyped\n", myname, lineno, input_file_name, i, s); done(1); } void unknown_rhs(int i) { fprintf(stderr, "%s: error - line %d of \"%s\", $%d is untyped\n", myname, lineno, input_file_name, i); done(1); } void default_action_warning(void) { fprintf(stderr, "%s: warning - line %d of \"%s\", the default action assigns an \ undefined value to $$\n", myname, lineno, input_file_name); } void undefined_goal(char *s) { fprintf(stderr, "%s: error - the start symbol %s is undefined\n", myname, s); done(1); } void undefined_symbol_warning(char *s) { fprintf(stderr, "%s: warning - the symbol %s is undefined\n", myname, s); } base-7.0.3.1/modules/libcom/src/yacc/lalr.c0000664000577000060420000002506713557101274017146 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "defs.h" typedef struct shorts { struct shorts *next; short value; } shorts; int tokensetsize; short *lookaheads; short *LAruleno; unsigned *LA; short *accessing_symbol; core **state_table; shifts **shift_table; reductions **reduction_table; short *goto_map; short *from_state; short *to_state; static void set_state_table(void); static void set_accessing_symbol(void); static void set_shift_table(void); static void set_reduction_table(void); static void set_maxrhs(void); static void initialize_LA(void); static void set_goto_map(void); static void initialize_F(void); static void build_relations(void); static void add_lookback_edge(int stateno, int ruleno, int gotono); static short **transpose(short int **R, int n); static void compute_FOLLOWS(void); static void compute_lookaheads(void); static void digraph(short **relation); static void traverse(int i); static int infinity; static int maxrhs; static int ngotos; static unsigned *F; static short **includes; static shorts **lookback; static short **R; static short *INDEX; static short *VERTICES; static int top; void lalr(void) { tokensetsize = WORDSIZE(ntokens); set_state_table(); set_accessing_symbol(); set_shift_table(); set_reduction_table(); set_maxrhs(); initialize_LA(); set_goto_map(); initialize_F(); build_relations(); compute_FOLLOWS(); compute_lookaheads(); } static void set_state_table(void) { core *sp; state_table = NEW2(nstates, core *); for (sp = first_state; sp; sp = sp->next) state_table[sp->number] = sp; } static void set_accessing_symbol(void) { core *sp; accessing_symbol = NEW2(nstates, short); for (sp = first_state; sp; sp = sp->next) accessing_symbol[sp->number] = sp->accessing_symbol; } static void set_shift_table(void) { shifts *sp; shift_table = NEW2(nstates, shifts *); for (sp = first_shift; sp; sp = sp->next) shift_table[sp->number] = sp; } static void set_reduction_table(void) { reductions *rp; reduction_table = NEW2(nstates, reductions *); for (rp = first_reduction; rp; rp = rp->next) reduction_table[rp->number] = rp; } static void set_maxrhs(void) { short *itemp; short *item_end; int length; int max; length = 0; max = 0; item_end = ritem + nitems; for (itemp = ritem; itemp < item_end; itemp++) { if (*itemp >= 0) { length++; } else { if (length > max) max = length; length = 0; } } maxrhs = max; } static void initialize_LA(void) { int i, j, k; reductions *rp; lookaheads = NEW2(nstates + 1, short); k = 0; for (i = 0; i < nstates; i++) { lookaheads[i] = k; rp = reduction_table[i]; if (rp) k += rp->nreds; } lookaheads[nstates] = k; LA = NEW2(k * tokensetsize, unsigned); LAruleno = NEW2(k, short); lookback = NEW2(k, shorts *); k = 0; for (i = 0; i < nstates; i++) { rp = reduction_table[i]; if (rp) { for (j = 0; j < rp->nreds; j++) { LAruleno[k] = rp->rules[j]; k++; } } } } static void set_goto_map(void) { shifts *sp; int i; int symbol; int k; short *temp_map; int state2; int state1; goto_map = NEW2(nvars + 1, short) - ntokens; temp_map = NEW2(nvars + 1, short) - ntokens; ngotos = 0; for (sp = first_shift; sp; sp = sp->next) { for (i = sp->nshifts - 1; i >= 0; i--) { symbol = accessing_symbol[sp->shift[i]]; if (ISTOKEN(symbol)) break; if (ngotos == MAXSHORT) fatal("too many gotos"); ngotos++; goto_map[symbol]++; } } k = 0; for (i = ntokens; i < nsyms; i++) { temp_map[i] = k; k += goto_map[i]; } for (i = ntokens; i < nsyms; i++) goto_map[i] = temp_map[i]; goto_map[nsyms] = ngotos; temp_map[nsyms] = ngotos; from_state = NEW2(ngotos, short); to_state = NEW2(ngotos, short); for (sp = first_shift; sp; sp = sp->next) { state1 = sp->number; for (i = sp->nshifts - 1; i >= 0; i--) { state2 = sp->shift[i]; symbol = accessing_symbol[state2]; if (ISTOKEN(symbol)) break; k = temp_map[symbol]++; from_state[k] = state1; to_state[k] = state2; } } FREE(temp_map + ntokens); } /* Map_goto maps a state/symbol pair into its numeric representation. */ int map_goto(int state, int symbol) { int high; int low; int middle; int s; low = goto_map[symbol]; high = goto_map[symbol + 1]; for (;;) { assert(low <= high); middle = (low + high) >> 1; s = from_state[middle]; if (s == state) return (middle); else if (s < state) low = middle + 1; else high = middle - 1; } } static void initialize_F(void) { int i; int j; int k; shifts *sp; short *edge; unsigned *rowp; short *rp; short **reads; int nedges; int stateno; int symbol; int nwords; nwords = ngotos * tokensetsize; F = NEW2(nwords, unsigned); reads = NEW2(ngotos, short *); edge = NEW2(ngotos + 1, short); nedges = 0; rowp = F; for (i = 0; i < ngotos; i++) { stateno = to_state[i]; sp = shift_table[stateno]; if (sp) { k = sp->nshifts; for (j = 0; j < k; j++) { symbol = accessing_symbol[sp->shift[j]]; if (ISVAR(symbol)) break; SETBIT(rowp, symbol); } for (; j < k; j++) { symbol = accessing_symbol[sp->shift[j]]; if (nullable[symbol]) edge[nedges++] = map_goto(stateno, symbol); } if (nedges) { reads[i] = rp = NEW2(nedges + 1, short); for (j = 0; j < nedges; j++) rp[j] = edge[j]; rp[nedges] = -1; nedges = 0; } } rowp += tokensetsize; } SETBIT(F, 0); digraph(reads); for (i = 0; i < ngotos; i++) { if (reads[i]) FREE(reads[i]); } FREE(reads); FREE(edge); } static void build_relations(void) { int i; int j; int k; short *rulep; short *rp; shifts *sp; int length; int nedges; int done; int state1; int stateno; int symbol1; int symbol2; short *shortp; short *edge; short *states; short **new_includes; includes = NEW2(ngotos, short *); edge = NEW2(ngotos + 1, short); states = NEW2(maxrhs + 1, short); for (i = 0; i < ngotos; i++) { nedges = 0; state1 = from_state[i]; symbol1 = accessing_symbol[to_state[i]]; for (rulep = derives[symbol1]; *rulep >= 0; rulep++) { length = 1; states[0] = state1; stateno = state1; for (rp = ritem + rrhs[*rulep]; *rp >= 0; rp++) { symbol2 = *rp; sp = shift_table[stateno]; k = sp->nshifts; for (j = 0; j < k; j++) { stateno = sp->shift[j]; if (accessing_symbol[stateno] == symbol2) break; } states[length++] = stateno; } add_lookback_edge(stateno, *rulep, i); length--; done = 0; while (!done) { done = 1; rp--; if (ISVAR(*rp)) { stateno = states[--length]; edge[nedges++] = map_goto(stateno, *rp); if (nullable[*rp] && length > 0) done = 0; } } } if (nedges) { includes[i] = shortp = NEW2(nedges + 1, short); for (j = 0; j < nedges; j++) shortp[j] = edge[j]; shortp[nedges] = -1; } } new_includes = transpose(includes, ngotos); for (i = 0; i < ngotos; i++) if (includes[i]) FREE(includes[i]); FREE(includes); includes = new_includes; FREE(edge); FREE(states); } static void add_lookback_edge(int stateno, int ruleno, int gotono) { int i, k; int found; shorts *sp; i = lookaheads[stateno]; k = lookaheads[stateno + 1]; found = 0; while (!found && i < k) { if (LAruleno[i] == ruleno) found = 1; else ++i; } assert(found); sp = NEW(shorts); sp->next = lookback[i]; sp->value = gotono; lookback[i] = sp; } short ** transpose(short int **R, int n) { short **new_R; short **temp_R; short *nedges; short *sp; int i; int k; nedges = NEW2(n, short); for (i = 0; i < n; i++) { sp = R[i]; if (sp) { while (*sp >= 0) nedges[*sp++]++; } } new_R = NEW2(n, short *); temp_R = NEW2(n, short *); for (i = 0; i < n; i++) { k = nedges[i]; if (k > 0) { sp = NEW2(k + 1, short); new_R[i] = sp; temp_R[i] = sp; sp[k] = -1; } } FREE(nedges); for (i = 0; i < n; i++) { sp = R[i]; if (sp) { while (*sp >= 0) *temp_R[*sp++]++ = i; } } FREE(temp_R); return (new_R); } static void compute_FOLLOWS(void) { digraph(includes); } static void compute_lookaheads(void) { int i, n; unsigned *fp1, *fp2, *fp3; shorts *sp, *next; unsigned *rowp; rowp = LA; n = lookaheads[nstates]; for (i = 0; i < n; i++) { fp3 = rowp + tokensetsize; for (sp = lookback[i]; sp; sp = sp->next) { fp1 = rowp; fp2 = F + tokensetsize * sp->value; while (fp1 < fp3) *fp1++ |= *fp2++; } rowp = fp3; } for (i = 0; i < n; i++) for (sp = lookback[i]; sp; sp = next) { next = sp->next; FREE(sp); } FREE(lookback); FREE(F); } static void digraph(short **relation) { int i; infinity = ngotos + 2; INDEX = NEW2(ngotos + 1, short); VERTICES = NEW2(ngotos + 1, short); top = 0; R = relation; for (i = 0; i < ngotos; i++) INDEX[i] = 0; for (i = 0; i < ngotos; i++) { if (INDEX[i] == 0 && R[i]) traverse(i); } FREE(INDEX); FREE(VERTICES); } static void traverse(int i) { unsigned *fp1; unsigned *fp2; unsigned *fp3; int j; short *rp; int height; unsigned *base; VERTICES[++top] = i; INDEX[i] = height = top; base = F + i * tokensetsize; fp3 = base + tokensetsize; rp = R[i]; if (rp) { while ((j = *rp++) >= 0) { if (INDEX[j] == 0) traverse(j); if (INDEX[i] > INDEX[j]) INDEX[i] = INDEX[j]; fp1 = base; fp2 = F + j * tokensetsize; while (fp1 < fp3) *fp1++ |= *fp2++; } } if (INDEX[i] == height) { for (;;) { j = VERTICES[top--]; INDEX[j] = infinity; if (i == j) break; fp1 = base; fp2 = F + j * tokensetsize; while (fp1 < fp3) *fp2++ = *fp1++; } } } base-7.0.3.1/modules/libcom/src/yacc/lr0.c0000664000577000060420000002343113557101274016702 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "defs.h" extern short *itemset; extern short *itemsetend; extern unsigned *ruleset; int nstates; core *first_state; shifts *first_shift; reductions *first_reduction; static core **state_set; static core *this_state; static core *last_state; static shifts *last_shift; static reductions *last_reduction; static int nshifts; static short *shift_symbol; static short *redset; static short *shiftset; static short **kernel_base; static short **kernel_end; static short *kernel_items; static int get_state(int symbol); static void initialize_states(void); static void new_itemsets(void); static core *new_state(int symbol); static void save_shifts(void); static void save_reductions(void); #ifdef DEBUG static void print_derives(void); #endif static void allocate_itemsets(void) { short *itemp; short *item_end; int symbol; int i; int count; int max; short *symbol_count; count = 0; symbol_count = NEW2(nsyms, short); item_end = ritem + nitems; for (itemp = ritem; itemp < item_end; itemp++) { symbol = *itemp; if (symbol >= 0) { count++; symbol_count[symbol]++; } } kernel_base = NEW2(nsyms, short *); kernel_items = NEW2(count, short); count = 0; max = 0; for (i = 0; i < nsyms; i++) { kernel_base[i] = kernel_items + count; count += symbol_count[i]; if (max < symbol_count[i]) max = symbol_count[i]; } shift_symbol = symbol_count; kernel_end = NEW2(nsyms, short *); } static void allocate_storage(void) { allocate_itemsets(); shiftset = NEW2(nsyms, short); redset = NEW2(nrules + 1, short); state_set = NEW2(nitems, core *); } static void append_states(void) { int i; int j; int symbol; #ifdef TRACE fprintf(stderr, "Entering append_states()\n"); #endif for (i = 1; i < nshifts; i++) { symbol = shift_symbol[i]; j = i; while (j > 0 && shift_symbol[j - 1] > symbol) { shift_symbol[j] = shift_symbol[j - 1]; j--; } shift_symbol[j] = symbol; } for (i = 0; i < nshifts; i++) { symbol = shift_symbol[i]; shiftset[i] = get_state(symbol); } } static void free_storage(void) { FREE(shift_symbol); FREE(redset); FREE(shiftset); FREE(kernel_base); FREE(kernel_end); FREE(kernel_items); FREE(state_set); } static void generate_states(void) { allocate_storage(); itemset = NEW2(nitems, short); ruleset = NEW2(WORDSIZE(nrules), unsigned); set_first_derives(); initialize_states(); while (this_state) { closure(this_state->items, this_state->nitems); save_reductions(); new_itemsets(); append_states(); if (nshifts > 0) save_shifts(); this_state = this_state->next; } finalize_closure(); free_storage(); } static int get_state(int symbol) { int key; short *isp1; short *isp2; short *iend; core *sp; int found; int n; #ifdef TRACE fprintf(stderr, "Entering get_state(%d)\n", symbol); #endif isp1 = kernel_base[symbol]; iend = kernel_end[symbol]; n = iend - isp1; key = *isp1; assert(0 <= key && key < nitems); sp = state_set[key]; if (sp) { found = 0; while (!found) { if (sp->nitems == n) { found = 1; isp1 = kernel_base[symbol]; isp2 = sp->items; while (found && isp1 < iend) { if (*isp1++ != *isp2++) found = 0; } } if (!found) { if (sp->link) { sp = sp->link; } else { sp = sp->link = new_state(symbol); found = 1; } } } } else { state_set[key] = sp = new_state(symbol); } return (sp->number); } static void initialize_states(void) { int i; short *start_derives; core *p; start_derives = derives[start_symbol]; for (i = 0; start_derives[i] >= 0; ++i) continue; p = (core *) MALLOC(sizeof(core) + i*sizeof(short)); if (p == 0) no_space(); p->next = 0; p->link = 0; p->number = 0; p->accessing_symbol = 0; p->nitems = i; for (i = 0; start_derives[i] >= 0; ++i) p->items[i] = rrhs[start_derives[i]]; first_state = last_state = this_state = p; nstates = 1; } static void new_itemsets(void) { int i; int shiftcount; short *isp; short *ksp; int symbol; for (i = 0; i < nsyms; i++) kernel_end[i] = 0; shiftcount = 0; isp = itemset; while (isp < itemsetend) { i = *isp++; symbol = ritem[i]; if (symbol > 0) { ksp = kernel_end[symbol]; if (!ksp) { shift_symbol[shiftcount++] = symbol; ksp = kernel_base[symbol]; } *ksp++ = i + 1; kernel_end[symbol] = ksp; } } nshifts = shiftcount; } static core * new_state(int symbol) { int n; core *p; short *isp1; short *isp2; short *iend; #ifdef TRACE fprintf(stderr, "Entering new_state(%d)\n", symbol); #endif if (nstates >= MAXSHORT) fatal("too many states"); isp1 = kernel_base[symbol]; iend = kernel_end[symbol]; n = iend - isp1; p = (core *) allocate((unsigned) (sizeof(core) + (n - 1) * sizeof(short))); p->accessing_symbol = symbol; p->number = nstates; p->nitems = n; isp2 = p->items; while (isp1 < iend) *isp2++ = *isp1++; last_state->next = p; last_state = p; nstates++; return (p); } #ifdef DEBUG static void show_cores(void) { core *p; int i, j, k, n; int itemno; k = 0; for (p = first_state; p; ++k, p = p->next) { if (k) printf("\n"); printf("state %d, number = %d, accessing symbol = %s\n", k, p->number, symbol_name[p->accessing_symbol]); n = p->nitems; for (i = 0; i < n; ++i) { itemno = p->items[i]; printf("%4d ", itemno); j = itemno; while (ritem[j] >= 0) ++j; printf("%s :", symbol_name[rlhs[-ritem[j]]]); j = rrhs[-ritem[j]]; while (j < itemno) printf(" %s", symbol_name[ritem[j++]]); printf(" ."); while (ritem[j] >= 0) printf(" %s", symbol_name[ritem[j++]]); printf("\n"); fflush(stdout); } } } static void show_ritems(void) { int i; for (i = 0; i < nitems; ++i) printf("ritem[%d] = %d\n", i, ritem[i]); } static void show_rrhs(void) { int i; for (i = 0; i < nrules; ++i) printf("rrhs[%d] = %d\n", i, rrhs[i]); } static void show_shifts(void) { shifts *p; int i, j, k; k = 0; for (p = first_shift; p; ++k, p = p->next) { if (k) printf("\n"); printf("shift %d, number = %d, nshifts = %d\n", k, p->number, p->nshifts); j = p->nshifts; for (i = 0; i < j; ++i) printf("\t%d\n", p->shift[i]); } } #endif static void save_shifts(void) { shifts *p; short *sp1; short *sp2; short *send; p = (shifts *) allocate((unsigned) (sizeof(shifts) + (nshifts - 1) * sizeof(short))); p->number = this_state->number; p->nshifts = nshifts; sp1 = shiftset; sp2 = p->shift; send = shiftset + nshifts; while (sp1 < send) *sp2++ = *sp1++; if (last_shift) { last_shift->next = p; last_shift = p; } else { first_shift = p; last_shift = p; } } static void save_reductions(void) { short *isp; short *rp1; short *rp2; int item; int count; reductions *p; short *rend; count = 0; for (isp = itemset; isp < itemsetend; isp++) { item = ritem[*isp]; if (item < 0) { redset[count++] = -item; } } if (count) { p = (reductions *) allocate((unsigned) (sizeof(reductions) + (count - 1) * sizeof(short))); p->number = this_state->number; p->nreds = count; rp1 = redset; rp2 = p->rules; rend = rp1 + count; while (rp1 < rend) *rp2++ = *rp1++; if (last_reduction) { last_reduction->next = p; last_reduction = p; } else { first_reduction = p; last_reduction = p; } } } static void set_derives(void) { int i, k; int lhs; short *rules; derives = NEW2(nsyms, short *); rules = NEW2(nvars + nrules, short); k = 0; for (lhs = start_symbol; lhs < nsyms; lhs++) { derives[lhs] = rules + k; for (i = 0; i < nrules; i++) { if (rlhs[i] == lhs) { rules[k] = i; k++; } } rules[k] = -1; k++; } #ifdef DEBUG print_derives(); #endif } void free_derives(void) { FREE(derives[start_symbol]); FREE(derives); } #ifdef DEBUG static void print_derives(void) { int i; short *sp; printf("\nDERIVES\n\n"); for (i = start_symbol; i < nsyms; i++) { printf("%s derives ", symbol_name[i]); for (sp = derives[i]; *sp >= 0; sp++) { printf(" %d", *sp); } putchar('\n'); } putchar('\n'); } #endif static void set_nullable(void) { int i, j; int empty; int done; nullable = MALLOC(nsyms); if (nullable == 0) no_space(); for (i = 0; i < nsyms; ++i) nullable[i] = 0; done = 0; while (!done) { done = 1; for (i = 1; i < nitems; i++) { empty = 1; while ((j = ritem[i]) >= 0) { if (!nullable[j]) empty = 0; ++i; } if (empty) { j = rlhs[-j]; if (!nullable[j]) { nullable[j] = 1; done = 0; } } } } #ifdef DEBUG for (i = 0; i < nsyms; i++) { if (nullable[i]) printf("%s is nullable\n", symbol_name[i]); else printf("%s is not nullable\n", symbol_name[i]); } #endif } void free_nullable(void) { FREE(nullable); } void lr0(void) { set_derives(); set_nullable(); generate_states(); } base-7.0.3.1/modules/libcom/src/yacc/mkpar.c0000664000577000060420000001541713557101274017324 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "defs.h" action **parser; int SRtotal; int RRtotal; short *SRconflicts; short *RRconflicts; short *defred; short *rules_used; short nunused; short final_state; static int SRcount; static int RRcount; static action *parse_actions(int stateno); static action *get_shifts(int stateno); static action *add_reductions(int stateno, action *actions); static action *add_reduce(action *actions, int ruleno, int symbol); static void find_final_state(void); static void unused_rules(void); static void remove_conflicts(void); static void total_conflicts(void); static void defreds(void); void make_parser(void) { int i; parser = NEW2(nstates, action *); for (i = 0; i < nstates; i++) parser[i] = parse_actions(i); find_final_state(); remove_conflicts(); unused_rules(); if (SRtotal + RRtotal > 0) total_conflicts(); defreds(); } static action * parse_actions(int stateno) { action *actions; actions = get_shifts(stateno); actions = add_reductions(stateno, actions); return (actions); } static action * get_shifts(int stateno) { action *actions, *temp; shifts *sp; short *to_state; int i, k; int symbol; actions = 0; sp = shift_table[stateno]; if (sp) { to_state = sp->shift; for (i = sp->nshifts - 1; i >= 0; i--) { k = to_state[i]; symbol = accessing_symbol[k]; if (ISTOKEN(symbol)) { temp = NEW(action); temp->next = actions; temp->symbol = symbol; temp->number = k; temp->prec = symbol_prec[symbol]; temp->action_code = SHIFT; temp->assoc = symbol_assoc[symbol]; actions = temp; } } } return (actions); } static action * add_reductions(int stateno, action *actions) { int i, j, m, n; int ruleno, tokensetsize; unsigned *rowp; tokensetsize = WORDSIZE(ntokens); m = lookaheads[stateno]; n = lookaheads[stateno + 1]; for (i = m; i < n; i++) { ruleno = LAruleno[i]; rowp = LA + i * tokensetsize; for (j = ntokens - 1; j >= 0; j--) { if (BIT(rowp, j)) actions = add_reduce(actions, ruleno, j); } } return (actions); } static action * add_reduce(action *actions, int ruleno, int symbol) { action *temp, *prev, *next; prev = 0; for (next = actions; next && next->symbol < symbol; next = next->next) prev = next; while (next && next->symbol == symbol && next->action_code == SHIFT) { prev = next; next = next->next; } while (next && next->symbol == symbol && next->action_code == REDUCE && next->number < ruleno) { prev = next; next = next->next; } temp = NEW(action); temp->next = next; temp->symbol = symbol; temp->number = ruleno; temp->prec = rprec[ruleno]; temp->action_code = REDUCE; temp->assoc = rassoc[ruleno]; if (prev) prev->next = temp; else actions = temp; return (actions); } static void find_final_state(void) { int goal, i; short *to_state; shifts *p; p = shift_table[0]; to_state = p->shift; goal = ritem[1]; for (i = p->nshifts - 1; i >= 0; --i) { final_state = to_state[i]; if (accessing_symbol[final_state] == goal) break; } } static void unused_rules(void) { int i; action *p; rules_used = (short *) MALLOC(nrules*sizeof(short)); if (rules_used == 0) no_space(); for (i = 0; i < nrules; ++i) rules_used[i] = 0; for (i = 0; i < nstates; ++i) { for (p = parser[i]; p; p = p->next) { if (p->action_code == REDUCE && p->suppressed == 0) rules_used[p->number] = 1; } } nunused = 0; for (i = 3; i < nrules; ++i) if (!rules_used[i]) ++nunused; if (nunused) { if (nunused == 1) fprintf(stderr, "%s: 1 rule never reduced\n", myname); else fprintf(stderr, "%s: %d rules never reduced\n", myname, nunused); } } static void remove_conflicts(void) { int i; int symbol; action *p, *pref = NULL; SRtotal = 0; RRtotal = 0; SRconflicts = NEW2(nstates, short); RRconflicts = NEW2(nstates, short); for (i = 0; i < nstates; i++) { SRcount = 0; RRcount = 0; symbol = -1; for (p = parser[i]; p; p = p->next) { if (p->symbol != symbol) { pref = p; symbol = p->symbol; } else if (i == final_state && symbol == 0) { SRcount++; p->suppressed = 1; } else if (pref && pref->action_code == SHIFT) { if (pref->prec > 0 && p->prec > 0) { if (pref->prec < p->prec) { pref->suppressed = 2; pref = p; } else if (pref->prec > p->prec) { p->suppressed = 2; } else if (pref->assoc == LEFT) { pref->suppressed = 2; pref = p; } else if (pref->assoc == RIGHT) { p->suppressed = 2; } else { pref->suppressed = 2; p->suppressed = 2; } } else { SRcount++; p->suppressed = 1; } } else { RRcount++; p->suppressed = 1; } } SRtotal += SRcount; RRtotal += RRcount; SRconflicts[i] = SRcount; RRconflicts[i] = RRcount; } } static void total_conflicts(void) { fprintf(stderr, "%s: ", myname); if (SRtotal == 1) fprintf(stderr, "1 shift/reduce conflict"); else if (SRtotal > 1) fprintf(stderr, "%d shift/reduce conflicts", SRtotal); if (SRtotal && RRtotal) fprintf(stderr, ", "); if (RRtotal == 1) fprintf(stderr, "1 reduce/reduce conflict"); else if (RRtotal > 1) fprintf(stderr, "%d reduce/reduce conflicts", RRtotal); fprintf(stderr, ".\n"); } static int sole_reduction(int stateno) { int count, ruleno; action *p; count = 0; ruleno = 0; for (p = parser[stateno]; p; p = p->next) { if (p->action_code == SHIFT && p->suppressed == 0) return 0; else if (p->action_code == REDUCE && p->suppressed == 0) { if (ruleno > 0 && p->number != ruleno) return 0; if (p->symbol != 1) ++count; ruleno = p->number; } } if (count == 0) return 0; return ruleno; } static void defreds(void) { int i; defred = NEW2(nstates, short); for (i = 0; i < nstates; i++) defred[i] = sole_reduction(i); } static void free_action_row(action *p) { action *q; while (p) { q = p->next; FREE(p); p = q; } } void free_parser(void) { int i; for (i = 0; i < nstates; i++) free_action_row(parser[i]); FREE(parser); } base-7.0.3.1/modules/libcom/src/yacc/output.c0000664000577000060420000005564413557101274017560 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "defs.h" static int nvectors; static int nentries; static short **froms; static short **tos; static short *tally; static short *width; static short *state_count; static short *order; static short *base; static short *pos; static int maxtable; static short *table; static short *check; static int lowzero; static int high; static void output_prefix(void); static void output_rule_data(void); static void output_yydefred(void); static void output_actions(void); static void token_actions(void); static void goto_actions(void); static void save_column(int symbol, int default_state); static void sort_actions(void); static void pack_table(void); static void output_base(void); static void output_table(void); static void output_check(void); static void output_defines(void); static void output_stored_text(void); static void output_debug(void); static void output_stype(void); static void output_trailing_text(void); static void output_semantic_actions(void); static void free_itemsets(void); static void free_shifts(void); static void free_reductions(void); void output(void) { free_itemsets(); free_shifts(); free_reductions(); output_prefix(); output_stored_text(); output_defines(); output_rule_data(); output_yydefred(); output_actions(); free_parser(); output_debug(); output_stype(); if (rflag) write_section(tables); write_section(header); output_trailing_text(); write_section(body); output_semantic_actions(); write_section(trailer); } static void output_prefix(void) { if (symbol_prefix == NULL) symbol_prefix = "yy"; else { ++outline; fprintf(code_file, "#define yyparse %sparse\n", symbol_prefix); ++outline; fprintf(code_file, "#define yylex %slex\n", symbol_prefix); ++outline; fprintf(code_file, "#define yyerror %serror\n", symbol_prefix); ++outline; fprintf(code_file, "#define yychar %schar\n", symbol_prefix); ++outline; fprintf(code_file, "#define yyval %sval\n", symbol_prefix); ++outline; fprintf(code_file, "#define yylval %slval\n", symbol_prefix); ++outline; fprintf(code_file, "#define yydebug %sdebug\n", symbol_prefix); ++outline; fprintf(code_file, "#define yynerrs %snerrs\n", symbol_prefix); ++outline; fprintf(code_file, "#define yyerrflag %serrflag\n", symbol_prefix); ++outline; fprintf(code_file, "#define yyss %sss\n", symbol_prefix); ++outline; fprintf(code_file, "#define yyssp %sssp\n", symbol_prefix); ++outline; fprintf(code_file, "#define yyvs %svs\n", symbol_prefix); ++outline; fprintf(code_file, "#define yyvsp %svsp\n", symbol_prefix); ++outline; fprintf(code_file, "#define yylhs %slhs\n", symbol_prefix); ++outline; fprintf(code_file, "#define yylen %slen\n", symbol_prefix); ++outline; fprintf(code_file, "#define yydefred %sdefred\n", symbol_prefix); ++outline; fprintf(code_file, "#define yydgoto %sdgoto\n", symbol_prefix); ++outline; fprintf(code_file, "#define yysindex %ssindex\n", symbol_prefix); ++outline; fprintf(code_file, "#define yyrindex %srindex\n", symbol_prefix); ++outline; fprintf(code_file, "#define yygindex %sgindex\n", symbol_prefix); ++outline; fprintf(code_file, "#define yytable %stable\n", symbol_prefix); ++outline; fprintf(code_file, "#define yycheck %scheck\n", symbol_prefix); ++outline; fprintf(code_file, "#define yyname %sname\n", symbol_prefix); ++outline; fprintf(code_file, "#define yyrule %srule\n", symbol_prefix); } ++outline; fprintf(code_file, "#define YYPREFIX \"%s\"\n", symbol_prefix); } static void output_rule_data(void) { int i; int j; fprintf(output_file, "static short %slhs[] = {%42d,", symbol_prefix, symbol_value[start_symbol]); j = 10; for (i = 3; i < nrules; i++) { if (j >= 10) { if (!rflag) ++outline; putc('\n', output_file); j = 1; } else ++j; fprintf(output_file, "%5d,", symbol_value[rlhs[i]]); } if (!rflag) outline += 2; fprintf(output_file, "\n};\n"); fprintf(output_file, "static short %slen[] = {%42d,", symbol_prefix, 2); j = 10; for (i = 3; i < nrules; i++) { if (j >= 10) { if (!rflag) ++outline; putc('\n', output_file); j = 1; } else j++; fprintf(output_file, "%5d,", rrhs[i + 1] - rrhs[i] - 1); } if (!rflag) outline += 2; fprintf(output_file, "\n};\n"); } static void output_yydefred(void) { int i, j; fprintf(output_file, "static short %sdefred[] = {%39d,", symbol_prefix, (defred[0] ? defred[0] - 2 : 0)); j = 10; for (i = 1; i < nstates; i++) { if (j < 10) ++j; else { if (!rflag) ++outline; putc('\n', output_file); j = 1; } fprintf(output_file, "%5d,", (defred[i] ? defred[i] - 2 : 0)); } if (!rflag) outline += 2; fprintf(output_file, "\n};\n"); } static void output_actions(void) { nvectors = 2*nstates + nvars; froms = NEW2(nvectors, short *); tos = NEW2(nvectors, short *); tally = NEW2(nvectors, short); width = NEW2(nvectors, short); token_actions(); FREE(lookaheads); FREE(LA); FREE(LAruleno); FREE(accessing_symbol); goto_actions(); FREE(goto_map + ntokens); FREE(from_state); FREE(to_state); sort_actions(); pack_table(); output_base(); output_table(); output_check(); } static void token_actions(void) { int i, j; int shiftcount, reducecount; int max, min; short *actionrow, *r, *s; action *p; actionrow = NEW2(2*ntokens, short); for (i = 0; i < nstates; ++i) { if (parser[i]) { for (j = 0; j < 2*ntokens; ++j) actionrow[j] = 0; shiftcount = 0; reducecount = 0; for (p = parser[i]; p; p = p->next) { if (p->suppressed == 0) { if (p->action_code == SHIFT) { ++shiftcount; actionrow[p->symbol] = p->number; } else if (p->action_code == REDUCE && p->number != defred[i]) { ++reducecount; actionrow[p->symbol + ntokens] = p->number; } } } tally[i] = shiftcount; tally[nstates+i] = reducecount; width[i] = 0; width[nstates+i] = 0; if (shiftcount > 0) { froms[i] = r = NEW2(shiftcount, short); tos[i] = s = NEW2(shiftcount, short); min = MAXSHORT; max = 0; for (j = 0; j < ntokens; ++j) { if (actionrow[j]) { if (min > symbol_value[j]) min = symbol_value[j]; if (max < symbol_value[j]) max = symbol_value[j]; *r++ = symbol_value[j]; *s++ = actionrow[j]; } } width[i] = max - min + 1; } if (reducecount > 0) { froms[nstates+i] = r = NEW2(reducecount, short); tos[nstates+i] = s = NEW2(reducecount, short); min = MAXSHORT; max = 0; for (j = 0; j < ntokens; ++j) { if (actionrow[ntokens+j]) { if (min > symbol_value[j]) min = symbol_value[j]; if (max < symbol_value[j]) max = symbol_value[j]; *r++ = symbol_value[j]; *s++ = actionrow[ntokens+j] - 2; } } width[nstates+i] = max - min + 1; } } } FREE(actionrow); } static void goto_actions(void) { int i, j, k; state_count = NEW2(nstates, short); k = default_goto(start_symbol + 1); fprintf(output_file, "static short %sdgoto[] = {%40d,", symbol_prefix, k); save_column(start_symbol + 1, k); j = 10; for (i = start_symbol + 2; i < nsyms; i++) { if (j >= 10) { if (!rflag) ++outline; putc('\n', output_file); j = 1; } else ++j; k = default_goto(i); fprintf(output_file, "%5d,", k); save_column(i, k); } if (!rflag) outline += 2; fprintf(output_file, "\n};\n"); FREE(state_count); } int default_goto(int symbol) { int i; int m; int n; int default_state; int max; m = goto_map[symbol]; n = goto_map[symbol + 1]; if (m == n) return (0); for (i = 0; i < nstates; i++) state_count[i] = 0; for (i = m; i < n; i++) state_count[to_state[i]]++; max = 0; default_state = 0; for (i = 0; i < nstates; i++) { if (state_count[i] > max) { max = state_count[i]; default_state = i; } } return (default_state); } static void save_column(int symbol, int default_state) { int i; int m; int n; short *sp; short *sp1; short *sp2; int count; int symno; m = goto_map[symbol]; n = goto_map[symbol + 1]; count = 0; for (i = m; i < n; i++) { if (to_state[i] != default_state) ++count; } if (count == 0) return; symno = symbol_value[symbol] + 2*nstates; froms[symno] = sp1 = sp = NEW2(count, short); tos[symno] = sp2 = NEW2(count, short); for (i = m; i < n; i++) { if (to_state[i] != default_state) { *sp1++ = from_state[i]; *sp2++ = to_state[i]; } } tally[symno] = count; width[symno] = sp1[-1] - sp[0] + 1; } static void sort_actions(void) { int i; int j; int k; int t; int w; order = NEW2(nvectors, short); nentries = 0; for (i = 0; i < nvectors; i++) { if (tally[i] > 0) { t = tally[i]; w = width[i]; j = nentries - 1; while (j >= 0 && (width[order[j]] < w)) j--; while (j >= 0 && (width[order[j]] == w) && (tally[order[j]] < t)) j--; for (k = nentries - 1; k > j; k--) order[k + 1] = order[k]; order[j + 1] = i; nentries++; } } } static void pack_table(void) { int i; int place; int state; base = NEW2(nvectors, short); pos = NEW2(nentries, short); maxtable = 1000; table = NEW2(maxtable, short); check = NEW2(maxtable, short); lowzero = 0; high = 0; for (i = 0; i < maxtable; i++) check[i] = -1; for (i = 0; i < nentries; i++) { state = matching_vector(i); if (state < 0) place = pack_vector(i); else place = base[state]; pos[i] = place; base[order[i]] = place; } for (i = 0; i < nvectors; i++) { if (froms[i]) FREE(froms[i]); if (tos[i]) FREE(tos[i]); } FREE(froms); FREE(tos); FREE(pos); } /* The function matching_vector determines if the vector specified by */ /* the input parameter matches a previously considered vector. The */ /* test at the start of the function checks if the vector represents */ /* a row of shifts over terminal symbols or a row of reductions, or a */ /* column of shifts over a nonterminal symbol. Berkeley Yacc does not */ /* check if a column of shifts over a nonterminal symbols matches a */ /* previously considered vector. Because of the nature of LR parsing */ /* tables, no two columns can match. Therefore, the only possible */ /* match would be between a row and a column. Such matches are */ /* unlikely. Therefore, to save time, no attempt is made to see if a */ /* column matches a previously considered vector. */ /* */ /* Matching_vector is poorly designed. The test could easily be made */ /* faster. Also, it depends on the vectors being in a specific */ /* order. */ int matching_vector(int vector) { int i; int j; int k; int t; int w; int match; int prev; i = order[vector]; if (i >= 2*nstates) return (-1); t = tally[i]; w = width[i]; for (prev = vector - 1; prev >= 0; prev--) { j = order[prev]; if (width[j] != w || tally[j] != t) return (-1); match = 1; for (k = 0; match && k < t; k++) { if (tos[j][k] != tos[i][k] || froms[j][k] != froms[i][k]) match = 0; } if (match) return (j); } return (-1); } int pack_vector(int vector) { int i, j, k, l; int t; int loc; int ok; short *from; short *to; int newmax; i = order[vector]; t = tally[i]; assert(t); from = froms[i]; to = tos[i]; j = lowzero - from[0]; for (k = 1; k < t; ++k) if (lowzero - from[k] > j) j = lowzero - from[k]; for (;; ++j) { if (j == 0) continue; ok = 1; for (k = 0; ok && k < t; k++) { loc = j + from[k]; if (loc >= maxtable) { if (loc >= MAXTABLE) fatal("maximum table size exceeded"); newmax = maxtable; do { newmax += 200; } while (newmax <= loc); table = (short *) REALLOC(table, newmax*sizeof(short)); if (table == 0) no_space(); check = (short *) REALLOC(check, newmax*sizeof(short)); if (check == 0) no_space(); for (l = maxtable; l < newmax; ++l) { table[l] = 0; check[l] = -1; } maxtable = newmax; } if (check[loc] != -1) ok = 0; } for (k = 0; ok && k < vector; k++) { if (pos[k] == j) ok = 0; } if (ok) { for (k = 0; k < t; k++) { loc = j + from[k]; table[loc] = to[k]; check[loc] = from[k]; if (loc > high) high = loc; } while (check[lowzero] != -1) ++lowzero; return (j); } } } static void output_base(void) { int i, j; fprintf(output_file, "static short %ssindex[] = {%39d,", symbol_prefix, base[0]); j = 10; for (i = 1; i < nstates; i++) { if (j >= 10) { if (!rflag) ++outline; putc('\n', output_file); j = 1; } else ++j; fprintf(output_file, "%5d,", base[i]); } if (!rflag) outline += 2; fprintf(output_file, "\n};\nstatic short %srindex[] = {%39d,", symbol_prefix, base[nstates]); j = 10; for (i = nstates + 1; i < 2*nstates; i++) { if (j >= 10) { if (!rflag) ++outline; putc('\n', output_file); j = 1; } else ++j; fprintf(output_file, "%5d,", base[i]); } if (!rflag) outline += 2; fprintf(output_file, "\n};\nstatic short %sgindex[] = {%39d,", symbol_prefix, base[2*nstates]); j = 10; for (i = 2*nstates + 1; i < nvectors - 1; i++) { if (j >= 10) { if (!rflag) ++outline; putc('\n', output_file); j = 1; } else ++j; fprintf(output_file, "%5d,", base[i]); } if (!rflag) outline += 2; fprintf(output_file, "\n};\n"); FREE(base); } static void output_table(void) { int i; int j; ++outline; fprintf(code_file, "#define YYTABLESIZE %d\n", high); fprintf(output_file, "static short %stable[] = {%40d,", symbol_prefix, table[0]); j = 10; for (i = 1; i <= high; i++) { if (j >= 10) { if (!rflag) ++outline; putc('\n', output_file); j = 1; } else ++j; fprintf(output_file, "%5d,", table[i]); } if (!rflag) outline += 2; fprintf(output_file, "\n};\n"); FREE(table); } static void output_check(void) { int i; int j; fprintf(output_file, "static short %scheck[] = {%40d,", symbol_prefix, check[0]); j = 10; for (i = 1; i <= high; i++) { if (j >= 10) { if (!rflag) ++outline; putc('\n', output_file); j = 1; } else ++j; fprintf(output_file, "%5d,", check[i]); } if (!rflag) outline += 2; fprintf(output_file, "\n};\n"); FREE(check); } static int is_C_identifier(char *name) { char *s; int c; s = name; c = *s; if (c == '"') { c = *++s; if (!isalpha(c) && c != '_' && c != '$') return (0); while ((c = *++s) != '"') { if (!isalnum(c) && c != '_' && c != '$') return (0); } return (1); } if (!isalpha(c) && c != '_' && c != '$') return (0); while ((c = *++s)) { if (!isalnum(c) && c != '_' && c != '$') return (0); } return (1); } static void output_defines(void) { int c, i; char *s; for (i = 2; i < ntokens; ++i) { s = symbol_name[i]; if (is_C_identifier(s)) { fprintf(code_file, "#define "); if (dflag) fprintf(defines_file, "#define "); c = *s; if (c == '"') { while ((c = *++s) != '"') { putc(c, code_file); if (dflag) putc(c, defines_file); } } else { do { putc(c, code_file); if (dflag) putc(c, defines_file); } while ((c = *++s)); } ++outline; fprintf(code_file, " %d\n", symbol_value[i]); if (dflag) fprintf(defines_file, " %d\n", symbol_value[i]); } } ++outline; fprintf(code_file, "#define YYERRCODE %d\n", symbol_value[1]); if (dflag && unionized) { rewind(union_file); while ((c = getc(union_file)) != EOF) putc(c, defines_file); fprintf(defines_file, " YYSTYPE;\nstatic YYSTYPE %slval;\n", symbol_prefix); } } static void output_stored_text(void) { int c; FILE *in, *out; rewind(text_file); in = text_file; if ((c = getc(in)) == EOF) return; out = code_file; if (c == '\n') ++outline; putc(c, out); while ((c = getc(in)) != EOF) { if (c == '\n') ++outline; putc(c, out); } if (!lflag) fprintf(out, line_format, ++outline + 1, code_file_name); } static void output_debug(void) { int i, j, k, max; char **symnam, *s; ++outline; fprintf(code_file, "#define YYFINAL %d\n", final_state); outline += 3; fprintf(code_file, "#ifndef YYDEBUG\n#define YYDEBUG %d\n#endif\n", tflag); if (rflag) fprintf(output_file, "#ifndef YYDEBUG\n#define YYDEBUG %d\n#endif\n", tflag); max = 0; for (i = 2; i < ntokens; ++i) if (symbol_value[i] > max) max = symbol_value[i]; ++outline; fprintf(code_file, "#define YYMAXTOKEN %d\n", max); symnam = (char **) MALLOC((max+1)*sizeof(char *)); if (symnam == 0) no_space(); /* Note that it is not necessary to initialize the element */ /* symnam[max]. */ for (i = 0; i < max; ++i) symnam[i] = 0; for (i = ntokens - 1; i >= 2; --i) symnam[symbol_value[i]] = symbol_name[i]; symnam[0] = "end-of-file"; if (!rflag) ++outline; fprintf(output_file, "#if YYDEBUG\nstatic char *%sname[] = {", symbol_prefix); j = 80; for (i = 0; i <= max; ++i) { if ((s = symnam[i])) { if (s[0] == '"') { k = 7; while (*++s != '"') { ++k; if (*s == '\\') { k += 2; if (*++s == '\\') ++k; } } j += k; if (j > 80) { if (!rflag) ++outline; putc('\n', output_file); j = k; } fprintf(output_file, "\"\\\""); s = symnam[i]; while (*++s != '"') { if (*s == '\\') { fprintf(output_file, "\\\\"); if (*++s == '\\') fprintf(output_file, "\\\\"); else putc(*s, output_file); } else putc(*s, output_file); } fprintf(output_file, "\\\"\","); } else if (s[0] == '\'') { if (s[1] == '"') { j += 7; if (j > 80) { if (!rflag) ++outline; putc('\n', output_file); j = 7; } fprintf(output_file, "\"'\\\"'\","); } else { k = 5; while (*++s != '\'') { ++k; if (*s == '\\') { k += 2; if (*++s == '\\') ++k; } } j += k; if (j > 80) { if (!rflag) ++outline; putc('\n', output_file); j = k; } fprintf(output_file, "\"'"); s = symnam[i]; while (*++s != '\'') { if (*s == '\\') { fprintf(output_file, "\\\\"); if (*++s == '\\') fprintf(output_file, "\\\\"); else putc(*s, output_file); } else putc(*s, output_file); } fprintf(output_file, "'\","); } } else { k = strlen(s) + 3; j += k; if (j > 80) { if (!rflag) ++outline; putc('\n', output_file); j = k; } putc('"', output_file); do { putc(*s, output_file); } while (*++s); fprintf(output_file, "\","); } } else { j += 2; if (j > 80) { if (!rflag) ++outline; putc('\n', output_file); j = 2; } fprintf(output_file, "0,"); } } if (!rflag) outline += 2; fprintf(output_file, "\n};\n"); FREE(symnam); if (!rflag) ++outline; fprintf(output_file, "static char *%srule[] = {\n", symbol_prefix); for (i = 2; i < nrules; ++i) { fprintf(output_file, "\"%s :", symbol_name[rlhs[i]]); for (j = rrhs[i]; ritem[j] > 0; ++j) { s = symbol_name[ritem[j]]; if (s[0] == '"') { fprintf(output_file, " \\\""); while (*++s != '"') { if (*s == '\\') { if (s[1] == '\\') fprintf(output_file, "\\\\\\\\"); else fprintf(output_file, "\\\\%c", s[1]); ++s; } else putc(*s, output_file); } fprintf(output_file, "\\\""); } else if (s[0] == '\'') { if (s[1] == '"') fprintf(output_file, " '\\\"'"); else if (s[1] == '\\') { if (s[2] == '\\') fprintf(output_file, " '\\\\\\\\"); else fprintf(output_file, " '\\\\%c", s[2]); s += 2; while (*++s != '\'') putc(*s, output_file); putc('\'', output_file); } else fprintf(output_file, " '%c'", s[1]); } else fprintf(output_file, " %s", s); } if (!rflag) ++outline; fprintf(output_file, "\",\n"); } if (!rflag) outline += 2; fprintf(output_file, "};\n#endif\n"); } static void output_stype(void) { if (!unionized && ntags == 0) { outline += 3; fprintf(code_file, "#ifndef YYSTYPE\ntypedef int YYSTYPE;\n#endif\n"); } } static void output_trailing_text(void) { int c, last; FILE *in, *out; if (line == 0) return; in = input_file; out = code_file; c = *cptr; if (c == '\n') { ++lineno; if ((c = getc(in)) == EOF) return; if (!lflag) { ++outline; fprintf(out, line_format, lineno, input_file_name); } if (c == '\n') ++outline; putc(c, out); last = c; } else { if (!lflag) { ++outline; fprintf(out, line_format, lineno, input_file_name); } do { putc(c, out); } while ((c = *++cptr) != '\n'); ++outline; putc('\n', out); last = '\n'; } while ((c = getc(in)) != EOF) { if (c == '\n') ++outline; putc(c, out); last = c; } if (last != '\n') { ++outline; putc('\n', out); } if (!lflag) fprintf(out, line_format, ++outline + 1, code_file_name); } static void output_semantic_actions(void) { int c, last; FILE *out; rewind(action_file); if ((c = getc(action_file)) == EOF) return; out = code_file; last = c; if (c == '\n') ++outline; putc(c, out); while ((c = getc(action_file)) != EOF) { if (c == '\n') ++outline; putc(c, out); last = c; } if (last != '\n') { ++outline; putc('\n', out); } if (!lflag) fprintf(out, line_format, ++outline + 1, code_file_name); } static void free_itemsets(void) { core *cp, *next; FREE(state_table); for (cp = first_state; cp; cp = next) { next = cp->next; FREE(cp); } } static void free_shifts(void) { shifts *sp, *next; FREE(shift_table); for (sp = first_shift; sp; sp = next) { next = sp->next; FREE(sp); } } static void free_reductions(void) { reductions *rp, *next; FREE(reduction_table); for (rp = first_reduction; rp; rp = next) { next = rp->next; FREE(rp); } } base-7.0.3.1/modules/libcom/src/yacc/reader.c0000664000577000060420000007460213557101274017455 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "defs.h" /* The line size must be a positive integer. One hundred was chosen */ /* because few lines in Yacc input grammars exceed 100 characters. */ /* Note that if a line exceeds LINESIZE characters, the line buffer */ /* will be expanded to accomodate it. */ #define LINESIZE 100 char *cache; int cinc, cache_size; int ntags, tagmax; char **tag_table; char saw_eof, unionized; char *cptr, *line; int linesize; bucket *goal; int prec; int gensym; char last_was_action; int maxitems; bucket **pitem; int maxrules; bucket **plhs; int name_pool_size; char *name_pool; char line_format[] = "#line %d \"%s\"\n"; #define static static void start_rule(bucket *bp, int s_lineno); static void cachec(int c) { assert(cinc >= 0); if (cinc >= cache_size) { cache_size += 256; cache = REALLOC(cache, cache_size); if (cache == 0) no_space(); } cache[cinc] = c; ++cinc; } static void get_line(void) { FILE *f = input_file; int c; int i; if (saw_eof || (c = getc(f)) == EOF) { if (line) { FREE(line); line = 0; } cptr = 0; saw_eof = 1; return; } if (line == 0 || linesize != (LINESIZE + 1)) { if (line) FREE(line); linesize = LINESIZE + 1; line = MALLOC(linesize); if (line == 0) no_space(); } i = 0; ++lineno; for (;;) { line[i] = c; if (c == '\n') { cptr = line; return; } if (++i >= linesize) { linesize += LINESIZE; line = REALLOC(line, linesize); if (line == 0) no_space(); } c = getc(f); if (c == EOF) { line[i] = '\n'; saw_eof = 1; cptr = line; return; } } } static char * dup_line(void) { char *p, *s, *t; if (line == 0) return (0); s = line; while (*s != '\n') ++s; p = MALLOC(s - line + 1); if (p == 0) no_space(); s = line; t = p; while ((*t++ = *s++) != '\n') continue; return (p); } static void skip_comment(void) { char *s; int st_lineno = lineno; char *st_line = dup_line(); char *st_cptr = st_line + (cptr - line); s = cptr + 2; for (;;) { if (*s == '*' && s[1] == '/') { cptr = s + 2; FREE(st_line); return; } if (*s == '\n') { get_line(); if (line == 0) unterminated_comment(st_lineno, st_line, st_cptr); s = cptr; } else ++s; } } static int nextc(void) { char *s; if (line == 0) { get_line(); if (line == 0) return (EOF); } s = cptr; for (;;) { switch (*s) { case '\n': get_line(); if (line == 0) return (EOF); s = cptr; break; case ' ': case '\t': case '\f': case '\r': case '\v': case ',': case ';': ++s; break; case '\\': cptr = s; return ('%'); case '/': if (s[1] == '*') { cptr = s; skip_comment(); s = cptr; break; } else if (s[1] == '/') { get_line(); if (line == 0) return (EOF); s = cptr; break; } /* fall through */ default: cptr = s; return (*s); } } } static int keyword(void) { int c; char *t_cptr = cptr; c = *++cptr; if (isalpha(c)) { cinc = 0; for (;;) { if (isalpha(c)) { if (isupper(c)) c = tolower(c); cachec(c); } else if (isdigit(c) || c == '_' || c == '.' || c == '$') cachec(c); else break; c = *++cptr; } cachec(NUL); if (strcmp(cache, "token") == 0 || strcmp(cache, "term") == 0) return (TOKEN); if (strcmp(cache, "type") == 0) return (TYPE); if (strcmp(cache, "left") == 0) return (LEFT); if (strcmp(cache, "right") == 0) return (RIGHT); if (strcmp(cache, "nonassoc") == 0 || strcmp(cache, "binary") == 0) return (NONASSOC); if (strcmp(cache, "start") == 0) return (START); if (strcmp(cache, "union") == 0) return (UNION); if (strcmp(cache, "ident") == 0) return (IDENT); } else { ++cptr; if (c == '{') return (TEXT); if (c == '%' || c == '\\') return (MARK); if (c == '<') return (LEFT); if (c == '>') return (RIGHT); if (c == '0') return (TOKEN); if (c == '2') return (NONASSOC); } syntax_error(lineno, line, t_cptr); /*NOTREACHED*/ } static void copy_ident(void) { int c; FILE *f = output_file; c = nextc(); if (c == EOF) unexpected_EOF(); if (c != '"') syntax_error(lineno, line, cptr); ++outline; fprintf(f, "#ident \""); for (;;) { c = *++cptr; if (c == '\n') { fprintf(f, "\"\n"); return; } putc(c, f); if (c == '"') { putc('\n', f); ++cptr; return; } } } static void copy_text(void) { int c; int quote; FILE *f = text_file; int need_newline = 0; int t_lineno = lineno; char *t_line = dup_line(); char *t_cptr = t_line + (cptr - line - 2); if (*cptr == '\n') { get_line(); if (line == 0) unterminated_text(t_lineno, t_line, t_cptr); } if (!lflag) fprintf(f, line_format, lineno, input_file_name); loop: c = *cptr++; switch (c) { case '\n': next_line: putc('\n', f); need_newline = 0; get_line(); if (line) goto loop; unterminated_text(t_lineno, t_line, t_cptr); case '\'': case '"': { int s_lineno = lineno; char *s_line = dup_line(); char *s_cptr = s_line + (cptr - line - 1); quote = c; putc(c, f); for (;;) { c = *cptr++; putc(c, f); if (c == quote) { need_newline = 1; FREE(s_line); goto loop; } if (c == '\n') unterminated_string(s_lineno, s_line, s_cptr); if (c == '\\') { c = *cptr++; putc(c, f); if (c == '\n') { get_line(); if (line == 0) unterminated_string(s_lineno, s_line, s_cptr); } } } } case '/': putc(c, f); need_newline = 1; c = *cptr; if (c == '/') { putc('*', f); while ((c = *++cptr) != '\n') { if (c == '*' && cptr[1] == '/') fprintf(f, "* "); else putc(c, f); } fprintf(f, "*/"); goto next_line; } if (c == '*') { int c_lineno = lineno; char *c_line = dup_line(); char *c_cptr = c_line + (cptr - line - 1); putc('*', f); ++cptr; for (;;) { c = *cptr++; putc(c, f); if (c == '*' && *cptr == '/') { putc('/', f); ++cptr; FREE(c_line); goto loop; } if (c == '\n') { get_line(); if (line == 0) unterminated_comment(c_lineno, c_line, c_cptr); } } } need_newline = 1; goto loop; case '%': case '\\': if (*cptr == '}') { if (need_newline) putc('\n', f); ++cptr; FREE(t_line); return; } /* fall through */ default: putc(c, f); need_newline = 1; goto loop; } } static void copy_union(void) { int c; int quote; int depth; int u_lineno = lineno; char *u_line = dup_line(); char *u_cptr = u_line + (cptr - line - 6); if (unionized) over_unionized(cptr - 6); unionized = 1; if (!lflag) fprintf(text_file, line_format, lineno, input_file_name); fprintf(text_file, "typedef union"); if (dflag) fprintf(union_file, "typedef union"); depth = 0; loop: c = *cptr++; putc(c, text_file); if (dflag) putc(c, union_file); switch (c) { case '\n': next_line: get_line(); if (line == 0) unterminated_union(u_lineno, u_line, u_cptr); goto loop; case '{': ++depth; goto loop; case '}': if (--depth == 0) { fprintf(text_file, " YYSTYPE;\n"); FREE(u_line); return; } goto loop; case '\'': case '"': { int s_lineno = lineno; char *s_line = dup_line(); char *s_cptr = s_line + (cptr - line - 1); quote = c; for (;;) { c = *cptr++; putc(c, text_file); if (dflag) putc(c, union_file); if (c == quote) { FREE(s_line); goto loop; } if (c == '\n') unterminated_string(s_lineno, s_line, s_cptr); if (c == '\\') { c = *cptr++; putc(c, text_file); if (dflag) putc(c, union_file); if (c == '\n') { get_line(); if (line == 0) unterminated_string(s_lineno, s_line, s_cptr); } } } } case '/': c = *cptr; if (c == '/') { putc('*', text_file); if (dflag) putc('*', union_file); while ((c = *++cptr) != '\n') { if (c == '*' && cptr[1] == '/') { fprintf(text_file, "* "); if (dflag) fprintf(union_file, "* "); } else { putc(c, text_file); if (dflag) putc(c, union_file); } } fprintf(text_file, "*/\n"); if (dflag) fprintf(union_file, "*/\n"); goto next_line; } if (c == '*') { int c_lineno = lineno; char *c_line = dup_line(); char *c_cptr = c_line + (cptr - line - 1); putc('*', text_file); if (dflag) putc('*', union_file); ++cptr; for (;;) { c = *cptr++; putc(c, text_file); if (dflag) putc(c, union_file); if (c == '*' && *cptr == '/') { putc('/', text_file); if (dflag) putc('/', union_file); ++cptr; FREE(c_line); goto loop; } if (c == '\n') { get_line(); if (line == 0) unterminated_comment(c_lineno, c_line, c_cptr); } } } goto loop; default: goto loop; } } static int hexval(int c) { if (c >= '0' && c <= '9') return (c - '0'); if (c >= 'A' && c <= 'F') return (c - 'A' + 10); if (c >= 'a' && c <= 'f') return (c - 'a' + 10); return (-1); } static bucket * get_literal(void) { int c, quote; int i; int n; char *s; bucket *bp; int s_lineno = lineno; char *s_line = dup_line(); char *s_cptr = s_line + (cptr - line); quote = *cptr++; cinc = 0; for (;;) { c = *cptr++; if (c == quote) break; if (c == '\n') unterminated_string(s_lineno, s_line, s_cptr); if (c == '\\') { char *c_cptr = cptr - 1; c = *cptr++; switch (c) { case '\n': get_line(); if (line == 0) unterminated_string(s_lineno, s_line, s_cptr); continue; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': n = c - '0'; c = *cptr; if (IS_OCTAL(c)) { n = (n << 3) + (c - '0'); c = *++cptr; if (IS_OCTAL(c)) { n = (n << 3) + (c - '0'); ++cptr; } } if (n > MAXCHAR) illegal_character(c_cptr); c = n; break; case 'x': c = *cptr++; n = hexval(c); if (n < 0 || n >= 16) illegal_character(c_cptr); for (;;) { c = *cptr; i = hexval(c); if (i < 0 || i >= 16) break; ++cptr; n = (n << 4) + i; if (n > MAXCHAR) illegal_character(c_cptr); } c = n; break; case 'a': c = 7; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; } } cachec(c); } FREE(s_line); n = cinc; s = MALLOC(n); if (s == 0) no_space(); for (i = 0; i < n; ++i) s[i] = cache[i]; cinc = 0; if (n == 1) cachec('\''); else cachec('"'); for (i = 0; i < n; ++i) { c = ((unsigned char *)s)[i]; if (c == '\\' || c == cache[0]) { cachec('\\'); cachec(c); } else if (isprint(c)) cachec(c); else { cachec('\\'); switch (c) { case 7: cachec('a'); break; case '\b': cachec('b'); break; case '\f': cachec('f'); break; case '\n': cachec('n'); break; case '\r': cachec('r'); break; case '\t': cachec('t'); break; case '\v': cachec('v'); break; default: cachec(((c >> 6) & 7) + '0'); cachec(((c >> 3) & 7) + '0'); cachec((c & 7) + '0'); break; } } } if (n == 1) cachec('\''); else cachec('"'); cachec(NUL); bp = lookup(cache); bp->class = TERM; if (n == 1 && bp->value == UNDEFINED) bp->value = *(unsigned char *)s; FREE(s); return (bp); } static int is_reserved(char *name) { char *s; if (strcmp(name, ".") == 0 || strcmp(name, "$accept") == 0 || strcmp(name, "$end") == 0) return (1); if (name[0] == '$' && name[1] == '$' && isdigit((int) name[2])) { s = name + 3; while (isdigit((int) *s)) ++s; if (*s == NUL) return (1); } return (0); } static bucket * get_name(void) { int c; cinc = 0; for (c = *cptr; IS_IDENT(c); c = *++cptr) cachec(c); cachec(NUL); if (is_reserved(cache)) used_reserved(cache); return (lookup(cache)); } static int get_number(void) { int c; int n; n = 0; for (c = *cptr; isdigit(c); c = *++cptr) n = 10*n + (c - '0'); return (n); } static char * get_tag(void) { int c; int i; char *s; int t_lineno = lineno; char *t_line = dup_line(); char *t_cptr = t_line + (cptr - line); ++cptr; c = nextc(); if (c == EOF) unexpected_EOF(); if (!isalpha(c) && c != '_' && c != '$') illegal_tag(t_lineno, t_line, t_cptr); cinc = 0; do { cachec(c); c = *++cptr; } while (IS_IDENT(c)); cachec(NUL); c = nextc(); if (c == EOF) unexpected_EOF(); if (c != '>') illegal_tag(t_lineno, t_line, t_cptr); ++cptr; for (i = 0; i < ntags; ++i) { if (strcmp(cache, tag_table[i]) == 0) return (tag_table[i]); } if (ntags >= tagmax) { tagmax += 16; tag_table = (char **) (tag_table ? REALLOC(tag_table, tagmax*sizeof(char *)) : MALLOC(tagmax*sizeof(char *))); if (tag_table == 0) no_space(); } s = MALLOC(cinc); if (s == 0) no_space(); strcpy(s, cache); tag_table[ntags] = s; ++ntags; FREE(t_line); return (s); } static void declare_tokens(int assoc) { int c; bucket *bp; int value; char *tag = 0; if (assoc != TOKEN) ++prec; c = nextc(); if (c == EOF) unexpected_EOF(); if (c == '<') { tag = get_tag(); c = nextc(); if (c == EOF) unexpected_EOF(); } for (;;) { if (isalpha(c) || c == '_' || c == '.' || c == '$') bp = get_name(); else if (c == '\'' || c == '"') bp = get_literal(); else return; if (bp == goal) tokenized_start(bp->name); bp->class = TERM; if (tag) { if (bp->tag && tag != bp->tag) retyped_warning(bp->name); bp->tag = tag; } if (assoc != TOKEN) { if (bp->prec && prec != bp->prec) reprec_warning(bp->name); bp->assoc = assoc; bp->prec = prec; } c = nextc(); if (c == EOF) unexpected_EOF(); value = UNDEFINED; if (isdigit(c)) { value = get_number(); if (bp->value != UNDEFINED && value != bp->value) revalued_warning(bp->name); bp->value = value; c = nextc(); if (c == EOF) unexpected_EOF(); } } } static void declare_types(void) { int c; bucket *bp; char *tag; c = nextc(); if (c == EOF) unexpected_EOF(); if (c != '<') syntax_error(lineno, line, cptr); tag = get_tag(); for (;;) { c = nextc(); if (isalpha(c) || c == '_' || c == '.' || c == '$') bp = get_name(); else if (c == '\'' || c == '"') bp = get_literal(); else return; if (bp->tag && tag != bp->tag) retyped_warning(bp->name); bp->tag = tag; } } static void declare_start(void) { int c; bucket *bp; c = nextc(); if (c == EOF) unexpected_EOF(); if (!isalpha(c) && c != '_' && c != '.' && c != '$') syntax_error(lineno, line, cptr); bp = get_name(); if (bp->class == TERM) terminal_start(bp->name); if (goal && goal != bp) restarted_warning(); goal = bp; } static void read_declarations(void) { int c, k; cache_size = 256; cache = MALLOC(cache_size); if (cache == 0) no_space(); for (;;) { c = nextc(); if (c == EOF) unexpected_EOF(); if (c != '%') syntax_error(lineno, line, cptr); switch (k = keyword()) { case MARK: return; case IDENT: copy_ident(); break; case TEXT: copy_text(); break; case UNION: copy_union(); break; case TOKEN: case LEFT: case RIGHT: case NONASSOC: declare_tokens(k); break; case TYPE: declare_types(); break; case START: declare_start(); break; } } } static void initialize_grammar(void) { nitems = 4; maxitems = 300; pitem = (bucket **) MALLOC(maxitems*sizeof(bucket *)); if (pitem == 0) no_space(); pitem[0] = 0; pitem[1] = 0; pitem[2] = 0; pitem[3] = 0; nrules = 3; maxrules = 100; plhs = (bucket **) MALLOC(maxrules*sizeof(bucket *)); if (plhs == 0) no_space(); plhs[0] = 0; plhs[1] = 0; plhs[2] = 0; rprec = (short *) MALLOC(maxrules*sizeof(short)); if (rprec == 0) no_space(); rprec[0] = 0; rprec[1] = 0; rprec[2] = 0; rassoc = (char *) MALLOC(maxrules*sizeof(char)); if (rassoc == 0) no_space(); rassoc[0] = TOKEN; rassoc[1] = TOKEN; rassoc[2] = TOKEN; } static void expand_items(void) { maxitems += 300; pitem = (bucket **) REALLOC(pitem, maxitems*sizeof(bucket *)); if (pitem == 0) no_space(); } static void expand_rules(void) { maxrules += 100; plhs = (bucket **) REALLOC(plhs, maxrules*sizeof(bucket *)); if (plhs == 0) no_space(); rprec = (short *) REALLOC(rprec, maxrules*sizeof(short)); if (rprec == 0) no_space(); rassoc = (char *) REALLOC(rassoc, maxrules*sizeof(char)); if (rassoc == 0) no_space(); } static void advance_to_start(void) { int c; bucket *bp; char *s_cptr; int s_lineno; for (;;) { c = nextc(); if (c != '%') break; s_cptr = cptr; switch (keyword()) { case MARK: no_grammar(); case TEXT: copy_text(); break; case START: declare_start(); break; default: syntax_error(lineno, line, s_cptr); } } c = nextc(); if (!isalpha(c) && c != '_' && c != '.' && c != '_') syntax_error(lineno, line, cptr); bp = get_name(); if (goal == 0) { if (bp->class == TERM) terminal_start(bp->name); goal = bp; } s_lineno = lineno; c = nextc(); if (c == EOF) unexpected_EOF(); if (c != ':') syntax_error(lineno, line, cptr); start_rule(bp, s_lineno); ++cptr; } static void start_rule(bucket *bp, int s_lineno) { if (bp->class == TERM) terminal_lhs(s_lineno); bp->class = NONTERM; if (nrules >= maxrules) expand_rules(); plhs[nrules] = bp; rprec[nrules] = UNDEFINED; rassoc[nrules] = TOKEN; } static void end_rule(void) { int i; if (!last_was_action && plhs[nrules]->tag) { for (i = nitems - 1; pitem[i]; --i) continue; if (pitem[i+1] == 0 || pitem[i+1]->tag != plhs[nrules]->tag) default_action_warning(); } last_was_action = 0; if (nitems >= maxitems) expand_items(); pitem[nitems] = 0; ++nitems; ++nrules; } static void insert_empty_rule(void) { bucket *bp, **bpp; assert(cache); sprintf(cache, "$$%d", ++gensym); bp = make_bucket(cache); last_symbol->next = bp; last_symbol = bp; bp->tag = plhs[nrules]->tag; bp->class = NONTERM; if ((nitems += 2) > maxitems) expand_items(); bpp = pitem + nitems - 1; *bpp-- = bp; while ((bpp[0] = bpp[-1])) --bpp; if (++nrules >= maxrules) expand_rules(); plhs[nrules] = plhs[nrules-1]; plhs[nrules-1] = bp; rprec[nrules] = rprec[nrules-1]; rprec[nrules-1] = 0; rassoc[nrules] = rassoc[nrules-1]; rassoc[nrules-1] = TOKEN; } static void add_symbol(void) { int c; bucket *bp; int s_lineno = lineno; c = *cptr; if (c == '\'' || c == '"') bp = get_literal(); else bp = get_name(); c = nextc(); if (c == ':') { end_rule(); start_rule(bp, s_lineno); ++cptr; return; } if (last_was_action) insert_empty_rule(); last_was_action = 0; if (++nitems > maxitems) expand_items(); pitem[nitems-1] = bp; } static void copy_action(void) { int c; int i, n; int depth; int quote; char *tag; FILE *f = action_file; int a_lineno = lineno; char *a_line = dup_line(); char *a_cptr = a_line + (cptr - line); if (last_was_action) insert_empty_rule(); last_was_action = 1; fprintf(f, "case %d:\n", nrules - 2); if (!lflag) fprintf(f, line_format, lineno, input_file_name); if (*cptr == '=') ++cptr; n = 0; for (i = nitems - 1; pitem[i]; --i) ++n; depth = 0; loop: c = *cptr; if (c == '$') { if (cptr[1] == '<') { int d_lineno = lineno; char *d_line = dup_line(); char *d_cptr = d_line + (cptr - line); ++cptr; tag = get_tag(); c = *cptr; if (c == '$') { fprintf(f, "yyval.%s", tag); ++cptr; FREE(d_line); goto loop; } else if (isdigit(c)) { i = get_number(); if (i > n) dollar_warning(d_lineno, i); fprintf(f, "yyvsp[%d].%s", i - n, tag); FREE(d_line); goto loop; } else if (c == '-' && isdigit((int) cptr[1])) { ++cptr; i = -get_number() - n; fprintf(f, "yyvsp[%d].%s", i, tag); FREE(d_line); goto loop; } else dollar_error(d_lineno, d_line, d_cptr); } else if (cptr[1] == '$') { if (ntags) { tag = plhs[nrules]->tag; if (tag == 0) untyped_lhs(); fprintf(f, "yyval.%s", tag); } else fprintf(f, "yyval"); cptr += 2; goto loop; } else if (isdigit((int) cptr[1])) { ++cptr; i = get_number(); if (ntags) { if (i <= 0 || i > n) unknown_rhs(i); tag = pitem[nitems + i - n - 1]->tag; if (tag == 0) untyped_rhs(i, pitem[nitems + i - n - 1]->name); fprintf(f, "yyvsp[%d].%s", i - n, tag); } else { if (i > n) dollar_warning(lineno, i); fprintf(f, "yyvsp[%d]", i - n); } goto loop; } else if (cptr[1] == '-') { cptr += 2; i = get_number(); if (ntags) unknown_rhs(-i); fprintf(f, "yyvsp[%d]", -i - n); goto loop; } } if (isalpha(c) || c == '_' || c == '$') { do { putc(c, f); c = *++cptr; } while (isalnum(c) || c == '_' || c == '$'); goto loop; } putc(c, f); ++cptr; switch (c) { case '\n': next_line: get_line(); if (line) goto loop; unterminated_action(a_lineno, a_line, a_cptr); case ';': if (depth > 0) goto loop; fprintf(f, "\nbreak;\n"); FREE(a_line); return; case '{': ++depth; goto loop; case '}': if (--depth > 0) goto loop; fprintf(f, "\nbreak;\n"); FREE(a_line); return; case '\'': case '"': { int s_lineno = lineno; char *s_line = dup_line(); char *s_cptr = s_line + (cptr - line - 1); quote = c; for (;;) { c = *cptr++; putc(c, f); if (c == quote) { FREE(s_line); goto loop; } if (c == '\n') unterminated_string(s_lineno, s_line, s_cptr); if (c == '\\') { c = *cptr++; putc(c, f); if (c == '\n') { get_line(); if (line == 0) unterminated_string(s_lineno, s_line, s_cptr); } } } } case '/': c = *cptr; if (c == '/') { putc('*', f); while ((c = *++cptr) != '\n') { if (c == '*' && cptr[1] == '/') fprintf(f, "* "); else putc(c, f); } fprintf(f, "*/\n"); goto next_line; } if (c == '*') { int c_lineno = lineno; char *c_line = dup_line(); char *c_cptr = c_line + (cptr - line - 1); putc('*', f); ++cptr; for (;;) { c = *cptr++; putc(c, f); if (c == '*' && *cptr == '/') { putc('/', f); ++cptr; FREE(c_line); goto loop; } if (c == '\n') { get_line(); if (line == 0) unterminated_comment(c_lineno, c_line, c_cptr); } } } goto loop; default: goto loop; } } static int mark_symbol(void) { int c; bucket *bp; c = cptr[1]; if (c == '%' || c == '\\') { cptr += 2; return (1); } if (c == '=') cptr += 2; else if ((c == 'p' || c == 'P') && ((c = cptr[2]) == 'r' || c == 'R') && ((c = cptr[3]) == 'e' || c == 'E') && ((c = cptr[4]) == 'c' || c == 'C') && ((c = cptr[5], !IS_IDENT(c)))) cptr += 5; else syntax_error(lineno, line, cptr); c = nextc(); if (isalpha(c) || c == '_' || c == '.' || c == '$') bp = get_name(); else if (c == '\'' || c == '"') bp = get_literal(); else { syntax_error(lineno, line, cptr); /*NOTREACHED*/ } if (rprec[nrules] != UNDEFINED && bp->prec != rprec[nrules]) prec_redeclared(); rprec[nrules] = bp->prec; rassoc[nrules] = bp->assoc; return (0); } static void read_grammar(void) { int c; initialize_grammar(); advance_to_start(); for (;;) { c = nextc(); if (c == EOF) break; if (isalpha(c) || c == '_' || c == '.' || c == '$' || c == '\'' || c == '"') add_symbol(); else if (c == '{' || c == '=') copy_action(); else if (c == '|') { end_rule(); start_rule(plhs[nrules-1], 0); ++cptr; } else if (c == '%') { if (mark_symbol()) break; } else syntax_error(lineno, line, cptr); } end_rule(); } static void free_tags(void) { int i; if (tag_table == 0) return; for (i = 0; i < ntags; ++i) { assert(tag_table[i]); FREE(tag_table[i]); } FREE(tag_table); } static void pack_names(void) { bucket *bp; char *p, *s, *t; name_pool_size = 13; /* 13 == sizeof("$end") + sizeof("$accept") */ for (bp = first_symbol; bp; bp = bp->next) name_pool_size += strlen(bp->name) + 1; name_pool = MALLOC(name_pool_size); if (name_pool == 0) no_space(); strcpy(name_pool, "$accept"); strcpy(name_pool+8, "$end"); t = name_pool + 13; for (bp = first_symbol; bp; bp = bp->next) { p = t; s = bp->name; while ((*t++ = *s++)) continue; FREE(bp->name); bp->name = p; } } static void check_symbols(void) { bucket *bp; if (goal->class == UNKNOWN) undefined_goal(goal->name); for (bp = first_symbol; bp; bp = bp->next) { if (bp->class == UNKNOWN) { undefined_symbol_warning(bp->name); bp->class = TERM; } } } static void pack_symbols(void) { bucket *bp; bucket **v; int i, j, k, n; nsyms = 2; ntokens = 1; for (bp = first_symbol; bp; bp = bp->next) { ++nsyms; if (bp->class == TERM) ++ntokens; } start_symbol = ntokens; nvars = nsyms - ntokens; symbol_name = (char **) MALLOC(nsyms*sizeof(char *)); if (symbol_name == 0) no_space(); symbol_value = (short *) MALLOC(nsyms*sizeof(short)); if (symbol_value == 0) no_space(); symbol_prec = (short *) MALLOC(nsyms*sizeof(short)); if (symbol_prec == 0) no_space(); symbol_assoc = MALLOC(nsyms); if (symbol_assoc == 0) no_space(); v = (bucket **) MALLOC(nsyms*sizeof(bucket *)); if (v == 0) no_space(); v[0] = 0; v[start_symbol] = 0; i = 1; j = start_symbol + 1; for (bp = first_symbol; bp; bp = bp->next) { if (bp->class == TERM) v[i++] = bp; else v[j++] = bp; } assert(i == ntokens && j == nsyms); for (i = 1; i < ntokens; ++i) v[i]->index = i; goal->index = start_symbol + 1; k = start_symbol + 2; while (++i < nsyms) if (v[i] != goal) { v[i]->index = k; ++k; } goal->value = 0; k = 1; for (i = start_symbol + 1; i < nsyms; ++i) { if (v[i] != goal) { v[i]->value = k; ++k; } } k = 0; for (i = 1; i < ntokens; ++i) { n = v[i]->value; if (n > 256) { for (j = k++; j > 0 && symbol_value[j-1] > n; --j) symbol_value[j] = symbol_value[j-1]; symbol_value[j] = n; } } if (v[1]->value == UNDEFINED) v[1]->value = 256; j = 0; n = 257; for (i = 2; i < ntokens; ++i) { if (v[i]->value == UNDEFINED) { while (j < k && n == symbol_value[j]) { while (++j < k && n == symbol_value[j]) continue; ++n; } v[i]->value = n; ++n; } } symbol_name[0] = name_pool + 8; symbol_value[0] = 0; symbol_prec[0] = 0; symbol_assoc[0] = TOKEN; for (i = 1; i < ntokens; ++i) { symbol_name[i] = v[i]->name; symbol_value[i] = v[i]->value; symbol_prec[i] = v[i]->prec; symbol_assoc[i] = v[i]->assoc; } symbol_name[start_symbol] = name_pool; symbol_value[start_symbol] = -1; symbol_prec[start_symbol] = 0; symbol_assoc[start_symbol] = TOKEN; for (++i; i < nsyms; ++i) { k = v[i]->index; symbol_name[k] = v[i]->name; symbol_value[k] = v[i]->value; symbol_prec[k] = v[i]->prec; symbol_assoc[k] = v[i]->assoc; } FREE(v); } static void pack_grammar(void) { int i, j; int assoc, prec; ritem = (short *) MALLOC(nitems*sizeof(short)); if (ritem == 0) no_space(); rlhs = (short *) MALLOC(nrules*sizeof(short)); if (rlhs == 0) no_space(); rrhs = (short *) MALLOC((nrules+1)*sizeof(short)); if (rrhs == 0) no_space(); rprec = (short *) REALLOC(rprec, nrules*sizeof(short)); if (rprec == 0) no_space(); rassoc = REALLOC(rassoc, nrules); if (rassoc == 0) no_space(); ritem[0] = -1; ritem[1] = goal->index; ritem[2] = 0; ritem[3] = -2; rlhs[0] = 0; rlhs[1] = 0; rlhs[2] = start_symbol; rrhs[0] = 0; rrhs[1] = 0; rrhs[2] = 1; j = 4; for (i = 3; i < nrules; ++i) { rlhs[i] = plhs[i]->index; rrhs[i] = j; assoc = TOKEN; prec = 0; while (pitem[j]) { ritem[j] = pitem[j]->index; if (pitem[j]->class == TERM) { prec = pitem[j]->prec; assoc = pitem[j]->assoc; } ++j; } ritem[j] = -i; ++j; if (rprec[i] == UNDEFINED) { rprec[i] = prec; rassoc[i] = assoc; } } rrhs[i] = j; FREE(plhs); FREE(pitem); } static void print_grammar(void) { int i, j, k; int spacing = 0; FILE *f = verbose_file; if (!vflag) return; k = 1; for (i = 2; i < nrules; ++i) { if (rlhs[i] != rlhs[i-1]) { if (i != 2) fprintf(f, "\n"); fprintf(f, "%4d %s :", i - 2, symbol_name[rlhs[i]]); spacing = strlen(symbol_name[rlhs[i]]) + 1; } else { fprintf(f, "%4d ", i - 2); j = spacing; while (--j >= 0) putc(' ', f); putc('|', f); } while (ritem[k] >= 0) { fprintf(f, " %s", symbol_name[ritem[k]]); ++k; } ++k; putc('\n', f); } } void reader(void) { write_section(banner); create_symbol_table(); read_declarations(); read_grammar(); free_symbol_table(); free_tags(); pack_names(); check_symbols(); pack_symbols(); pack_grammar(); free_symbols(); print_grammar(); } base-7.0.3.1/modules/libcom/src/yacc/skeleton.c0000664000577000060420000002150213557101274020026 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "defs.h" /* If the skeleton is changed, the banner should be changed so that */ /* the altered version can be easily distinguished from the original. */ /* */ /* The #defines included with the banner are there because they are */ /* useful in subsequent code. The macros #defined in the header or */ /* the body either are not useful outside of semantic actions or */ /* are conditional. */ char *banner[] = { "#define YYBYACC 1", "#define YYMAJOR 1", "#define YYMINOR 9", "#define yyclearin (yychar=(-1))", "#define yyerrok (yyerrflag=0)", "#define YYRECOVERING (yyerrflag!=0)", "static int yyparse(void);",/* JRW */ 0 }; char *tables[] = { "static short yylhs[];", /* JRW */ "static short yylen[];", /* JRW */ "static short yydefred[];", /* JRW */ "static short yydgoto[];", /* JRW */ "static short yysindex[];", /* JRW */ "static short yyrindex[];", /* JRW */ "static short yygindex[];", /* JRW */ "static short yytable[];", /* JRW */ "static short yycheck[];", /* JRW */ "#if YYDEBUG", "static char *yyname[];", /* JRW */ "static char *yyrule[];", /* JRW */ "#endif", 0 }; char *header[] = { "#ifdef YYSTACKSIZE", "#undef YYMAXDEPTH", "#define YYMAXDEPTH YYSTACKSIZE", "#else", "#ifdef YYMAXDEPTH", "#define YYSTACKSIZE YYMAXDEPTH", "#else", "#define YYSTACKSIZE 500", "#define YYMAXDEPTH 500", "#endif", "#endif", "#if YYDEBUG", /* MRK */ "static int yydebug;", /* JRW */ "#endif", /* MRK */ "static int yynerrs;", /* JRW */ "static int yyerrflag;", /* JRW */ "static int yychar;", /* JRW */ "static short *yyssp;", /* JRW */ "static YYSTYPE *yyvsp;", /* JRW */ "static YYSTYPE yyval;", /* JRW */ "static YYSTYPE yylval;", /* JRW */ "static short yyss[YYSTACKSIZE];", /* JRW */ "static YYSTYPE yyvs[YYSTACKSIZE];", /* JRW */ "#define yystacksize YYSTACKSIZE", 0 }; char *body[] = { "#define YYABORT goto yyabort", "#define YYREJECT goto yyabort", "#define YYACCEPT goto yyaccept", "#define YYERROR goto yyerrlab", "static int", /* JRW */ "yyparse(void)", /* JRW */ "{", " int yym, yyn, yystate;", "#if YYDEBUG", " char *yys;", " extern char *getenv();", "", " if ((yys = getenv(\"YYDEBUG\")))", " {", " yyn = *yys;", " if (yyn >= '0' && yyn <= '9')", " yydebug = yyn - '0';", " }", "#endif", "", " yynerrs = 0;", " yyerrflag = 0;", " yychar = (-1);", "", " yyssp = yyss;", " yyvsp = yyvs;", " *yyssp = yystate = 0;", "", "yyloop:", " if ((yyn = yydefred[yystate])) goto yyreduce;", " if (yychar < 0)", " {", " if ((yychar = yylex()) < 0) yychar = 0;", "#if YYDEBUG", " if (yydebug)", " {", " yys = 0;", " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", " if (!yys) yys = \"illegal-symbol\";", " printf(\"%sdebug: state %d, reading %d (%s)\\n\",", " YYPREFIX, yystate, yychar, yys);", " }", "#endif", " }", " if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&", " yyn <= YYTABLESIZE && yycheck[yyn] == yychar)", " {", "#if YYDEBUG", " if (yydebug)", " printf(\"%sdebug: state %d, shifting to state %d\\n\",", " YYPREFIX, yystate, yytable[yyn]);", "#endif", " if (yyssp >= yyss + yystacksize - 1)", " {", " goto yyoverflow;", " }", " *++yyssp = yystate = yytable[yyn];", " *++yyvsp = yylval;", " yychar = (-1);", " if (yyerrflag > 0) --yyerrflag;", " goto yyloop;", " }", " if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&", " yyn <= YYTABLESIZE && yycheck[yyn] == yychar)", " {", " yyn = yytable[yyn];", " goto yyreduce;", " }", " if (yyerrflag) goto yyinrecovery;", " yyerror(\"syntax error\");", " ++yynerrs;", "yyinrecovery:", " if (yyerrflag < 3)", " {", " yyerrflag = 3;", " for (;;)", " {", " if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&", " yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)", " {", "#if YYDEBUG", " if (yydebug)", " printf(\"%sdebug: state %d, error recovery shifting\\", " to state %d\\n\", YYPREFIX, *yyssp, yytable[yyn]);", "#endif", " if (yyssp >= yyss + yystacksize - 1)", " {", " goto yyoverflow;", " }", " *++yyssp = yystate = yytable[yyn];", " *++yyvsp = yylval;", " goto yyloop;", " }", " else", " {", "#if YYDEBUG", " if (yydebug)", " printf(\"%sdebug: error recovery discarding state %d\ \\n\",", " YYPREFIX, *yyssp);", "#endif", " if (yyssp <= yyss) goto yyabort;", " --yyssp;", " --yyvsp;", " }", " }", " }", " else", " {", " if (yychar == 0) goto yyabort;", "#if YYDEBUG", " if (yydebug)", " {", " yys = 0;", " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", " if (!yys) yys = \"illegal-symbol\";", " printf(\"%sdebug: state %d, error recovery discards token %d\ (%s)\\n\",", " YYPREFIX, yystate, yychar, yys);", " }", "#endif", " yychar = (-1);", " goto yyloop;", " }", "yyreduce:", "#if YYDEBUG", " if (yydebug)", " printf(\"%sdebug: state %d, reducing by rule %d (%s)\\n\",", " YYPREFIX, yystate, yyn, yyrule[yyn]);", "#endif", " yym = yylen[yyn];", " yyval = yyvsp[1-yym];", " switch (yyn)", " {", 0 }; char *trailer[] = { " }", " yyssp -= yym;", " yystate = *yyssp;", " yyvsp -= yym;", " yym = yylhs[yyn];", " if (yystate == 0 && yym == 0)", " {", "#if YYDEBUG", " if (yydebug)", " printf(\"%sdebug: after reduction, shifting from state 0 to\\", " state %d\\n\", YYPREFIX, YYFINAL);", "#endif", " yystate = YYFINAL;", " *++yyssp = YYFINAL;", " *++yyvsp = yyval;", " if (yychar < 0)", " {", " if ((yychar = yylex()) < 0) yychar = 0;", "#if YYDEBUG", " if (yydebug)", " {", " yys = 0;", " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", " if (!yys) yys = \"illegal-symbol\";", " printf(\"%sdebug: state %d, reading %d (%s)\\n\",", " YYPREFIX, YYFINAL, yychar, yys);", " }", "#endif", " }", " if (yychar == 0) goto yyaccept;", " goto yyloop;", " }", " if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&", " yyn <= YYTABLESIZE && yycheck[yyn] == yystate)", " yystate = yytable[yyn];", " else", " yystate = yydgoto[yym];", "#if YYDEBUG", " if (yydebug)", " printf(\"%sdebug: after reduction, shifting from state %d \\", "to state %d\\n\", YYPREFIX, *yyssp, yystate);", "#endif", " if (yyssp >= yyss + yystacksize - 1)", " {", " goto yyoverflow;", " }", " *++yyssp = yystate;", " *++yyvsp = yyval;", " goto yyloop;", "yyoverflow:", " yyerror(\"yacc stack overflow\");", "yyabort:", " return (1);", "yyaccept:", " return (0);", "}", 0 }; void write_section(char *section[]) { int c; int i; char *s; FILE *f; f = code_file; for (i = 0; (s = section[i]); ++i) { ++outline; while ((c = *s)) { putc(c, f); ++s; } putc('\n', f); } } base-7.0.3.1/modules/libcom/src/yacc/symtab.c0000664000577000060420000000437213557101274017507 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "defs.h" /* TABLE_SIZE is the number of entries in the symbol table. */ /* TABLE_SIZE must be a power of two. */ #define TABLE_SIZE 1024 bucket **symbol_table; bucket *first_symbol; bucket *last_symbol; static int hash(char *name) { char *s; int c, k; assert(name && *name); s = name; k = *s; while ((c = *++s)) k = (31*k + c) & (TABLE_SIZE - 1); return (k); } bucket * make_bucket(char *name) { bucket *bp; assert(name); bp = (bucket *) MALLOC(sizeof(bucket)); if (bp == 0) no_space(); bp->link = 0; bp->next = 0; bp->name = MALLOC(strlen(name) + 1); if (bp->name == 0) no_space(); bp->tag = 0; bp->value = UNDEFINED; bp->index = 0; bp->prec = 0; bp-> class = UNKNOWN; bp->assoc = TOKEN; if (bp->name == 0) no_space(); strcpy(bp->name, name); return (bp); } bucket * lookup(char *name) { bucket *bp, **bpp; bpp = symbol_table + hash(name); bp = *bpp; while (bp) { if (strcmp(name, bp->name) == 0) return (bp); bpp = &bp->link; bp = *bpp; } *bpp = bp = make_bucket(name); last_symbol->next = bp; last_symbol = bp; return (bp); } void create_symbol_table(void) { int i; bucket *bp; symbol_table = (bucket **) MALLOC(TABLE_SIZE*sizeof(bucket *)); if (symbol_table == 0) no_space(); for (i = 0; i < TABLE_SIZE; i++) symbol_table[i] = 0; bp = make_bucket("error"); bp->index = 1; bp->class = TERM; first_symbol = bp; last_symbol = bp; symbol_table[hash("error")] = bp; } void free_symbol_table(void) { FREE(symbol_table); symbol_table = 0; } void free_symbols(void) { bucket *p, *q; for (p = first_symbol; p; p = q) { q = p->next; FREE(p); } } base-7.0.3.1/modules/libcom/src/yacc/verbose.c0000664000577000060420000001557513557101274017664 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "defs.h" static short *null_rules; static void log_unused(void); static void log_conflicts(void); static void print_state(int state); static void print_conflicts(int state); static void print_core(int state); static void print_nulls(int state); static void print_actions(int state); static void print_shifts(action *p); static void print_reductions(action *p, int defred); static void print_gotos(int stateno); void verbose(void) { int i; if (!vflag) return; null_rules = (short *) MALLOC(nrules*sizeof(short)); if (null_rules == 0) no_space(); fprintf(verbose_file, "\f\n"); for (i = 0; i < nstates; i++) print_state(i); FREE(null_rules); if (nunused) log_unused(); if (SRtotal || RRtotal) log_conflicts(); fprintf(verbose_file, "\n\n%d terminals, %d nonterminals\n", ntokens, nvars); fprintf(verbose_file, "%d grammar rules, %d states\n", nrules - 2, nstates); } static void log_unused(void) { int i; short *p; fprintf(verbose_file, "\n\nRules never reduced:\n"); for (i = 3; i < nrules; ++i) { if (!rules_used[i]) { fprintf(verbose_file, "\t%s :", symbol_name[rlhs[i]]); for (p = ritem + rrhs[i]; *p >= 0; ++p) fprintf(verbose_file, " %s", symbol_name[*p]); fprintf(verbose_file, " (%d)\n", i - 2); } } } static void log_conflicts(void) { int i; fprintf(verbose_file, "\n\n"); for (i = 0; i < nstates; i++) { if (SRconflicts[i] || RRconflicts[i]) { fprintf(verbose_file, "State %d contains ", i); if (SRconflicts[i] == 1) fprintf(verbose_file, "1 shift/reduce conflict"); else if (SRconflicts[i] > 1) fprintf(verbose_file, "%d shift/reduce conflicts", SRconflicts[i]); if (SRconflicts[i] && RRconflicts[i]) fprintf(verbose_file, ", "); if (RRconflicts[i] == 1) fprintf(verbose_file, "1 reduce/reduce conflict"); else if (RRconflicts[i] > 1) fprintf(verbose_file, "%d reduce/reduce conflicts", RRconflicts[i]); fprintf(verbose_file, ".\n"); } } } static void print_state(int state) { if (state) fprintf(verbose_file, "\n\n"); if (SRconflicts[state] || RRconflicts[state]) print_conflicts(state); fprintf(verbose_file, "state %d\n", state); print_core(state); print_nulls(state); print_actions(state); } static void print_conflicts(int state) { int symbol, act = 0, number = 0; action *p; symbol = -1; for (p = parser[state]; p; p = p->next) { if (p->suppressed == 2) continue; if (p->symbol != symbol) { symbol = p->symbol; number = p->number; if (p->action_code == SHIFT) act = SHIFT; else act = REDUCE; } else if (p->suppressed == 1) { if (state == final_state && symbol == 0) { fprintf(verbose_file, "%d: shift/reduce conflict \ (accept, reduce %d) on $end\n", state, p->number - 2); } else { if (act == SHIFT) { fprintf(verbose_file, "%d: shift/reduce conflict \ (shift %d, reduce %d) on %s\n", state, number, p->number - 2, symbol_name[symbol]); } else { fprintf(verbose_file, "%d: reduce/reduce conflict \ (reduce %d, reduce %d) on %s\n", state, number - 2, p->number - 2, symbol_name[symbol]); } } } } } static void print_core(int state) { int i; int k; int rule; core *statep; short *sp; short *sp1; statep = state_table[state]; k = statep->nitems; for (i = 0; i < k; i++) { sp1 = sp = ritem + statep->items[i]; while (*sp >= 0) ++sp; rule = -(*sp); fprintf(verbose_file, "\t%s : ", symbol_name[rlhs[rule]]); for (sp = ritem + rrhs[rule]; sp < sp1; sp++) fprintf(verbose_file, "%s ", symbol_name[*sp]); putc('.', verbose_file); while (*sp >= 0) { fprintf(verbose_file, " %s", symbol_name[*sp]); sp++; } fprintf(verbose_file, " (%d)\n", -2 - *sp); } } static void print_nulls(int state) { action *p; int i, j, k, nnulls; nnulls = 0; for (p = parser[state]; p; p = p->next) { if (p->action_code == REDUCE && (p->suppressed == 0 || p->suppressed == 1)) { i = p->number; if (rrhs[i] + 1 == rrhs[i+1]) { for (j = 0; j < nnulls && i > null_rules[j]; ++j) continue; if (j == nnulls) { ++nnulls; null_rules[j] = i; } else if (i != null_rules[j]) { ++nnulls; for (k = nnulls - 1; k > j; --k) null_rules[k] = null_rules[k-1]; null_rules[j] = i; } } } } for (i = 0; i < nnulls; ++i) { j = null_rules[i]; fprintf(verbose_file, "\t%s : . (%d)\n", symbol_name[rlhs[j]], j - 2); } fprintf(verbose_file, "\n"); } static void print_actions(int stateno) { action *p; shifts *sp; int as; if (stateno == final_state) fprintf(verbose_file, "\t$end accept\n"); p = parser[stateno]; if (p) { print_shifts(p); print_reductions(p, defred[stateno]); } sp = shift_table[stateno]; if (sp && sp->nshifts > 0) { as = accessing_symbol[sp->shift[sp->nshifts - 1]]; if (ISVAR(as)) print_gotos(stateno); } } static void print_shifts(action *p) { int count; action *q; count = 0; for (q = p; q; q = q->next) { if (q->suppressed < 2 && q->action_code == SHIFT) ++count; } if (count > 0) { for (; p; p = p->next) { if (p->action_code == SHIFT && p->suppressed == 0) fprintf(verbose_file, "\t%s shift %d\n", symbol_name[p->symbol], p->number); } } } static void print_reductions(action *p, int defred) { int k, anyreds; action *q; anyreds = 0; for (q = p; q ; q = q->next) { if (q->action_code == REDUCE && q->suppressed < 2) { anyreds = 1; break; } } if (anyreds == 0) fprintf(verbose_file, "\t. error\n"); else { for (; p; p = p->next) { if (p->action_code == REDUCE && p->number != defred) { k = p->number - 2; if (p->suppressed == 0) fprintf(verbose_file, "\t%s reduce %d\n", symbol_name[p->symbol], k); } } if (defred > 0) fprintf(verbose_file, "\t. reduce %d\n", defred - 2); } } static void print_gotos(int stateno) { int i, k; int as; short *to_state; shifts *sp; putc('\n', verbose_file); sp = shift_table[stateno]; to_state = sp->shift; for (i = 0; i < sp->nshifts; ++i) { k = to_state[i]; as = accessing_symbol[k]; if (ISVAR(as)) fprintf(verbose_file, "\t%s goto %d\n", symbol_name[as], k); } } base-7.0.3.1/modules/libcom/src/yacc/warshall.c0000664000577000060420000000306613557101274020024 0ustar anjaesctl/*************************************************************************\ * 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. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "defs.h" static void transitive_closure(unsigned int *R, int n) { int rowsize; unsigned i; unsigned *rowj; unsigned *rp; unsigned *rend; unsigned *ccol; unsigned *relend; unsigned *cword; unsigned *rowi; rowsize = WORDSIZE(n); relend = R + n*rowsize; cword = R; i = 0; rowi = R; while (rowi < relend) { ccol = cword; rowj = R; while (rowj < relend) { if (*ccol & (1 << i)) { rp = rowi; rend = rowj + rowsize; while (rowj < rend) *rowj++ |= *rp++; } else { rowj += rowsize; } ccol += rowsize; } if (++i >= BITS_PER_WORD) { i = 0; cword++; } rowi += rowsize; } } void reflexive_transitive_closure(unsigned int *R, int n) { int rowsize; unsigned i; unsigned *rp; unsigned *relend; transitive_closure(R, n); rowsize = WORDSIZE(n); relend = R + n*rowsize; i = 0; rp = R; while (rp < relend) { *rp |= (1 << i); if (++i >= BITS_PER_WORD) { i = 0; rp++; } rp += rowsize; } } base-7.0.3.1/modules/libcom/src/yacc/yacc.html0000664000577000060420000000756713557101274017662 0ustar anjaesctl/*************************************************************************\ * 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. * 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. \*************************************************************************/


NAME

     Yacc - an LALR(1) parser generator


SYNOPSIS

     yacc [ -dlrtv ] [ -b file_prefix  ]  [  -p  symbol_prefix  ]
     filename


DESCRIPTION

     Yacc reads the grammar specification in  the  file  filename
     and  generates  an LR(1) parser for it.  The parsers consist
     of a set of LALR(1) parsing  tables  and  a  driver  routine
     written in the C programming language.  Yacc normally writes
     the parse tables and the driver routine to the file y.tab.c.

     The following options are available:

          -b file_prefix
               The -b option changes the prefix prepended to  the
               output   file  names  to  the  string  denoted  by
               file_prefix.  The default prefix is the  character
               y.

          -d   The -d option causes the header file y.tab.h to be
               written.

          -l   If the -l  option  is  not  specified,  yacc  will
               insert  #line  directives  in  the generated code.
               The #line directives let  the  C  compiler  relate
               errors  in the generated code to the user's origi-
               nal code.  If the -l  option  is  specified,  yacc
               will  not  insert  the  #line  directives.   #line
               directives specified by the user will be retained.

          -p symbol_prefix
               The -p option  changes  the  prefix  prepended  to
               yacc-generated  symbols  to  the string denoted by
               symbol_prefix.  The default prefix is  the  string
               yy.

          -r   The -r option  causes  yacc  to  produce  separate
               files for code and tables.  The code file is named
               y.code.c, and the tables file is named y.tab.c.

          -t   The -t option changes the preprocessor  directives
               generated  by  yacc  so  that debugging statements
               will be incorporated in the compiled code.

          -v   The -v option causes a human-readable  description
               of  the generated parser to be written to the file
               y.output.


     If the  environment  variable  TMPDIR  is  set,  the  string
     denoted  by TMPDIR will be used as the name of the directory
     where the temporary files are created.


FILES

     y.code.c
     y.tab.c
     y.tab.h
     y.output
     /tmp/yacc.aXXXXXX
     /tmp/yacc.tXXXXXX
     /tmp/yacc.uXXXXXX


DIAGNOSTICS

     If there are rules that are never  reduced,  the  number  of
     such  rules is reported on standard error.  If there are any
     LALR(1) conflicts, the number of conflicts  is  reported  on
     standard error.



































base-7.0.3.1/modules/libcom/src/yajl/Makefile0000664000577000060420000000164513557101274017524 0ustar anjaesctl#************************************************************************* # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. SRC_DIRS += $(LIBCOM)/yacc # Yet Another JSON Library SRC_DIRS += $(LIBCOM)/yajl INC += yajl_alloc.h INC += yajl_common.h INC += yajl_gen.h INC += yajl_parse.h # The other yajl_*.h files are for internal use only Com_SRCS += yajl.c Com_SRCS += yajl_alloc.c Com_SRCS += yajl_buf.c Com_SRCS += yajl_encode.c Com_SRCS += yajl_gen.c Com_SRCS += yajl_lex.c Com_SRCS += yajl_parser.c base-7.0.3.1/modules/libcom/src/yajl/RULES0000664000577000060420000000143213557101274016673 0ustar anjaesctl#************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/libCom/Makefile. # Ensure epicsVersion.h gets built first yajl$(DEP): $(COMMON_DIR)/epicsVersion.h yajl_alloc$(DEP): $(COMMON_DIR)/epicsVersion.h yajl_buf$(DEP): $(COMMON_DIR)/epicsVersion.h yajl_encode$(DEP): $(COMMON_DIR)/epicsVersion.h yajl_gen$(DEP): $(COMMON_DIR)/epicsVersion.h yajl_lex$(DEP): $(COMMON_DIR)/epicsVersion.h yajl_parser$(DEP): $(COMMON_DIR)/epicsVersion.h base-7.0.3.1/modules/libcom/src/yajl/yajl.c0000664000577000060420000001176113557101274017167 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #define epicsExportSharedSymbols #include "yajl_parse.h" #include "yajl_lex.h" #include "yajl_parser.h" #include "yajl_alloc.h" const char * yajl_status_to_string(yajl_status stat) { const char * statStr = "unknown"; switch (stat) { case yajl_status_ok: statStr = "ok, no error"; break; case yajl_status_client_canceled: statStr = "client canceled parse"; break; case yajl_status_error: statStr = "parse error"; break; } return statStr; } yajl_handle yajl_alloc(const yajl_callbacks * callbacks, yajl_alloc_funcs * afs, void * ctx) { yajl_handle hand = NULL; yajl_alloc_funcs afsBuffer; /* first order of business is to set up memory allocation routines */ if (afs != NULL) { if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL) { return NULL; } } else { yajl_set_default_alloc_funcs(&afsBuffer); afs = &afsBuffer; } hand = (yajl_handle) YA_MALLOC(afs, sizeof(struct yajl_handle_t)); if (hand == NULL) { return NULL; } /* copy in pointers to allocation routines */ memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); hand->callbacks = callbacks; hand->ctx = ctx; hand->lexer = NULL; hand->bytesConsumed = 0; hand->decodeBuf = yajl_buf_alloc(&(hand->alloc)); hand->flags = 0; yajl_bs_init(hand->stateStack, &(hand->alloc)); yajl_bs_push(hand->stateStack, yajl_state_start); return hand; } int yajl_config(yajl_handle h, yajl_option opt, ...) { int rv = 1; va_list ap; va_start(ap, opt); switch(opt) { case yajl_allow_comments: case yajl_dont_validate_strings: case yajl_allow_trailing_garbage: case yajl_allow_multiple_values: case yajl_allow_partial_values: if (va_arg(ap, int)) h->flags |= opt; else h->flags &= ~opt; break; default: rv = 0; } va_end(ap); return rv; } void yajl_free(yajl_handle handle) { yajl_bs_free(handle->stateStack); yajl_buf_free(handle->decodeBuf); if (handle->lexer) { yajl_lex_free(handle->lexer); handle->lexer = NULL; } YA_FREE(&(handle->alloc), handle); } yajl_status yajl_parse(yajl_handle hand, const unsigned char * jsonText, size_t jsonTextLen) { yajl_status status; /* lazy allocation of the lexer */ if (hand->lexer == NULL) { hand->lexer = yajl_lex_alloc(&(hand->alloc), hand->flags & yajl_allow_comments, !(hand->flags & yajl_dont_validate_strings)); } if (hand->lexer == NULL) { return yajl_status_error; } status = yajl_do_parse(hand, jsonText, jsonTextLen); return status; } yajl_status yajl_complete_parse(yajl_handle hand) { /* The lexer is lazy allocated in the first call to parse. if parse is * never called, then no data was provided to parse at all. This is a * "premature EOF" error unless yajl_allow_partial_values is specified. * allocating the lexer now is the simplest possible way to handle this * case while preserving all the other semantics of the parser * (multiple values, partial values, etc). */ if (hand->lexer == NULL) { hand->lexer = yajl_lex_alloc(&(hand->alloc), hand->flags & yajl_allow_comments, !(hand->flags & yajl_dont_validate_strings)); } return yajl_do_finish(hand); } unsigned char * yajl_get_error(yajl_handle hand, int verbose, const unsigned char * jsonText, size_t jsonTextLen) { return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose); } size_t yajl_get_bytes_consumed(yajl_handle hand) { if (!hand) return 0; else return hand->bytesConsumed; } void yajl_free_error(yajl_handle hand, unsigned char * str) { /* use memory allocation functions if set */ YA_FREE(&(hand->alloc), str); } /* XXX: add utility routines to parse from file */ base-7.0.3.1/modules/libcom/src/yajl/yajl_alloc.c0000664000577000060420000000274313557101274020341 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * \file yajl_alloc.h * default memory allocation routines for yajl which use malloc/realloc and * free */ #include #define epicsExportSharedSymbols #include "yajl_alloc.h" static void * yajl_internal_malloc(void *ctx, size_t sz) { return malloc(sz); } static void * yajl_internal_realloc(void *ctx, void * previous, size_t sz) { return realloc(previous, sz); } static void yajl_internal_free(void *ctx, void * ptr) { free(ptr); } void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf) { yaf->malloc = yajl_internal_malloc; yaf->free = yajl_internal_free; yaf->realloc = yajl_internal_realloc; yaf->ctx = NULL; } base-7.0.3.1/modules/libcom/src/yajl/yajl_alloc.h0000664000577000060420000000235413557101274020344 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * \file yajl_alloc.h * default memory allocation routines for yajl which use malloc/realloc and * free */ #ifndef __YAJL_ALLOC_H__ #define __YAJL_ALLOC_H__ #include "yajl_common.h" #define YA_MALLOC(afs, sz) (afs)->malloc((afs)->ctx, (sz)) #define YA_FREE(afs, ptr) (afs)->free((afs)->ctx, (ptr)) #define YA_REALLOC(afs, ptr, sz) (afs)->realloc((afs)->ctx, (ptr), (sz)) YAJL_API void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf); #endif base-7.0.3.1/modules/libcom/src/yajl/yajl_buf.c0000664000577000060420000000506213557101274020020 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #define epicsExportSharedSymbols #include "yajl_buf.h" #define YAJL_BUF_INIT_SIZE 2048 struct yajl_buf_t { size_t len; size_t used; unsigned char * data; yajl_alloc_funcs * alloc; }; static void yajl_buf_ensure_available(yajl_buf buf, size_t want) { size_t need; assert(buf != NULL); /* first call */ if (buf->data == NULL) { buf->len = YAJL_BUF_INIT_SIZE; buf->data = (unsigned char *) YA_MALLOC(buf->alloc, buf->len); buf->data[0] = 0; } need = buf->len; while (want >= (need - buf->used)) need <<= 1; if (need != buf->len) { buf->data = (unsigned char *) YA_REALLOC(buf->alloc, buf->data, need); buf->len = need; } } yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc) { yajl_buf b = YA_MALLOC(alloc, sizeof(struct yajl_buf_t)); if (b == NULL) { return NULL; } memset((void *) b, 0, sizeof(struct yajl_buf_t)); b->alloc = alloc; return b; } void yajl_buf_free(yajl_buf buf) { assert(buf != NULL); if (buf->data) YA_FREE(buf->alloc, buf->data); YA_FREE(buf->alloc, buf); } void yajl_buf_append(yajl_buf buf, const void * data, size_t len) { yajl_buf_ensure_available(buf, len); if (len > 0) { assert(data != NULL); memcpy(buf->data + buf->used, data, len); buf->used += len; buf->data[buf->used] = 0; } } void yajl_buf_clear(yajl_buf buf) { buf->used = 0; if (buf->data) buf->data[buf->used] = 0; } const unsigned char * yajl_buf_data(yajl_buf buf) { return buf->data; } size_t yajl_buf_len(yajl_buf buf) { return buf->used; } void yajl_buf_truncate(yajl_buf buf, size_t len) { assert(len <= buf->used); buf->used = len; } base-7.0.3.1/modules/libcom/src/yajl/yajl_buf.h0000664000577000060420000000350513557101274020025 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __YAJL_BUF_H__ #define __YAJL_BUF_H__ #include "yajl_common.h" #include "yajl_alloc.h" /* * Implementation/performance notes. If this were moved to a header * only implementation using #define's where possible we might be * able to sqeeze a little performance out of the guy by killing function * call overhead. YMMV. */ /** * yajl_buf is a buffer with exponential growth. the buffer ensures that * you are always null padded. */ typedef struct yajl_buf_t * yajl_buf; /* allocate a new buffer */ yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc); /* free the buffer */ void yajl_buf_free(yajl_buf buf); /* append a number of bytes to the buffer */ void yajl_buf_append(yajl_buf buf, const void * data, size_t len); /* empty the buffer */ void yajl_buf_clear(yajl_buf buf); /* get a pointer to the beginning of the buffer */ const unsigned char * yajl_buf_data(yajl_buf buf); /* get the length of the buffer */ size_t yajl_buf_len(yajl_buf buf); /* truncate the buffer */ void yajl_buf_truncate(yajl_buf buf, size_t len); #endif base-7.0.3.1/modules/libcom/src/yajl/yajl_bytestack.h0000664000577000060420000000451513557101274021244 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * A header only implementation of a simple stack of bytes, used in YAJL * to maintain parse state. */ #ifndef __YAJL_BYTESTACK_H__ #define __YAJL_BYTESTACK_H__ #include "yajl_common.h" #define YAJL_BS_INC 128 typedef struct yajl_bytestack_t { unsigned char * stack; size_t size; size_t used; yajl_alloc_funcs * yaf; } yajl_bytestack; /* initialize a bytestack */ #define yajl_bs_init(obs, _yaf) { \ (obs).stack = NULL; \ (obs).size = 0; \ (obs).used = 0; \ (obs).yaf = (_yaf); \ } \ /* initialize a bytestack */ #define yajl_bs_free(obs) \ if ((obs).stack) (obs).yaf->free((obs).yaf->ctx, (obs).stack); #define yajl_bs_current(obs) \ (assert((obs).used > 0), (obs).stack[(obs).used - 1]) #define yajl_bs_push(obs, byte) { \ if (((obs).size - (obs).used) == 0) { \ (obs).size += YAJL_BS_INC; \ (obs).stack = (obs).yaf->realloc((obs).yaf->ctx,\ (void *) (obs).stack, (obs).size);\ } \ (obs).stack[((obs).used)++] = (byte); \ } /* removes the top item of the stack, returns nothing */ #define yajl_bs_pop(obs) { ((obs).used)--; } #define yajl_bs_set(obs, byte) \ (obs).stack[((obs).used) - 1] = (byte); #endif base-7.0.3.1/modules/libcom/src/yajl/yajl_common.h0000664000577000060420000000506213557101274020541 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __YAJL_COMMON_H__ #define __YAJL_COMMON_H__ #include #include #include #ifdef __cplusplus extern "C" { #endif /** YAJL API history in brief * * Originally macro not defined * YAJL 1.0.12 * Bundled with EPICS Base 3.15.0.1 * * YAJL 2.1.0 * Changes argument type for yajl_integer() from 'int' to 'long long' * Changes argument type for yajl_string() and yajl_map_key() from 'unsigned' to 'size_t' * Replacement of struct yajl_parser_config with yajl_config() * Replacement of yajl_parse_complete() with yajl_complete_parse() */ #define EPICS_YAJL_VERSION VERSION_INT(2,1,0,0) #define YAJL_MAX_DEPTH 128 #define YAJL_API epicsShareFunc /** pointer to a malloc function, supporting client overriding memory * allocation routines */ typedef void * (*yajl_malloc_func)(void *ctx, size_t sz); /** pointer to a free function, supporting client overriding memory * allocation routines */ typedef void (*yajl_free_func)(void *ctx, void * ptr); /** pointer to a realloc function which can resize an allocation. */ typedef void * (*yajl_realloc_func)(void *ctx, void * ptr, size_t sz); /** A structure which can be passed to yajl_*_alloc routines to allow the * client to specify memory allocation functions to be used. */ typedef struct { /** pointer to a function that can allocate uninitialized memory */ yajl_malloc_func malloc; /** pointer to a function that can resize memory allocations */ yajl_realloc_func realloc; /** pointer to a function that can free memory allocated using * reallocFunction or mallocFunction */ yajl_free_func free; /** a context pointer that will be passed to above allocation routines */ void * ctx; } yajl_alloc_funcs; #ifdef __cplusplus } #endif #endif base-7.0.3.1/modules/libcom/src/yajl/yajl_encode.c0000664000577000060420000001621713557101274020505 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #define epicsExportSharedSymbols #include "yajl_encode.h" static void CharToHex(unsigned char c, char * hexBuf) { const char * hexchar = "0123456789ABCDEF"; hexBuf[0] = hexchar[c >> 4]; hexBuf[1] = hexchar[c & 0x0F]; } void yajl_string_encode(const yajl_print_t print, void * ctx, const unsigned char * str, size_t len, int escape_solidus) { size_t beg = 0; size_t end = 0; char hexBuf[7]; hexBuf[0] = '\\'; hexBuf[1] = 'u'; hexBuf[2] = '0'; hexBuf[3] = '0'; hexBuf[6] = 0; while (end < len) { const char * escaped = NULL; switch (str[end]) { case '\r': escaped = "\\r"; break; case '\n': escaped = "\\n"; break; case '\\': escaped = "\\\\"; break; /* it is not required to escape a solidus in JSON: * read sec. 2.5: http://www.ietf.org/rfc/rfc4627.txt * specifically, this production from the grammar: * unescaped = %x20-21 / %x23-5B / %x5D-10FFFF */ case '/': if (escape_solidus) escaped = "\\/"; break; case '"': escaped = "\\\""; break; case '\f': escaped = "\\f"; break; case '\b': escaped = "\\b"; break; case '\t': escaped = "\\t"; break; default: if ((unsigned char) str[end] < 32) { CharToHex(str[end], hexBuf + 4); escaped = hexBuf; } break; } if (escaped != NULL) { print(ctx, (const char *) (str + beg), end - beg); print(ctx, escaped, (unsigned int)strlen(escaped)); beg = ++end; } else { ++end; } } print(ctx, (const char *) (str + beg), end - beg); } static void hexToDigit(unsigned int * val, const unsigned char * hex) { unsigned int i; for (i=0;i<4;i++) { unsigned char c = hex[i]; if (c >= 'A') c = (c & ~0x20) - 7; c -= '0'; assert(!(c & 0xF0)); *val = (*val << 4) | c; } } static void Utf32toUtf8(unsigned int codepoint, char * utf8Buf) { if (codepoint < 0x80) { utf8Buf[0] = (char) codepoint; utf8Buf[1] = 0; } else if (codepoint < 0x0800) { utf8Buf[0] = (char) ((codepoint >> 6) | 0xC0); utf8Buf[1] = (char) ((codepoint & 0x3F) | 0x80); utf8Buf[2] = 0; } else if (codepoint < 0x10000) { utf8Buf[0] = (char) ((codepoint >> 12) | 0xE0); utf8Buf[1] = (char) (((codepoint >> 6) & 0x3F) | 0x80); utf8Buf[2] = (char) ((codepoint & 0x3F) | 0x80); utf8Buf[3] = 0; } else if (codepoint < 0x200000) { utf8Buf[0] =(char)((codepoint >> 18) | 0xF0); utf8Buf[1] =(char)(((codepoint >> 12) & 0x3F) | 0x80); utf8Buf[2] =(char)(((codepoint >> 6) & 0x3F) | 0x80); utf8Buf[3] =(char)((codepoint & 0x3F) | 0x80); utf8Buf[4] = 0; } else { utf8Buf[0] = '?'; utf8Buf[1] = 0; } } void yajl_string_decode(yajl_buf buf, const unsigned char * str, size_t len) { size_t beg = 0; size_t end = 0; while (end < len) { if (str[end] == '\\') { char utf8Buf[5]; const char * unescaped = "?"; yajl_buf_append(buf, str + beg, end - beg); switch (str[++end]) { case 'r': unescaped = "\r"; break; case 'n': unescaped = "\n"; break; case '\\': unescaped = "\\"; break; case '/': unescaped = "/"; break; case '"': unescaped = "\""; break; case 'f': unescaped = "\f"; break; case 'b': unescaped = "\b"; break; case 't': unescaped = "\t"; break; case 'u': { unsigned int codepoint = 0; hexToDigit(&codepoint, str + ++end); end+=3; /* check if this is a surrogate */ if ((codepoint & 0xFC00) == 0xD800) { end++; if (str[end] == '\\' && str[end + 1] == 'u') { unsigned int surrogate = 0; hexToDigit(&surrogate, str + end + 2); codepoint = (((codepoint & 0x3F) << 10) | ((((codepoint >> 6) & 0xF) + 1) << 16) | (surrogate & 0x3FF)); end += 5; } else { unescaped = "?"; break; } } Utf32toUtf8(codepoint, utf8Buf); unescaped = utf8Buf; if (codepoint == 0) { yajl_buf_append(buf, unescaped, 1); beg = ++end; continue; } break; } default: assert("this should never happen" == NULL); } yajl_buf_append(buf, unescaped, (unsigned int)strlen(unescaped)); beg = ++end; } else { end++; } } yajl_buf_append(buf, str + beg, end - beg); } #define ADV_PTR s++; if (!(len--)) return 0; int yajl_string_validate_utf8(const unsigned char * s, size_t len) { if (!len) return 1; if (!s) return 0; while (len--) { /* single byte */ if (*s <= 0x7f) { /* noop */ } /* two byte */ else if ((*s >> 5) == 0x6) { ADV_PTR; if (!((*s >> 6) == 0x2)) return 0; } /* three byte */ else if ((*s >> 4) == 0x0e) { ADV_PTR; if (!((*s >> 6) == 0x2)) return 0; ADV_PTR; if (!((*s >> 6) == 0x2)) return 0; } /* four byte */ else if ((*s >> 3) == 0x1e) { ADV_PTR; if (!((*s >> 6) == 0x2)) return 0; ADV_PTR; if (!((*s >> 6) == 0x2)) return 0; ADV_PTR; if (!((*s >> 6) == 0x2)) return 0; } else { return 0; } s++; } return 1; } base-7.0.3.1/modules/libcom/src/yajl/yajl_encode.h0000664000577000060420000000243213557101274020504 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __YAJL_ENCODE_H__ #define __YAJL_ENCODE_H__ #include "yajl_buf.h" #include "yajl_gen.h" void yajl_string_encode(const yajl_print_t printer, void * ctx, const unsigned char * str, size_t length, int escape_solidus); void yajl_string_decode(yajl_buf buf, const unsigned char * str, size_t length); int yajl_string_validate_utf8(const unsigned char * s, size_t len); #endif base-7.0.3.1/modules/libcom/src/yajl/yajl_gen.c0000664000577000060420000002457313557101274020025 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #define epicsExportSharedSymbols #include "epicsMath.h" #include "yajl_gen.h" #include "yajl_buf.h" #include "yajl_encode.h" typedef enum { yajl_gen_start, yajl_gen_map_start, yajl_gen_map_key, yajl_gen_map_val, yajl_gen_array_start, yajl_gen_in_array, yajl_gen_complete, yajl_gen_error } yajl_gen_state; struct yajl_gen_t { unsigned int flags; unsigned int depth; const char * indentString; yajl_gen_state state[YAJL_MAX_DEPTH]; yajl_print_t print; void * ctx; /* yajl_buf */ /* memory allocation routines */ yajl_alloc_funcs alloc; }; int yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...) { int rv = 1; va_list ap; va_start(ap, opt); switch(opt) { case yajl_gen_beautify: case yajl_gen_validate_utf8: if (va_arg(ap, int)) g->flags |= opt; else g->flags &= ~opt; break; case yajl_gen_indent_string: { const char *indent = va_arg(ap, const char *); g->indentString = indent; for (; *indent; indent++) { if (*indent != '\n' && *indent != '\v' && *indent != '\f' && *indent != '\t' && *indent != '\r' && *indent != ' ') { g->indentString = NULL; rv = 0; } } break; } case yajl_gen_print_callback: yajl_buf_free(g->ctx); g->print = va_arg(ap, const yajl_print_t); g->ctx = va_arg(ap, void *); break; default: rv = 0; } va_end(ap); return rv; } yajl_gen yajl_gen_alloc(const yajl_alloc_funcs * afs) { yajl_gen g = NULL; yajl_alloc_funcs afsBuffer; /* first order of business is to set up memory allocation routines */ if (afs != NULL) { if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL) { return NULL; } } else { yajl_set_default_alloc_funcs(&afsBuffer); afs = &afsBuffer; } g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t)); if (!g) return NULL; memset((void *) g, 0, sizeof(struct yajl_gen_t)); /* copy in pointers to allocation routines */ memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); g->print = (yajl_print_t)&yajl_buf_append; g->ctx = yajl_buf_alloc(&(g->alloc)); g->indentString = " "; return g; } void yajl_gen_free(yajl_gen g) { if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_free((yajl_buf)g->ctx); YA_FREE(&(g->alloc), g); } #define INSERT_SEP \ if (g->state[g->depth] == yajl_gen_map_key || \ g->state[g->depth] == yajl_gen_in_array) { \ g->print(g->ctx, ",", 1); \ if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); \ } else if (g->state[g->depth] == yajl_gen_map_val) { \ g->print(g->ctx, ":", 1); \ if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, " ", 1); \ } #define INSERT_WHITESPACE \ if ((g->flags & yajl_gen_beautify)) { \ if (g->state[g->depth] != yajl_gen_map_val) { \ unsigned int _i; \ for (_i=0;_idepth;_i++) \ g->print(g->ctx, \ g->indentString, \ (unsigned int)strlen(g->indentString)); \ } \ } #define ENSURE_NOT_KEY \ if (g->state[g->depth] == yajl_gen_map_key || \ g->state[g->depth] == yajl_gen_map_start) { \ return yajl_gen_keys_must_be_strings; \ } \ /* check that we're not complete, or in error state. in a valid state * to be generating */ #define ENSURE_VALID_STATE \ if (g->state[g->depth] == yajl_gen_error) { \ return yajl_gen_in_error_state;\ } else if (g->state[g->depth] == yajl_gen_complete) { \ return yajl_gen_generation_complete; \ } #define INCREMENT_DEPTH \ if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded; #define DECREMENT_DEPTH \ if (--(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded; #define APPENDED_ATOM \ switch (g->state[g->depth]) { \ case yajl_gen_start: \ g->state[g->depth] = yajl_gen_complete; \ break; \ case yajl_gen_map_start: \ case yajl_gen_map_key: \ g->state[g->depth] = yajl_gen_map_val; \ break; \ case yajl_gen_array_start: \ g->state[g->depth] = yajl_gen_in_array; \ break; \ case yajl_gen_map_val: \ g->state[g->depth] = yajl_gen_map_key; \ break; \ default: \ break; \ } \ #define FINAL_NEWLINE \ if ((g->flags & yajl_gen_beautify) && g->state[g->depth] == yajl_gen_complete) \ g->print(g->ctx, "\n", 1); yajl_gen_status yajl_gen_integer(yajl_gen g, long long int number) { char i[32]; ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; sprintf(i, "%lld", number); g->print(g->ctx, i, (unsigned int)strlen(i)); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_double(yajl_gen g, double number) { char i[32]; ENSURE_VALID_STATE; ENSURE_NOT_KEY; if (isnan(number) || isinf(number)) return yajl_gen_invalid_number; INSERT_SEP; INSERT_WHITESPACE; sprintf(i, "%.20g", number); g->print(g->ctx, i, (unsigned int)strlen(i)); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_number(yajl_gen g, const char * s, size_t l) { ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; g->print(g->ctx, s, l); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_string(yajl_gen g, const unsigned char * str, size_t len) { // if validation is enabled, check that the string is valid utf8 // XXX: This checking could be done a little faster, in the same pass as // the string encoding if (g->flags & yajl_gen_validate_utf8) { if (!yajl_string_validate_utf8(str, len)) { return yajl_gen_invalid_string; } } ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE; g->print(g->ctx, "\"", 1); yajl_string_encode(g->print, g->ctx, str, len, g->flags & yajl_gen_escape_solidus); g->print(g->ctx, "\"", 1); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_null(yajl_gen g) { ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; g->print(g->ctx, "null", strlen("null")); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_bool(yajl_gen g, int boolean) { const char * val = boolean ? "true" : "false"; ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; g->print(g->ctx, val, (unsigned int)strlen(val)); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_map_open(yajl_gen g) { ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; INCREMENT_DEPTH; g->state[g->depth] = yajl_gen_map_start; g->print(g->ctx, "{", 1); if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_map_close(yajl_gen g) { ENSURE_VALID_STATE; DECREMENT_DEPTH; if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); APPENDED_ATOM; INSERT_WHITESPACE; g->print(g->ctx, "}", 1); FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_array_open(yajl_gen g) { ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; INCREMENT_DEPTH; g->state[g->depth] = yajl_gen_array_start; g->print(g->ctx, "[", 1); if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_array_close(yajl_gen g) { ENSURE_VALID_STATE; DECREMENT_DEPTH; if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); APPENDED_ATOM; INSERT_WHITESPACE; g->print(g->ctx, "]", 1); FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf, size_t * len) { if (g->print != (yajl_print_t)&yajl_buf_append) return yajl_gen_no_buf; *buf = yajl_buf_data((yajl_buf)g->ctx); *len = yajl_buf_len((yajl_buf)g->ctx); return yajl_gen_status_ok; } void yajl_gen_clear(yajl_gen g) { if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_clear((yajl_buf)g->ctx); } base-7.0.3.1/modules/libcom/src/yajl/yajl_gen.h0000664000577000060420000001472713557101274020032 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * \file yajl_gen.h * Interface to YAJL's JSON generation facilities. */ #ifndef __YAJL_GEN_H__ #define __YAJL_GEN_H__ #include "yajl_common.h" #ifdef __cplusplus extern "C" { #endif /** generator status codes */ typedef enum { /** no error */ yajl_gen_status_ok = 0, /** at a point where a map key is generated, a function other than * yajl_gen_string was called */ yajl_gen_keys_must_be_strings, /** YAJL's maximum generation depth was exceeded. see * YAJL_MAX_DEPTH */ yajl_max_depth_exceeded, /** A generator function (yajl_gen_XXX) was called while in an error * state */ yajl_gen_in_error_state, /** A complete JSON document has been generated */ yajl_gen_generation_complete, /** yajl_gen_double was passed an invalid floating point value * (infinity or NaN). */ yajl_gen_invalid_number, /** A print callback was passed in, so there is no internal * buffer to get from */ yajl_gen_no_buf, /** returned from yajl_gen_string() when the yajl_gen_validate_utf8 * option is enabled and an invalid was passed by client code. */ yajl_gen_invalid_string } yajl_gen_status; /** an opaque handle to a generator */ typedef struct yajl_gen_t * yajl_gen; /** a callback used for "printing" the results. */ typedef void (*yajl_print_t)(void * ctx, const char * str, size_t len); /** configuration parameters for the parser, these may be passed to * yajl_gen_config() along with option specific argument(s). In general, * all configuration parameters default to *off*. */ typedef enum { /** generate indented (beautiful) output */ yajl_gen_beautify = 0x01, /** * Set an indent string which is used when yajl_gen_beautify * is enabled. Maybe something like \\t or some number of * spaces. The default is four spaces ' '. */ yajl_gen_indent_string = 0x02, /** * Set a function and context argument that should be used to * output generated json. the function should conform to the * yajl_print_t prototype while the context argument is a * void * of your choosing. * * example: * yajl_gen_config(g, yajl_gen_print_callback, myFunc, myVoidPtr); */ yajl_gen_print_callback = 0x04, /** * Normally the generator does not validate that strings you * pass to it via yajl_gen_string() are valid UTF8. Enabling * this option will cause it to do so. */ yajl_gen_validate_utf8 = 0x08, /** * the forward solidus (slash or '/' in human) is not required to be * escaped in json text. By default, YAJL will not escape it in the * iterest of saving bytes. Setting this flag will cause YAJL to * always escape '/' in generated JSON strings. */ yajl_gen_escape_solidus = 0x10 } yajl_gen_option; /** allow the modification of generator options subsequent to handle * allocation (via yajl_alloc) * \returns zero in case of errors, non-zero otherwise */ YAJL_API int yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...); /** allocate a generator handle * \param allocFuncs an optional pointer to a structure which allows * the client to overide the memory allocation * used by yajl. May be NULL, in which case * malloc/free/realloc will be used. * * \returns an allocated handle on success, NULL on failure (bad params) */ YAJL_API yajl_gen yajl_gen_alloc(const yajl_alloc_funcs * allocFuncs); /** free a generator handle */ YAJL_API void yajl_gen_free(yajl_gen handle); YAJL_API yajl_gen_status yajl_gen_integer(yajl_gen hand, long long int number); /** generate a floating point number. number may not be infinity or * NaN, as these have no representation in JSON. In these cases the * generator will return 'yajl_gen_invalid_number' */ YAJL_API yajl_gen_status yajl_gen_double(yajl_gen hand, double number); YAJL_API yajl_gen_status yajl_gen_number(yajl_gen hand, const char * num, size_t len); YAJL_API yajl_gen_status yajl_gen_string(yajl_gen hand, const unsigned char * str, size_t len); YAJL_API yajl_gen_status yajl_gen_null(yajl_gen hand); YAJL_API yajl_gen_status yajl_gen_bool(yajl_gen hand, int boolean); YAJL_API yajl_gen_status yajl_gen_map_open(yajl_gen hand); YAJL_API yajl_gen_status yajl_gen_map_close(yajl_gen hand); YAJL_API yajl_gen_status yajl_gen_array_open(yajl_gen hand); YAJL_API yajl_gen_status yajl_gen_array_close(yajl_gen hand); /** access the null terminated generator buffer. If incrementally * outputing JSON, one should call yajl_gen_clear to clear the * buffer. This allows stream generation. */ YAJL_API yajl_gen_status yajl_gen_get_buf(yajl_gen hand, const unsigned char ** buf, size_t * len); /** clear yajl's output buffer, but maintain all internal generation * state. This function will not "reset" the generator state, and is * intended to enable incremental JSON outputing. */ YAJL_API void yajl_gen_clear(yajl_gen hand); #ifdef __cplusplus } #endif #endif base-7.0.3.1/modules/libcom/src/yajl/yajl_lex.c0000664000577000060420000006363713557101274020050 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #define epicsExportSharedSymbols #include "yajl_lex.h" #include "yajl_buf.h" #ifdef YAJL_LEXER_DEBUG static const char * tokToStr(yajl_tok tok) { switch (tok) { case yajl_tok_bool: return "bool"; case yajl_tok_colon: return "colon"; case yajl_tok_comma: return "comma"; case yajl_tok_eof: return "eof"; case yajl_tok_error: return "error"; case yajl_tok_left_brace: return "brace"; case yajl_tok_left_bracket: return "bracket"; case yajl_tok_null: return "null"; case yajl_tok_integer: return "integer"; case yajl_tok_double: return "double"; case yajl_tok_right_brace: return "brace"; case yajl_tok_right_bracket: return "bracket"; case yajl_tok_string: return "string"; case yajl_tok_string_with_escapes: return "string_with_escapes"; } return "unknown"; } #endif /* Impact of the stream parsing feature on the lexer: * * YAJL support stream parsing. That is, the ability to parse the first * bits of a chunk of JSON before the last bits are available (still on * the network or disk). This makes the lexer more complex. The * responsibility of the lexer is to handle transparently the case where * a chunk boundary falls in the middle of a token. This is * accomplished is via a buffer and a character reading abstraction. * * Overview of implementation * * When we lex to end of input string before end of token is hit, we * copy all of the input text composing the token into our lexBuf. * * Every time we read a character, we do so through the readChar function. * readChar's responsibility is to handle pulling all chars from the buffer * before pulling chars from input text */ struct yajl_lexer_t { /* the overal line and char offset into the data */ size_t lineOff; size_t charOff; /* error */ yajl_lex_error error; /* a input buffer to handle the case where a token is spread over * multiple chunks */ yajl_buf buf; /* in the case where we have data in the lexBuf, bufOff holds * the current offset into the lexBuf. */ size_t bufOff; /* are we using the lex buf? */ unsigned int bufInUse; /* shall we allow comments? */ unsigned int allowComments; /* shall we validate utf8 inside strings? */ unsigned int validateUTF8; yajl_alloc_funcs * alloc; }; #define readChar(lxr, txt, off) \ (((lxr)->bufInUse && yajl_buf_len((lxr)->buf) && lxr->bufOff < yajl_buf_len((lxr)->buf)) ? \ (*((const unsigned char *) yajl_buf_data((lxr)->buf) + ((lxr)->bufOff)++)) : \ ((txt)[(*(off))++])) #define unreadChar(lxr, off) ((*(off) > 0) ? (*(off))-- : ((lxr)->bufOff--)) yajl_lexer yajl_lex_alloc(yajl_alloc_funcs * alloc, unsigned int allowComments, unsigned int validateUTF8) { yajl_lexer lxr = (yajl_lexer) YA_MALLOC(alloc, sizeof(struct yajl_lexer_t)); if (lxr == NULL) { return NULL; } memset((void *) lxr, 0, sizeof(struct yajl_lexer_t)); lxr->buf = yajl_buf_alloc(alloc); lxr->allowComments = allowComments; lxr->validateUTF8 = validateUTF8; lxr->alloc = alloc; return lxr; } void yajl_lex_free(yajl_lexer lxr) { yajl_buf_free(lxr->buf); YA_FREE(lxr->alloc, lxr); return; } /* a lookup table which lets us quickly determine three things: * VEC - valid escaped control char * note. the solidus '/' may be escaped or not. * IJC - invalid json char * VHC - valid hex char * NFP - needs further processing (from a string scanning perspective) * NUC - needs utf8 checking when enabled (from a string scanning perspective) */ #define VEC 0x01 #define IJC 0x02 #define VHC 0x04 #define NFP 0x08 #define NUC 0x10 static const char charLookupTable[256] = { /*00*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , /*08*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , /*10*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , /*18*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , /*20*/ 0 , 0 , NFP|VEC|IJC, 0 , 0 , 0 , 0 , 0 , /*28*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , VEC , /*30*/ VHC , VHC , VHC , VHC , VHC , VHC , VHC , VHC , /*38*/ VHC , VHC , 0 , 0 , 0 , 0 , 0 , 0 , /*40*/ 0 , VHC , VHC , VHC , VHC , VHC , VHC , 0 , /*48*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /*50*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /*58*/ 0 , 0 , 0 , 0 , NFP|VEC|IJC, 0 , 0 , 0 , /*60*/ 0 , VHC , VEC|VHC, VHC , VHC , VHC , VEC|VHC, 0 , /*68*/ 0 , 0 , 0 , 0 , 0 , 0 , VEC , 0 , /*70*/ 0 , 0 , VEC , 0 , VEC , 0 , 0 , 0 , /*78*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC }; /** process a variable length utf8 encoded codepoint. * * returns: * yajl_tok_string - if valid utf8 char was parsed and offset was * advanced * yajl_tok_eof - if end of input was hit before validation could * complete * yajl_tok_error - if invalid utf8 was encountered * * NOTE: on error the offset will point to the first char of the * invalid utf8 */ #define UTF8_CHECK_EOF if (*offset >= jsonTextLen) { return yajl_tok_eof; } static yajl_tok yajl_lex_utf8_char(yajl_lexer lexer, const unsigned char * jsonText, size_t jsonTextLen, size_t * offset, unsigned char curChar) { if (curChar <= 0x7f) { /* single byte */ return yajl_tok_string; } else if ((curChar >> 5) == 0x6) { /* two byte */ UTF8_CHECK_EOF; curChar = readChar(lexer, jsonText, offset); if ((curChar >> 6) == 0x2) return yajl_tok_string; } else if ((curChar >> 4) == 0x0e) { /* three byte */ UTF8_CHECK_EOF; curChar = readChar(lexer, jsonText, offset); if ((curChar >> 6) == 0x2) { UTF8_CHECK_EOF; curChar = readChar(lexer, jsonText, offset); if ((curChar >> 6) == 0x2) return yajl_tok_string; } } else if ((curChar >> 3) == 0x1e) { /* four byte */ UTF8_CHECK_EOF; curChar = readChar(lexer, jsonText, offset); if ((curChar >> 6) == 0x2) { UTF8_CHECK_EOF; curChar = readChar(lexer, jsonText, offset); if ((curChar >> 6) == 0x2) { UTF8_CHECK_EOF; curChar = readChar(lexer, jsonText, offset); if ((curChar >> 6) == 0x2) return yajl_tok_string; } } } return yajl_tok_error; } /* lex a string. input is the lexer, pointer to beginning of * json text, and start of string (offset). * a token is returned which has the following meanings: * yajl_tok_string: lex of string was successful. offset points to * terminating '"'. * yajl_tok_eof: end of text was encountered before we could complete * the lex. * yajl_tok_error: embedded in the string were unallowable chars. offset * points to the offending char */ #define STR_CHECK_EOF \ if (*offset >= jsonTextLen) { \ tok = yajl_tok_eof; \ goto finish_string_lex; \ } /** scan a string for interesting characters that might need further * review. return the number of chars that are uninteresting and can * be skipped. * (lth) hi world, any thoughts on how to make this routine faster? */ static size_t yajl_string_scan(const unsigned char * buf, size_t len, int utf8check) { unsigned char mask = IJC|NFP|(utf8check ? NUC : 0); size_t skip = 0; while (skip < len && !(charLookupTable[*buf] & mask)) { skip++; buf++; } return skip; } static yajl_tok yajl_lex_string(yajl_lexer lexer, const unsigned char * jsonText, size_t jsonTextLen, size_t * offset) { yajl_tok tok = yajl_tok_error; int hasEscapes = 0; for (;;) { unsigned char curChar; /* now jump into a faster scanning routine to skip as much * of the buffers as possible */ { const unsigned char * p; size_t len; if ((lexer->bufInUse && yajl_buf_len(lexer->buf) && lexer->bufOff < yajl_buf_len(lexer->buf))) { p = ((const unsigned char *) yajl_buf_data(lexer->buf) + (lexer->bufOff)); len = yajl_buf_len(lexer->buf) - lexer->bufOff; lexer->bufOff += yajl_string_scan(p, len, lexer->validateUTF8); } else if (*offset < jsonTextLen) { p = jsonText + *offset; len = jsonTextLen - *offset; *offset += yajl_string_scan(p, len, lexer->validateUTF8); } } STR_CHECK_EOF; curChar = readChar(lexer, jsonText, offset); /* quote terminates */ if (curChar == '"') { tok = yajl_tok_string; break; } /* backslash escapes a set of control chars, */ else if (curChar == '\\') { hasEscapes = 1; STR_CHECK_EOF; /* special case \u */ curChar = readChar(lexer, jsonText, offset); if (curChar == 'u') { unsigned int i = 0; for (i=0;i<4;i++) { STR_CHECK_EOF; curChar = readChar(lexer, jsonText, offset); if (!(charLookupTable[curChar] & VHC)) { /* back up to offending char */ unreadChar(lexer, offset); lexer->error = yajl_lex_string_invalid_hex_char; goto finish_string_lex; } } } else if (!(charLookupTable[curChar] & VEC)) { /* back up to offending char */ unreadChar(lexer, offset); lexer->error = yajl_lex_string_invalid_escaped_char; goto finish_string_lex; } } /* when not validating UTF8 it's a simple table lookup to determine * if the present character is invalid */ else if(charLookupTable[curChar] & IJC) { /* back up to offending char */ unreadChar(lexer, offset); lexer->error = yajl_lex_string_invalid_json_char; goto finish_string_lex; } /* when in validate UTF8 mode we need to do some extra work */ else if (lexer->validateUTF8) { yajl_tok t = yajl_lex_utf8_char(lexer, jsonText, jsonTextLen, offset, curChar); if (t == yajl_tok_eof) { tok = yajl_tok_eof; goto finish_string_lex; } else if (t == yajl_tok_error) { lexer->error = yajl_lex_string_invalid_utf8; goto finish_string_lex; } } /* accept it, and move on */ } finish_string_lex: /* tell our buddy, the parser, wether he needs to process this string * again */ if (hasEscapes && tok == yajl_tok_string) { tok = yajl_tok_string_with_escapes; } return tok; } #define RETURN_IF_EOF if (*offset >= jsonTextLen) return yajl_tok_eof; static yajl_tok yajl_lex_number(yajl_lexer lexer, const unsigned char * jsonText, size_t jsonTextLen, size_t * offset) { /** XXX: numbers are the only entities in json that we must lex * _beyond_ in order to know that they are complete. There * is an ambiguous case for integers at EOF. */ unsigned char c; yajl_tok tok = yajl_tok_integer; RETURN_IF_EOF; c = readChar(lexer, jsonText, offset); /* optional leading minus */ if (c == '-') { RETURN_IF_EOF; c = readChar(lexer, jsonText, offset); } /* a single zero, or a series of integers */ if (c == '0') { RETURN_IF_EOF; c = readChar(lexer, jsonText, offset); } else if (c >= '1' && c <= '9') { do { RETURN_IF_EOF; c = readChar(lexer, jsonText, offset); } while (c >= '0' && c <= '9'); } else { unreadChar(lexer, offset); lexer->error = yajl_lex_missing_integer_after_minus; return yajl_tok_error; } /* optional fraction (indicates this is floating point) */ if (c == '.') { int numRd = 0; RETURN_IF_EOF; c = readChar(lexer, jsonText, offset); while (c >= '0' && c <= '9') { numRd++; RETURN_IF_EOF; c = readChar(lexer, jsonText, offset); } if (!numRd) { unreadChar(lexer, offset); lexer->error = yajl_lex_missing_integer_after_decimal; return yajl_tok_error; } tok = yajl_tok_double; } /* optional exponent (indicates this is floating point) */ if (c == 'e' || c == 'E') { RETURN_IF_EOF; c = readChar(lexer, jsonText, offset); /* optional sign */ if (c == '+' || c == '-') { RETURN_IF_EOF; c = readChar(lexer, jsonText, offset); } if (c >= '0' && c <= '9') { do { RETURN_IF_EOF; c = readChar(lexer, jsonText, offset); } while (c >= '0' && c <= '9'); } else { unreadChar(lexer, offset); lexer->error = yajl_lex_missing_integer_after_exponent; return yajl_tok_error; } tok = yajl_tok_double; } /* we always go "one too far" */ unreadChar(lexer, offset); return tok; } static yajl_tok yajl_lex_comment(yajl_lexer lexer, const unsigned char * jsonText, size_t jsonTextLen, size_t * offset) { unsigned char c; yajl_tok tok = yajl_tok_comment; RETURN_IF_EOF; c = readChar(lexer, jsonText, offset); /* either slash or star expected */ if (c == '/') { /* now we throw away until end of line */ do { RETURN_IF_EOF; c = readChar(lexer, jsonText, offset); } while (c != '\n'); } else if (c == '*') { /* now we throw away until end of comment */ for (;;) { RETURN_IF_EOF; c = readChar(lexer, jsonText, offset); if (c == '*') { RETURN_IF_EOF; c = readChar(lexer, jsonText, offset); if (c == '/') { break; } else { unreadChar(lexer, offset); } } } } else { lexer->error = yajl_lex_invalid_char; tok = yajl_tok_error; } return tok; } yajl_tok yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText, size_t jsonTextLen, size_t * offset, const unsigned char ** outBuf, size_t * outLen) { yajl_tok tok = yajl_tok_error; unsigned char c; size_t startOffset = *offset; *outBuf = NULL; *outLen = 0; for (;;) { assert(*offset <= jsonTextLen); if (*offset >= jsonTextLen) { tok = yajl_tok_eof; goto lexed; } c = readChar(lexer, jsonText, offset); switch (c) { case '{': tok = yajl_tok_left_brace; goto lexed; case '}': tok = yajl_tok_right_brace; goto lexed; case '[': tok = yajl_tok_left_bracket; goto lexed; case ']': tok = yajl_tok_right_bracket; goto lexed; case ',': tok = yajl_tok_comma; goto lexed; case ':': tok = yajl_tok_colon; goto lexed; case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': startOffset++; break; case 't': { const char * want = "rue"; do { if (*offset >= jsonTextLen) { tok = yajl_tok_eof; goto lexed; } c = readChar(lexer, jsonText, offset); if (c != *want) { unreadChar(lexer, offset); lexer->error = yajl_lex_invalid_string; tok = yajl_tok_error; goto lexed; } } while (*(++want)); tok = yajl_tok_bool; goto lexed; } case 'f': { const char * want = "alse"; do { if (*offset >= jsonTextLen) { tok = yajl_tok_eof; goto lexed; } c = readChar(lexer, jsonText, offset); if (c != *want) { unreadChar(lexer, offset); lexer->error = yajl_lex_invalid_string; tok = yajl_tok_error; goto lexed; } } while (*(++want)); tok = yajl_tok_bool; goto lexed; } case 'n': { const char * want = "ull"; do { if (*offset >= jsonTextLen) { tok = yajl_tok_eof; goto lexed; } c = readChar(lexer, jsonText, offset); if (c != *want) { unreadChar(lexer, offset); lexer->error = yajl_lex_invalid_string; tok = yajl_tok_error; goto lexed; } } while (*(++want)); tok = yajl_tok_null; goto lexed; } case '"': { tok = yajl_lex_string(lexer, (const unsigned char *) jsonText, jsonTextLen, offset); goto lexed; } case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { /* integer parsing wants to start from the beginning */ unreadChar(lexer, offset); tok = yajl_lex_number(lexer, (const unsigned char *) jsonText, jsonTextLen, offset); goto lexed; } case '/': /* hey, look, a probable comment! If comments are disabled * it's an error. */ if (!lexer->allowComments) { unreadChar(lexer, offset); lexer->error = yajl_lex_unallowed_comment; tok = yajl_tok_error; goto lexed; } /* if comments are enabled, then we should try to lex * the thing. possible outcomes are * - successful lex (tok_comment, which means continue), * - malformed comment opening (slash not followed by * '*' or '/') (tok_error) * - eof hit. (tok_eof) */ tok = yajl_lex_comment(lexer, (const unsigned char *) jsonText, jsonTextLen, offset); if (tok == yajl_tok_comment) { /* "error" is silly, but that's the initial * state of tok. guilty until proven innocent. */ tok = yajl_tok_error; yajl_buf_clear(lexer->buf); lexer->bufInUse = 0; startOffset = *offset; break; } /* hit error or eof, bail */ goto lexed; default: lexer->error = yajl_lex_invalid_char; tok = yajl_tok_error; goto lexed; } } lexed: /* need to append to buffer if the buffer is in use or * if it's an EOF token */ if (tok == yajl_tok_eof || lexer->bufInUse) { if (!lexer->bufInUse) yajl_buf_clear(lexer->buf); lexer->bufInUse = 1; yajl_buf_append(lexer->buf, jsonText + startOffset, *offset - startOffset); lexer->bufOff = 0; if (tok != yajl_tok_eof) { *outBuf = yajl_buf_data(lexer->buf); *outLen = yajl_buf_len(lexer->buf); lexer->bufInUse = 0; } } else if (tok != yajl_tok_error) { *outBuf = jsonText + startOffset; *outLen = *offset - startOffset; } /* special case for strings. skip the quotes. */ if (tok == yajl_tok_string || tok == yajl_tok_string_with_escapes) { assert(*outLen >= 2); (*outBuf)++; *outLen -= 2; } #ifdef YAJL_LEXER_DEBUG if (tok == yajl_tok_error) { printf("lexical error: %s\n", yajl_lex_error_to_string(yajl_lex_get_error(lexer))); } else if (tok == yajl_tok_eof) { printf("EOF hit\n"); } else { printf("lexed %s: '", tokToStr(tok)); fwrite(*outBuf, 1, *outLen, stdout); printf("'\n"); } #endif return tok; } const char * yajl_lex_error_to_string(yajl_lex_error error) { switch (error) { case yajl_lex_e_ok: return "ok, no error"; case yajl_lex_string_invalid_utf8: return "invalid bytes in UTF8 string."; case yajl_lex_string_invalid_escaped_char: return "inside a string, '\\' occurs before a character " "which it may not."; case yajl_lex_string_invalid_json_char: return "invalid character inside string."; case yajl_lex_string_invalid_hex_char: return "invalid (non-hex) character occurs after '\\u' inside " "string."; case yajl_lex_invalid_char: return "invalid char in json text."; case yajl_lex_invalid_string: return "invalid string in json text."; case yajl_lex_missing_integer_after_exponent: return "malformed number, a digit is required after the exponent."; case yajl_lex_missing_integer_after_decimal: return "malformed number, a digit is required after the " "decimal point."; case yajl_lex_missing_integer_after_minus: return "malformed number, a digit is required after the " "minus sign."; case yajl_lex_unallowed_comment: return "probable comment found in input text, comments are " "not enabled."; } return "unknown error code"; } /** allows access to more specific information about the lexical * error when yajl_lex_lex returns yajl_tok_error. */ yajl_lex_error yajl_lex_get_error(yajl_lexer lexer) { if (lexer == NULL) return (yajl_lex_error) -1; return lexer->error; } size_t yajl_lex_current_line(yajl_lexer lexer) { return lexer->lineOff; } size_t yajl_lex_current_char(yajl_lexer lexer) { return lexer->charOff; } yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText, size_t jsonTextLen, size_t offset) { const unsigned char * outBuf; size_t outLen; size_t bufLen = yajl_buf_len(lexer->buf); size_t bufOff = lexer->bufOff; unsigned int bufInUse = lexer->bufInUse; yajl_tok tok; tok = yajl_lex_lex(lexer, jsonText, jsonTextLen, &offset, &outBuf, &outLen); lexer->bufOff = bufOff; lexer->bufInUse = bufInUse; yajl_buf_truncate(lexer->buf, bufLen); return tok; } base-7.0.3.1/modules/libcom/src/yajl/yajl_lex.h0000664000577000060420000001017513557101274020042 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __YAJL_LEX_H__ #define __YAJL_LEX_H__ #include "yajl_common.h" typedef enum { yajl_tok_bool, yajl_tok_colon, yajl_tok_comma, yajl_tok_eof, yajl_tok_error, yajl_tok_left_brace, yajl_tok_left_bracket, yajl_tok_null, yajl_tok_right_brace, yajl_tok_right_bracket, /* we differentiate between integers and doubles to allow the * parser to interpret the number without re-scanning */ yajl_tok_integer, yajl_tok_double, /* we differentiate between strings which require further processing, * and strings that do not */ yajl_tok_string, yajl_tok_string_with_escapes, /* comment tokens are not currently returned to the parser, ever */ yajl_tok_comment } yajl_tok; typedef struct yajl_lexer_t * yajl_lexer; yajl_lexer yajl_lex_alloc(yajl_alloc_funcs * alloc, unsigned int allowComments, unsigned int validateUTF8); void yajl_lex_free(yajl_lexer lexer); /** * run/continue a lex. "offset" is an input/output parameter. * It should be initialized to zero for a * new chunk of target text, and upon subsetquent calls with the same * target text should passed with the value of the previous invocation. * * the client may be interested in the value of offset when an error is * returned from the lexer. This allows the client to render useful n * error messages. * * When you pass the next chunk of data, context should be reinitialized * to zero. * * Finally, the output buffer is usually just a pointer into the jsonText, * however in cases where the entity being lexed spans multiple chunks, * the lexer will buffer the entity and the data returned will be * a pointer into that buffer. * * This behavior is abstracted from client code except for the performance * implications which require that the client choose a reasonable chunk * size to get adequate performance. */ yajl_tok yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText, size_t jsonTextLen, size_t * offset, const unsigned char ** outBuf, size_t * outLen); /** have a peek at the next token, but don't move the lexer forward */ yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText, size_t jsonTextLen, size_t offset); typedef enum { yajl_lex_e_ok = 0, yajl_lex_string_invalid_utf8, yajl_lex_string_invalid_escaped_char, yajl_lex_string_invalid_json_char, yajl_lex_string_invalid_hex_char, yajl_lex_invalid_char, yajl_lex_invalid_string, yajl_lex_missing_integer_after_decimal, yajl_lex_missing_integer_after_exponent, yajl_lex_missing_integer_after_minus, yajl_lex_unallowed_comment } yajl_lex_error; const char * yajl_lex_error_to_string(yajl_lex_error error); /** allows access to more specific information about the lexical * error when yajl_lex_lex returns yajl_tok_error. */ yajl_lex_error yajl_lex_get_error(yajl_lexer lexer); /** get the current offset into the most recently lexed json string. */ size_t yajl_lex_current_offset(yajl_lexer lexer); /** get the number of lines lexed by this lexer instance */ size_t yajl_lex_current_line(yajl_lexer lexer); /** get the number of chars lexed by this lexer instance since the last * \n or \r */ size_t yajl_lex_current_char(yajl_lexer lexer); #endif base-7.0.3.1/modules/libcom/src/yajl/yajl_parse.h0000664000577000060420000002311113557101274020356 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * \file yajl_parse.h * Interface to YAJL's JSON stream parsing facilities. */ #ifndef __YAJL_PARSE_H__ #define __YAJL_PARSE_H__ #include "yajl_common.h" #ifdef __cplusplus extern "C" { #endif /** error codes returned from this interface */ typedef enum { /** no error was encountered */ yajl_status_ok, /** a client callback returned zero, stopping the parse */ yajl_status_client_canceled, /** An error occured during the parse. Call yajl_get_error for * more information about the encountered error */ yajl_status_error } yajl_status; /** attain a human readable, english, string for an error */ YAJL_API const char * yajl_status_to_string(yajl_status code); /** an opaque handle to a parser */ typedef struct yajl_handle_t * yajl_handle; /** yajl is an event driven parser. this means as json elements are * parsed, you are called back to do something with the data. The * functions in this table indicate the various events for which * you will be called back. Each callback accepts a "context" * pointer, this is a void * that is passed into the yajl_parse * function which the client code may use to pass around context. * * All callbacks return an integer. If non-zero, the parse will * continue. If zero, the parse will be canceled and * yajl_status_client_canceled will be returned from the parse. * * \attention { * A note about the handling of numbers: * * yajl will only convert numbers that can be represented in a * double or a 64 bit (long long) int. All other numbers will * be passed to the client in string form using the yajl_number * callback. Furthermore, if yajl_number is not NULL, it will * always be used to return numbers, that is yajl_integer and * yajl_double will be ignored. If yajl_number is NULL but one * of yajl_integer or yajl_double are defined, parsing of a * number larger than is representable in a double or 64 bit * integer will result in a parse error. * } */ typedef struct { int (* yajl_null)(void * ctx); int (* yajl_boolean)(void * ctx, int boolVal); int (* yajl_integer)(void * ctx, long long integerVal); int (* yajl_double)(void * ctx, double doubleVal); /** A callback which passes the string representation of the number * back to the client. Will be used for all numbers when present */ int (* yajl_number)(void * ctx, const char * numberVal, size_t numberLen); /** strings are returned as pointers into the JSON text when, * possible, as a result, they are _not_ null padded */ int (* yajl_string)(void * ctx, const unsigned char * stringVal, size_t stringLen); int (* yajl_start_map)(void * ctx); int (* yajl_map_key)(void * ctx, const unsigned char * key, size_t stringLen); int (* yajl_end_map)(void * ctx); int (* yajl_start_array)(void * ctx); int (* yajl_end_array)(void * ctx); } yajl_callbacks; /** allocate a parser handle * \param callbacks a yajl callbacks structure specifying the * functions to call when different JSON entities * are encountered in the input text. May be NULL, * which is only useful for validation. * \param afs memory allocation functions, may be NULL for to use * C runtime library routines (malloc and friends) * \param ctx a context pointer that will be passed to callbacks. */ YAJL_API yajl_handle yajl_alloc(const yajl_callbacks * callbacks, yajl_alloc_funcs * afs, void * ctx); /** configuration parameters for the parser, these may be passed to * yajl_config() along with option specific argument(s). In general, * all configuration parameters default to *off*. */ typedef enum { /** Ignore javascript style comments present in * JSON input. Non-standard, but rather fun * arguments: toggled off with integer zero, on otherwise. * * example: * yajl_config(h, yajl_allow_comments, 1); // turn comment support on */ yajl_allow_comments = 0x01, /** * When set the parser will verify that all strings in JSON input are * valid UTF8 and will emit a parse error if this is not so. When set, * this option makes parsing slightly more expensive (~7% depending * on processor and compiler in use) * * example: * yajl_config(h, yajl_dont_validate_strings, 1); // disable utf8 checking */ yajl_dont_validate_strings = 0x02, /** * By default, upon calls to yajl_complete_parse(), yajl will * ensure the entire input text was consumed and will raise an error * otherwise. Enabling this flag will cause yajl to disable this * check. This can be useful when parsing json out of a that contains more * than a single JSON document. */ yajl_allow_trailing_garbage = 0x04, /** * Allow multiple values to be parsed by a single handle. The * entire text must be valid JSON, and values can be seperated * by any kind of whitespace. This flag will change the * behavior of the parser, and cause it continue parsing after * a value is parsed, rather than transitioning into a * complete state. This option can be useful when parsing multiple * values from an input stream. */ yajl_allow_multiple_values = 0x08, /** * When yajl_complete_parse() is called the parser will * check that the top level value was completely consumed. I.E., * if called whilst in the middle of parsing a value * yajl will enter an error state (premature EOF). Setting this * flag suppresses that check and the corresponding error. */ yajl_allow_partial_values = 0x10 } yajl_option; /** allow the modification of parser options subsequent to handle * allocation (via yajl_alloc) * \returns zero in case of errors, non-zero otherwise */ YAJL_API int yajl_config(yajl_handle h, yajl_option opt, ...); /** free a parser handle */ YAJL_API void yajl_free(yajl_handle handle); /** Parse some json! * \param hand - a handle to the json parser allocated with yajl_alloc * \param jsonText - a pointer to the UTF8 json text to be parsed * \param jsonTextLength - the length, in bytes, of input text */ YAJL_API yajl_status yajl_parse(yajl_handle hand, const unsigned char * jsonText, size_t jsonTextLength); /** Parse any remaining buffered json. * Since yajl is a stream-based parser, without an explicit end of * input, yajl sometimes can't decide if content at the end of the * stream is valid or not. For example, if "1" has been fed in, * yajl can't know whether another digit is next or some character * that would terminate the integer token. * * \param hand - a handle to the json parser allocated with yajl_alloc */ YAJL_API yajl_status yajl_complete_parse(yajl_handle hand); /** get an error string describing the state of the * parse. * * If verbose is non-zero, the message will include the JSON * text where the error occured, along with an arrow pointing to * the specific char. * * \returns A dynamically allocated string will be returned which should * be freed with yajl_free_error */ YAJL_API unsigned char * yajl_get_error(yajl_handle hand, int verbose, const unsigned char * jsonText, size_t jsonTextLength); /** * get the amount of data consumed from the last chunk passed to YAJL. * * In the case of a successful parse this can help you understand if * the entire buffer was consumed (which will allow you to handle * "junk at end of input"). * * In the event an error is encountered during parsing, this function * affords the client a way to get the offset into the most recent * chunk where the error occured. 0 will be returned if no error * was encountered. */ YAJL_API size_t yajl_get_bytes_consumed(yajl_handle hand); /** free an error returned from yajl_get_error */ YAJL_API void yajl_free_error(yajl_handle hand, unsigned char * str); #ifdef __cplusplus } #endif #endif base-7.0.3.1/modules/libcom/src/yajl/yajl_parser.c0000664000577000060420000005033313557101274020541 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #define epicsExportSharedSymbols #include "yajl_parse.h" #include "yajl_lex.h" #include "yajl_parser.h" #include "yajl_encode.h" #include "yajl_bytestack.h" #ifndef LLONG_MAX #define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL #define LLONG_MIN (-0x7FFFFFFFFFFFFFFFLL - 1) #endif #define MAX_VALUE_TO_MULTIPLY ((LLONG_MAX / 10) + (LLONG_MAX % 10)) /* same semantics as strtol */ long long yajl_parse_integer(const unsigned char *number, size_t length) { long long ret = 0; long sign = 1; const unsigned char *pos = number; if (*pos == '-') { pos++; sign = -1; } if (*pos == '+') { pos++; } while (pos < number + length) { if ( ret > MAX_VALUE_TO_MULTIPLY ) { errno = ERANGE; return sign == 1 ? LLONG_MAX : LLONG_MIN; } ret *= 10; if (LLONG_MAX - ret < (*pos - '0')) { errno = ERANGE; return sign == 1 ? LLONG_MAX : LLONG_MIN; } ret += (*pos++ - '0'); } return sign * ret; } unsigned char * yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, size_t jsonTextLen, int verbose) { size_t offset = hand->bytesConsumed; unsigned char * str; const char * errorType = NULL; const char * errorText = NULL; char text[72]; const char * arrow = " (right here) ------^\n"; if (yajl_bs_current(hand->stateStack) == yajl_state_parse_error) { errorType = "parse"; errorText = hand->parseError; } else if (yajl_bs_current(hand->stateStack) == yajl_state_lexical_error) { errorType = "lexical"; errorText = yajl_lex_error_to_string(yajl_lex_get_error(hand->lexer)); } else { errorType = "unknown"; } { size_t memneeded = 0; memneeded += strlen(errorType); memneeded += strlen(" error"); if (errorText != NULL) { memneeded += strlen(": "); memneeded += strlen(errorText); } str = (unsigned char *) YA_MALLOC(&(hand->alloc), memneeded + 2); if (!str) return NULL; str[0] = 0; strcat((char *) str, errorType); strcat((char *) str, " error"); if (errorText != NULL) { strcat((char *) str, ": "); strcat((char *) str, errorText); } strcat((char *) str, "\n"); } /* now we append as many spaces as needed to make sure the error * falls at char 41, if verbose was specified */ if (verbose) { size_t start, end, i; size_t spacesNeeded; spacesNeeded = (offset < 30 ? 40 - offset : 10); start = (offset >= 30 ? offset - 30 : 0); end = (offset + 30 > jsonTextLen ? jsonTextLen : offset + 30); for (i=0;ialloc), (unsigned int)(strlen((char *) str) + strlen((char *) text) + strlen(arrow) + 1)); if (newStr) { newStr[0] = 0; strcat((char *) newStr, (char *) str); strcat((char *) newStr, text); strcat((char *) newStr, arrow); } YA_FREE(&(hand->alloc), str); str = (unsigned char *) newStr; } } return str; } /* check for client cancelation */ #define _CC_CHK(x) \ if (!(x)) { \ yajl_bs_set(hand->stateStack, yajl_state_parse_error); \ hand->parseError = \ "client cancelled parse via callback return value"; \ return yajl_status_client_canceled; \ } yajl_status yajl_do_finish(yajl_handle hand) { yajl_status stat; stat = yajl_do_parse(hand,(const unsigned char *) " ",1); if (stat != yajl_status_ok) return stat; switch(yajl_bs_current(hand->stateStack)) { case yajl_state_parse_error: case yajl_state_lexical_error: return yajl_status_error; case yajl_state_got_value: case yajl_state_parse_complete: return yajl_status_ok; default: if (!(hand->flags & yajl_allow_partial_values)) { yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "premature EOF"; return yajl_status_error; } return yajl_status_ok; } } yajl_status yajl_do_parse(yajl_handle hand, const unsigned char * jsonText, size_t jsonTextLen) { yajl_tok tok; const unsigned char * buf; size_t bufLen; size_t * offset = &(hand->bytesConsumed); *offset = 0; around_again: switch (yajl_bs_current(hand->stateStack)) { case yajl_state_parse_complete: if (hand->flags & yajl_allow_multiple_values) { yajl_bs_set(hand->stateStack, yajl_state_got_value); goto around_again; } if (!(hand->flags & yajl_allow_trailing_garbage)) { if (*offset != jsonTextLen) { tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); if (tok != yajl_tok_eof) { yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "trailing garbage"; } goto around_again; } } return yajl_status_ok; case yajl_state_lexical_error: case yajl_state_parse_error: return yajl_status_error; case yajl_state_start: case yajl_state_got_value: case yajl_state_map_need_val: case yajl_state_array_need_val: case yajl_state_array_start: { /* for arrays and maps, we advance the state for this * depth, then push the state of the next depth. * If an error occurs during the parsing of the nesting * enitity, the state at this level will not matter. * a state that needs pushing will be anything other * than state_start */ yajl_state stateToPush = yajl_state_start; tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_eof: return yajl_status_ok; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; case yajl_tok_string: if (hand->callbacks && hand->callbacks->yajl_string) { _CC_CHK(hand->callbacks->yajl_string(hand->ctx, buf, bufLen)); } break; case yajl_tok_string_with_escapes: if (hand->callbacks && hand->callbacks->yajl_string) { yajl_buf_clear(hand->decodeBuf); yajl_string_decode(hand->decodeBuf, buf, bufLen); _CC_CHK(hand->callbacks->yajl_string( hand->ctx, yajl_buf_data(hand->decodeBuf), yajl_buf_len(hand->decodeBuf))); } break; case yajl_tok_bool: if (hand->callbacks && hand->callbacks->yajl_boolean) { _CC_CHK(hand->callbacks->yajl_boolean(hand->ctx, *buf == 't')); } break; case yajl_tok_null: if (hand->callbacks && hand->callbacks->yajl_null) { _CC_CHK(hand->callbacks->yajl_null(hand->ctx)); } break; case yajl_tok_left_brace: if (hand->callbacks && hand->callbacks->yajl_start_map) { _CC_CHK(hand->callbacks->yajl_start_map(hand->ctx)); } stateToPush = yajl_state_map_start; break; case yajl_tok_left_bracket: if (hand->callbacks && hand->callbacks->yajl_start_array) { _CC_CHK(hand->callbacks->yajl_start_array(hand->ctx)); } stateToPush = yajl_state_array_start; break; case yajl_tok_integer: if (hand->callbacks) { if (hand->callbacks->yajl_number) { _CC_CHK(hand->callbacks->yajl_number( hand->ctx,(const char *) buf, bufLen)); } else if (hand->callbacks->yajl_integer) { long long int i = 0; i = yajl_parse_integer(buf, bufLen); if ((i == LLONG_MIN || i == LLONG_MAX) && errno == ERANGE) { yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "integer overflow" ; /* try to restore error offset */ if (*offset >= bufLen) *offset -= bufLen; else *offset = 0; goto around_again; } _CC_CHK(hand->callbacks->yajl_integer(hand->ctx, i)); } } break; case yajl_tok_double: if (hand->callbacks) { if (hand->callbacks->yajl_number) { _CC_CHK(hand->callbacks->yajl_number( hand->ctx, (const char *) buf, bufLen)); } else if (hand->callbacks->yajl_double) { double d = 0.0; yajl_buf_clear(hand->decodeBuf); yajl_buf_append(hand->decodeBuf, buf, bufLen); buf = yajl_buf_data(hand->decodeBuf); d = strtod((char *) buf, NULL); if ((d == HUGE_VAL || d == -HUGE_VAL) && errno == ERANGE) { yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "numeric (floating point) " "overflow"; /* try to restore error offset */ if (*offset >= bufLen) *offset -= bufLen; else *offset = 0; goto around_again; } _CC_CHK(hand->callbacks->yajl_double(hand->ctx, d)); } } break; case yajl_tok_right_bracket: { yajl_state s = yajl_bs_current(hand->stateStack); if (s == yajl_state_array_start || s == yajl_state_array_need_val) { if (hand->callbacks && hand->callbacks->yajl_end_array) { _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); } yajl_bs_pop(hand->stateStack); goto around_again; } /* intentional fall-through */ } case yajl_tok_colon: case yajl_tok_comma: case yajl_tok_right_brace: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "unallowed token at this point in JSON text"; goto around_again; default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "invalid token, internal error"; goto around_again; } /* got a value. transition depends on the state we're in. */ { yajl_state s = yajl_bs_current(hand->stateStack); if (s == yajl_state_start || s == yajl_state_got_value) { yajl_bs_set(hand->stateStack, yajl_state_parse_complete); } else if (s == yajl_state_map_need_val) { yajl_bs_set(hand->stateStack, yajl_state_map_got_val); } else { yajl_bs_set(hand->stateStack, yajl_state_array_got_val); } } if (stateToPush != yajl_state_start) { yajl_bs_push(hand->stateStack, stateToPush); } goto around_again; } case yajl_state_map_start: case yajl_state_map_need_key: { /* only difference between these two states is that in * start '}' is valid, whereas in need_key, we've parsed * a comma, and a string key _must_ follow */ tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_eof: return yajl_status_ok; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; case yajl_tok_string_with_escapes: if (hand->callbacks && hand->callbacks->yajl_map_key) { yajl_buf_clear(hand->decodeBuf); yajl_string_decode(hand->decodeBuf, buf, bufLen); buf = yajl_buf_data(hand->decodeBuf); bufLen = yajl_buf_len(hand->decodeBuf); } /* intentional fall-through */ case yajl_tok_string: if (hand->callbacks && hand->callbacks->yajl_map_key) { _CC_CHK(hand->callbacks->yajl_map_key(hand->ctx, buf, bufLen)); } yajl_bs_set(hand->stateStack, yajl_state_map_sep); goto around_again; case yajl_tok_right_brace: { yajl_state s = yajl_bs_current(hand->stateStack); if (s == yajl_state_map_start || s == yajl_state_map_need_key) { if (hand->callbacks && hand->callbacks->yajl_end_map) { _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); } yajl_bs_pop(hand->stateStack); goto around_again; } } default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "invalid object key (must be a string)"; goto around_again; } } case yajl_state_map_sep: { tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_colon: yajl_bs_set(hand->stateStack, yajl_state_map_need_val); goto around_again; case yajl_tok_eof: return yajl_status_ok; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "object key and value must " "be separated by a colon (':')"; goto around_again; } } case yajl_state_map_got_val: { tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_right_brace: if (hand->callbacks && hand->callbacks->yajl_end_map) { _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); } yajl_bs_pop(hand->stateStack); goto around_again; case yajl_tok_comma: yajl_bs_set(hand->stateStack, yajl_state_map_need_key); goto around_again; case yajl_tok_eof: return yajl_status_ok; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "after key and value, inside map, " "I expect ',' or '}'"; /* try to restore error offset */ if (*offset >= bufLen) *offset -= bufLen; else *offset = 0; goto around_again; } } case yajl_state_array_got_val: { tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, offset, &buf, &bufLen); switch (tok) { case yajl_tok_right_bracket: if (hand->callbacks && hand->callbacks->yajl_end_array) { _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); } yajl_bs_pop(hand->stateStack); goto around_again; case yajl_tok_comma: yajl_bs_set(hand->stateStack, yajl_state_array_need_val); goto around_again; case yajl_tok_eof: return yajl_status_ok; case yajl_tok_error: yajl_bs_set(hand->stateStack, yajl_state_lexical_error); goto around_again; default: yajl_bs_set(hand->stateStack, yajl_state_parse_error); hand->parseError = "after array element, I expect ',' or ']'"; goto around_again; } } } abort(); return yajl_status_error; } base-7.0.3.1/modules/libcom/src/yajl/yajl_parser.h0000664000577000060420000000467413557101274020555 0ustar anjaesctl/* * Copyright (c) 2007-2011, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __YAJL_PARSER_H__ #define __YAJL_PARSER_H__ #include "yajl_parse.h" #include "yajl_bytestack.h" #include "yajl_buf.h" #include "yajl_lex.h" typedef enum { yajl_state_start = 0, yajl_state_parse_complete, yajl_state_parse_error, yajl_state_lexical_error, yajl_state_map_start, yajl_state_map_sep, yajl_state_map_need_val, yajl_state_map_got_val, yajl_state_map_need_key, yajl_state_array_start, yajl_state_array_got_val, yajl_state_array_need_val, yajl_state_got_value, } yajl_state; struct yajl_handle_t { const yajl_callbacks * callbacks; void * ctx; yajl_lexer lexer; const char * parseError; /* the number of bytes consumed from the last client buffer, * in the case of an error this will be an error offset, in the * case of an error this can be used as the error offset */ size_t bytesConsumed; /* temporary storage for decoded strings */ yajl_buf decodeBuf; /* a stack of states. access with yajl_state_XXX routines */ yajl_bytestack stateStack; /* memory allocation routines */ yajl_alloc_funcs alloc; /* bitfield */ unsigned int flags; }; yajl_status yajl_do_parse(yajl_handle handle, const unsigned char * jsonText, size_t jsonTextLen); yajl_status yajl_do_finish(yajl_handle handle); unsigned char * yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, size_t jsonTextLen, int verbose); /* A little built in integer parsing routine with the same semantics as strtol * that's unaffected by LOCALE. */ long long yajl_parse_integer(const unsigned char *number, size_t length); #endif base-7.0.3.1/modules/libcom/test/Makefile0000775000577000060420000002257513557101274016765 0ustar anjaesctl#************************************************************************* # Copyright (c) 2006 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. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* TOP = ../../.. include $(TOP)/configure/CONFIG PROD_LIBS += Com PROD_SYS_LIBS_WIN32 += ws2_32 advapi32 user32 PROD_SYS_LIBS_solaris += socket nsl PROD_SRCS_RTEMS += rtemsTestData.c ifeq ($(EPICS_HOST_ARCH),$(T_A)) # skip except for host arch due to custom .plt TESTPROD_HOST += epicsUnitTestTest epicsUnitTestTest_SRCS += epicsUnitTestTest.c # Not much point running this on vxWorks or RTEMS... TESTS += epicsUnitTestTest endif TESTPROD_HOST += epicsTypesTest epicsTypesTest_SRCS += epicsTypesTest.cpp testHarness_SRCS += epicsTypesTest.cpp TESTS += epicsTypesTest TESTPROD_HOST += epicsInlineTest epicsInlineTest_SRCS += epicsInlineTest1.c epicsInlineTest_SRCS += epicsInlineTest2.c epicsInlineTest_SRCS += epicsInlineTest3.cpp epicsInlineTest_SRCS += epicsInlineTest4.cpp testHarness_SRCS += $(epicsInlineTest_SRCS) TESTS += epicsInlineTest TESTPROD_HOST += epicsCalcTest epicsCalcTest_SRCS += epicsCalcTest.cpp testHarness_SRCS += epicsCalcTest.cpp TESTS += epicsCalcTest TESTPROD_HOST += epicsAlgorithmTest epicsAlgorithmTest_SRCS += epicsAlgorithmTest.cpp testHarness_SRCS += epicsAlgorithmTest.cpp TESTS += epicsAlgorithmTest TESTPROD_HOST += epicsMathTest epicsMathTest_SRCS += epicsMathTest.c testHarness_SRCS += epicsMathTest.c TESTS += epicsMathTest TESTPROD_HOST += epicsMMIOTest epicsMMIOTest_SRCS += epicsMMIOTest.c testHarness_SRCS += epicsMMIOTest.c TESTS += epicsMMIOTest TESTPROD_HOST += epicsEllTest epicsEllTest_SRCS += epicsEllTest.c testHarness_SRCS += epicsEllTest.c TESTS += epicsEllTest TESTPROD_HOST += epicsEnvTest epicsEnvTest_SRCS += epicsEnvTest.c testHarness_SRCS += epicsEnvTest.c TESTS += epicsEnvTest TESTPROD_HOST += epicsEnvUnsetTest epicsEnvUnsetTest_SRCS += epicsEnvUnsetTest.c testHarness_SRCS += epicsEnvUnsetTest.c TESTS += epicsEnvUnsetTest TESTPROD_HOST += epicsErrlogTest epicsErrlogTest_SRCS += epicsErrlogTest.c testHarness_SRCS += epicsErrlogTest.c TESTS += epicsErrlogTest TESTPROD_HOST += epicsStdioTest epicsStdioTest_SRCS += epicsStdioTest.c testHarness_SRCS += epicsStdioTest.c TESTS += epicsStdioTest TESTPROD_HOST += epicsStdlibTest epicsStdlibTest_SRCS += epicsStdlibTest.c testHarness_SRCS += epicsStdlibTest.c TESTS += epicsStdlibTest TESTPROD_HOST += epicsSockResolveTest epicsSockResolveTest_SRCS += epicsSockResolveTest.c testHarness_SRCS += epicsSockResolveTest.c TESTS += epicsSockResolveTest TESTPROD_HOST += epicsStringTest epicsStringTest_SRCS += epicsStringTest.c testHarness_SRCS += epicsStringTest.c TESTS += epicsStringTest TESTPROD_HOST += epicsTimeTest epicsTimeTest_SRCS += epicsTimeTest.cpp testHarness_SRCS += epicsTimeTest.cpp TESTS += epicsTimeTest TESTPROD_HOST += epicsTimeZoneTest epicsTimeZoneTest_SRCS += epicsTimeZoneTest.c libComTestHarness_SRCS_RTEMS += epicsTimeZoneTest.c TESTS += epicsTimeZoneTest TESTPROD_HOST += epicsThreadTest epicsThreadTest_SRCS += epicsThreadTest.cpp testHarness_SRCS += epicsThreadTest.cpp TESTS += epicsThreadTest TESTPROD_HOST += epicsThreadOnceTest epicsThreadOnceTest_SRCS += epicsThreadOnceTest.c testHarness_SRCS += epicsThreadOnceTest.c TESTS += epicsThreadOnceTest TESTPROD_HOST += epicsThreadPriorityTest epicsThreadPriorityTest_SRCS += epicsThreadPriorityTest.cpp testHarness_SRCS += epicsThreadPriorityTest.cpp TESTS += epicsThreadPriorityTest TESTPROD_HOST += epicsThreadPrivateTest epicsThreadPrivateTest_SRCS += epicsThreadPrivateTest.cpp testHarness_SRCS += epicsThreadPrivateTest.cpp TESTS += epicsThreadPrivateTest TESTPROD_HOST += epicsThreadHooksTest epicsThreadHooksTest_SRCS += epicsThreadHooksTest.c testHarness_SRCS += epicsThreadHooksTest.c TESTS += epicsThreadHooksTest TESTPROD_HOST += epicsThreadPoolTest epicsThreadPoolTest_SRCS += epicsThreadPoolTest.c testHarness_SRCS += epicsThreadPoolTest.c TESTS += epicsThreadPoolTest TESTPROD_HOST += epicsExitTest epicsExitTest_SRCS += epicsExitTest.c testHarness_SRCS += epicsExitTest.c TESTS += epicsExitTest TESTPROD_HOST += epicsTimerTest epicsTimerTest_SRCS += epicsTimerTest.cpp testHarness_SRCS += epicsTimerTest.cpp TESTS += epicsTimerTest TESTPROD_HOST += ringPointerTest ringPointerTest_SRCS += ringPointerTest.c testHarness_SRCS += ringPointerTest.c TESTS += ringPointerTest TESTPROD_HOST += ringBytesTest ringBytesTest_SRCS += ringBytesTest.c testHarness_SRCS += ringBytesTest.c TESTS += ringBytesTest TESTPROD_HOST += epicsEventTest epicsEventTest_SRCS += epicsEventTest.cpp testHarness_SRCS += epicsEventTest.cpp TESTS += epicsEventTest TESTPROD_HOST += epicsMutexTest epicsMutexTest_SRCS += epicsMutexTest.cpp testHarness_SRCS += epicsMutexTest.cpp TESTS += epicsMutexTest TESTPROD_HOST += epicsSpinTest epicsSpinTest_SRCS += epicsSpinTest.c testHarness_SRCS += epicsSpinTest.c TESTS += epicsSpinTest TESTPROD_HOST += epicsAtomicTest epicsAtomicTest_SRCS += epicsAtomicTest.cpp testHarness_SRCS += epicsAtomicTest.cpp TESTS += epicsAtomicTest TESTPROD_HOST += macDefExpandTest macDefExpandTest_SRCS += macDefExpandTest.c testHarness_SRCS += macDefExpandTest.c TESTS += macDefExpandTest TESTPROD_HOST += cvtFastTest cvtFastTest_SRCS += cvtFastTest.cpp testHarness_SRCS += cvtFastTest.cpp TESTS += cvtFastTest TESTPROD_HOST += macLibTest macLibTest_SRCS += macLibTest.c testHarness_SRCS += macLibTest.c TESTS += macLibTest TESTPROD_HOST += aslibtest aslibtest_SRCS += aslibtest.c testHarness_SRCS += aslibtest.c TESTS += aslibtest # Perl module tests: TESTS += macLib TESTPROD_HOST += taskwdTest taskwdTest_SRCS += taskwdTest.c testHarness_SRCS += taskwdTest.c TESTS += taskwdTest TESTPROD_HOST += blockingSockTest blockingSockTest_SRCS += blockingSockTest.cpp testHarness_SRCS += blockingSockTest.cpp TESTS += blockingSockTest TESTPROD_HOST += epicsMessageQueueTest epicsMessageQueueTest_SRCS += epicsMessageQueueTest.cpp testHarness_SRCS += epicsMessageQueueTest.cpp TESTS += epicsMessageQueueTest # we need to build this with debug symbols in all configurations # otherwise the test will not be able to lookup names and so fail TESTPROD_HOST += epicsStackTraceTest epicsStackTraceTest_SRCS += epicsStackTraceTest.c ifneq ($(findstring mingw,$(T_A)),) epicsStackTraceTest_CFLAGS_WIN32 += -g -O0 epicsStackTraceTest_LDFLAGS_WIN32 += -g else epicsStackTraceTest_CFLAGS_WIN32 += -Zi epicsStackTraceTest_LDFLAGS_WIN32 += -DEBUG endif testHarness_SRCS += epicsStackTraceTest.c TESTS += epicsStackTraceTest TESTPROD_HOST += ipAddrToAsciiTest ipAddrToAsciiTest_SRCS += ipAddrToAsciiTest.cpp testHarness_SRCS += ipAddrToAsciiTest.cpp TESTS += ipAddrToAsciiTest TESTPROD_HOST += osiSockTest osiSockTest_SRCS += osiSockTest.c testHarness_SRCS += osiSockTest.c TESTS += osiSockTest TESTPROD_HOST += testexecname testexecname_SRCS += testexecname.c # no point in including in testHarness. Not implemented for RTEMS/vxWorks. TESTS += testexecname ifeq ($(BUILD_CLASS),HOST) ifneq ($(OS_CLASS),WIN32) # This test can only be run on a build host, and is broken on Windows TESTPROD_HOST += yajl_test yajl_test_SRCS += yajl_test.c TESTS += yajlTest endif endif TESTPROD_HOST += iocshTest iocshTest_SRCS += iocshTest.cpp TESTS += iocshTest TESTFILES += $(wildcard ../iocshTest*.cmd) # The testHarness runs all the test programs in a known working order. testHarness_SRCS += epicsRunLibComTests.c libComTestHarness_SRCS += $(testHarness_SRCS) libComTestHarness_SRCS_RTEMS += rtemsTestHarness.c PROD_vxWorks = libComTestHarness PROD_RTEMS += libComTestHarness TESTSPEC_vxWorks = libComTestHarness.munch; epicsRunLibComTests TESTSPEC_RTEMS = libComTestHarness.boot; epicsRunLibComTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) TESTPROD = $(TESTPROD_HOST) TESTSCRIPTS += $(filter-out epicsUnitTestTest.t, $(TESTS:%=%.t)) endif # The following are not test programs, they measure performance. # They should not be added to TESTS or to epicsRunLibComTests.c TESTPROD_HOST += epicsThreadPerform epicsThreadPerform_SRCS += epicsThreadPerform.cpp testHarness_SRCS += epicsThreadPerform.cpp TESTPROD_HOST += epicsMaxThreads epicsMaxThreads_SRCS += epicsMaxThreads.c testHarness_SRCS += epicsMaxThreads.c TESTPROD_HOST += buckTest buckTest_SRCS += buckTest.c testHarness_SRCS += buckTest.c #TESTPROD_HOST += fdmgrTest fdmgrTest_SRCS += fdmgrTest.c fdmgrTest_LIBS += ca # FIXME: program never exits. TESTPROD_HOST += epicsAtomicPerform epicsAtomicPerform_SRCS += epicsAtomicPerform.cpp testHarness_SRCS += epicsAtomicPerform.cpp TESTPROD_HOST += cvtFastPerform cvtFastPerform_SRCS += cvtFastPerform.cpp testHarness_SRCS += cvtFastPerform.cpp ifeq ($(OS_CLASS),Linux) ifeq ($(USE_POSIX_THREAD_PRIORITY_SCHEDULING),YES) TESTPROD_HOST += nonEpicsThreadPriorityTest nonEpicsThreadPriorityTest_SRCS += nonEpicsThreadPriorityTest.cpp nonEpicsThreadPriorityTest_SYS_LIBS += $(POSIX_LDLIBS:-l%=%) testHarness_SRCS += nonEpicsThreadPriorityTest.cpp epicsRunLibComTests_CFLAGS += -DHAVE_PTHREAD_PRIORITY_SCHEDULING TESTS += nonEpicsThreadPriorityTest endif endif include $(TOP)/configure/RULES rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES) base-7.0.3.1/modules/libcom/test/aslibtest.c0000664000577000060420000000721613557101274017453 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2018 Michael Davidsaver * EPICS BASE 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 static char *asUser, *asHost; static int asAsl; static void setUser(const char *name) { free(asUser); asUser = epicsStrDup(name); } static void setHost(const char *name) { free(asHost); asHost = epicsStrDup(name); } static void testAccess(const char *asg, unsigned mask) { ASMEMBERPVT asp = 0; /* aka dbCommon::asp */ ASCLIENTPVT client = 0; long ret; ret = asAddMember(&asp, asg); if(ret) { testFail("testAccess(ASG:%s, USER:%s, HOST:%s, ASL:%d) -> asAddMember error: %s", asg, asUser, asHost, asAsl, errSymMsg(ret)); } else { ret = asAddClient(&client, asp, asAsl, asUser, asHost); } if(ret) { testFail("testAccess(ASG:%s, USER:%s, HOST:%s, ASL:%d) -> asAddClient error: %s", asg, asUser, asHost, asAsl, errSymMsg(ret)); } else { unsigned actual = 0; actual |= asCheckGet(client) ? 1 : 0; actual |= asCheckPut(client) ? 2 : 0; testOk(actual==mask, "testAccess(ASG:%s, USER:%s, HOST:%s, ASL:%d) -> %x == %x", asg, asUser, asHost, asAsl, actual, mask); } if(client) asRemoveClient(&client); if(asp) asRemoveMember(&asp); } static void testSyntaxErrors(void) { static const char empty[] = "\n#almost empty file\n\n"; long ret; testDiag("testSyntaxErrors()"); eltc(0); ret = asInitMem(empty, NULL); testOk(ret==S_asLib_badConfig, "load \"empty\" config -> %s", errSymMsg(ret)); eltc(1); } static const char hostname_config[] = "" "HAG(foo) {localhost}\n" "ASG(DEFAULT) {RULE(0, NONE)}\n" "ASG(ro) {RULE(0, NONE)RULE(1, READ) {HAG(foo)}}\n" "ASG(rw) {RULE(1, WRITE) {HAG(foo)}}\n" ; static void testHostNames(void) { testDiag("testHostNames()"); asCheckClientIP = 0; testOk1(asInitMem(hostname_config, NULL)==0); setUser("testing"); setHost("localhost"); asAsl = 0; testAccess("invalid", 0); testAccess("DEFAULT", 0); testAccess("ro", 1); testAccess("rw", 3); setHost("127.0.0.1"); testAccess("invalid", 0); testAccess("DEFAULT", 0); testAccess("ro", 0); testAccess("rw", 0); setHost("guaranteed.invalid."); testAccess("invalid", 0); testAccess("DEFAULT", 0); testAccess("ro", 0); testAccess("rw", 0); } static void testUseIP(void) { testDiag("testUseIP()"); asCheckClientIP = 1; /* still host names in .acf */ testOk1(asInitMem(hostname_config, NULL)==0); /* now resolved to IPs */ setUser("testing"); setHost("localhost"); /* will not match against resolved IP */ asAsl = 0; testAccess("invalid", 0); testAccess("DEFAULT", 0); testAccess("ro", 0); testAccess("rw", 0); setHost("127.0.0.1"); testAccess("invalid", 0); testAccess("DEFAULT", 0); testAccess("ro", 1); testAccess("rw", 3); setHost("guaranteed.invalid."); testAccess("invalid", 0); testAccess("DEFAULT", 0); testAccess("ro", 0); testAccess("rw", 0); } MAIN(aslibtest) { testPlan(27); testSyntaxErrors(); testHostNames(); testUseIP(); errlogFlush(); return testDone(); } base-7.0.3.1/modules/libcom/test/blockingSockTest.cpp0000664000577000060420000001720213557101274021265 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include "osiSock.h" #include "osiWireFormat.h" #include "epicsThread.h" #include "epicsSignal.h" #include "epicsUnitTest.h" #include "testMain.h" union address { struct sockaddr_in ia; struct sockaddr sa; }; class circuit { public: circuit ( SOCKET ); void recvTest (); void shutdown (); void close (); bool recvWakeupDetected () const; bool sendWakeupDetected () const; virtual const char * pName () = 0; protected: SOCKET sock; epicsThreadId id; bool recvWakeup; bool sendWakeup; protected: virtual ~circuit() {} }; class serverCircuit : public circuit { public: serverCircuit ( SOCKET ); private: const char * pName (); }; class clientCircuit : public circuit { public: clientCircuit ( const address & ); private: const char * pName (); }; class server { public: server ( const address & ); void start (); void daemon (); void stop (); address addr () const; protected: address srvaddr; SOCKET sock; epicsThreadId id; bool exit; }; circuit::circuit ( SOCKET sockIn ) : sock ( sockIn ), id ( 0 ), recvWakeup ( false ), sendWakeup ( false ) { testOk ( this->sock != INVALID_SOCKET, "Socket valid" ); } bool circuit::recvWakeupDetected () const { return this->recvWakeup; } bool circuit::sendWakeupDetected () const { return this->sendWakeup; } void circuit::shutdown () { int status = ::shutdown ( this->sock, SHUT_RDWR ); testOk ( status == 0, "Shutdown() returned Ok" ); } void circuit::close () { epicsSocketDestroy ( this->sock ); } void circuit::recvTest () { char buf [1]; while ( true ) { int status = recv ( this->sock, buf, (int) sizeof ( buf ), 0 ); if ( status == 0 ) { testDiag ( "%s was disconnected", this->pName () ); this->recvWakeup = true; break; } else if ( status > 0 ) { testDiag ( "%s received %i characters", this->pName (), status ); } else { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); testDiag ( "%s socket recv() error was \"%s\"", this->pName (), sockErrBuf ); this->recvWakeup = true; break; } } } extern "C" void socketRecvTest ( void * pParm ) { circuit * pCir = reinterpret_cast < circuit * > ( pParm ); pCir->recvTest (); } clientCircuit::clientCircuit ( const address & addrIn ) : circuit ( epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ) { address tmpAddr = addrIn; int status = ::connect ( this->sock, & tmpAddr.sa, sizeof ( tmpAddr ) ); testOk ( status == 0, "Client end connected" ); circuit * pCir = this; this->id = epicsThreadCreate ( "client circuit", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), socketRecvTest, pCir ); testOk ( this->id != 0, "Client thread created" ); } const char * clientCircuit::pName () { return "client circuit"; } extern "C" void serverDaemon ( void * pParam ) { server * pSrv = reinterpret_cast < server * > ( pParam ); pSrv->daemon (); } server::server ( const address & addrIn ) : srvaddr ( addrIn ), sock ( epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ), id ( 0 ), exit ( false ) { testOk ( this->sock != INVALID_SOCKET, "Server socket valid" ); // setup server side osiSocklen_t slen = sizeof ( this->srvaddr ); int status = bind ( this->sock, & this->srvaddr.sa, slen ); if ( status ) { testDiag ( "bind to server socket failed, status = %d", status ); } if ( getsockname(this->sock, & this->srvaddr.sa, & slen) != 0 ) { testAbort ( "Failed to read socket address" ); } status = listen ( this->sock, 10 ); testOk ( status == 0, "Server socket listening" ); } void server::start () { this->id = epicsThreadCreate ( "server daemon", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), serverDaemon, this ); testOk ( this->id != 0, "Server thread created" ); } void server::daemon () { while ( ! this->exit ) { // accept client side address addr; osiSocklen_t addressSize = sizeof ( addr ); SOCKET ns = accept ( this->sock, & addr.sa, & addressSize ); if ( this->exit ) break; testOk ( ns != INVALID_SOCKET, "Accepted socket valid" ); circuit * pCir = new serverCircuit ( ns ); testOk ( pCir != 0, "Server circuit created" ); } } void server::stop () { this->exit = true; epicsSocketDestroy ( this->sock ); } address server::addr () const { return this->srvaddr; } serverCircuit::serverCircuit ( SOCKET sockIn ) : circuit ( sockIn ) { circuit * pCir = this; epicsThreadId threadId = epicsThreadCreate ( "server circuit", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), socketRecvTest, pCir ); testOk ( threadId != 0, "Server circuit thread created" ); } const char * serverCircuit::pName () { return "server circuit"; } static const char *mechName(int mech) { static const struct { int mech; const char *name; } mechs[] = { {-1, "Unknown shutdown mechanism" }, {esscimqi_socketCloseRequired, "esscimqi_socketCloseRequired" }, {esscimqi_socketBothShutdownRequired, "esscimqi_socketBothShutdownRequired" }, {esscimqi_socketSigAlarmRequired, "esscimqi_socketSigAlarmRequired" } }; for (unsigned i=0; i < (sizeof(mechs) / sizeof(mechs[0])); ++i) { if (mech == mechs[i].mech) return mechs[i].name; } return "Unknown shutdown mechanism value"; } MAIN(blockingSockTest) { testPlan(13); osiSockAttach(); address addr; memset ( (char *) & addr, 0, sizeof ( addr ) ); addr.ia.sin_family = AF_INET; addr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK ); addr.ia.sin_port = 0; server srv ( addr ); srv.start (); addr = srv.addr (); clientCircuit client ( addr ); epicsThreadSleep ( 1.0 ); testOk ( ! client.recvWakeupDetected (), "Client is asleep" ); testDiag("Trying Shutdown mechanism"); client.shutdown (); epicsThreadSleep ( 1.0 ); int mech = -1; if ( client.recvWakeupDetected () ) { mech = esscimqi_socketBothShutdownRequired; testDiag("Shutdown succeeded"); } else { testDiag("Trying Close mechanism"); client.close (); epicsThreadSleep ( 1.0 ); if ( client.recvWakeupDetected () ) { mech = esscimqi_socketCloseRequired; testDiag("Close succeeded"); } } testDiag("This OS behaves like \"%s\".", mechName(mech)); int query = epicsSocketSystemCallInterruptMechanismQuery (); if (! testOk(mech == query, "Declared mechanism works") ) testDiag("epicsSocketSystemCallInterruptMechanismQuery returned \"%s\"", mechName(query)); srv.stop (); epicsThreadSleep ( 1.0 ); osiSockRelease(); return testDone(); } base-7.0.3.1/modules/libcom/test/buckTest.c0000664000577000060420000000501113557101274017234 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include "epicsTime.h" #include "epicsAssert.h" #include "bucketLib.h" #include "testMain.h" #define verify(exp) ((exp) ? (void)0 : \ epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) MAIN(buckTest) { unsigned id1; unsigned id2; char * pValSave1; char * pValSave2; int s; BUCKET * pb; char * pVal; unsigned i; epicsTimeStamp start, finish; double duration; const int LOOPS = 500000; pb = bucketCreate(8); if (!pb) { return -1; } id1 = 0x1000a432; pValSave1 = "fred"; s = bucketAddItemUnsignedId(pb, &id1, pValSave1); verify (s == S_bucket_success); pValSave2 = "jane"; id2 = 0x0000a432; s = bucketAddItemUnsignedId(pb, &id2, pValSave2); verify (s == S_bucket_success); epicsTimeGetCurrent(&start); for (i=0; i #include #include #include #include #include #include #include "epicsStdio.h" #include "cvtFast.h" #include "epicsTime.h" #include "testMain.h" using namespace std; class PerfConverter { public: virtual const char * name(void) const = 0; virtual int maxPrecision(void) const = 0; virtual void target (double srcD, float srcF, char *dst, size_t len, int prec) const = 0; virtual void add(int prec, double elapsed) = 0; virtual double total(int prec) = 0; virtual ~PerfConverter () {}; }; class Perf { public: Perf ( int maxConverters ); virtual ~Perf (); void addConverter( PerfConverter * c ); void execute (int count, bool verbose); void report (const char *title, int count); protected: static unsigned const nUnrolled = 10; static const unsigned uSecPerSec = 1000000; static unsigned const nIterations = 10000; const int maxConverters; PerfConverter **converters; int nConverters; int maxPrecision; bool verbose; void measure ( double srcD, float srcF, int prec ); private: Perf ( const Perf & ); Perf & operator = ( Perf & ); }; Perf :: Perf ( int maxConverters_ ) : maxConverters ( maxConverters_ ), converters ( new PerfConverter * [ maxConverters_ ] ), nConverters ( 0 ), maxPrecision ( 0 ) { } Perf :: ~Perf () { for ( int j = 0; j < nConverters; j++ ) delete converters[ j ]; delete [] converters; } void Perf :: addConverter(PerfConverter *c) { if ( nConverters >= maxConverters ) throw std :: runtime_error ( "Too many converters" ); converters[ nConverters++ ] = c; int prec = c->maxPrecision(); if ( prec > maxPrecision ) maxPrecision = prec; } void Perf :: execute (const int count, bool verbose_) { verbose = verbose_; for ( int i = 0; i < count; i++ ) { double srcDbl = rand (); srcDbl /= (RAND_MAX + 1.0); srcDbl *= 20.0; srcDbl -= 10.0; float srcFlt = (float) srcDbl; for ( int prec = 0; prec <= maxPrecision; prec++ ) { measure (srcFlt, srcDbl, prec); } } report ( "Small numbers, -10..+10", count ); for ( int i = 0; i < count; i++ ) { double mVal = rand (); mVal /= (RAND_MAX + 1.0); double eVal = rand (); eVal /= (RAND_MAX + 1.0); double dVal = eVal; dVal *= FLT_MAX_EXP - FLT_MIN_EXP; dVal += FLT_MIN_EXP; int dEVal = static_cast < int > ( dVal + 0.5 ); double srcDbl = ldexp ( mVal, dEVal ); float srcFlt = (float) srcDbl; for ( int prec = 0; prec <= maxPrecision; prec++ ) { measure (srcFlt, srcDbl, prec); } } report ( "Random mantissa+exponent", count ); } void Perf :: report (const char *title, const int count) { printf( "\n%s\n\nprec\t", title ); for ( int j = 0; j < nConverters; j++ ) printf( "%-16s ", converters[j]->name() ); for (int prec = 0; prec <= maxPrecision; prec++ ) { printf( "\n %2d\t", prec ); for (int j = 0; j < nConverters; j++ ) { PerfConverter *c = converters[j]; if (prec > c->maxPrecision()) printf( "%11s ", "-" ); else { printf( "%11.9f sec ", c->total(prec) / count ); } } } printf( "\n\n" ); } void Perf :: measure (double srcD, float srcF, int prec) { char buf[40]; for ( int j = 0; j < nConverters; j++ ) { PerfConverter *c = converters[j]; if (prec > c->maxPrecision()) continue; std::memset(buf, 0, sizeof(buf)); epicsTime beg = epicsTime :: getMonotonic (); for ( unsigned i = 0; i < nIterations; i++ ) { c->target (srcD, srcF, buf, sizeof(buf) - 1, prec); } epicsTime end = epicsTime :: getMonotonic (); double elapsed = end - beg; elapsed /= nIterations * nUnrolled; c->add( prec, elapsed ); if (verbose) printf ( "%17s: %11.9f sec, prec=%2i '%s'\n", c->name (), elapsed, prec, buf ); } } // Conversions to be measured class PerfCvtFastFloat : public PerfConverter { static const int digits = 12; public: PerfCvtFastFloat () { for (int i = 0; i <= digits; i++) measured[i] = 0; // Some targets seem to need this } int maxPrecision (void) const { return digits; } const char *name (void) const { return "cvtFloatToString"; } void target (double srcD, float srcF, char *dst, size_t len, int prec) const { cvtFloatToString ( srcF, dst, prec ); cvtFloatToString ( srcF, dst, prec ); cvtFloatToString ( srcF, dst, prec ); cvtFloatToString ( srcF, dst, prec ); cvtFloatToString ( srcF, dst, prec ); cvtFloatToString ( srcF, dst, prec ); cvtFloatToString ( srcF, dst, prec ); cvtFloatToString ( srcF, dst, prec ); cvtFloatToString ( srcF, dst, prec ); cvtFloatToString ( srcF, dst, prec ); } void add (int prec, double elapsed) { measured[prec] += elapsed; } double total (int prec) { double total = measured[prec]; measured[prec] = 0; return total; } private: double measured[digits+1]; }; class PerfCvtFastDouble : public PerfConverter { static const int digits = 17; public: PerfCvtFastDouble () { for (int i = 0; i <= digits; i++) measured[i] = 0; // Some targets seem to need this } int maxPrecision (void) const { return digits; } const char *name (void) const { return "cvtDoubleToString"; } void target (double srcD, float srcF, char *dst, size_t len, int prec) const { cvtDoubleToString ( srcD, dst, prec ); cvtDoubleToString ( srcD, dst, prec ); cvtDoubleToString ( srcD, dst, prec ); cvtDoubleToString ( srcD, dst, prec ); cvtDoubleToString ( srcD, dst, prec ); cvtDoubleToString ( srcD, dst, prec ); cvtDoubleToString ( srcD, dst, prec ); cvtDoubleToString ( srcD, dst, prec ); cvtDoubleToString ( srcD, dst, prec ); cvtDoubleToString ( srcD, dst, prec ); } void add(int prec, double elapsed) { measured[prec] += elapsed; } double total (int prec) { double total = measured[prec]; measured[prec] = 0; return total; } private: double measured[digits+1]; }; class PerfSNPrintf : public PerfConverter { static const int digits = 17; public: PerfSNPrintf () { for (int i = 0; i <= digits; i++) measured[i] = 0; // Some targets seem to need this } int maxPrecision (void) const { return digits; } const char *name (void) const { return "epicsSnprintf"; } void target (double srcD, float srcF, char *dst, size_t len, int prec) const { epicsSnprintf ( dst, len, "%.*g", prec, srcD ); epicsSnprintf ( dst, len, "%.*g", prec, srcD ); epicsSnprintf ( dst, len, "%.*g", prec, srcD ); epicsSnprintf ( dst, len, "%.*g", prec, srcD ); epicsSnprintf ( dst, len, "%.*g", prec, srcD ); epicsSnprintf ( dst, len, "%.*g", prec, srcD ); epicsSnprintf ( dst, len, "%.*g", prec, srcD ); epicsSnprintf ( dst, len, "%.*g", prec, srcD ); epicsSnprintf ( dst, len, "%.*g", prec, srcD ); epicsSnprintf ( dst, len, "%.*g", prec, srcD ); } void add(int prec, double elapsed) { measured[prec] += elapsed; } double total (int prec) { double total = measured[prec]; measured[prec] = 0; return total; } private: double measured[digits+1]; }; // This is a quick-and-dirty std::streambuf converter that writes directly // into the output buffer. Performance is slower than epicsSnprintf(). struct membuf: public std::streambuf { membuf(char *array, size_t size) { this->setp(array, array + size - 1); } }; struct omemstream: virtual membuf, std::ostream { omemstream(char *array, size_t size): membuf(array, size), std::ostream(this) { } }; static void ossConvertD(char *dst, size_t len, int prec, double src) { omemstream oss(dst, len); oss.precision(prec); oss << src << ends; } class PerfStreamBuf : public PerfConverter { static const int digits = 17; public: PerfStreamBuf () { for (int i = 0; i <= digits; i++) measured[i] = 0; // Some targets seem to need this } int maxPrecision (void) const { return digits; } const char *name (void) const { return "std::streambuf"; } void target (double srcD, float srcF, char *dst, size_t len, int prec) const { ossConvertD ( dst, len, prec, srcD ); ossConvertD ( dst, len, prec, srcD ); ossConvertD ( dst, len, prec, srcD ); ossConvertD ( dst, len, prec, srcD ); ossConvertD ( dst, len, prec, srcD ); ossConvertD ( dst, len, prec, srcD ); ossConvertD ( dst, len, prec, srcD ); ossConvertD ( dst, len, prec, srcD ); ossConvertD ( dst, len, prec, srcD ); ossConvertD ( dst, len, prec, srcD ); } void add(int prec, double elapsed) { measured[prec] += elapsed; } double total (int prec) { double total = measured[prec]; measured[prec] = 0; return total; } private: double measured[digits+1]; }; MAIN(cvtFastPerform) { Perf t(4); t.addConverter( new PerfCvtFastFloat ); t.addConverter( new PerfCvtFastDouble ); t.addConverter( new PerfSNPrintf ); t.addConverter( new PerfStreamBuf ); // The parameter to execute() below are: // count = number of different random numbers to measure // verbose = whether to display individual measurements #ifdef vxWorks t.execute (3, true); // Slow... #else t.execute (5, false); #endif return 0; } base-7.0.3.1/modules/libcom/test/cvtFastTest.c0000664000577000060420000004037613557101274017737 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* cvtFastTest.c * * Test the string converters in libCom/cvtFast/cvtFast.c * * To Do: Test Char/Uchar/Short/Ushort/Long/Ulong versions */ #include #include #include #include "epicsUnitTest.h" #include "cvtFast.h" #include "epicsStdlib.h" #include "testMain.h" #define tryIString(typ, lit, siz) \ len = cvt##typ##ToString(lit, buf); \ testOk(len == siz, "cvt"#typ"ToString(" #lit ") == " #siz " (%u) -> \"%s\"", (unsigned)len, buf); \ status = epicsParse##typ(buf, &val_##typ, 10, NULL); \ testOk(!status, "epicsParse"#typ"('%s') OK", buf); \ testOk(val_##typ == lit, #lit " => '%s'", buf); #define tryFString(typ, lit, prec, siz) \ len = cvt##typ##ToString(lit, buf, prec); \ testOk(len == siz, "cvt"#typ"ToString(" #lit ", %d) == " #siz " (%u) -> \"%s\"", prec, (unsigned)len, buf); \ status = epicsParse##typ(buf, &val_##typ, NULL); \ testOk(!status, "epicsParse"#typ"('%s') OK", buf); \ testOk(fabs(val_##typ - lit) < 0.5 * pow(10, -prec), #lit " => '%s'", buf); MAIN(cvtFastTest) { char buf[80]; size_t len; long status; epicsUInt32 val_UInt32; epicsInt32 val_Int32; epicsUInt64 val_UInt64; epicsInt64 val_Int64; epicsFloat32 val_Float; epicsFloat64 val_Double; #ifdef _WIN32 #if (defined(_MSC_VER) && _MSC_VER < 1900) || \ (defined(_MINGW) && defined(_TWO_DIGIT_EXPONENT)) _set_output_format(_TWO_DIGIT_EXPONENT); #endif #endif testPlan(1062); /* Arguments: type, value, num chars */ testDiag("------------------------------------------------------"); testDiag("** Positive Int32 **"); tryIString(Int32, 0, 1); tryIString(Int32, 1, 1); tryIString(Int32, 10, 2); tryIString(Int32, 100, 3); tryIString(Int32, 254, 3); tryIString(Int32, 255, 3); tryIString(Int32, 256, 3); tryIString(Int32, 257, 3); tryIString(Int32, 1000, 4); tryIString(Int32, 10000, 5); tryIString(Int32, 32766, 5); tryIString(Int32, 32767, 5); tryIString(Int32, 32768, 5); tryIString(Int32, 32769, 5); tryIString(Int32, 65534, 5); tryIString(Int32, 65535, 5); tryIString(Int32, 65536, 5); tryIString(Int32, 65537, 5); tryIString(Int32, 2147483646, 10); tryIString(Int32, 2147483647, 10); testDiag("------------------------------------------------------"); testDiag("** Negative Int32 **"); tryIString(Int32, -1, 2); tryIString(Int32, -10, 3); tryIString(Int32, -100, 4); tryIString(Int32, -254, 4); tryIString(Int32, -255, 4); tryIString(Int32, -256, 4); tryIString(Int32, -257, 4); tryIString(Int32, -1000, 5); tryIString(Int32, -10000, 6); tryIString(Int32, -32766, 6); tryIString(Int32, -32767, 6); tryIString(Int32, -32768, 6); tryIString(Int32, -32769, 6); tryIString(Int32, -65534, 6); tryIString(Int32, -65535, 6); tryIString(Int32, -65536, 6); tryIString(Int32, -65537, 6); tryIString(Int32, -2147483647, 11); tryIString(Int32, -2147483648LL, 11); testDiag("------------------------------------------------------"); testDiag("** UInt32 **"); tryIString(UInt32, 0, 1); tryIString(UInt32, 1, 1); tryIString(UInt32, 10, 2); tryIString(UInt32, 100, 3); tryIString(UInt32, 254, 3); tryIString(UInt32, 255, 3); tryIString(UInt32, 256, 3); tryIString(UInt32, 257, 3); tryIString(UInt32, 1000, 4); tryIString(UInt32, 10000, 5); tryIString(UInt32, 32766, 5); tryIString(UInt32, 32767, 5); tryIString(UInt32, 32768, 5); tryIString(UInt32, 32769, 5); tryIString(UInt32, 65534, 5); tryIString(UInt32, 65535, 5); tryIString(UInt32, 65536, 5); tryIString(UInt32, 65537, 5); tryIString(UInt32, 2147483646ULL, 10); tryIString(UInt32, 2147483647ULL, 10); tryIString(UInt32, 2147483648ULL, 10); tryIString(UInt32, 4294967294ULL, 10); tryIString(UInt32, 4294967295ULL, 10); testDiag("------------------------------------------------------"); testDiag("** Positive Int64 **"); tryIString(Int64, 0, 1); tryIString(Int64, 1, 1); tryIString(Int64, 10, 2); tryIString(Int64, 100, 3); tryIString(Int64, 254, 3); tryIString(Int64, 255, 3); tryIString(Int64, 256, 3); tryIString(Int64, 257, 3); tryIString(Int64, 1000, 4); tryIString(Int64, 10000, 5); tryIString(Int64, 32766, 5); tryIString(Int64, 32767, 5); tryIString(Int64, 32768, 5); tryIString(Int64, 32769, 5); tryIString(Int64, 65534, 5); tryIString(Int64, 65535, 5); tryIString(Int64, 65536, 5); tryIString(Int64, 65537, 5); tryIString(Int64, 2147483646, 10); tryIString(Int64, 2147483647, 10); tryIString(Int64, 2147483648LL, 10); tryIString(Int64, 9223372036854775806LL, 19); tryIString(Int64, 9223372036854775807LL, 19); testDiag("------------------------------------------------------"); testDiag("** Negative Int64 **"); tryIString(Int64, -1, 2); tryIString(Int64, -10, 3); tryIString(Int64, -100, 4); tryIString(Int64, -254, 4); tryIString(Int64, -255, 4); tryIString(Int64, -256, 4); tryIString(Int64, -257, 4); tryIString(Int64, -1000, 5); tryIString(Int64, -10000, 6); tryIString(Int64, -32766, 6); tryIString(Int64, -32767, 6); tryIString(Int64, -32768, 6); tryIString(Int64, -32769, 6); tryIString(Int64, -65534, 6); tryIString(Int64, -65535, 6); tryIString(Int64, -65536, 6); tryIString(Int64, -65537, 6); tryIString(Int64, -2147483647, 11); tryIString(Int64, -2147483648LL, 11); tryIString(Int64, -2147483649LL, 11); tryIString(Int64, -9223372036854775806LL, 20); tryIString(Int64, -9223372036854775807LL, 20); tryIString(Int64, -9223372036854775807LL-1, 20); testDiag("------------------------------------------------------"); testDiag("** UInt64 **"); tryIString(UInt64, 0, 1); tryIString(UInt64, 1, 1); tryIString(UInt64, 10, 2); tryIString(UInt64, 100, 3); tryIString(UInt64, 254, 3); tryIString(UInt64, 255, 3); tryIString(UInt64, 256, 3); tryIString(UInt64, 257, 3); tryIString(UInt64, 1000, 4); tryIString(UInt64, 10000, 5); tryIString(UInt64, 32766, 5); tryIString(UInt64, 32767, 5); tryIString(UInt64, 32768, 5); tryIString(UInt64, 32769, 5); tryIString(UInt64, 65534, 5); tryIString(UInt64, 65535, 5); tryIString(UInt64, 65536, 5); tryIString(UInt64, 65537, 5); tryIString(UInt64, 2147483646, 10); tryIString(UInt64, 2147483647, 10); tryIString(UInt64, 2147483648U, 10); tryIString(UInt64, 2147483649U, 10); tryIString(UInt64, 4294967294U, 10); tryIString(UInt64, 4294967295U, 10); tryIString(UInt64, 4294967296ULL, 10); tryIString(UInt64, 4294967297ULL, 10); tryIString(UInt64, 9223372036854775806ULL, 19); tryIString(UInt64, 9223372036854775807ULL, 19); tryIString(UInt64, 9223372036854775808ULL, 19); tryIString(UInt64, 18446744073709551614ULL, 20); tryIString(UInt64, 18446744073709551615ULL, 20); /* Arguments: type, value, precision, num chars */ testDiag("------------------------------------------------------"); testDiag("** Positive Float fixed-point **"); tryFString(Float, 0, 0, 1); tryFString(Float, 0, 1, 3); tryFString(Float, 0, 2, 4); tryFString(Float, 0, 3, 5); tryFString(Float, 0, 4, 6); tryFString(Float, 0, 5, 7); tryFString(Float, 0, 6, 8); tryFString(Float, 0, 7, 9); tryFString(Float, 0, 8, 10); tryFString(Float, FLT_MIN, 0, 1); tryFString(Float, FLT_MIN, 1, 3); tryFString(Float, FLT_MIN, 2, 4); tryFString(Float, FLT_MIN, 3, 5); tryFString(Float, FLT_MIN, 4, 6); tryFString(Float, FLT_MIN, 5, 7); tryFString(Float, FLT_MIN, 6, 8); tryFString(Float, FLT_MIN, 7, 9); tryFString(Float, FLT_MIN, 8, 10); tryFString(Float, 0.000000004999999, 8, 10); tryFString(Float, 0.000000005000001, 8, 10); tryFString(Float, 0.00000004999999, 7, 9); tryFString(Float, 0.00000005000001, 7, 9); tryFString(Float, 0.0000004999999, 6, 8); tryFString(Float, 0.0000005000001, 6, 8); tryFString(Float, 0.000004999999, 5, 7); tryFString(Float, 0.000005000001, 5, 7); tryFString(Float, 0.00004999999, 4, 6); tryFString(Float, 0.00005000001, 4, 6); tryFString(Float, 0.0004999999, 3, 5); tryFString(Float, 0.0005000001, 3, 5); tryFString(Float, 0.004999999, 2, 4); tryFString(Float, 0.005000001, 2, 4); tryFString(Float, 0.04999999, 1, 3); tryFString(Float, 0.05000001, 1, 3); tryFString(Float, 0.4999999, 0, 1); tryFString(Float, 0.5000001, 0, 1); tryFString(Float, 1, 0, 1); tryFString(Float, 1, 1, 3); tryFString(Float, 1, 2, 4); tryFString(Float, 1, 3, 5); tryFString(Float, 1, 4, 6); tryFString(Float, 1, 5, 7); tryFString(Float, 1, 6, 8); tryFString(Float, 1, 7, 9); tryFString(Float, 1, 8, 10); tryFString(Float, 1.0500001, 1, 3); tryFString(Float, 1.1, 1, 3); tryFString(Float, 1.1499999, 1, 3); tryFString(Float, 9.5000001, 0, 2); tryFString(Float, 10, 0, 2); tryFString(Float, 10, 1, 4); tryFString(Float, 10, 8, 11); tryFString(Float, 100, 0, 3); tryFString(Float, 100, 1, 5); tryFString(Float, 100, 8, 12); tryFString(Float, 1000, 0, 4); tryFString(Float, 1000, 1, 6); tryFString(Float, 1000, 8, 13); tryFString(Float, 10000, 0, 5); tryFString(Float, 10000, 1, 7); tryFString(Float, 10000, 8, 14); tryFString(Float, 100000, 0, 6); tryFString(Float, 100000, 1, 8); tryFString(Float, 100000, 8, 15); tryFString(Float, 1000000, 0, 7); tryFString(Float, 1000000, 1, 9); tryFString(Float, 1000000, 8, 16); tryFString(Float, 10000000, 0, 8); tryFString(Float, 10000000, 1, 10); tryFString(Float, 10000000, 8, 17); testDiag("------------------------------------------------------"); testDiag("** Negative Float fixed-point **"); tryFString(Float, -FLT_MIN, 0, 2); tryFString(Float, -FLT_MIN, 1, 4); tryFString(Float, -FLT_MIN, 8, 11); tryFString(Float, -1, 0, 2); tryFString(Float, -1, 1, 4); tryFString(Float, -1, 8, 11); tryFString(Float, -1.0500001, 1, 4); tryFString(Float, -1.1, 1, 4); tryFString(Float, -1.1499999, 1, 4); tryFString(Float, -9.5000001, 0, 3); tryFString(Float, -10, 0, 3); tryFString(Float, -10, 1, 5); tryFString(Float, -10, 8, 12); tryFString(Float, -100, 0, 4); tryFString(Float, -100, 1, 6); tryFString(Float, -100, 8, 13); tryFString(Float, -1000, 0, 5); tryFString(Float, -1000, 1, 7); tryFString(Float, -1000, 8, 14); tryFString(Float, -10000, 0, 6); tryFString(Float, -10000, 1, 8); tryFString(Float, -10000, 8, 15); tryFString(Float, -100000, 0, 7); tryFString(Float, -100000, 1, 9); tryFString(Float, -100000, 8, 16); tryFString(Float, -1000000, 0, 8); tryFString(Float, -1000000, 1, 10); tryFString(Float, -1000000, 8, 17); tryFString(Float, -10000000, 0, 9); tryFString(Float, -10000000, 1, 11); tryFString(Float, -10000000, 8, 18); /* * Values > 1e7 trigger the %e format. */ testDiag("------------------------------------------------------"); testDiag("** Positive Float scientific **"); tryFString(Float, 1e+08, 0, 6); tryFString(Float, 1e+08, 1, 7); tryFString(Float, 1e+08, 2, 8); tryFString(Float, 1e+08, 3, 9); tryFString(Float, 1e+08, 4, 10); tryFString(Float, 1e+08, 5, 11); tryFString(Float, 1e+08, 6, 12); testDiag("------------------------------------------------------"); testDiag("** Positive Double fixed-point **"); tryFString(Double, 0, 0, 1); tryFString(Double, 0, 1, 3); tryFString(Double, 0, 2, 4); tryFString(Double, 0, 3, 5); tryFString(Double, 0, 4, 6); tryFString(Double, 0, 5, 7); tryFString(Double, 0, 6, 8); tryFString(Double, 0, 7, 9); tryFString(Double, 0, 8, 10); tryFString(Double, DBL_MIN, 0, 1); tryFString(Double, DBL_MIN, 1, 3); tryFString(Double, DBL_MIN, 2, 4); tryFString(Double, DBL_MIN, 3, 5); tryFString(Double, DBL_MIN, 4, 6); tryFString(Double, DBL_MIN, 5, 7); tryFString(Double, DBL_MIN, 6, 8); tryFString(Double, DBL_MIN, 7, 9); tryFString(Double, DBL_MIN, 8, 10); tryFString(Double, 0.000000004999999, 8, 10); tryFString(Double, 0.000000005000001, 8, 10); tryFString(Double, 0.00000004999999, 7, 9); tryFString(Double, 0.00000005000001, 7, 9); tryFString(Double, 0.0000004999999, 6, 8); tryFString(Double, 0.0000005000001, 6, 8); tryFString(Double, 0.000004999999, 5, 7); tryFString(Double, 0.000005000001, 5, 7); tryFString(Double, 0.00004999999, 4, 6); tryFString(Double, 0.00005000001, 4, 6); tryFString(Double, 0.0004999999, 3, 5); tryFString(Double, 0.0005000001, 3, 5); tryFString(Double, 0.004999999, 2, 4); tryFString(Double, 0.005000001, 2, 4); tryFString(Double, 0.04999999, 1, 3); tryFString(Double, 0.05000001, 1, 3); tryFString(Double, 0.4999999, 0, 1); tryFString(Double, 0.5000001, 0, 1); tryFString(Double, 1, 0, 1); tryFString(Double, 1, 1, 3); tryFString(Double, 1, 2, 4); tryFString(Double, 1, 3, 5); tryFString(Double, 1, 4, 6); tryFString(Double, 1, 5, 7); tryFString(Double, 1, 6, 8); tryFString(Double, 1, 7, 9); tryFString(Double, 1, 8, 10); tryFString(Double, 1.0500001, 1, 3); tryFString(Double, 1.1, 1, 3); tryFString(Double, 1.1499999, 1, 3); tryFString(Double, 9.5000001, 0, 2); tryFString(Double, 10, 0, 2); tryFString(Double, 10, 1, 4); tryFString(Double, 10, 8, 11); tryFString(Double, 100, 0, 3); tryFString(Double, 100, 1, 5); tryFString(Double, 100, 8, 12); tryFString(Double, 1000, 0, 4); tryFString(Double, 1000, 1, 6); tryFString(Double, 1000, 8, 13); tryFString(Double, 10000, 0, 5); tryFString(Double, 10000, 1, 7); tryFString(Double, 10000, 8, 14); tryFString(Double, 100000, 0, 6); tryFString(Double, 100000, 1, 8); tryFString(Double, 100000, 8, 15); tryFString(Double, 1000000, 0, 7); tryFString(Double, 1000000, 1, 9); tryFString(Double, 1000000, 8, 16); tryFString(Double, 10000000, 0, 8); tryFString(Double, 10000000, 1, 10); tryFString(Double, 10000000, 8, 17); testDiag("------------------------------------------------------"); testDiag("** Negative Double fixed-point **"); tryFString(Double, -DBL_MIN, 0, 2); tryFString(Double, -DBL_MIN, 1, 4); tryFString(Double, -DBL_MIN, 8, 11); tryFString(Double, -1, 0, 2); tryFString(Double, -1, 1, 4); tryFString(Double, -1, 8, 11); tryFString(Double, -1.0500001, 1, 4); tryFString(Double, -1.1, 1, 4); tryFString(Double, -1.1499999, 1, 4); tryFString(Double, -9.5000001, 0, 3); tryFString(Double, -10, 0, 3); tryFString(Double, -10, 1, 5); tryFString(Double, -10, 8, 12); tryFString(Double, -100, 0, 4); tryFString(Double, -100, 1, 6); tryFString(Double, -100, 8, 13); tryFString(Double, -1000, 0, 5); tryFString(Double, -1000, 1, 7); tryFString(Double, -1000, 8, 14); tryFString(Double, -10000, 0, 6); tryFString(Double, -10000, 1, 8); tryFString(Double, -10000, 8, 15); tryFString(Double, -100000, 0, 7); tryFString(Double, -100000, 1, 9); tryFString(Double, -100000, 8, 16); tryFString(Double, -1000000, 0, 8); tryFString(Double, -1000000, 1, 10); tryFString(Double, -1000000, 8, 17); tryFString(Double, -10000000, 0, 9); tryFString(Double, -10000000, 1, 11); tryFString(Double, -10000000, 8, 18); /* * Values > 1e7 trigger the %e format. * Windows may print 3 digit exponents... */ testDiag("------------------------------------------------------"); testDiag("** Positive Double scientific **"); tryFString(Double, 1e+17, 0, 7); tryFString(Double, 1e+17, 1, 8); tryFString(Double, 1e+17, 2, 9); tryFString(Double, 1e+17, 3, 10); tryFString(Double, 1e+17, 4, 11); tryFString(Double, 1e+17, 5, 12); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsAlgorithmTest.cpp0000664000577000060420000000432613557101274021632 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ // epicsAlgorithmTest.cpp // Authors: Jeff Hill & Andrew Johnson #include "epicsUnitTest.h" #include "epicsAlgorithm.h" #include "epicsMath.h" #include "testMain.h" MAIN(epicsAlgorithm) { testPlan(22); float f1 = 3.3f; float f2 = 3.4f; float Inf = epicsINF; float NaN = epicsNAN; testOk(epicsMin(f1, f2) == f1, "epicsMin(f1, f2)"); testOk(epicsMin(f1, -Inf) == -Inf, "epicsMin(f1, -Inf)"); testOk(isnan(epicsMin(f1, NaN)), "epicsMin(f1, NaN)"); testOk(epicsMin(f1, Inf) == f1, "epicsMin(f1, Inf)"); testOk(epicsMin(f2, f1) == f1, "epicsMin(f2, f1)"); testOk(epicsMin(-Inf, f1) == -Inf, "epicsMin(-Inf, f1)"); testOk(isnan(epicsMin(NaN, f1)), "epicsMin(NaN, f1)"); testOk(epicsMin(Inf, f1) == f1, "epicsMin(Inf, f1)"); testOk(epicsMax(f2, f1) == f2, "epicsMax(f2, f1)"); testOk(epicsMax(-Inf, f1) == f1, "epicsMax(-Inf, f1)"); testOk(isnan(epicsMax(NaN, f1)), "epicsMax(NaN, f1)"); testOk(epicsMax(Inf, f1) == Inf, "epicsMax(Inf, f1)"); testOk(epicsMax(f1, f2) == f2, "epicsMax(f1, f2)"); testOk(epicsMax(f1, -Inf) == f1, "epicsMax(f1, -Inf)"); testOk(isnan(epicsMax(f1, NaN)), "epicsMax(f1, NaN)"); testOk(epicsMax(f1, Inf) == Inf, "epicsMax(f1, Inf)"); epicsSwap(f1, f2); testOk(f1==3.4f && f2==3.3f, "epicsSwap(f1, f2)"); int i1 = 3; int i2 = 4; testOk(epicsMin(i1, i2) == i1, "epicsMin(i1,i2)"); testOk(epicsMin(i2, i1) == i1, "epicsMin(i2,i1)"); testOk(epicsMax(i1, i2) == i2, "epicsMax(i1,i2)"); testOk(epicsMax(i2, i1) == i2, "epicsMax(i2,i1)"); epicsSwap(i1, i2); testOk(i1 == 4 && i2 == 3, "epicsSwap(i1, i2)"); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsAtomicPerform.cpp0000664000577000060420000004113513557101274021612 0ustar anjaesctl #include #include #include #include #include "epicsInterrupt.h" #include "epicsAtomic.h" #include "epicsTime.h" #include "epicsUnitTest.h" #include "testMain.h" using std :: size_t; using namespace epics; using namespace atomic; class RefCtr { public: RefCtr (); ~RefCtr (); void reference (); void unreference (); private: size_t m_cnt; }; class Ownership { public: Ownership (); Ownership ( RefCtr & refCtr ); Ownership ( const Ownership & ); ~Ownership (); Ownership & operator = ( const Ownership & ); private: RefCtr * _pRefCtr; static RefCtr m_noOwnership; }; inline RefCtr :: RefCtr () { epicsAtomicSetSizeT ( & m_cnt, 0 ); } inline RefCtr :: ~RefCtr () { unsigned cnt = epicsAtomicGetSizeT ( & m_cnt ); assert ( cnt == 0u ); } inline void RefCtr :: reference () { epicsAtomicIncrSizeT ( & m_cnt ); } inline void RefCtr :: unreference () { epicsAtomicDecrSizeT ( & m_cnt ); } RefCtr Ownership :: m_noOwnership; inline Ownership :: Ownership () : _pRefCtr ( & m_noOwnership ) { m_noOwnership.reference (); } inline Ownership :: Ownership ( RefCtr & refCtr ) : _pRefCtr ( & refCtr ) { refCtr.reference (); } inline Ownership :: Ownership ( const Ownership & ownership ) : _pRefCtr ( ownership._pRefCtr ) { _pRefCtr->reference (); } inline Ownership :: ~Ownership () { _pRefCtr->unreference (); } inline Ownership & Ownership :: operator = ( const Ownership & ownership ) { RefCtr * const pOldRefCtr = _pRefCtr; _pRefCtr = ownership._pRefCtr; _pRefCtr->reference (); pOldRefCtr->unreference (); return *this; } inline Ownership retOwnership ( const Ownership & ownership ) { return Ownership ( ownership ); } inline Ownership recurRetOwner10 ( const Ownership & ownershipIn ) { Ownership ownership = retOwnership ( retOwnership ( retOwnership ( retOwnership ( retOwnership ( ownershipIn ) ) ) ) ); return retOwnership ( retOwnership ( retOwnership ( retOwnership ( retOwnership ( ownership ) ) ) ) ); } inline Ownership recurRetOwner100 ( const Ownership & ownershipIn ) { Ownership ownership = recurRetOwner10 ( recurRetOwner10 ( recurRetOwner10 ( recurRetOwner10 ( recurRetOwner10 ( ownershipIn ) ) ) ) ); return recurRetOwner10 ( recurRetOwner10 ( recurRetOwner10 ( recurRetOwner10 ( recurRetOwner10 ( ownership ) ) ) ) ); } inline Ownership recurRetOwner1000 ( const Ownership & ownershipIn ) { Ownership ownership = recurRetOwner100 ( recurRetOwner100 ( recurRetOwner100 ( recurRetOwner100 ( recurRetOwner100 ( ownershipIn ) ) ) ) ); return recurRetOwner100 ( recurRetOwner100 ( recurRetOwner100 ( recurRetOwner100 ( recurRetOwner100 ( ownership ) ) ) ) ); } inline void passRefOwnership ( const Ownership & ownershipIn, Ownership & ownershipOut ) { ownershipOut = ownershipIn; } inline void passRefOwnership10 ( const Ownership & ownershipIn, Ownership & ownershipOut ) { Ownership ownershipTmp0; passRefOwnership ( ownershipIn, ownershipTmp0 ); Ownership ownershipTmp1; passRefOwnership ( ownershipTmp0, ownershipTmp1 ); Ownership ownershipTmp2; passRefOwnership ( ownershipTmp1, ownershipTmp2 ); Ownership ownershipTmp3; passRefOwnership ( ownershipTmp2, ownershipTmp3 ); Ownership ownershipTmp4; passRefOwnership ( ownershipTmp3, ownershipTmp4 ); Ownership ownershipTmp5; passRefOwnership ( ownershipTmp4, ownershipTmp5 ); Ownership ownershipTmp6; passRefOwnership ( ownershipTmp5, ownershipTmp6 ); Ownership ownershipTmp7; passRefOwnership ( ownershipTmp6, ownershipTmp7 ); Ownership ownershipTmp8; passRefOwnership ( ownershipTmp7, ownershipTmp8 ); passRefOwnership ( ownershipTmp8, ownershipOut ); } inline void passRefOwnership100 ( const Ownership & ownershipIn, Ownership & ownershipOut ) { Ownership ownershipTmp0; passRefOwnership10 ( ownershipIn, ownershipTmp0 ); Ownership ownershipTmp1; passRefOwnership10 ( ownershipTmp0, ownershipTmp1 ); Ownership ownershipTmp2; passRefOwnership10 ( ownershipTmp1, ownershipTmp2 ); Ownership ownershipTmp3; passRefOwnership10 ( ownershipTmp2, ownershipTmp3 ); Ownership ownershipTmp4; passRefOwnership10 ( ownershipTmp3, ownershipTmp4 ); Ownership ownershipTmp5; passRefOwnership10 ( ownershipTmp4, ownershipTmp5 ); Ownership ownershipTmp6; passRefOwnership10 ( ownershipTmp5, ownershipTmp6 ); Ownership ownershipTmp7; passRefOwnership10 ( ownershipTmp6, ownershipTmp7 ); Ownership ownershipTmp8; passRefOwnership10 ( ownershipTmp7, ownershipTmp8 ); passRefOwnership10 ( ownershipTmp8, ownershipOut ); } inline void passRefOwnership1000 ( const Ownership & ownershipIn, Ownership & ownershipOut ) { Ownership ownershipTmp0; passRefOwnership100 ( ownershipIn, ownershipTmp0 ); Ownership ownershipTmp1; passRefOwnership100 ( ownershipTmp0, ownershipTmp1 ); Ownership ownershipTmp2; passRefOwnership100 ( ownershipTmp1, ownershipTmp2 ); Ownership ownershipTmp3; passRefOwnership100 ( ownershipTmp2, ownershipTmp3 ); Ownership ownershipTmp4; passRefOwnership100 ( ownershipTmp3, ownershipTmp4 ); Ownership ownershipTmp5; passRefOwnership100 ( ownershipTmp4, ownershipTmp5 ); Ownership ownershipTmp6; passRefOwnership100 ( ownershipTmp5, ownershipTmp6 ); Ownership ownershipTmp7; passRefOwnership100 ( ownershipTmp6, ownershipTmp7 ); Ownership ownershipTmp8; passRefOwnership100 ( ownershipTmp7, ownershipTmp8 ); passRefOwnership100 ( ownershipTmp8, ownershipOut ); } time_t extTime = 0; template < class T > class OrdinaryIncr { public: OrdinaryIncr () : m_target ( 0 ) {} void run (); void diagnostic ( double delay ); private: T m_target; }; // tests the time it takes to perform a call to an external // function and also increment an integer word. The // epicsInterruptIsInterruptContext function is an // out-of-line function implemented in a sharable library // so hopefully it wont be optimized away. template < class T > inline void OrdinaryIncr < T > :: run () { m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); } template < class T > void OrdinaryIncr < T > :: diagnostic ( double delay ) { delay /= 10.0; delay *= 1e6; const char * const pName = typeid ( T ) . name (); testDiag ( "raw incr of \"%s\" and a NOOP function call takes %f microseconds", pName, delay ); } template < class T > class AtomicIncr { public: AtomicIncr () : m_target ( 0 ) {} void run (); void diagnostic ( double delay ); private: T m_target; }; template < class T > inline void AtomicIncr < T > :: run () { increment ( m_target ); increment ( m_target ); increment ( m_target ); increment ( m_target ); increment ( m_target ); increment ( m_target ); increment ( m_target ); increment ( m_target ); increment ( m_target ); increment ( m_target ); } template < class T > void AtomicIncr < T > :: diagnostic ( double delay ) { delay /= 10.0; delay *= 1e6; const char * const pName = typeid ( T ) . name (); testDiag ( "epicsAtomicIncr \"%s\" takes %f microseconds", pName, delay ); } template < class T > T trueValue (); template < class T > T falseValue (); // int template <> inline int trueValue < int > () { return 1; } template <> inline int falseValue < int > () { return 0; } // size_t template <> inline size_t trueValue < size_t > () { return 1u; } template <> inline size_t falseValue < size_t > () { return 0u; } // EpicsAtomicPtrT template <> inline EpicsAtomicPtrT trueValue < EpicsAtomicPtrT > () { static char c; return & c; } template <> inline EpicsAtomicPtrT falseValue < EpicsAtomicPtrT > () { return 0u; } template < class T > class AtomicCmpAndSwap { public: AtomicCmpAndSwap () : m_target ( 0 ) {} void run (); void diagnostic ( double delay ); private: T m_target; }; template < class T > inline void AtomicCmpAndSwap < T > :: run () { compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); } template < class T > void AtomicCmpAndSwap < T > :: diagnostic ( double delay ) { delay /= 10.0; delay *= 1e6; const char * const pName = typeid ( T ) . name (); testDiag ( "epicsAtomicCmpAndSwap of \"%s\" takes %f microseconds", pName, delay ); } template < class T > class AtomicSet { public: AtomicSet () : m_target ( 0 ) {} void run (); void diagnostic ( double delay ); private: T m_target; }; template < class T > inline void AtomicSet < T > :: run () { set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); } template < class T > void AtomicSet < T > :: diagnostic ( double delay ) { delay /= 10.0; delay *= 1e6; const char * const pName = typeid ( T ) . name (); testDiag ( "epicsAtomicSet of \"%s\" takes %f microseconds", pName, delay ); } static const unsigned N = 10000; void recursiveOwnershipRetPerformance () { RefCtr refCtr; epicsTime begin = epicsTime::getMonotonic (); for ( size_t i = 0; i < N; i++ ) { Ownership ownership ( refCtr ); recurRetOwner1000 ( ownership ); } double delay = epicsTime::getMonotonic () - begin; delay /= N * 1000u; // convert to delay per call delay *= 1e6; // convert to micro seconds testDiag ( "retOwnership() takes %f microseconds", delay ); } void ownershipPassRefPerformance () { RefCtr refCtr; epicsTime begin = epicsTime::getMonotonic (); for ( size_t i = 0; i < N; i++ ) { Ownership ownershipSrc ( refCtr ); Ownership ownershipDest; passRefOwnership1000 ( ownershipSrc, ownershipDest ); } double delay = epicsTime::getMonotonic () - begin; delay /= N * 1000u; // convert to delay per call delay *= 1e6; // convert to micro seconds testDiag ( "passRefOwnership() takes %f microseconds", delay ); } template < class T > class Ten { public: void run (); void diagnostic ( double delay ); typedef Ten < Ten < T > > Hundred; typedef Ten < Hundred > Thousand; private: T m_target; }; template < class T > inline void Ten < T > :: run () { m_target.run (); m_target.run (); m_target.run (); m_target.run (); m_target.run (); m_target.run (); m_target.run (); m_target.run (); m_target.run (); m_target.run (); } template < class T > void Ten < T > :: diagnostic ( double delay ) { m_target.diagnostic ( delay / 10.0 ); } template < class T > void measurePerformance () { epicsTime begin = epicsTime::getMonotonic (); T target; for ( size_t i = 0; i < N; i++ ) { target.run (); target.run (); target.run (); target.run (); target.run (); target.run (); target.run (); target.run (); target.run (); target.run (); } double delay = epicsTime::getMonotonic () - begin; delay /= ( N * 10u ); // convert to delay per call target.diagnostic ( delay ); } template < class T > void measure () { measurePerformance < typename Ten < T > :: Hundred > (); } // template instances, needed for vxWorks 5.5.2 #ifdef _MSC_VER # pragma warning ( push ) # pragma warning ( disable:4660 ) #endif template class AtomicSet < int >; template class AtomicSet < size_t >; template class AtomicSet < EpicsAtomicPtrT >; template class OrdinaryIncr < int >; template class OrdinaryIncr < size_t >; template class AtomicIncr < int >; template class AtomicIncr < size_t >; template class AtomicCmpAndSwap < int >; template class AtomicCmpAndSwap < size_t >; template class AtomicCmpAndSwap < EpicsAtomicPtrT >; template class Ten >; template class Ten >; template class Ten >; template class Ten >; template class Ten >; template class Ten >; template class Ten >; template class Ten >; template class Ten >; template class Ten >; template class Ten > >; template class Ten > >; template class Ten > >; template class Ten > >; template class Ten > >; template class Ten > >; template class Ten > >; template class Ten > >; template class Ten > >; template class Ten > >; template void measurePerformance < Ten < Ten < AtomicSet < int > > > >(void); template void measurePerformance < Ten < Ten < AtomicSet < size_t > > > >(void); template void measurePerformance < Ten < Ten < AtomicSet < EpicsAtomicPtrT > > > >(void); template void measurePerformance < Ten < Ten < OrdinaryIncr < int > > > >(void); template void measurePerformance < Ten < Ten < OrdinaryIncr < size_t > > > >(void); template void measurePerformance < Ten < Ten < AtomicIncr < int > > > >(void); template void measurePerformance < Ten < Ten < AtomicIncr < size_t > > > >(void); template void measurePerformance < Ten < Ten < AtomicCmpAndSwap < int > > > >(void); template void measurePerformance < Ten < Ten < AtomicCmpAndSwap < size_t > > > >(void); template void measurePerformance < Ten < Ten < AtomicCmpAndSwap < EpicsAtomicPtrT > > > >(void); template void measure < AtomicSet < int > > (void); template void measure < AtomicSet < size_t > > (void); template void measure < AtomicSet < EpicsAtomicPtrT > > (void); template void measure < OrdinaryIncr < int > > (void); template void measure < OrdinaryIncr < size_t > > (void); template void measure < AtomicIncr < int > > (void); template void measure < AtomicIncr < size_t > > (void); template void measure < AtomicCmpAndSwap < int > > (void); template void measure < AtomicCmpAndSwap < size_t > > (void); template void measure < AtomicCmpAndSwap < EpicsAtomicPtrT > > (void); #ifdef _MSC_VER # pragma warning ( pop ) #endif MAIN ( epicsAtomicPerform ) { testPlan ( 0 ); // // The tests running here are measuring fast // functions so they tend to be impacted // by where the cache lines are wrt to the // virtual pages perhap // measure < AtomicSet < int > > (); measure < AtomicSet < size_t > > (); measure < AtomicSet < void * > > (); measure < OrdinaryIncr < int > > (); measure < OrdinaryIncr < size_t > > (); measure < AtomicIncr < int > > (); measure < AtomicIncr < size_t > > (); measure < AtomicCmpAndSwap < int > > (); measure < AtomicCmpAndSwap < size_t > > (); measure < AtomicCmpAndSwap < void * > > (); recursiveOwnershipRetPerformance (); ownershipPassRefPerformance (); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsAtomicTest.cpp0000664000577000060420000002732613557101274021125 0ustar anjaesctl #include #include #include "epicsAtomic.h" #include "epicsTime.h" #include "epicsThread.h" #include "epicsUnitTest.h" #include "testMain.h" namespace { template < class T > struct TestDataIncrDecr { T m_testValue; size_t m_testIterations; }; template < class T > struct TestDataAddSub { T m_testValue; size_t m_testIterations; static const T delta = 17; }; template < class T > static void incr ( void *arg ) { using epics::atomic::increment; TestDataIncrDecr < T > * const pTestData = reinterpret_cast < TestDataIncrDecr < T > * > ( arg ); increment ( pTestData->m_testValue ); increment ( pTestData->m_testIterations ); } template < class T > static void decr ( void *arg ) { using epics::atomic::decrement; using epics::atomic::increment; TestDataIncrDecr < T > * const pTestData = reinterpret_cast < TestDataIncrDecr < T > * > ( arg ); decrement ( pTestData->m_testValue ); increment ( pTestData->m_testIterations ); } template < class T > static void add ( void *arg ) { using epics::atomic::add; using epics::atomic::increment; TestDataAddSub < T > * const pTestData = reinterpret_cast < TestDataAddSub < T > * > ( arg ); add ( pTestData->m_testValue, TestDataAddSub < T > :: delta ); increment ( pTestData->m_testIterations ); } template < class T > static void sub ( void *arg ) { using epics::atomic::subtract; using epics::atomic::increment; TestDataAddSub < T > * const pTestData = reinterpret_cast < TestDataAddSub < T > * > ( arg ); subtract ( pTestData->m_testValue, TestDataAddSub < T > :: delta ); increment ( pTestData->m_testIterations ); } template < class T > struct TestDataCAS { T m_testValue; size_t m_testIterationsSet; size_t m_testIterationsNotSet; }; template < class T > static T trueValue (); template < class T > static T falseValue (); // int template <> inline int trueValue < int > () { return 1; } template <> inline int falseValue < int > () { return 0; } // size_t template <> inline size_t trueValue < size_t > () { return 1u; } template <> inline size_t falseValue < size_t > () { return 0u; } // EpicsAtomicPtrT template <> inline EpicsAtomicPtrT trueValue < EpicsAtomicPtrT > () { static char c; return & c; } template <> inline EpicsAtomicPtrT falseValue < EpicsAtomicPtrT > () { return 0u; } template < class T > static void cas ( void *arg ) { using epics::atomic::set; using epics::atomic::increment; using epics::atomic::decrement; using epics::atomic::compareAndSwap; TestDataCAS < T > * const pTestData = reinterpret_cast < TestDataCAS < T > * > ( arg ); /* * intentionally waste cpu and maximize * contention for the shared data */ increment ( pTestData->m_testIterationsNotSet ); while ( ! compareAndSwap ( pTestData->m_testValue, falseValue < T > (), trueValue < T > () ) ) { } decrement ( pTestData->m_testIterationsNotSet ); set ( pTestData->m_testValue, falseValue < T > () ); increment ( pTestData->m_testIterationsSet ); } template < class T > void testIncrDecr () { using epics::atomic::set; using epics::atomic::get; static const size_t N = 90; static const T NT = static_cast < T > ( N ); const unsigned int stackSize = epicsThreadGetStackSize ( epicsThreadStackSmall ); TestDataIncrDecr < T > testData = { 0, N }; set ( testData.m_testValue, NT ); testOk ( get ( testData.m_testValue ) == NT, "get returns initial incr/decr test data value that was set" ); set ( testData.m_testIterations, 0u ); testOk ( get ( testData.m_testIterations ) == 0u, "get returns initial incr/decr test thread iterations value that was set" ); for ( size_t i = 0u; i < N; i++ ) { epicsThreadMustCreate ( "incr", 50, stackSize, incr < T >, & testData ); epicsThreadMustCreate ( "decr", 50, stackSize, decr < T >, & testData ); if(i%10==0) testDiag("iteration %u", (unsigned)i); } while ( testData.m_testIterations < 2 * N ) { epicsThreadSleep ( 0.01 ); } testOk ( get ( testData.m_testIterations ) == 2 * N, "proper number of incr/decr test thread iterations" ); testOk ( get ( testData.m_testValue ) == NT, "proper final incr/decr test value" ); } template < class T > void testAddSub () { using epics::atomic::set; using epics::atomic::get; static const size_t N = 90; static const T NDT = TestDataAddSub < T > :: delta * static_cast < T > ( N ); const unsigned int stackSize = epicsThreadGetStackSize ( epicsThreadStackSmall ); TestDataIncrDecr < T > testData = { 0, N }; set ( testData.m_testValue, NDT ); testOk ( get ( testData.m_testValue ) == NDT, "get returns initial add/sub test data value that was set" ); set ( testData.m_testIterations, 0u ); testOk ( get ( testData.m_testIterations ) == 0u, "get returns initial incr/decr test thread iterations value that was set" ); for ( size_t i = 0u; i < N; i++ ) { epicsThreadMustCreate ( "add", 50, stackSize, add < T >, & testData ); epicsThreadMustCreate ( "sub", 50, stackSize, sub < T >, & testData ); } while ( testData.m_testIterations < 2 * N ) { epicsThreadSleep ( 0.01 ); } testOk ( get ( testData.m_testIterations ) == 2 * N, "proper number of add/sub test thread iterations" ); testOk ( get ( testData.m_testValue ) == NDT, "proper final add/sub test value" ); } template < class T > void testCAS () { using epics::atomic::set; using epics::atomic::get; static const size_t N = 10; const unsigned int stackSize = epicsThreadGetStackSize ( epicsThreadStackSmall ); TestDataCAS < T > testData = { 0, N, N }; set ( testData.m_testIterationsSet, 0 ); testOk ( get ( testData.m_testIterationsSet ) == 0u, "get returns initial CAS test thread " "iterations set value" ); set ( testData.m_testIterationsNotSet, 0 ); testOk ( get ( testData.m_testIterationsNotSet ) == 0u, "get returns initial CAS test thread " "iterations not set value" ); set ( testData.m_testValue, trueValue < T > () ); testOk ( get ( testData.m_testValue ) == trueValue < T > (), "set/get a true value" ); for ( size_t i = 0u; i < N; i++ ) { epicsThreadMustCreate ( "tns", 50, stackSize, cas < T >, & testData ); } set ( testData.m_testValue, falseValue < T > () ); while ( testData.m_testIterationsSet < N ) { epicsThreadSleep ( 0.01 ); } testOk ( get ( testData.m_testIterationsSet ) == N, "proper number of CAS test thread set iterations" ); testOk ( get ( testData.m_testIterationsNotSet ) == 0u, "proper number of CAS test thread not set iterations" ); } // template instances, needed for vxWorks 5.5.2 #ifdef _MSC_VER # pragma warning ( push ) # pragma warning ( disable:4660 ) #endif template void incr < int > (void *); template void decr < int > (void *); template void incr < size_t > (void *); template void decr < size_t > (void *); template void add < int > (void *); template void sub < int > (void *); template void add < size_t > (void *); template void sub < size_t > (void *); template void cas < int > (void *); template void cas < size_t > (void *); template void cas < EpicsAtomicPtrT > (void *); template void testIncrDecr < int > (void); template void testIncrDecr < size_t > (void); template void testAddSub < int > (void); template void testAddSub < size_t > (void); template void testCAS < int > (void); template void testCAS < size_t > (void); template void testCAS < EpicsAtomicPtrT > (void); #ifdef _MSC_VER # pragma warning ( pop ) #endif static void testClassify() { testDiag("Classify Build conditions"); #ifdef EPICS_ATOMIC_CMPLR_NAME testDiag("Compiler dependent impl name %s", EPICS_ATOMIC_CMPLR_NAME); #else testDiag("Compiler dependent impl name undefined"); #endif #ifdef EPICS_ATOMIC_OS_NAME testDiag("OS dependent impl name %s", EPICS_ATOMIC_OS_NAME); #else testDiag("OS dependent impl name undefined"); #endif #ifdef __GNUC__ #if GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER testDiag("GCC using atomic builtin memory barrier"); #else testDiag("GCC using asm memory barrier"); #endif #if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER testDiag("GCC use builtin for int"); #endif #if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER testDiag("GCC use builtin for size_t"); #endif #ifndef EPICS_ATOMIC_INCR_INTT testDiag("Use default epicsAtomicIncrIntT()"); #endif #ifndef EPICS_ATOMIC_INCR_SIZET testDiag("Use default epicsAtomicIncrSizeT()"); #endif #ifndef EPICS_ATOMIC_DECR_INTT testDiag("Use default epicsAtomicDecrIntT()"); #endif #ifndef EPICS_ATOMIC_DECR_SIZET testDiag("Use default epicsAtomicDecrSizeT()"); #endif #ifndef EPICS_ATOMIC_ADD_INTT testDiag("Use default epicsAtomicAddIntT()"); #endif #ifndef EPICS_ATOMIC_ADD_SIZET testDiag("Use default epicsAtomicAddSizeT()"); #endif #ifndef EPICS_ATOMIC_SUB_SIZET testDiag("Use default epicsAtomicSubSizeT()"); #endif #ifndef EPICS_ATOMIC_SET_INTT testDiag("Use default epicsAtomicSetIntT()"); #endif #ifndef EPICS_ATOMIC_SET_SIZET testDiag("Use default epicsAtomicSetSizeT()"); #endif #ifndef EPICS_ATOMIC_SET_PTRT testDiag("Use default epicsAtomicSetPtrT()"); #endif #ifndef EPICS_ATOMIC_GET_INTT testDiag("Use default epicsAtomicGetIntT()"); #endif #ifndef EPICS_ATOMIC_GET_SIZET testDiag("Use default epicsAtomicGetSizeT()"); #endif #ifndef EPICS_ATOMIC_GET_PTRT testDiag("Use default epicsAtomicGetPtrT()"); #endif #ifndef EPICS_ATOMIC_CAS_INTT testDiag("Use default epicsAtomicCmpAndSwapIntT()"); #endif #ifndef EPICS_ATOMIC_CAS_SIZET testDiag("Use default epicsAtomicCmpAndSwapSizeT()"); #endif #ifndef EPICS_ATOMIC_CAS_PTRT testDiag("Use default epicsAtomicCmpAndSwapPtrT()"); #endif #endif /* __GNUC__ */ } static void testBasic() { using epics::atomic::set; using epics::atomic::get; using epics::atomic::decrement; using epics::atomic::increment; using epics::atomic::add; using epics::atomic::subtract; using epics::atomic::compareAndSwap; testDiag("Test basic operation symantics"); int Int = 0; size_t Sizet = 0; void *voidp = NULL; set(Int, -42); set(Sizet, 42); set(voidp, (void*)&voidp); increment(Int); increment(Sizet); testOk1(get(Int)==-41); testOk1(get(Sizet)==43); testOk1(get(voidp)==(void*)&voidp); decrement(Int); decrement(Sizet); testOk1(get(Int)==-42); testOk1(get(Sizet)==42); add(Int, -2); subtract(Sizet, 2); testOk1(get(Int)==-44); testOk1(get(Sizet)==40); testOk1(compareAndSwap(Int, -34, -10)==-44); testOk1(compareAndSwap(Sizet, 34, 10)==40); testOk1(compareAndSwap(voidp, NULL, (void*)&Sizet)==(void*)&voidp); testOk1(get(Int)==-44); testOk1(get(Sizet)==40); testOk1(get(voidp)==(void*)&voidp); testOk1(compareAndSwap(Int, -44, -10)==-44); testOk1(compareAndSwap(Sizet, 40, 10)==40); testOk1(compareAndSwap(voidp, (void*)&voidp, (void*)&Sizet)==(void*)&voidp); testOk1(get(Int)==-10); testOk1(get(Sizet)==10); testOk1(get(voidp)==(void*)&Sizet); } } // namespace MAIN ( epicsAtomicTest ) { testPlan ( 50 ); testDiag("In %s", EPICS_FUNCTION); testClassify (); testBasic(); #if defined(__rtems__) testSkip(31, "Tests assume time sliced thread scheduling"); #else testIncrDecr < int > (); testIncrDecr < size_t > (); testAddSub < int > (); testAddSub < size_t > (); testCAS < int > (); testCAS < size_t > (); testCAS < EpicsAtomicPtrT > (); #endif return testDone (); } base-7.0.3.1/modules/libcom/test/epicsCalcTest.cpp0000664000577000060420000007330113557101274020545 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ // Author: Andrew Johnson #include #include #include "epicsUnitTest.h" #include "epicsTypes.h" #include "epicsMath.h" #include "epicsAlgorithm.h" #include "postfix.h" #include "testMain.h" /* Infrastructure for running tests */ double doCalc(const char *expr) { /* Evaluate expression, return result */ double args[CALCPERFORM_NARGS] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 }; char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); short err; double result = 0.0; result /= result; /* Start as NaN */ if(!rpn) { testAbort("postfix: %s no memory", expr); return epicsNAN; } if (postfix(expr, rpn, &err)) { testDiag("postfix: %s in expression '%s'", calcErrorStr(err), expr); } else if (calcPerform(args, &result, rpn) && finite(result)) { testDiag("calcPerform: error evaluating '%s'", expr); } free(rpn); return result; } void testCalc(const char *expr, double expected) { /* Evaluate expression, test against expected result */ bool pass = false; double args[CALCPERFORM_NARGS] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 }; char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); short err; double result = 0.0; result /= result; /* Start as NaN */ if(!rpn) { testFail("postfix: %s no memory", expr); return; } if (postfix(expr, rpn, &err)) { testDiag("postfix: %s in expression '%s'", calcErrorStr(err), expr); } else if (calcPerform(args, &result, rpn) && finite(result)) { testDiag("calcPerform: error evaluating '%s'", expr); } if (finite(expected) && finite(result)) { pass = fabs(expected - result) < 1e-8; } else if (isnan(expected)) { pass = (bool) isnan(result); } else { pass = (result == expected); } if (!testOk(pass, "%s", expr)) { testDiag("Expected result is %g, actually got %g", expected, result); calcExprDump(rpn); } free(rpn); } void testUInt32Calc(const char *expr, epicsUInt32 expected) { /* Evaluate expression, test against expected result */ bool pass = false; double args[CALCPERFORM_NARGS] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 }; char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); short err; epicsUInt32 uresult; double result = 0.0; result /= result; /* Start as NaN */ if(!rpn) { testFail("postfix: %s no memory", expr); return; } if (postfix(expr, rpn, &err)) { testDiag("postfix: %s in expression '%s'", calcErrorStr(err), expr); } else if (calcPerform(args, &result, rpn) && finite(result)) { testDiag("calcPerform: error evaluating '%s'", expr); } uresult = (epicsUInt32) result; pass = (uresult == expected); if (!testOk(pass, "%s", expr)) { testDiag("Expected result is 0x%x (%u), actually got 0x%x (%u)", expected, expected, uresult, uresult); calcExprDump(rpn); } free(rpn); } void testArgs(const char *expr, unsigned long einp, unsigned long eout) { char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); short err = 0; unsigned long vinp, vout; if(!rpn) { testFail("postfix: %s no memory", expr); return; } if (postfix(expr, rpn, &err)) { testFail("postfix: %s in expression '%s'", calcErrorStr(err), expr); return; } if (calcArgUsage(rpn, &vinp, &vout)) { testFail("calcArgUsage returned error for '%s'", expr); return; } if (!testOk(vinp == einp && vout == eout, "Args for '%s'", expr)) { testDiag("Expected (%lx, %lx) got (%lx, %lx)", einp, eout, vinp, vout); } free(rpn); } void testBadExpr(const char *expr, short expected_err) { /* Parse an invalid expression, test against expected error code */ char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); short err = 0; if(!rpn) { testFail("postfix: %s no memory", expr); return; } postfix(expr, rpn, &err); if (!testOk(err == expected_err, "Bad expression '%s'", expr)) { testDiag("Expected '%s', actually got '%s'", calcErrorStr(expected_err), calcErrorStr(err)); calcExprDump(rpn); } free(rpn); } /* Test an expression that is also valid C code */ #define testExpr(expr) testCalc(#expr, expr); /* These are the argument bits for testArgs */ #define A_A 0x001 #define A_B 0x002 #define A_C 0x004 #define A_D 0x008 #define A_E 0x010 #define A_F 0x020 #define A_G 0x040 #define A_H 0x080 #define A_I 0x100 #define A_J 0x200 #define A_K 0x400 #define A_L 0x800 /* Macros and functions to make some expressions into valid C code */ #ifndef PI #define PI 3.14159265358979 #endif #define Inf epicsINF #define NaN epicsNAN #define D2R (PI/180.) #define R2D (180./PI) #define ABS(x) fabs(x) #define AND & #define ATAN2(x,y) atan2(y,x) #define LN(x) log(x) #define LOG(x) log10(x) #define LOGE(x) log(x) #define NINT(x) (double)(long)((x) >= 0 ? (x)+0.5 : (x)-0.5) #define OR | #define SQR(x) sqrt(x) #define XOR ^ static inline double MAX(double a) { return a; } static inline double MAX(double a, double b) { return epicsMax(a,b); } static inline double MAX(double a, double b, double c) { return MAX(MAX(a,b),c); } static inline double MAX(double a, double b, double c, double d) { return MAX(MAX(a,b,c),d); } static inline double MAX(double a, double b, double c, double d, double e) { return MAX(MAX(a,b,c,d),e); } static inline double MAX(double a, double b, double c, double d, double e, double f) { return MAX(MAX(a,b,c,d,e),f); } static inline double MAX(double a, double b, double c, double d, double e, double f, double g) { return MAX(MAX(a,b,c,d,e,f),g); } static inline double MAX(double a, double b, double c, double d, double e, double f, double g, double h) { return MAX(MAX(a,b,c,d,e,f,g),h); } static inline double MAX(double a, double b, double c, double d, double e, double f, double g, double h, double i) { return MAX(MAX(a,b,c,d,e,f,g,h),i); } static inline double MAX(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j) { return MAX(MAX(a,b,c,d,e,f,g,h,i),j); } static inline double MAX(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j, double k) { return MAX(MAX(a,b,c,d,e,f,g,h,i,j),k); } static inline double MAX(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j, double k, double l) { return MAX(MAX(a,b,c,d,e,f,g,h,i,j,k),l); } static inline double MIN(double a) { return a; } static inline double MIN(double a, double b) { return epicsMin(a,b); } static inline double MIN(double a, double b, double c) { return MIN(MIN(a,b),c); } static inline double MIN(double a, double b, double c, double d) { return MIN(MIN(a,b,c),d); } static inline double MIN(double a, double b, double c, double d, double e) { return MIN(MIN(a,b,c,d),e); } static inline double MIN(double a, double b, double c, double d, double e, double f) { return MIN(MIN(a,b,c,d,e),f); } static inline double MIN(double a, double b, double c, double d, double e, double f, double g) { return MIN(MIN(a,b,c,d,e,f),g); } static inline double MIN(double a, double b, double c, double d, double e, double f, double g, double h) { return MIN(MIN(a,b,c,d,e,f,g),h); } static inline double MIN(double a, double b, double c, double d, double e, double f, double g, double h, double i) { return MIN(MIN(a,b,c,d,e,f,g,h),i); } static inline double MIN(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j) { return MIN(MIN(a,b,c,d,e,f,g,h,i),j); } static inline double MIN(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j, double k) { return MIN(MIN(a,b,c,d,e,f,g,h,i,j),k); } static inline double MIN(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j, double k, double l) { return MIN(MIN(a,b,c,d,e,f,g,h,i,j,k),l); } /* The test code below generates lots of spurious warnings because * it's making sure that our operator priorities match those of C. * Disable them to quieten the compilation process where possible. */ #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2) # pragma GCC diagnostic ignored "-Wparentheses" #endif MAIN(epicsCalcTest) { int repeat; const double a=1.0, b=2.0, c=3.0, d=4.0, e=5.0, f=6.0, g=7.0, h=8.0, i=9.0, j=10.0, k=11.0, l=12.0; testPlan(613); /* LITERAL_OPERAND elements */ testExpr(0); testExpr(1); testExpr(2); testExpr(3); testExpr(4); testExpr(5); testExpr(6); testExpr(7); testExpr(8); testExpr(9); testExpr(.1); testExpr(0.1); testExpr(0X0); testExpr(0x10); testExpr(0x7fffffff); testCalc("0x80000000", -2147483648.0); testCalc("0xffffffff", -1); testExpr(Inf); testCalc("Infinity", Inf); testExpr(NaN); /* OPERAND elements */ testExpr(a); testExpr(b); testExpr(c); testExpr(d); testExpr(e); testExpr(f); testExpr(g); testExpr(h); testExpr(i); testExpr(j); testExpr(k); testExpr(l); testExpr(PI); testExpr(D2R); testExpr(R2D); for (repeat=0; repeat<100; repeat++) { double res = doCalc("rndm"); if (res<0 || res >1) { testDiag("rndm returned %g", res); break; } } testOk(repeat == 100, "rndm"); /* UNARY_MINUS element */ testExpr(-1); testExpr(-Inf); testExpr(- -1); testCalc("-0x80000000", 2147483648.0); /* UNARY_OPERATOR elements */ testExpr((1)); testExpr(!0); testExpr(!1); testExpr(!!0); testExpr(ABS(1.0)); testExpr(ABS(-1.)); testExpr(acos(1.)); testExpr(asin(0.5)); testExpr(atan(0.5)); testExpr(ATAN2(1., 2.)); testExpr(ceil(0.5)); testExpr(cos(0.5)); testExpr(cosh(0.5)); testExpr(exp(1.)); testExpr(floor(1.5)); testExpr(finite(0.)); testExpr(finite(Inf)); testExpr(finite(-Inf)); testExpr(finite(NaN)); testCalc("finite(0,1,2)", 1); testCalc("finite(0,1,NaN)", 0); testCalc("finite(0,NaN,2)", 0); testCalc("finite(NaN,1,2)", 0); testCalc("finite(0,1,Inf)", 0); testCalc("finite(0,Inf,2)", 0); testCalc("finite(Inf,1,2)", 0); testCalc("finite(0,1,-Inf)", 0); testCalc("finite(0,-Inf,2)", 0); testCalc("finite(-Inf,1,2)", 0); testExpr(isinf(0.)); testExpr(isinf(Inf)); testExpr(!!isinf(-Inf)); // Some GCCs return -1/0/+1 rather than 0/+1 testExpr(isinf(NaN)); testExpr(isnan(0.)); testExpr(isnan(Inf)); testExpr(isnan(-Inf)); testExpr(!!isnan(NaN)); // As above testCalc("isnan(0,1,2)", 0); testCalc("isnan(0,1,NaN)", 1); testCalc("isnan(0,NaN,2)", 1); testCalc("isnan(NaN,1,2)", 1); testCalc("isnan(0,1,Inf)", 0); testCalc("isnan(0,Inf,2)", 0); testCalc("isnan(Inf,1,2)", 0); testCalc("isnan(0,1,-Inf)", 0); testCalc("isnan(0,-Inf,2)", 0); testCalc("isnan(-Inf,1,2)", 0); testExpr(LN(5.)); testExpr(LOG(5.)); testExpr(LOGE(2.)); testExpr(MAX(-99)); testExpr(MAX( 1., 2.)); testExpr(MAX( 1., Inf)); testExpr(MAX( 1.,-Inf)); testExpr(MAX( 1., NaN)); testExpr(MAX( Inf, 1.)); testExpr(MAX(-Inf, 1.)); testExpr(MAX( NaN, 1.)); testExpr(MAX( 1., 2.,3.)); testExpr(MAX( 1., 3.,2.)); testExpr(MAX( 2., 1.,3.)); testExpr(MAX( 2., 3.,1.)); testExpr(MAX( 3., 1.,2.)); testExpr(MAX( 3., 2.,1.)); testExpr(MAX( 1., 2., Inf)); testExpr(MAX( 1., 2.,-Inf)); testExpr(MAX( 1., 2., NaN)); testExpr(MAX( 1., Inf,2.)); testExpr(MAX( 1.,-Inf,2.)); testExpr(MAX( 1., NaN,2.)); testExpr(MAX( Inf, 1.,2.)); testExpr(MAX(-Inf, 1.,2.)); testExpr(MAX( NaN, 1.,2.)); testExpr(MAX( 1., 2., 3., 4.)); testExpr(MAX( 1., 2., 4., 3.)); testExpr(MAX( 1., 4., 3., 2.)); testExpr(MAX( 4., 2., 3., 1.)); testExpr(MAX( 1., 2., 3.,NaN)); testExpr(MAX( 1., 2.,NaN, 3.)); testExpr(MAX( 1.,NaN, 3., 2.)); testExpr(MAX(NaN, 2., 3., 1.)); testExpr(MAX( 1., 2., 3., 4., 5.)); testExpr(MAX( 1., 2., 3., 5., 4.)); testExpr(MAX( 1., 2., 5., 4., 3.)); testExpr(MAX( 1., 5., 3., 4., 2.)); testExpr(MAX( 5., 2., 3., 4., 1.)); testExpr(MAX( 1., 2., 3., 4.,NaN)); testExpr(MAX( 1., 2., 3.,NaN, 4.)); testExpr(MAX( 1., 2.,NaN, 4., 3.)); testExpr(MAX( 1.,NaN, 3., 4., 2.)); testExpr(MAX(NaN, 2., 3., 4., 1.)); testExpr(MAX( 1., 2., 3., 4., 5., 6.)); testExpr(MAX( 1., 2., 3., 4., 6., 5.)); testExpr(MAX( 1., 2., 3., 6., 5., 4.)); testExpr(MAX( 1., 2., 6., 4., 5., 3.)); testExpr(MAX( 1., 6., 3., 4., 5., 2.)); testExpr(MAX( 6., 2., 3., 4., 5., 1.)); testExpr(MAX( 1., 2., 3., 4., 5.,NaN)); testExpr(MAX( 1., 2., 3., 4.,NaN, 5.)); testExpr(MAX( 1., 2., 3.,NaN, 5., 4.)); testExpr(MAX( 1., 2.,NaN, 4., 5., 3.)); testExpr(MAX( 1.,NaN, 3., 4., 5., 2.)); testExpr(MAX(NaN, 2., 3., 4., 5., 1.)); testExpr(MAX( 1., 2., 3., 4., 5.,Inf)); testExpr(MAX( 1., 2., 3., 4.,Inf, 5.)); testExpr(MAX( 1., 2., 3.,Inf, 5., 4.)); testExpr(MAX( 1., 2.,Inf, 4., 5., 3.)); testExpr(MAX( 1.,Inf, 3., 4., 5., 2.)); testExpr(MAX(Inf, 2., 3., 4., 5., 1.)); testExpr(MAX(1,2,3,4,5,6,7,8,9,10,11,12)); testExpr(MAX(5,4,3,2,1,0,-1,-2,-3,-4,-5,-6)); testExpr(MAX(-1,1,0)); testExpr(MIN(99)); testExpr(MIN(1.,2.)); testExpr(MIN(1.,Inf)); testExpr(MIN(1.,-Inf)); testExpr(MIN(1.,NaN)); testExpr(MIN(NaN,1.)); testExpr(MIN( 1., 2.,3.)); testExpr(MIN( 1., 3.,2.)); testExpr(MIN( 2., 1.,3.)); testExpr(MIN( 2., 3.,1.)); testExpr(MIN( 3., 1.,2.)); testExpr(MIN( 3., 2.,1.)); testExpr(MIN( 1., 2., Inf)); testExpr(MIN( 1., 2.,-Inf)); testExpr(MIN( 1., 2., NaN)); testExpr(MIN( 1., Inf,2.)); testExpr(MIN( 1.,-Inf,2.)); testExpr(MIN( 1., NaN,2.)); testExpr(MIN( Inf, 1.,2.)); testExpr(MIN(-Inf, 1.,2.)); testExpr(MIN( NaN, 1.,2.)); testExpr(MIN( 1., 2., 3., 4.)); testExpr(MIN( 1., 2., 4., 3.)); testExpr(MIN( 1., 4., 3., 2.)); testExpr(MIN( 4., 2., 3., 1.)); testExpr(MIN( 1., 2., 3.,NaN)); testExpr(MIN( 1., 2.,NaN, 3.)); testExpr(MIN( 1.,NaN, 3., 2.)); testExpr(MIN(NaN, 2., 3., 1.)); testExpr(MIN( 1., 2., 3., 4., 5.)); testExpr(MIN( 1., 2., 3., 5., 4.)); testExpr(MIN( 1., 2., 5., 4., 3.)); testExpr(MIN( 1., 5., 3., 4., 2.)); testExpr(MIN( 5., 2., 3., 4., 1.)); testExpr(MIN( 1., 2., 3., 4.,NaN)); testExpr(MIN( 1., 2., 3.,NaN, 4.)); testExpr(MIN( 1., 2.,NaN, 4., 3.)); testExpr(MIN( 1.,NaN, 3., 4., 2.)); testExpr(MIN(NaN, 2., 3., 4., 1.)); testExpr(MIN( 1., 2., 3., 4., 5., 6.)); testExpr(MIN( 2., 1., 3., 4., 5., 6.)); testExpr(MIN( 3., 2., 1., 4., 5., 6.)); testExpr(MIN( 4., 2., 3., 1., 5., 6.)); testExpr(MIN( 5., 2., 3., 4., 1., 6.)); testExpr(MIN( 6., 2., 3., 4., 5., 1.)); testExpr(MIN( 1., 2., 3., 4., 5.,NaN)); testExpr(MIN( 1., 2., 3., 4.,NaN, 5.)); testExpr(MIN( 1., 2., 3.,NaN, 5., 4.)); testExpr(MIN( 1., 2.,NaN, 4., 5., 3.)); testExpr(MIN( 1.,NaN, 3., 4., 5., 2.)); testExpr(MIN(NaN, 2., 3., 4., 5., 1.)); testExpr(MIN( 1., 2., 3., 4., 5.,-Inf)); testExpr(MIN( 1., 2., 3., 4.,-Inf, 5.)); testExpr(MIN( 1., 2., 3.,-Inf, 5., 4.)); testExpr(MIN( 1., 2.,-Inf, 4., 5., 3.)); testExpr(MIN( 1.,-Inf, 3., 4., 5., 2.)); testExpr(MIN(-Inf, 2., 3., 4., 5., 1.)); testExpr(MIN(1,2,3,4,5,6,7,8,9,10,11,12)); testExpr(MIN(5,4,3,2,1,0,-1,-2,-3,-4,-5,-6)); testExpr(MIN(1,-1,0)); testExpr(MAX(MIN(0,2),MAX(0),MIN(3,2,1))); testExpr(NINT(0.4)); testExpr(NINT(0.6)); testExpr(NINT(-0.4)); testExpr(NINT(-0.6)); testExpr(sin(0.5)); testExpr(sinh(0.5)); testExpr(SQR(10.)); testExpr(sqrt(16.)); testExpr(tan(0.5)); testExpr(tanh(0.5)); testExpr(~5); testExpr(~~5); /* BINARY_OPERATOR elements */ testExpr(0 != 1); testExpr(0 != 0); testExpr(1 != 0); testExpr(1 != 0 != 2); testExpr(0.0 != Inf); testExpr(0.0 != -Inf); testExpr(0.0 != NaN); testExpr(Inf != 0.0); testExpr(Inf != Inf); testExpr(Inf != -Inf); testExpr(Inf != NaN); testExpr(-Inf != 0.0); testExpr(-Inf != Inf); testExpr(-Inf != -Inf); testExpr(-Inf != NaN); testExpr(NaN != 0.0); testExpr(NaN != Inf); testExpr(NaN != -Inf); testExpr(NaN != NaN); testCalc("0 # 1", 0 != 1); testCalc("0 # 0", 0 != 0); testCalc("1 # 0", 1 != 0); testCalc("1 # 0 # 2", 1 != 0 != 2); testExpr(7 % 4); testExpr(-7 % 4); testExpr(63 % 16 % 6) testCalc("1 % 0", NaN); testExpr(7 & 4); testExpr(0 && 0); testExpr(0 && 1); testExpr(1 && 0); testExpr(1 && 1); testExpr(2 * 2); testExpr(0.0 * Inf); testExpr(0.0 * -Inf); testExpr(0.0 * NaN); testExpr(Inf * 0.0); testExpr(Inf * Inf); testExpr(Inf * -Inf); testExpr(Inf * NaN); testExpr(-Inf * 0.0); testExpr(-Inf * Inf); testExpr(-Inf * -Inf); testExpr(-Inf * NaN); testExpr(NaN * 0.0); testExpr(NaN * Inf); testExpr(NaN * -Inf); testExpr(NaN * NaN); testCalc("2 ** 0.2", pow(2., 0.2)); testCalc("2 ** -0.2", pow(2., -0.2)); testCalc("-0.2 ** 2", pow(-0.2, 2.)); testCalc("-0.2 ** -2", pow(-0.2, -2)); testCalc("2 ** 2 ** 3", pow(pow(2., 2.), 3.)); testExpr(0 + 1); testExpr(0.0 + Inf); testExpr(0.0 + -Inf); testExpr(0.0 + NaN); testExpr(Inf + 0.0); testExpr(Inf + Inf); // only test CALC as MSVC seems to incorrectly evaluate this expression at compile time. // see note in epicsMathTest testCalc("Inf + -Inf", NaN); testExpr(Inf + NaN); testExpr(-Inf + 0.0); testCalc("-Inf + Inf", NaN); testExpr(-Inf + -Inf); testExpr(-Inf + NaN); testExpr(NaN + 0.0); testExpr(NaN + Inf); testExpr(NaN + -Inf); testExpr(NaN + NaN); testExpr(0 - 1); testExpr(0 - 1 - 2); testExpr(0.0 - Inf); testExpr(0.0 - -Inf); testExpr(0.0 - NaN); testExpr(Inf - 0.0); testExpr(Inf - Inf); testExpr(Inf - -Inf); testExpr(Inf - NaN); testExpr(-Inf - 0.0); testExpr(-Inf - Inf); testExpr(-Inf - -Inf); testExpr(-Inf - NaN); testExpr(NaN - 0.0); testExpr(NaN - Inf); testExpr(NaN - -Inf); testExpr(NaN - NaN); testExpr(2.0 / 3.0); testExpr(1.0 / 2.0 / 3.0); testExpr(0.0 / Inf); testExpr(0.0 / -Inf); testExpr(0.0 / NaN); testExpr(Inf / 1.0); testExpr(Inf / Inf); testExpr(Inf / -Inf); testExpr(Inf / NaN); testExpr(-Inf / 1.0); testExpr(-Inf / Inf); testExpr(-Inf / -Inf); testExpr(-Inf / NaN); testExpr(NaN / 1.0); testExpr(NaN / Inf); testExpr(NaN / -Inf); testExpr(NaN / NaN); testExpr(0 < 1); testExpr(0 < 0); testExpr(1 < 0); testExpr(2 < 0 < 2) testExpr(0.0 < Inf); testExpr(0.0 < -Inf); testExpr(0.0 < NaN); testExpr(Inf < 0.0); testExpr(Inf < Inf); testExpr(Inf < -Inf); testExpr(Inf < NaN); testExpr(-Inf < 0.0); testExpr(-Inf < Inf); testExpr(-Inf < -Inf); testExpr(-Inf < NaN); testExpr(NaN < 0.0); testExpr(NaN < Inf); testExpr(NaN < -Inf); testExpr(NaN < NaN); testExpr(1 << 2); testExpr(1 << 3 << 2) testExpr(0 <= 1); testExpr(0 <= 0); testExpr(1 <= 0); testExpr(3 <= 2 <= 3) testExpr(0.0 <= Inf); testExpr(0.0 <= -Inf); testExpr(0.0 <= NaN); testExpr(Inf <= 0.0); testExpr(Inf <= Inf); testExpr(Inf <= -Inf); testExpr(Inf <= NaN); testExpr(-Inf <= 0.0); testExpr(-Inf <= Inf); testExpr(-Inf <= -Inf); testExpr(-Inf <= NaN); testExpr(NaN <= 0.0); testExpr(NaN <= Inf); testExpr(NaN <= -Inf); testExpr(NaN <= NaN); testCalc("0 = 1", 0 == 1); testCalc("0 = 0", 0 == 0); testCalc("1 = 0", 1 == 0); testCalc("2 = 2 = 1", 2 == 2 == 1); testExpr(0 == 1); testExpr(0 == 0); testExpr(1 == 0); testExpr(2 == 2 == 1); testExpr(0.0 == Inf); testExpr(0.0 == -Inf); testExpr(0.0 == NaN); testExpr(Inf == 0.0); testExpr(Inf == Inf); testExpr(Inf == -Inf); testExpr(Inf == NaN); testExpr(-Inf == 0.0); testExpr(-Inf == Inf); testExpr(-Inf == -Inf); testExpr(-Inf == NaN); testExpr(NaN == 0.0); testExpr(NaN == Inf); testExpr(NaN == -Inf); testExpr(NaN == NaN); testExpr(0 > 1); testExpr(0 > 0); testExpr(1 > 0); testExpr(2 > 0 > 2); testExpr(0.0 > Inf); testExpr(0.0 > -Inf); testExpr(0.0 > NaN); testExpr(Inf > 0.0); testExpr(Inf > Inf); testExpr(Inf > -Inf); testExpr(Inf > NaN); testExpr(-Inf > 0.0); testExpr(-Inf > Inf); testExpr(-Inf > -Inf); testExpr(-Inf > NaN); testExpr(NaN > 0.0); testExpr(NaN > Inf); testExpr(NaN > -Inf); testExpr(NaN > NaN); testExpr(0 >= 1); testExpr(0 >= 0); testExpr(1 >= 0); testExpr(3 >= 2 >= 3); testExpr(0.0 >= Inf); testExpr(0.0 >= -Inf); testExpr(0.0 >= NaN); testExpr(Inf >= 0.0); testExpr(Inf >= Inf); testExpr(Inf >= -Inf); testExpr(Inf >= NaN); testExpr(-Inf >= 0.0); testExpr(-Inf >= Inf); testExpr(-Inf >= -Inf); testExpr(-Inf >= NaN); testExpr(NaN >= 0.0); testExpr(NaN >= Inf); testExpr(NaN >= -Inf); testExpr(NaN >= NaN); testExpr(8 >> 1); testExpr(64 >> 2 >> 1); testExpr(7 AND 4); testExpr(1 OR 8); testExpr(3 XOR 9); testCalc("2 ^ 0.2", pow(2., 0.2)); testCalc("2 ^ -0.2", pow(2., -0.2)); testCalc("(-0.2) ^ 2", pow(-0.2, 2.)); testCalc("(-0.2) ^ -2", pow(-0.2, -2.)); testCalc("2 ^ 2 ^ 3", pow(pow(2., 2.), 3.)); testExpr(1 | 8); testExpr(0 || 0); testExpr(0 || 1); testExpr(1 || 0); testExpr(1 || 1); /* CONDITIONAL elements */ testExpr(0 ? 1 : 2); testExpr(1 ? 1 : 2); testExpr(Inf ? 1 : 2); testExpr(NaN ? 1 : 2); testExpr(0 ? 0 ? 2 : 3 : 4); testExpr(0 ? 1 ? 2 : 3 : 4); testExpr(1 ? 0 ? 2 : 3 : 4); testExpr(1 ? 1 ? 2 : 3 : 4); testExpr(0 ? 2 : 0 ? 3 : 4); testExpr(0 ? 2 : 1 ? 3 : 4); testExpr(1 ? 2 : 0 ? 3 : 4); testExpr(1 ? 2 : 1 ? 3 : 4); /* STORE_OPERATOR and EXPR_TERM elements*/ testCalc("a := 0; a", 0); testCalc("b := 0; b", 0); testCalc("c := 0; c", 0); testCalc("d := 0; d", 0); testCalc("e := 0; e", 0); testCalc("f := 0; f", 0); testCalc("g := 0; g", 0); testCalc("h := 0; h", 0); testCalc("i := 0; i", 0); testCalc("j := 0; j", 0); testCalc("k := 0; k", 0); testCalc("l := 0; l", 0); testCalc("a; a := 0", a); testCalc("b; b := 0", b); testCalc("c; c := 0", c); testCalc("d; d := 0", d); testCalc("e; e := 0", e); testCalc("f; f := 0", f); testCalc("g; g := 0", g); testCalc("h; h := 0", h); testCalc("i; i := 0", i); testCalc("j; j := 0", j); testCalc("k; k := 0", k); testCalc("l; l := 0", l); // Check relative precedences. testExpr(0 ? 1 : 2 | 4); // 0 1 testExpr(1 ? 1 : 2 | 4); // 0 1 testExpr(0 ? 2 | 4 : 1); // 0 1 testExpr(1 ? 2 | 4 : 1); // 0 1 testExpr(0 ? 1 : 2 & 3); // 0 2 testExpr(1 ? 1 : 2 & 3); // 0 2 testExpr(0 ? 2 & 3 : 1); // 0 2 testExpr(1 ? 2 & 3 : 1); // 0 2 testExpr(0 ? 2 : 3 >= 1); // 0 3 testExpr(0 ? 3 >= 1 : 2); // 0 3 testExpr(1 ? 0 == 1 : 2); // 0 3 testExpr(1 ? 2 : 0 == 1); // 0 3 testExpr(0 ? 1 : 2 + 4); // 0 4 testExpr(1 ? 1 : 2 + 4); // 0 4 testExpr(0 ? 2 + 4 : 1); // 0 4 testExpr(1 ? 2 + 4 : 1); // 0 4 testExpr(0 ? 1 : 2 * 4); // 0 5 testExpr(1 ? 1 : 2 * 4); // 0 5 testExpr(0 ? 2 * 4 : 1); // 0 5 testExpr(1 ? 2 * 4 : 1); // 0 5 testCalc("0 ? 1 : 2 ** 3", 8); // 0 6 testCalc("1 ? 1 : 2 ** 3", 1); // 0 6 testCalc("0 ? 2 ** 3 : 1", 1); // 0 6 testCalc("1 ? 2 ** 3 : 1", 8); // 0 6 testCalc("1 | 3 XOR 1", (1 | 3) ^ 1); // 1 1 testExpr(1 XOR 3 | 1); // 1 1 testExpr(3 | 1 & 2); // 1 2 testExpr(2 | 4 > 3); // 1 3 testExpr(2 OR 4 > 3); // 1 3 testExpr(2 XOR 3 >= 0); // 1 3 testExpr(2 | 1 - 3); // 1 4 testExpr(2 | 4 / 2); // 1 5 testCalc("1 | 2 ** 3", 1 | (int) pow(2., 3.));// 1 6 testExpr(3 << 2 & 10); // 2 2 testCalc("18 & 6 << 2", (18 & 6) << 2); // 2 2 testExpr(36 >> 2 & 10); // 2 2 testCalc("18 & 20 >> 2", (18 & 20) >> 2); // 2 2 testExpr(3 & 4 == 4); // 2 3 testExpr(3 AND 4 == 4); // 2 3 testCalc("1 << 2 != 4", 1 << (2 != 4)); // 2 3 testCalc("16 >> 2 != 4", 16 >> (2 != 4)); // 2 3 testExpr(3 AND -2); // 2 8 testExpr(0 < 1 ? 2 : 3); // 3 0 testExpr(1 <= 0 ? 2 : 3); // 3 0 testExpr(0 + -1); // 4 8 testExpr(0 - -1); // 4 8 testExpr(10 + 10 * 2); // 4 5 testExpr(20 + 20 / 2); // 4 5 testExpr(-1 + 1); // 7 4 testExpr(-1 - 2); // 7 4 testCalc("-2 ** 2", pow(-2., 2.)); // 7 6 testCalc("-2 ^ 2", pow(-2., 2.)); // 7 6 // Check parentheses testCalc("(1 | 2) ** 3", pow((double) (1 | 2), 3.));// 8 6 testCalc("1+(1|2)**3", 1+pow((double) (1 | 2), 3.));// 8 6 testExpr(1+(1?(1<2):(1>2))*2); testArgs("a", A_A, 0); testArgs("A", A_A, 0); testArgs("B", A_B, 0); testArgs("C", A_C, 0); testArgs("D", A_D, 0); testArgs("E", A_E, 0); testArgs("F", A_F, 0); testArgs("G", A_G, 0); testArgs("H", A_H, 0); testArgs("I", A_I, 0); testArgs("J", A_J, 0); testArgs("K", A_K, 0); testArgs("L", A_L, 0); testArgs("A+B+C+D+E+F+G+H+I+J+K+L", A_A|A_B|A_C|A_D|A_E|A_F|A_G|A_H|A_I|A_J|A_K|A_L, 0); testArgs("0.1;A:=0", 0, A_A); testArgs("1.1;B:=0", 0, A_B); testArgs("2.1;C:=0", 0, A_C); testArgs("3.1;D:=0", 0, A_D); testArgs("4.1;E:=0", 0, A_E); testArgs("5.1;F:=0", 0, A_F); testArgs("6.1;G:=0", 0, A_G); testArgs("7.1;H:=0", 0, A_H); testArgs("8.1;I:=0", 0, A_I); testArgs("9.1;J:=0", 0, A_J); testArgs("10.1;K:=0", 0, A_K); testArgs("11.1;L:=0", 0, A_L); testArgs("12.1;A:=0;B:=A;C:=B;D:=C", 0, A_A|A_B|A_C|A_D); testArgs("13.1;B:=A;A:=B;C:=D;D:=C", A_A|A_D, A_A|A_B|A_C|A_D); // Malformed expressions testBadExpr("0x0.1", CALC_ERR_SYNTAX); testBadExpr("1*", CALC_ERR_INCOMPLETE); testBadExpr("*1", CALC_ERR_SYNTAX); testBadExpr("MIN", CALC_ERR_INCOMPLETE); testBadExpr("MIN()", CALC_ERR_SYNTAX); testBadExpr("MIN(A,)", CALC_ERR_SYNTAX); testBadExpr("MIN(A,B,)", CALC_ERR_SYNTAX); testBadExpr("MAX", CALC_ERR_INCOMPLETE); testBadExpr("MAX()", CALC_ERR_SYNTAX); testBadExpr("MAX(A,)", CALC_ERR_SYNTAX); testBadExpr("MAX(A,B,)", CALC_ERR_SYNTAX); testBadExpr("1?", CALC_ERR_CONDITIONAL); testBadExpr("1?1", CALC_ERR_CONDITIONAL); testBadExpr(":1", CALC_ERR_SYNTAX); // Bit manipulations wrt bit 31 (bug lp:1514520) // using integer literals testUInt32Calc("0xaaaaaaaa AND 0xffff0000", 0xaaaa0000u); testUInt32Calc("0xaaaaaaaa OR 0xffff0000", 0xffffaaaau); testUInt32Calc("0xaaaaaaaa XOR 0xffff0000", 0x5555aaaau); testUInt32Calc("~0xaaaaaaaa", 0x55555555u); testUInt32Calc("~~0xaaaaaaaa", 0xaaaaaaaau); testUInt32Calc("0xaaaaaaaa >> 8", 0xffaaaaaau); testUInt32Calc("0xaaaaaaaa << 8", 0xaaaaaa00u); // using integer literals assigned to variables testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a AND b", 0xaaaa0000u); testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a OR b", 0xffffaaaau); testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a XOR b", 0x5555aaaau); testUInt32Calc("a:=0xaaaaaaaa; ~a", 0x55555555u); testUInt32Calc("a:=0xaaaaaaaa; ~~a", 0xaaaaaaaau); testUInt32Calc("a:=0xaaaaaaaa; a >> 8", 0xffaaaaaau); testUInt32Calc("a:=0xaaaaaaaa; a << 8", 0xaaaaaa00u); // Test proper conversion of double values (+ 0.1 enforces double literal) // when used as inputs to the bitwise operations. // 0xaaaaaaaa = -1431655766 or 2863311530u testUInt32Calc("-1431655766.1 OR 0", 0xaaaaaaaau); testUInt32Calc("2863311530.1 OR 0", 0xaaaaaaaau); testUInt32Calc("0 OR -1431655766.1", 0xaaaaaaaau); testUInt32Calc("0 OR 2863311530.1", 0xaaaaaaaau); testUInt32Calc("-1431655766.1 XOR 0", 0xaaaaaaaau); testUInt32Calc("2863311530.1 XOR 0", 0xaaaaaaaau); testUInt32Calc("0 XOR -1431655766.1", 0xaaaaaaaau); testUInt32Calc("0 XOR 2863311530.1", 0xaaaaaaaau); testUInt32Calc("-1431655766.1 AND 0xffffffff", 0xaaaaaaaau); testUInt32Calc("2863311530.1 AND 0xffffffff", 0xaaaaaaaau); testUInt32Calc("0xffffffff AND -1431655766.1", 0xaaaaaaaau); testUInt32Calc("0xffffffff AND 2863311530.1", 0xaaaaaaaau); testUInt32Calc("~ -1431655766.1", 0x55555555u); testUInt32Calc("~ 2863311530.1", 0x55555555u); testUInt32Calc("-1431655766.1 >> 0", 0xaaaaaaaau); testUInt32Calc("2863311530.1 >> 0", 0xaaaaaaaau); testUInt32Calc("-1431655766.1 >> 0.1", 0xaaaaaaaau); testUInt32Calc("2863311530.1 >> 0.1", 0xaaaaaaaau); testUInt32Calc("-1431655766.1 << 0", 0xaaaaaaaau); testUInt32Calc("2863311530.1 << 0", 0xaaaaaaaau); testUInt32Calc("-1431655766.1 << 0.1", 0xaaaaaaaau); testUInt32Calc("2863311530.1 << 0.1", 0xaaaaaaaau); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsEllTest.c0000664000577000060420000001736513557101274020067 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2016 Michael Davidsaver * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include "ellLib.h" #include "dbDefs.h" #include "epicsUnitTest.h" #include "testMain.h" struct myItem { ELLNODE node; int list; int num; }; static void testList(void) { ELLLIST list1; ELLLIST list2 = ELLLIST_INIT; int i1 = 1; struct myItem *pitem, *pfirst, *pick; list1.count = 27; list1.node.next = (ELLNODE *) 0x01010101; list1.node.previous = (ELLNODE *) 0x10101010; ellInit(&list1); testOk1(list1.count == 0); testOk1(list1.node.next == NULL); testOk1(list1.node.previous == NULL); testOk1(list2.count == 0); testOk1(list2.node.next == NULL); testOk1(list2.node.previous == NULL); /* First build a couple lists and fill them with nodes. */ pitem = (struct myItem *) malloc(sizeof(struct myItem)); pitem->node.next = (ELLNODE *) 0x10101010; pitem->node.previous = (ELLNODE *) 0x10101010; pitem->list = 1; pitem->num = i1++; ellAdd(&list1, &pitem->node); testOk1(list1.count == 1); testOk1(list1.node.next == &pitem->node); testOk1(list1.node.previous == &pitem->node); testOk1(pitem->node.next == NULL); testOk1(pitem->node.previous == NULL); pfirst = pitem; while (i1 <= 21) { /* put 21 nodes into LIST1 */ pitem = (struct myItem *) malloc(sizeof(struct myItem)); pitem->list = 1; pitem->num = i1++; ellAdd(&list1, &pitem->node); } testOk1(list1.count == 21); testOk1(list1.node.next == &pfirst->node); testOk1(list1.node.previous == &pitem->node); testOk1(pitem->node.next == NULL); testOk1(ellFirst(&list1) == &pfirst->node); testOk1(ellLast(&list1) == &pitem->node); testOk1(ellNext(&pitem->node) == NULL); testOk1(ellNext(&pfirst->node) == pfirst->node.next); testOk1(ellNth(&list1, 0) == NULL); pick = (struct myItem *)ellNth(&list1, 21); testOk1(pick == pitem); testOk1(ellNth(&list1, 22) == NULL); testOk1(ellNth(&list1, 1) == &pfirst->node); testOk1(ellNth(&list1, 2) == pfirst->node.next); testOk1(ellNth(&list1, 20) == pitem->node.previous); testOk1(ellPrevious(&pitem->node) == pitem->node.previous); testOk1(ellPrevious(&pfirst->node) == NULL); testOk1(ellPrevious(pfirst->node.next) == &pfirst->node); pick = (struct myItem *)ellGet(&list1); testOk1(pick == pfirst); testOk1(list1.node.next == pfirst->node.next); ellAdd(&list2, &pick->node); pick = (struct myItem *)ellGet(&list2); testOk1(pick == pfirst); testOk1(list2.node.next == NULL); testOk1(list2.node.previous == NULL); testOk1(ellCount(&list1) == 20); testOk1(ellCount(&list2) == 0); ellConcat(&list1, &list2); testOk1(ellCount(&list1) == 20); testOk1(ellCount(&list2) == 0); testOk1(list1.node.previous == &pitem->node); ellAdd(&list2, &pick->node); ellConcat(&list1, &list2); testOk1(ellCount(&list1) == 21); testOk1(ellCount(&list2) == 0); testOk1(list1.node.previous == &pick->node); testOk1(list2.node.next == NULL); testOk1(list2.node.previous == NULL); ellDelete(&list1, &pick->node); testOk1(ellCount(&list1) == 20); ellAdd(&list2, &pick->node); ellConcat(&list2, &list1); testOk1(ellFind(&list1, &pick->node) == -1); /* empty list */ testOk1(ellFind(&list2, &pick->node) == pick->num); /* first node */ pick = (struct myItem *)ellNth(&list2, 18); testOk1(ellFind(&list2, &pick->node) == 18); /* 18th node */ ellDelete(&list2, &pick->node); ellInsert(&list2, NULL, &pick->node); /* move #18 to top */ testOk1(ellCount(&list2) == 21); testOk1(((struct myItem *)list2.node.next)->num == 18); testOk1(ellFind(&list2, ellNth(&list2, 21)) == 21); pick = (struct myItem *)ellGet(&list2); pitem = (struct myItem *)ellNth(&list2, 17); ellInsert(&list2, &pitem->node, &pick->node); testOk1(ellFind(&list2, ellNth(&list2, 21)) == 21); testOk1(((struct myItem *)ellFirst(&list2))->num == 1); testOk1(((struct myItem *)ellNth(&list2,17))->num == 17); testOk1(((struct myItem *)ellNth(&list2,18))->num == 18); pick = (struct myItem *)ellLast(&list2); pitem = (struct myItem *) malloc(sizeof(struct myItem)); ellInsert(&list2, &pick->node, &pitem->node); testOk1(ellCount(&list2) == 22); testOk1(ellFind(&list2, ellNth(&list2, 22)) == 22); ellDelete(&list2, &pitem->node); free(pitem); ellExtract(&list2, ellNth(&list2,9), ellNth(&list2, 19), &list1); testOk1(ellCount(&list2) == 10); testOk1(ellCount(&list1) == 11); testOk1(ellFind(&list2, ellNth(&list2, 10)) == 10); testOk1(ellFind(&list1, ellNth(&list1, 11)) == 11); ellFree(&list2); testOk1(ellCount(&list2) == 0); pick = (struct myItem *)ellFirst(&list1); i1 = 1; while(pick != NULL) { pick->num = i1++; pick = (struct myItem *)ellNext(&pick->node); } pick = (struct myItem *)ellFirst(&list1); testOk1(pick != NULL); pitem = (struct myItem *)ellNStep(&pick->node, 3); testOk1(pitem != NULL); testOk1(pitem->num == 4); pitem = (struct myItem *)ellNStep(&pick->node, 30); testOk1(pitem == NULL); pitem = (struct myItem *)ellNStep(&pick->node, 10); testOk1(pitem != NULL); testOk1(pitem->num == 11); pitem = (struct myItem *)ellNStep(&pitem->node, 0); testOk1(pitem->num == 11); pitem = (struct myItem *)ellNStep(&pitem->node, -4); testOk1(pitem->num == 7); ellFree2(&list1, free); testOk1(ellCount(&list1) == 0); } typedef struct { int A, B; } input_t; static int myItemCmp(const ELLNODE *a, const ELLNODE *b) { struct myItem *A = CONTAINER(a, struct myItem, node), *B = CONTAINER(b, struct myItem, node); if (A->num < B->num) return -1; else if(A->num > B->num) return 1; else if(A->list < B->list) return -1; else if(A->list > B->list) return 1; else return 0; } static const input_t input[] = { {-4, 0}, {-5, 0}, {0,0}, {50,0}, {0,1}, {5,0}, {5,1} }; static void testSort(const input_t *inp, size_t ninp) { unsigned i; ELLLIST list = ELLLIST_INIT; struct myItem *alloc = calloc(ninp, sizeof(*alloc)); if(!alloc) testAbort("testSort allocation fails"); for(i=0; inum = inp[i].A; it->list= inp[i].B; ellAdd(&list, &it->node); } ellSortStable(&list, &myItemCmp); testOk(ellCount(&list)==ninp, "output length %u == %u", (unsigned)ellCount(&list), (unsigned)ninp); if(ellCount(&list)==0) { testSkip(ninp-1, "all items lost"); } { struct myItem *prev = CONTAINER(ellFirst(&list), struct myItem, node), *next; for(next = CONTAINER(ellNext(&prev->node), struct myItem, node); next; prev = next, next = CONTAINER(ellNext(&next->node), struct myItem, node)) { int cond = (prev->numnum) || (prev->num==next->num && prev->listlist); testOk(cond, "%d:%d < %d:%d", prev->num, prev->list, next->num, next->list); } } } MAIN(epicsEllTest) { testPlan(77); testList(); testSort(input, NELEMENTS(input)); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsEnvTest.c0000664000577000060420000000426613557101274020077 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsEnvTest.c */ /* Author: Andrew Johnson * Date: 2013-12-13 */ /* Check environment variable APIs. * TODO: Add tests for envDefs.h routines. * * The thread test is needed on VxWorks 6.x, where the OS can be * configured to maintain separate, totally independent sets * of environment variables for each thread. This configuration * is not supported by EPICS which expects child threads to at * least inherit the partent thread's environment variables. */ #include #include #include "envDefs.h" #include "epicsThread.h" #include "epicsUnitTest.h" #include "testMain.h" #define PARENT "Parent" #define CHILD "Child" static void child(void *arg) { const char *value = getenv(PARENT); if (!testOk(value && (strcmp(value, PARENT) == 0), "Child thread sees parent environment values")) { #ifdef vxWorks testDiag("VxWorks image needs ENV_VAR_USE_HOOKS configured as FALSE"); #else testDiag("Check OS configuration, environment inheritance needed"); #endif } epicsEnvSet(CHILD, CHILD); } MAIN(epicsEnvTest) { unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); const char *value; testPlan(3); epicsEnvSet(PARENT, PARENT); value = getenv(PARENT); if (!testOk(value && (strcmp(value, PARENT) == 0), "epicsEnvSet correctly modifies environment")) testAbort("environment variables not working"); epicsThreadCreate("child", 50, stackSize, child, NULL); epicsThreadSleep(0.1); value = getenv(CHILD); if (value && (strcmp(value, CHILD) == 0)) testDiag("Child and parent threads share a common environment"); value = getenv(PARENT); testOk(value && (strcmp(value, PARENT) == 0), "PARENT environment variable not modified"); testDone(); return 0; } base-7.0.3.1/modules/libcom/test/epicsEnvUnsetTest.c0000664000577000060420000000141313557101274021105 0ustar anjaesctl#include #include #include #include #include "macLib.h" #include "envDefs.h" #include "errlog.h" #include "epicsUnitTest.h" #include "testMain.h" static void check(const char* variable, const char* expected) { const char* value; value = getenv(variable); if (!testOk((!expected && !value) || (expected && value && strcmp(expected, value) == 0), "%s = \"%s\"", variable, value)) { testDiag("should have been \"%s\"", expected); } } MAIN(epicsEnvUnsetTest) { eltc(0); testPlan(3); check("TEST_VAR_A",NULL); epicsEnvSet("TEST_VAR_A","test value"); check("TEST_VAR_A","test value"); epicsEnvUnset("TEST_VAR_A"); check("TEST_VAR_A",NULL); testDone(); return 0; } base-7.0.3.1/modules/libcom/test/epicsErrlogTest.c0000664000577000060420000003452413557101274020601 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver * Date: 2010-09-24 */ #include #include #include #include "epicsAssert.h" #include "epicsThread.h" #include "epicsEvent.h" #include "dbDefs.h" #include "errlog.h" #include "epicsUnitTest.h" #include "testMain.h" #include "iocLog.h" #include "logClient.h" #include "envDefs.h" #include "osiSock.h" #include "fdmgr.h" #define LOGBUFSIZE 2048 static const char longmsg[]="A0123456789abcdef" "B0123456789abcdef" "C0123456789abcdef" "D0123456789abcdef" "E0123456789abcdef" "F0123456789abcdef" "G0123456789abcdef" "H0123456789abcdef" "I0123456789abcdef" "J0123456789abcdef" "K0123456789abcdef" "L0123456789abcdef" "M0123456789abcdef" "N0123456789abcdef" "O0123456789abcdef" "P0123456789abcdef" "Q0123456789abcdef" "R0123456789abcdef" "S0123456789abcdef" ; STATIC_ASSERT(NELEMENTS(longmsg)==324); static const char truncmsg[]="A0123456789abcdef" "B0123456789abcdef" "C0123456789abcdef" "D0123456789abcdef" "E0123456789abcdef" "F0123456789abcdef" "G0123456789abcdef" "H0123456789abcdef" "I0123456789abcdef" "J0123456789abcdef" "K0123456789abcdef" "L0123456789abcdef" "M0123456789abcdef" "N0123456789abcdef" "O01<>\n" ; STATIC_ASSERT(NELEMENTS(truncmsg)==256); typedef struct { unsigned int count; const char* expect; size_t checkLen; epicsEventId jammer; int jam; epicsEventId done; } clientPvt; static void testLogPrefix(void); static void acceptNewClient( void *pParam ); static void readFromClient( void *pParam ); static void testPrefixLogandCompare( const char* logmessage); static void *pfdctx; static SOCKET sock; static SOCKET insock; static const char* prefixactualmsg[]= { "A message without prefix", "A message with prefix", "DONE" }; static const char *prefixstring = "fac=LI21 "; static const char prefixexpectedmsg[] = "A message without prefix" "fac=LI21 A message with prefix" "fac=LI21 DONE" ; static char prefixmsgbuffer[1024]; static void testEqInt_(int line, int lhs, int rhs, const char *LHS, const char *RHS) { testOk(lhs==rhs, "%d: %s (%d) == %s (%d)", line, LHS, lhs, RHS, rhs); } #define testEqInt(L, R) testEqInt_(__LINE__, L, R, #L, #R); static void logClient(void* raw, const char* msg) { clientPvt *pvt = raw; size_t len; char show[46]; /* Simulate thread priority on non-realtime * OSs like Linux. This will cause the logging * thread to sleep with the buffer lock held. */ if (pvt->jam > 0) { pvt->jam = 0; epicsEventMustWait(pvt->jammer); } else if (pvt->jam < 0) { pvt->jam++; if (pvt->jam == 0) epicsEventMustWait(pvt->jammer); } len = strlen(msg); if (len > 45) { /* Only show start and end of long messages */ strncpy(show, msg, 20); show[20] = 0; strcat(show + 20, " ... "); strcat(show + 25, msg + len - 20); } else { strcpy(show, msg); } if (pvt->checkLen) if (!testOk(pvt->checkLen == len, "Received %d chars", (int) len)) { testDiag("Expected %d", (int) pvt->checkLen); if (!pvt->expect) testDiag("Message is \"%s\"", show); } if (pvt->expect) { if (!testOk(strcmp(pvt->expect, msg) == 0, "Message is \"%s\"", show)) { len = strlen(pvt->expect); if (len > 45) { testDiag("Expected \"%.20s ... %s\"", pvt->expect, pvt->expect + len - 20); } else { testDiag("Expected \"%s\"", pvt->expect); } } } pvt->count++; epicsEventSignal(pvt->done); } MAIN(epicsErrlogTest) { size_t mlen, i, N; char msg[256]; clientPvt pvt, pvt2; testPlan(40); strcpy(msg, truncmsg); errlogInit2(LOGBUFSIZE, 256); pvt.count = 0; pvt2.count = 0; pvt.expect = NULL; pvt2.expect = NULL; pvt.checkLen = 0; pvt2.checkLen = 0; pvt.jam = 0; pvt2.jam = 0; pvt.jammer = epicsEventMustCreate(epicsEventEmpty); pvt.done = epicsEventMustCreate(epicsEventEmpty); pvt2.jammer = epicsEventMustCreate(epicsEventEmpty); pvt2.done = epicsEventMustCreate(epicsEventEmpty); testDiag("Check listener registration"); errlogAddListener(&logClient, &pvt); pvt.expect = "Testing"; pvt.checkLen = strlen(pvt.expect); errlogPrintfNoConsole("%s", pvt.expect); errlogFlush(); epicsEventMustWait(pvt.done); testEqInt(pvt.count, 1); errlogAddListener(&logClient, &pvt2); pvt2.expect = pvt.expect = "Testing2"; pvt2.checkLen = pvt.checkLen = strlen(pvt.expect); errlogPrintfNoConsole("%s", pvt.expect); errlogFlush(); epicsEventMustWait(pvt.done); testEqInt(pvt.count, 2); epicsEventMustWait(pvt2.done); testEqInt(pvt2.count, 1); /* Removes the first listener */ testOk(1 == errlogRemoveListeners(&logClient, &pvt), "Removed 1 listener"); pvt2.expect = "Testing3"; pvt2.checkLen = strlen(pvt2.expect); errlogPrintfNoConsole("%s", pvt2.expect); errlogFlush(); testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout, "%d: Listener 1 didn't run", __LINE__); testOk(epicsEventTryWait(pvt2.done) == epicsEventOK, "%d: Listener 2 ran", __LINE__); testEqInt(pvt.count, 2); testEqInt(pvt2.count, 2); /* Add the second listener again, then remove both instances */ errlogAddListener(&logClient, &pvt2); testOk(2 == errlogRemoveListeners(&logClient, &pvt2), "Removed 2 listeners"); errlogPrintfNoConsole("Something different"); errlogFlush(); testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout, "%d: Listener 1 didn't run", __LINE__); testOk(epicsEventTryWait(pvt2.done) == epicsEventWaitTimeout, "%d: Listener 2 didn't run", __LINE__); testEqInt(pvt.count, 2); testEqInt(pvt2.count, 2); /* Re-add one listener */ errlogAddListener(&logClient, &pvt); testDiag("Check truncation"); pvt.expect = truncmsg; pvt.checkLen = 255; errlogPrintfNoConsole("%s", longmsg); errlogFlush(); epicsEventMustWait(pvt.done); testEqInt(pvt.count, 3); pvt.expect = NULL; testDiag("Check priority"); /* For the following tests it is important that * the buffer should not flush until we request it */ pvt.jam = 1; errlogPrintfNoConsole("%s", longmsg); testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout, "%d: Listener 1 didn't run", __LINE__); testEqInt(pvt.count, 3); epicsEventSignal(pvt.jammer); errlogFlush(); epicsEventMustWait(pvt.done); testEqInt(pvt.count, 4); testDiag("Find buffer capacity (%u theoretical)",LOGBUFSIZE); pvt.checkLen = 0; for (mlen = 8; mlen <= 255; mlen *= 2) { double eff; char save = msg[mlen - 1]; N = LOGBUFSIZE / mlen; /* # of of messages to send */ msg[mlen - 1] = '\0'; pvt.count = 0; /* pvt.checkLen = mlen - 1; */ pvt.jam = 1; for (i = 0; i < N; i++) { errlogPrintfNoConsole("%s", msg); } epicsEventSignal(pvt.jammer); errlogFlush(); eff = (double) (pvt.count * mlen) / LOGBUFSIZE * 100.0; testDiag(" For %d messages of length %d got %u (%.1f%% efficient)", (int) N, (int) mlen, pvt.count, eff); msg[mlen - 1] = save; N = pvt.count; /* Save final count for the test below */ /* Clear "errlog: messages were discarded" status */ pvt.checkLen = 0; errlogPrintfNoConsole("."); errlogFlush(); } testOk(epicsEventTryWait(pvt.done) == epicsEventOK, "%d: Listener 1 ran", __LINE__); testDiag("Checking buffer use after partial flush"); /* Use the numbers from the largest block size above */ mlen /= 2; msg[mlen - 1] = '\0'; pvt.jam = 1; pvt.count = 0; testDiag("Filling with %d messages of size %d", (int) N, (int) mlen); for (i = 0; i < N; i++) { errlogPrintfNoConsole("%s", msg); } testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout, "%d: Listener 1 didn't run", __LINE__); testEqInt(pvt.count, 0); /* Extract the first 2 messages, 2*(sizeof(msgNode) + 128) bytes */ pvt.jam = -2; epicsEventSignal(pvt.jammer); epicsThreadSleep(0.5); testDiag("Drained %u messages", pvt.count); epicsEventMustWait(pvt.done); testEqInt(pvt.count, 2); /* The buffer has space for 1 more message: sizeof(msgNode) + 256 bytes */ errlogPrintfNoConsole("%s", msg); /* Use up that space */ testDiag("Overflow the buffer"); errlogPrintfNoConsole("%s", msg); testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout, "%d: Listener 1 didn't run", __LINE__); testEqInt(pvt.count, 2); epicsEventSignal(pvt.jammer); /* Empty */ errlogFlush(); testDiag("Logged %u messages", pvt.count); epicsEventMustWait(pvt.done); testEqInt(pvt.count, N+1); /* Clean up */ testOk(1 == errlogRemoveListeners(&logClient, &pvt), "Removed 1 listener"); testLogPrefix(); return testDone(); } /* * Tests the log prefix code * The prefix is only applied to log messages as they go out to the socket, * so we need to create a server listening on a port, accept connections etc. * This code is a reduced version of the code in iocLogServer. */ static void testLogPrefix(void) { struct sockaddr_in serverAddr; int status; struct timeval timeout; struct sockaddr_in actualServerAddr; osiSocklen_t actualServerAddrSize; char portstring[16]; testDiag("Testing iocLogPrefix"); timeout.tv_sec = 5; /* in seconds */ timeout.tv_usec = 0; memset((void*)prefixmsgbuffer, 0, sizeof prefixmsgbuffer); /* Clear "errlog: messages were discarded" status */ errlogPrintfNoConsole("."); errlogFlush(); sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { testAbort("epicsSocketCreate failed."); } /* We listen on a an available port. */ memset((void *)&serverAddr, 0, sizeof serverAddr); serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(0); status = bind (sock, (struct sockaddr *)&serverAddr, sizeof (serverAddr) ); if (status < 0) { testAbort("bind failed; all ports in use?"); } status = listen(sock, 10); if (status < 0) { testAbort("listen failed!"); } /* Determine the port that the OS chose */ actualServerAddrSize = sizeof actualServerAddr; memset((void *)&actualServerAddr, 0, sizeof serverAddr); status = getsockname(sock, (struct sockaddr *) &actualServerAddr, &actualServerAddrSize); if (status < 0) { testAbort("Can't find port number!"); } sprintf(portstring, "%d", ntohs(actualServerAddr.sin_port)); testDiag("Listening on port %s", portstring); /* Set the EPICS environment variables for logging. */ epicsEnvSet ( "EPICS_IOC_LOG_INET", "localhost" ); epicsEnvSet ( "EPICS_IOC_LOG_PORT", portstring ); pfdctx = (void *) fdmgr_init(); if (status < 0) { testAbort("fdmgr_init failed!"); } status = fdmgr_add_callback(pfdctx, sock, fdi_read, acceptNewClient, &serverAddr); if (status < 0) { testAbort("fdmgr_add_callback failed!"); } testOk1(iocLogInit() == 0); fdmgr_pend_event(pfdctx, &timeout); testPrefixLogandCompare(prefixactualmsg[0]); iocLogPrefix(prefixstring); testPrefixLogandCompare(prefixactualmsg[1]); testPrefixLogandCompare(prefixactualmsg[2]); epicsSocketDestroy(sock); } static void testPrefixLogandCompare( const char* logmessage ) { struct timeval timeout; timeout.tv_sec = 5; /* in seconds */ timeout.tv_usec = 0; errlogPrintfNoConsole("%s", logmessage); errlogFlush(); iocLogFlush(); fdmgr_pend_event(pfdctx, &timeout); } static void acceptNewClient ( void *pParam ) { osiSocklen_t addrSize; struct sockaddr_in addr; int status; addrSize = sizeof ( addr ); insock = epicsSocketAccept ( sock, (struct sockaddr *)&addr, &addrSize ); testOk(insock != INVALID_SOCKET && addrSize >= sizeof (addr), "Accepted new client"); status = fdmgr_add_callback(pfdctx, insock, fdi_read, readFromClient, NULL); testOk(status >= 0, "Client read configured"); } static void readFromClient(void *pParam) { char recvbuf[1024]; int recvLength; memset(recvbuf, 0, 1024); recvLength = recv(insock, recvbuf, 1024, 0); if (recvLength > 0) { strcat(prefixmsgbuffer, recvbuf); /* If we have received all of the messages. */ if (strstr(prefixmsgbuffer, "DONE") != NULL) { size_t msglen = strlen(prefixexpectedmsg); int prefixcmp = strncmp(prefixexpectedmsg, prefixmsgbuffer, msglen); if (!testOk(prefixcmp == 0, "prefix matches")) { testDiag("Expected '%s'\n", prefixexpectedmsg); testDiag("Obtained '%s'\n", prefixmsgbuffer); } } } } base-7.0.3.1/modules/libcom/test/epicsEventTest.cpp0000664000577000060420000001747213557101274020773 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsEventTest.cpp */ /* Author: Marty Kraimer Date: 26JAN2000 */ /* timeout accuracy tests by Jeff Hill */ #include #include #include #include #include #include #include #include #include #include "epicsThread.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "epicsRingPointer.h" #include "epicsTime.h" #include "errlog.h" #include "epicsUnitTest.h" #include "testMain.h" typedef struct info { epicsEventId event; epicsMutexId lockRing; int quit; epicsRingPointerId ring; } info; extern "C" { static void consumer(void *arg) { info *pinfo = (info *)arg; int errors = 0; testDiag("consumer: starting"); while (!pinfo->quit) { epicsEventStatus status = epicsEventWait(pinfo->event); if (status != epicsEventOK) { testDiag("consumer: epicsEventWait returned %d", status); errors++; } while (epicsRingPointerGetUsed(pinfo->ring) >= 2) { epicsRingPointerId message[2]; for (int i = 0; i < 2; i++) { message[i] = epicsRingPointerPop(pinfo->ring); if (message[i] == 0) { testDiag("consumer: epicsRingPointerPop returned 0"); errors++; } } if (message[0] != message[1]) { testDiag("consumer: message %p %p\n", message[0], message[1]); errors++; } } } testOk(errors == 0, "consumer: errors = %d", errors); } static void producer(void *arg) { info *pinfo = (info *)arg; const char *name = epicsThreadGetNameSelf(); epicsThreadId myId = epicsThreadGetIdSelf(); int errors = 0; int ntimes = 0; testDiag("%s: starting", name); while(!pinfo->quit) { ++ntimes; epicsMutexLockStatus status = epicsMutexLock(pinfo->lockRing); if (status != epicsMutexLockOK) { testDiag("%s: epicsMutexLock returned %d", name, status); errors++; } if (epicsRingPointerGetFree(pinfo->ring) >= 2) { for (int i = 0; i < 2; i++) { if (!epicsRingPointerPush(pinfo->ring, myId)) { testDiag("%s: epicsRingPointerPush fail", name); errors++; } if (i == 0 && (ntimes % 4 == 0)) epicsThreadSleep(0.1); } } else { testFail("%s: ring buffer full", name); errors++; } epicsMutexUnlock(pinfo->lockRing); epicsThreadSleep(1.0); epicsEventMustTrigger(pinfo->event); } testOk(errors == 0, "%s: errors = %d", name, errors); } #define SLEEPERCOUNT 3 struct wakeInfo { epicsEventId event; epicsMutexId countMutex; int count; }; static void sleeper(void *arg) { struct wakeInfo *wp = (struct wakeInfo *)arg; epicsEventMustWait(wp->event); epicsMutexLock(wp->countMutex); wp->count++; epicsMutexUnlock(wp->countMutex); } static void eventWakeupTest(void) { struct wakeInfo wakeInfo, *wp = &wakeInfo; int i, c; wp->event = epicsEventMustCreate(epicsEventEmpty); wp->countMutex = epicsMutexMustCreate(); wp->count = 0; for (i = 0 ; i < SLEEPERCOUNT ; i++) epicsThreadCreate("Sleeper", epicsThreadPriorityScanHigh, epicsThreadGetStackSize(epicsThreadStackSmall), sleeper, wp); epicsThreadSleep(0.5); epicsMutexLock(wp->countMutex); c = wp->count; epicsMutexUnlock(wp->countMutex); testOk(c == 0, "all threads still sleeping"); for (i = 1 ; i <= SLEEPERCOUNT ; i++) { epicsEventMustTrigger(wp->event); epicsThreadSleep(0.5); epicsMutexLock(wp->countMutex); c = wp->count; epicsMutexUnlock(wp->countMutex); testOk(c == i, "%d thread%s awakened, expected %d", c, c == 1 ? "" : "s", i); } epicsEventDestroy(wp->event); epicsMutexDestroy(wp->countMutex); } } // extern "C" static double eventWaitMeasureDelayError( const epicsEventId &id, const double & delay ) { epicsTime beg = epicsTime::getMonotonic(); epicsEventWaitWithTimeout ( id, delay ); epicsTime end = epicsTime::getMonotonic(); double meas = end - beg; double error = fabs ( delay - meas ); testDiag("epicsEventWaitWithTimeout(%.6f) delay error %.6f sec", delay, error ); return error; } static void eventWaitTest() { double errorSum = 0.0; epicsEventId event = epicsEventMustCreate ( epicsEventEmpty ); int i; for ( i = 0u; i < 20; i++ ) { double delay = ldexp ( 1.0 , -i ); errorSum += eventWaitMeasureDelayError ( event, delay ); } errorSum += eventWaitMeasureDelayError ( event, 0.0 ); epicsEventDestroy ( event ); double meanError = errorSum / ( i + 1 ); testOk(meanError < 0.05, "Average error %.6f sec", meanError); } MAIN(epicsEventTest) { const int nthreads = 3; epicsThreadId *id; char **name; epicsEventId event; int status; testPlan(13+SLEEPERCOUNT); event = epicsEventMustCreate(epicsEventEmpty); status = epicsEventWaitWithTimeout(event, 0.0); testOk(status == epicsEventWaitTimeout, "epicsEventWaitWithTimeout(event, 0.0) = %d", status); status = epicsEventWaitWithTimeout(event, 1.0); testOk(status == epicsEventWaitTimeout, "epicsEventWaitWithTimeout(event, 1.0) = %d", status); status = epicsEventTryWait(event); testOk(status == epicsEventWaitTimeout, "epicsEventTryWait(event) = %d", status); status = epicsEventTrigger(event); testOk(status == epicsEventOK, "epicsEventTrigger(event) = %d", status); status = epicsEventWaitWithTimeout(event, 1.0); testOk(status == epicsEventOK, "epicsEventWaitWithTimeout(event, 1.0) = %d", status); epicsEventMustTrigger(event); status = epicsEventWaitWithTimeout(event,DBL_MAX); testOk(status == epicsEventOK, "epicsEventWaitWithTimeout(event, DBL_MAX) = %d", status); epicsEventMustTrigger(event); status = epicsEventTryWait(event); testOk(status == epicsEventOK, "epicsEventTryWait(event) = %d", status); info *pinfo = (info *)calloc(1,sizeof(info)); pinfo->event = event; pinfo->lockRing = epicsMutexCreate(); pinfo->ring = epicsRingPointerCreate(1024*2); unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); epicsThreadCreate("consumer", 50, stackSize, consumer, pinfo); id = (epicsThreadId *)calloc(nthreads, sizeof(epicsThreadId)); name = (char **)calloc(nthreads, sizeof(char *)); for(int i = 0; i < nthreads; i++) { name[i] = (char *)calloc(16, sizeof(char)); sprintf(name[i],"producer %d",i); id[i] = epicsThreadCreate(name[i], 40, stackSize, producer, pinfo); epicsThreadSleep(0.1); } epicsThreadSleep(5.0); testDiag("setting quit"); pinfo->quit = 1; epicsThreadSleep(2.0); epicsEventMustTrigger(pinfo->event); epicsThreadSleep(1.0); eventWaitTest(); eventWakeupTest(); free(name); free(id); epicsRingPointerDelete(pinfo->ring); epicsMutexDestroy(pinfo->lockRing); epicsEventDestroy(event); free(pinfo); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsExitTest.c0000664000577000060420000000564513557101274020262 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsExitTest.cpp */ /* Author: Marty Kraimer Date: 09JUL2004*/ #include #include #include #include #include #include #include "epicsThread.h" #include "epicsAssert.h" #include "epicsEvent.h" #include "epicsExit.h" #include "epicsUnitTest.h" #include "testMain.h" typedef struct info { char name[64]; epicsEventId terminate; epicsEventId terminated; }info; static void atExit(void *pvt) { info *pinfo = (info *)pvt; testPass("%s reached atExit", pinfo->name); epicsEventSignal(pinfo->terminate); /*Now wait for thread to terminate*/ epicsEventMustWait(pinfo->terminated); testPass("%s destroying pinfo", pinfo->name); epicsEventDestroy(pinfo->terminate); epicsEventDestroy(pinfo->terminated); free(pinfo); } static void atThreadExit(void *pvt) { info *pinfo = (info *)pvt; testPass("%s terminating", pinfo->name); epicsEventSignal(pinfo->terminated); } static void thread(void *arg) { info *pinfo = (info *)arg; strcpy(pinfo->name, epicsThreadGetNameSelf()); testDiag("%s starting", pinfo->name); pinfo->terminate = epicsEventMustCreate(epicsEventEmpty); pinfo->terminated = epicsEventMustCreate(epicsEventEmpty); testOk(!epicsAtExit(atExit, pinfo), "Registered atExit(%p)", pinfo); testOk(!epicsAtThreadExit(atThreadExit, pinfo), "Registered atThreadExit(%p)", pinfo); testDiag("%s waiting for atExit", pinfo->name); epicsEventMustWait(pinfo->terminate); } int count; static void counter(void *pvt) { count++; } static void mainExit(void *pvt) { testPass("Reached mainExit"); testDone(); } MAIN(epicsExitTest) { unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); info *pinfoA = (info *)calloc(1, sizeof(info)); info *pinfoB = (info *)calloc(1, sizeof(info)); testPlan(15); testOk(!epicsAtExit(counter, NULL), "Registered counter()"); count = 0; epicsExitCallAtExits(); testOk(count == 1, "counter() called once"); epicsExitCallAtExits(); testOk(count == 1, "unregistered counter() not called"); testOk(!epicsAtExit(mainExit, NULL), "Registered mainExit()"); epicsThreadCreate("threadA", 50, stackSize, thread, pinfoA); epicsThreadSleep(0.1); epicsThreadCreate("threadB", 50, stackSize, thread, pinfoB); epicsThreadSleep(1.0); testDiag("Calling epicsExit"); epicsExit(0); return 0; } base-7.0.3.1/modules/libcom/test/epicsInlineTest1.c0000664000577000060420000000341313557101274020637 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* This test checks the variations on inline function defintions. * * "static inline int func(void) {...}" * * Consistent meaning in C89, C99, and C++ (98 and 11). * If not inline'd results in a private symbol in each compilation unit. * Thus the non-inline'd version is duplicated in each compilation unit. * However, definitions in different compilation units may be different. * * "inline int func(void) {...}" * Warning: Not consistent, avoid use in headers meant for C or C++ * * In C++ this may be safely defined in more than one compilation unit. * Where not inlined it will result in a weak public symbol. * Thus non-inline'd version isn't duplicated, but must be the same * in all compilation units. * */ #include "compilerSpecific.h" #include "epicsUnitTest.h" #include "testMain.h" static EPICS_ALWAYS_INLINE int epicsInlineTestFn1(void) { return 1; } /* Fails to link in C99 inline int epicsInlineTestFn2(void) { return 42; } */ void epicsInlineTest1(void) { testDiag("epicsInlineTest1()"); testOk1(epicsInlineTestFn1()==1); /*testOk1(epicsInlineTestFn2()==42);*/ } void epicsInlineTest2(void); void epicsInlineTest3(void); void epicsInlineTest4(void); MAIN(epicsInlineTest) { testPlan(6); testDiag("Test variation on inline int func()"); epicsInlineTest1(); epicsInlineTest2(); epicsInlineTest3(); epicsInlineTest4(); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsInlineTest2.c0000664000577000060420000000136413557101274020643 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "compilerSpecific.h" #include "epicsUnitTest.h" static EPICS_ALWAYS_INLINE int epicsInlineTestFn1(void) { return 2; } /* Fails to link in C99 inline int epicsInlineTestFn2(void) { return 42; } */ void epicsInlineTest2(void) { testDiag("epicsInlineTest2()"); testOk1(epicsInlineTestFn1()==2); /*testOk1(epicsInlineTestFn2()==42);*/ } base-7.0.3.1/modules/libcom/test/epicsInlineTest3.cpp0000664000577000060420000000134013557101274021176 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "compilerSpecific.h" #include "epicsUnitTest.h" static EPICS_ALWAYS_INLINE int epicsInlineTestFn1(void) { return 3; } inline int epicsInlineTestFn2(void) { return 42; } extern "C" void epicsInlineTest3(void) { testDiag("epicsInlineTest3()"); testOk1(epicsInlineTestFn1()==3); testOk1(epicsInlineTestFn2()==42); } base-7.0.3.1/modules/libcom/test/epicsInlineTest4.cpp0000664000577000060420000000134013557101274021177 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "compilerSpecific.h" #include "epicsUnitTest.h" static EPICS_ALWAYS_INLINE int epicsInlineTestFn1(void) { return 4; } inline int epicsInlineTestFn2(void) { return 42; } extern "C" void epicsInlineTest4(void) { testDiag("epicsInlineTest4()"); testOk1(epicsInlineTestFn1()==4); testOk1(epicsInlineTestFn2()==42); } base-7.0.3.1/modules/libcom/test/epicsMMIOTest.c0000664000577000060420000000375713557101274020114 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2013 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver */ #include "epicsAssert.h" #include "epicsEndian.h" #include "epicsTypes.h" #include "epicsUnitTest.h" #include "testMain.h" #include "epicsMMIO.h" #if EPICS_BYTE_ORDER==EPICS_ENDIAN_BIG #define BE16 0x1234 #define BE32 0x12345678 #define LE16 0x3412 #define LE32 0x78563412 #else #define LE16 0x1234 #define LE32 0x12345678 #define BE16 0x3412 #define BE32 0x78563412 #endif union hydra16 { epicsUInt16 u16; epicsUInt8 bytes[2]; }; union hydra32 { epicsUInt32 u32; epicsUInt8 bytes[4]; }; MAIN(epicsMMIOTest) { epicsUInt8 B; union hydra16 H16; union hydra32 H32; STATIC_ASSERT(sizeof(H16)==2); STATIC_ASSERT(sizeof(H32)==4); testPlan(14); testDiag("8-bit ops"); iowrite8(&B, 5); testOk1(B==5); testOk1(ioread8(&B)==5); testDiag("16-bit ops"); nat_iowrite16(&H16.bytes, 0x1234); testOk1(H16.u16==0x1234); testOk1(nat_ioread16(&H16.bytes)==0x1234); be_iowrite16(&H16.bytes, 0x1234); testOk1(H16.u16==BE16); testOk1(be_ioread16(&H16.bytes)==0x1234); le_iowrite16(&H16.bytes, 0x1234); testOk1(H16.u16==LE16); testOk1(le_ioread16(&H16.bytes)==0x1234); testDiag("32-bit ops"); nat_iowrite32(&H32.bytes, 0x12345678); testOk1(H32.u32==0x12345678); testOk1(nat_ioread32(&H32.bytes)==0x12345678); be_iowrite32(&H32.bytes, 0x12345678); testOk1(H32.u32==BE32); testOk1(be_ioread32(&H32.bytes)==0x12345678); le_iowrite32(&H32.bytes, 0x12345678); testOk1(H32.u32==LE32); testOk1(le_ioread32(&H32.bytes)==0x12345678); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsMathTest.c0000664000577000060420000000526513557101274020240 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsMathTest.c * * Author Marty Kraimer */ #include "epicsUnitTest.h" #include "epicsMath.h" #include "testMain.h" MAIN(epicsMathTest) { double huge = 1e300; double tiny = 1e-300; double c; testPlan(35); testOk1(!isnan(0.0)); testOk1(!isinf(0.0)); testOk1(!isnan(epicsINF)); testOk1(isinf(epicsINF)); testOk1(epicsINF == epicsINF); testOk1(epicsINF > 0.0); testOk1(epicsINF - epicsINF != 0.0); #if defined(_MSC_VER) testTodoBegin("Known failure on windows (MSVC optimizer bug?)"); #endif testOk1(epicsINF + -epicsINF != 0.0); testOk1(-epicsINF + epicsINF != 0.0); #if defined(_MSC_VER) testTodoEnd(); #endif testOk1(isnan(epicsINF - epicsINF)); #if defined(_MSC_VER) testTodoBegin("Known failure on windows (MSVC optimizer bug?)"); #endif testOk1(isnan(epicsINF + -epicsINF)); testOk1(isnan(-epicsINF + epicsINF)); #if defined(_MSC_VER) testTodoEnd(); #endif testOk1(isnan(epicsNAN)); testOk1(!isinf(epicsNAN)); testOk1(epicsNAN != epicsNAN); testOk1(!(epicsNAN < epicsNAN)); testOk1(!(epicsNAN <= epicsNAN)); testOk1(!(epicsNAN == epicsNAN)); testOk1(!(epicsNAN >= epicsNAN)); testOk1(!(epicsNAN > epicsNAN)); testOk1(isnan(epicsNAN - epicsNAN)); #if defined(_MSC_VER) testTodoBegin("Known failure on windows (MSVC optimizer bug?)"); #endif testOk1(isnan(epicsNAN + -epicsNAN)); testOk1(isnan(-epicsNAN + epicsNAN)); #if defined(_MSC_VER) testTodoEnd(); #endif c = huge / tiny; testOk(!isnan(c), "!isnan(1e300 / 1e-300)"); testOk(isinf(c), "isinf(1e300 / 1e-300)"); testOk(c > 0.0, "1e300 / 1e-300 > 0.0"); c = (-huge) / tiny; testOk(!isnan(c), "!isnan(-1e300 / 1e-300)"); testOk(isinf(c), "isinf(-1e300 / 1e-300)"); testOk(c < 0.0, "-1e300 / 1e-300 < 0.0"); c = huge / huge; testOk(!isnan(c), "!isnan(1e300 / 1e300)"); testOk(!isinf(c), "!isinf(1e300 / 1e300)"); testOk(c == 1.0, "1e300 / 1e300 == 1.0"); c = tiny / tiny; testOk(!isnan(c), "!isnan(1e-300 / 1e-300)"); testOk(!isinf(c), "!isinf(1e-300 / 1e-300)"); testOk(c == 1.0, "1e300 / 1e-300 == 1.0"); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsMaxThreads.c0000664000577000060420000000274313557101274020545 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsMaxThreads.cpp */ /* Author: Marty Kraimer Date: 09JUL2004*/ #include #include #include #include #include #include #include "epicsThread.h" #include "epicsEvent.h" #include "epicsExit.h" #include "errlog.h" #include "testMain.h" static epicsEventId started; static void thread(void *arg) { epicsEventSignal(started); epicsThreadSuspendSelf(); } MAIN(epicsMaxThreads) { unsigned int stackSize; epicsThreadId id; int i = 0; stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); printf("stackSize %d\n",stackSize); started = epicsEventMustCreate(epicsEventEmpty); while(1) { id = epicsThreadCreate("thread",50,stackSize,thread,0); if(!id) break; i++; if ((i % 100) == 0) printf ("Reached %d...\n", i); epicsEventMustWait(started); } printf("Max number of \"Small\" threads on this OS is %d\n", i); return 0; } base-7.0.3.1/modules/libcom/test/epicsMessageQueueTest.cpp0000664000577000060420000002477713557101274022311 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author W. Eric Norum */ #include #include #include #include #include "epicsMessageQueue.h" #include "epicsThread.h" #include "epicsExit.h" #include "epicsEvent.h" #include "epicsAssert.h" #include "epicsUnitTest.h" #include "testMain.h" static const char *msg1 = "1234567890This is a very long message."; static volatile int sendExit = 0; static volatile int recvExit = 0; static epicsEventId finished; static unsigned int mediumStack; /* * In Numerical Recipes in C: The Art of Scientific Computing (William H. * Press, Brian P. Flannery, Saul A. Teukolsky, William T. Vetterling; New * York: Cambridge University Press, 1992 (2nd ed., p. 277)), the follow- * ing comments are made: * "If you want to generate a random integer between 1 and 10, you * should always do it by using high-order bits, as in * j=1+(int) (10.0*rand()/(RAND_MAX+1.0)); * and never by anything resembling * j=1+(rand() % 10); */ static int randBelow(int n) { return (int)((double)n*rand()/(RAND_MAX+1.0)); } extern "C" void badReceiver(void *arg) { epicsMessageQueue *q = (epicsMessageQueue *)arg; char cbuf[80]; int len; cbuf[0] = '\0'; len = q->receive(cbuf, 1); if (len < 0 && cbuf[0] == '\0') testPass("receive into undersized buffer returned error (%d)", len); else if (len == 1 && cbuf[0] == *msg1) testPass("receive into undersized buffer truncated message"); else testFail("receive into undersized buffer returned %d", len); epicsThreadSleep(3.0); cbuf[0] = '\0'; len = q->receive(cbuf, 1); if (len < 0 && cbuf[0] == '\0') testPass("receive into undersized buffer returned error (%d)", len); else if (len == 1 && cbuf[0] == *msg1) testPass("receive into undersized buffer truncated message"); else testFail("receive into undersized buffer returned %d", len); } extern "C" void receiver(void *arg) { epicsMessageQueue *q = (epicsMessageQueue *)arg; const char *myName = epicsThreadGetNameSelf(); char cbuf[80]; int expectmsg[4]; int len; int sender, msgNum; int errors = 0; for (sender = 1 ; sender <= 4 ; sender++) expectmsg[sender-1] = 1; while (!recvExit) { cbuf[0] = '\0'; len = q->receive(cbuf, sizeof cbuf, 5.0); if (len < 0 && !recvExit) { testDiag("receiver() received unexpected timeout"); ++errors; } else if (sscanf(cbuf, "Sender %d -- %d", &sender, &msgNum) == 2 && sender >= 1 && sender <= 4) { if (expectmsg[sender-1] != msgNum) { ++errors; testDiag("%s received %d '%.*s' -- expected %d", myName, len, len, cbuf, expectmsg[sender-1]); } expectmsg[sender-1] = msgNum + 1; epicsThreadSleep(0.001 * (randBelow(20))); } } for (sender = 1 ; sender <= 4 ; sender++) { if (expectmsg[sender-1] > 1) testDiag("Received %d messages from Sender %d", expectmsg[sender-1]-1, sender); } if (!testOk1(errors == 0)) testDiag("Error count was %d", errors); testDiag("%s exiting", myName); epicsEventSignal(finished); } extern "C" void sender(void *arg) { epicsMessageQueue *q = (epicsMessageQueue *)arg; char cbuf[80]; int len; int i = 0; while (!sendExit) { len = sprintf(cbuf, "%s -- %d.", epicsThreadGetNameSelf(), ++i); while (q->trySend((void *)cbuf, len) < 0) epicsThreadSleep(0.005 * (randBelow(5))); epicsThreadSleep(0.005 * (randBelow(20))); } testDiag("%s exiting, sent %d messages", epicsThreadGetNameSelf(), i); } extern "C" void messageQueueTest(void *parm) { epicsThreadId myThreadId = epicsThreadGetIdSelf(); unsigned int i; char cbuf[80]; int len; int pass; int want; epicsMessageQueue *q1 = new epicsMessageQueue(4, 20); testDiag("Simple single-thread tests:"); i = 0; testOk1(q1->pending() == 0); while (q1->trySend((void *)msg1, i ) == 0) { i++; testOk(q1->pending() == i, "q1->pending() == %d", i); } testOk1(q1->pending() == 4); want = 0; len = q1->receive(cbuf, sizeof cbuf); testOk1(q1->pending() == 3); if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); want++; len = q1->receive(cbuf, sizeof cbuf); testOk1(q1->pending() == 2); if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); q1->trySend((void *)msg1, i++); want++; len = q1->receive(cbuf, sizeof cbuf); testOk1(q1->pending() == 2); if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); q1->trySend((void *)msg1, i++); testOk1(q1->pending() == 3); i = 3; while ((len = q1->receive(cbuf, sizeof cbuf, 1.0)) >= 0) { --i; testOk(q1->pending() == i, "q1->pending() == %d", i); want++; if (!testOk1((len == want) & (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); } testOk1(q1->pending() == 0); testDiag("Test sender timeout:"); i = 0; testOk1(q1->pending() == 0); while (q1->send((void *)msg1, i, 1.0 ) == 0) { i++; testOk(q1->pending() == i, "q1->pending() == %d", i); } testOk1(q1->pending() == 4); want = 0; len = q1->receive(cbuf, sizeof cbuf); testOk1(q1->pending() == 3); if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); want++; len = q1->receive(cbuf, sizeof cbuf); testOk1(q1->pending() == 2); if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); q1->send((void *)msg1, i++, 1.0); want++; len = q1->receive(cbuf, sizeof cbuf); testOk1(q1->pending() == 2); if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); q1->send((void *)msg1, i++, 1.0); testOk1(q1->pending() == 3); i = 3; while ((len = q1->receive(cbuf, sizeof cbuf, 1.0)) >= 0) { --i; testOk(q1->pending() == i, "q1->pending() == %d", i); want++; if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); } testOk1(q1->pending() == 0); testDiag("Test receiver with timeout:"); for (i = 0 ; i < 4 ; i++) testOk1 (q1->send((void *)msg1, i, 1.0) == 0); testOk1(q1->pending() == 4); for (i = 0 ; i < 4 ; i++) testOk(q1->receive((void *)cbuf, sizeof cbuf, 1.0) == (int)i, "q1->receive(...) == %d", i); testOk1(q1->pending() == 0); testOk1(q1->receive((void *)cbuf, sizeof cbuf, 1.0) < 0); testOk1(q1->pending() == 0); testDiag("Single receiver with invalid size, single sender tests:"); epicsThreadCreate("Bad Receiver", epicsThreadPriorityMedium, mediumStack, badReceiver, q1); epicsThreadSleep(1.0); testOk(q1->send((void *)msg1, 10) == 0, "Send with waiting receiver"); epicsThreadSleep(2.0); testOk(q1->send((void *)msg1, 10) == 0, "Send with no receiver"); epicsThreadSleep(2.0); testDiag("Single receiver, single sender tests:"); epicsThreadSetPriority(myThreadId, epicsThreadPriorityHigh); epicsThreadCreate("Receiver one", epicsThreadPriorityMedium, mediumStack, receiver, q1); for (pass = 1 ; pass <= 3 ; pass++) { for (i = 0 ; i < 10 ; i++) { if (q1->trySend((void *)msg1, i) < 0) break; if (pass >= 3) epicsThreadSleep(0.5); } switch (pass) { case 1: if (i<6) testDiag(" priority-based scheduler, sent %d messages", i); epicsThreadSetPriority(myThreadId, epicsThreadPriorityLow); break; case 2: if (i<10) testDiag(" scheduler not strict priority, sent %d messages", i); else testDiag(" strict priority scheduler, sent 10 messages"); break; case 3: testOk(i == 10, "%d of 10 messages sent with sender pauses", i); break; } epicsThreadSleep(1.0); } /* * Single receiver, multiple sender tests */ testDiag("Single receiver, multiple sender tests:"); testDiag("This test lasts 60 seconds..."); testOk(!!epicsThreadCreate("Sender 1", epicsThreadPriorityLow, mediumStack, sender, q1), "Created Sender 1"); testOk(!!epicsThreadCreate("Sender 2", epicsThreadPriorityMedium, mediumStack, sender, q1), "Created Sender 2"); testOk(!!epicsThreadCreate("Sender 3", epicsThreadPriorityHigh, mediumStack, sender, q1), "Created Sender 3"); testOk(!!epicsThreadCreate("Sender 4", epicsThreadPriorityHigh, mediumStack, sender, q1), "Created Sender 4"); for (i = 0; i < 10; i++) { testDiag("... %2d", 10 - i); epicsThreadSleep(6.0); } sendExit = 1; epicsThreadSleep(1.0); recvExit = 1; testDiag("Scheduler exiting"); } MAIN(epicsMessageQueueTest) { testPlan(62); finished = epicsEventMustCreate(epicsEventEmpty); mediumStack = epicsThreadGetStackSize(epicsThreadStackMedium); epicsThreadCreate("messageQueueTest", epicsThreadPriorityMedium, mediumStack, messageQueueTest, NULL); epicsEventMustWait(finished); testDiag("Main thread signalled"); epicsThreadSleep(1.0); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsMutexTest.cpp0000664000577000060420000002006113557101274021000 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsMutexTest.c */ /* * Author: Marty Kraimer Date: 26JAN2000 * Jeff Hill (added mutex performance test ) */ #include #include #include #include #include #include #include #include "epicsTime.h" #include "epicsThread.h" #include "epicsMutex.h" #include "epicsEvent.h" #include "errlog.h" #include "epicsUnitTest.h" #include "testMain.h" typedef struct info { int threadnum; epicsMutexId mutex; int quit; }info; extern "C" void mutexThread(void * arg) { info *pinfo = (info *)arg; testDiag("mutexThread %d starting", pinfo->threadnum); while (pinfo->quit--) { epicsMutexLockStatus status = epicsMutexLock(pinfo->mutex); testOk(status == epicsMutexLockOK, "mutexThread %d epicsMutexLock returned %d", pinfo->threadnum, (int)status); epicsThreadSleep(.1); epicsMutexUnlock(pinfo->mutex); epicsThreadSleep(.9); } testDiag("mutexThread %d exiting", pinfo->threadnum); return; } inline void lockPair ( epicsMutex & mutex ) { mutex.lock (); mutex.unlock (); } inline void tenLockPairs ( epicsMutex & mutex ) { lockPair ( mutex ); lockPair ( mutex ); lockPair ( mutex ); lockPair ( mutex ); lockPair ( mutex ); lockPair ( mutex ); lockPair ( mutex ); lockPair ( mutex ); lockPair ( mutex ); lockPair ( mutex ); } inline void tenLockPairsSquared ( epicsMutex & mutex ) { tenLockPairs ( mutex ); tenLockPairs ( mutex ); tenLockPairs ( mutex ); tenLockPairs ( mutex ); tenLockPairs ( mutex ); tenLockPairs ( mutex ); tenLockPairs ( mutex ); tenLockPairs ( mutex ); tenLockPairs ( mutex ); tenLockPairs ( mutex ); } inline void doubleRecursiveLockPair ( epicsMutex & mutex ) { mutex.lock (); mutex.lock (); mutex.unlock (); mutex.unlock (); } inline void tenDoubleRecursiveLockPairs ( epicsMutex & mutex ) { doubleRecursiveLockPair ( mutex ); doubleRecursiveLockPair ( mutex ); doubleRecursiveLockPair ( mutex ); doubleRecursiveLockPair ( mutex ); doubleRecursiveLockPair ( mutex ); doubleRecursiveLockPair ( mutex ); doubleRecursiveLockPair ( mutex ); doubleRecursiveLockPair ( mutex ); doubleRecursiveLockPair ( mutex ); doubleRecursiveLockPair ( mutex ); } inline void tenDoubleRecursiveLockPairsSquared ( epicsMutex & mutex ) { tenDoubleRecursiveLockPairs ( mutex ); tenDoubleRecursiveLockPairs ( mutex ); tenDoubleRecursiveLockPairs ( mutex ); tenDoubleRecursiveLockPairs ( mutex ); tenDoubleRecursiveLockPairs ( mutex ); tenDoubleRecursiveLockPairs ( mutex ); tenDoubleRecursiveLockPairs ( mutex ); tenDoubleRecursiveLockPairs ( mutex ); tenDoubleRecursiveLockPairs ( mutex ); tenDoubleRecursiveLockPairs ( mutex ); } inline void quadRecursiveLockPair ( epicsMutex & mutex ) { mutex.lock (); mutex.lock (); mutex.lock (); mutex.lock (); mutex.unlock (); mutex.unlock (); mutex.unlock (); mutex.unlock (); } inline void tenQuadRecursiveLockPairs ( epicsMutex & mutex ) { quadRecursiveLockPair ( mutex ); quadRecursiveLockPair ( mutex ); quadRecursiveLockPair ( mutex ); quadRecursiveLockPair ( mutex ); quadRecursiveLockPair ( mutex ); quadRecursiveLockPair ( mutex ); quadRecursiveLockPair ( mutex ); quadRecursiveLockPair ( mutex ); quadRecursiveLockPair ( mutex ); quadRecursiveLockPair ( mutex ); } inline void tenQuadRecursiveLockPairsSquared ( epicsMutex & mutex ) { tenQuadRecursiveLockPairs ( mutex ); tenQuadRecursiveLockPairs ( mutex ); tenQuadRecursiveLockPairs ( mutex ); tenQuadRecursiveLockPairs ( mutex ); tenQuadRecursiveLockPairs ( mutex ); tenQuadRecursiveLockPairs ( mutex ); tenQuadRecursiveLockPairs ( mutex ); tenQuadRecursiveLockPairs ( mutex ); tenQuadRecursiveLockPairs ( mutex ); tenQuadRecursiveLockPairs ( mutex ); } void epicsMutexPerformance () { epicsMutex mutex; unsigned i; // test a single lock pair epicsTime begin = epicsTime::getMonotonic (); static const unsigned N = 1000; for ( i = 0; i < N; i++ ) { tenLockPairsSquared ( mutex ); } double delay = epicsTime::getMonotonic () - begin; delay /= N * 100u; // convert to delay per lock pair delay *= 1e6; // convert to micro seconds testDiag("lock()*1/unlock()*1 takes %f microseconds", delay); // test a two times recursive lock pair begin = epicsTime::getMonotonic (); for ( i = 0; i < N; i++ ) { tenDoubleRecursiveLockPairsSquared ( mutex ); } delay = epicsTime::getMonotonic () - begin; delay /= N * 100u; // convert to delay per lock pair delay *= 1e6; // convert to micro seconds testDiag("lock()*2/unlock()*2 takes %f microseconds", delay); // test a four times recursive lock pair begin = epicsTime::getMonotonic (); for ( i = 0; i < N; i++ ) { tenQuadRecursiveLockPairsSquared ( mutex ); } delay = epicsTime::getMonotonic () - begin; delay /= N * 100u; // convert to delay per lock pair delay *= 1e6; // convert to micro seconds testDiag("lock()*4/unlock()*4 takes %f microseconds", delay); } struct verifyTryLock { epicsMutexId mutex; epicsEventId done; }; extern "C" void verifyTryLockThread ( void *pArg ) { struct verifyTryLock *pVerify = ( struct verifyTryLock * ) pArg; testOk1(epicsMutexTryLock(pVerify->mutex) == epicsMutexLockTimeout); epicsEventSignal ( pVerify->done ); } void verifyTryLock () { struct verifyTryLock verify; verify.mutex = epicsMutexMustCreate (); verify.done = epicsEventMustCreate ( epicsEventEmpty ); testOk1(epicsMutexTryLock(verify.mutex) == epicsMutexLockOK); epicsThreadCreate ( "verifyTryLockThread", 40, epicsThreadGetStackSize(epicsThreadStackSmall), verifyTryLockThread, &verify ); testOk1(epicsEventWait ( verify.done ) == epicsEventWaitOK); epicsMutexUnlock ( verify.mutex ); epicsMutexDestroy ( verify.mutex ); epicsEventDestroy ( verify.done ); } MAIN(epicsMutexTest) { const int nthreads = 3; const int nrounds = 5; unsigned int stackSize; epicsThreadId *id; int i; char **name; void **arg; info **pinfo; epicsMutexId mutex; int status; testPlan(5 + nthreads * nrounds); verifyTryLock (); mutex = epicsMutexMustCreate(); status = epicsMutexLock(mutex); testOk(status == 0, "epicsMutexLock returned %d", status); status = epicsMutexTryLock(mutex); testOk(status == 0, "epicsMutexTryLock returned %d", status); epicsMutexUnlock(mutex); epicsMutexUnlock(mutex); id = (epicsThreadId *)calloc(nthreads,sizeof(epicsThreadId)); name = (char **)calloc(nthreads,sizeof(char *)); arg = (void **)calloc(nthreads,sizeof(void *)); pinfo = (info **)calloc(nthreads,sizeof(info *)); stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); for(i=0; ithreadnum = i; pinfo[i]->mutex = mutex; pinfo[i]->quit = nrounds; arg[i] = pinfo[i]; id[i] = epicsThreadCreate(name[i],40,stackSize, mutexThread, arg[i]); } epicsThreadSleep(2.0 + nrounds); epicsMutexPerformance (); free(pinfo); free(arg); free(name); free(id); epicsMutexDestroy(mutex); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsRunLibComTests.c0000664000577000060420000000634613557101274021365 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Run libCom tests as a batch. * * Do *not* include performance measurements here, they don't help to * prove functionality (which is the point of this convenience routine). */ #include #include #include int aslibtest(void); int blockingSockTest(void); int epicsAlgorithm(void); int epicsAtomicTest(void); int epicsCalcTest(void); int epicsEllTest(void); int epicsEnvTest(void); int epicsErrlogTest(void); int epicsEventTest(void); int epicsExitTest(void); int epicsMathTest(void); int epicsMessageQueueTest(void); int epicsMMIOTest(void); int epicsMutexTest(void); int epicsSockResolveTest(void); int epicsSpinTest(void); int epicsStackTraceTest(void); int epicsStdioTest(void); int epicsStdlibTest(void); int epicsStringTest(void); int epicsThreadHooksTest(void); int epicsThreadOnceTest(void); int epicsThreadPoolTest(void); int epicsThreadPriorityTest(void); int epicsThreadPrivateTest(void); int epicsThreadTest(void); int epicsTimerTest(void); int epicsTimeTest(void); #ifdef __rtems__ int epicsTimeZoneTest(void); #endif int epicsTypesTest(void); int epicsInlineTest(void); int ipAddrToAsciiTest(void); int macDefExpandTest(void); int macLibTest(void); int osiSockTest(void); int ringBytesTest(void); int ringPointerTest(void); int taskwdTest(void); void epicsRunLibComTests(void) { testHarness(); /* * Thread startup sets some internal variables so do it first */ runTest(epicsThreadTest); /* * Timer tests get confused if run after some of the other tests */ runTest(epicsTimerTest); /* * Run the regular tests in alphabetical order */ runTest(aslibtest); runTest(blockingSockTest); runTest(epicsAlgorithm); runTest(epicsAtomicTest); runTest(epicsCalcTest); runTest(epicsEllTest); runTest(epicsEnvTest); runTest(epicsErrlogTest); runTest(epicsEventTest); runTest(epicsInlineTest); runTest(epicsMathTest); runTest(epicsMessageQueueTest); runTest(epicsMMIOTest); runTest(epicsMutexTest); runTest(epicsSockResolveTest); runTest(epicsSpinTest); runTest(epicsStackTraceTest); runTest(epicsStdioTest); runTest(epicsStdlibTest); runTest(epicsStringTest); runTest(epicsThreadHooksTest); runTest(epicsThreadOnceTest); runTest(epicsThreadPoolTest); runTest(epicsThreadPriorityTest); runTest(epicsThreadPrivateTest); runTest(epicsTimeTest); #ifdef __rtems__ runTest(epicsTimeZoneTest); #endif runTest(epicsTypesTest); runTest(ipAddrToAsciiTest); runTest(macDefExpandTest); runTest(macLibTest); runTest(osiSockTest); runTest(ringBytesTest); runTest(ringPointerTest); runTest(taskwdTest); /* * Report now in case epicsExitTest dies */ testHarnessDone(); /* * epicsExitTest must come last as it never returns */ runTest(epicsExitTest); } base-7.0.3.1/modules/libcom/test/epicsSockResolveTest.c0000664000577000060420000000512413557101274021600 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 Brookhaven Science Associates as Operator of * Brookhaven National Lab. * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. \*************************************************************************/ #include "dbDefs.h" #include "osiSock.h" #include "epicsUnitTest.h" #include "testMain.h" #define DEFAULT_PORT 4000 typedef struct { const char *input; unsigned long IP; unsigned short port; } testData; static testData okdata[] = { {"127.0.0.1", 0x7f000001, DEFAULT_PORT}, {"127.0.0.1:42", 0x7f000001, 42}, {"localhost", 0x7f000001, DEFAULT_PORT}, {"localhost:42", 0x7f000001, 42}, {"2424", 2424, DEFAULT_PORT}, {"2424:42", 2424, 42}, {"255.255.255.255", 0xffffffff, DEFAULT_PORT}, {"255.255.255.255:65535", 0xffffffff, 65535}, }; static const char * baddata[] = { "127.0.0.1:NaN", "127.0.0.test", "127.0.0.test:42", "16name.invalid", "16name.invalid:42", "1.2.3.4.5", "1.2.3.4.5:6", "1.2.3.4:5.6", "256.255.255.255", "255.256.255.255", "255.255.256.255", "255.255.255.256", "255.255.255.255:65536", }; MAIN(epicsSockResolveTest) { int i; testPlan(3*NELEMENTS(okdata) + NELEMENTS(baddata)); osiSockAttach(); { struct in_addr addr; /* See RFCs 2606 and 6761 */ if (hostToIPAddr("guaranteed.invalid.", &addr) == 0) { testAbort("hostToIPAddr() is broken, testing not possible"); } } testDiag("Tests of aToIPAddr"); for (i=0; i %d", okdata[i].input, DEFAULT_PORT, ret); if (ret) { testSkip(2, " aToIPAddr() failed"); } else { testOk(addr.sin_addr.s_addr == htonl(okdata[i].IP), " IP correct"); testOk(addr.sin_port == htons(okdata[i].port), " Port correct"); } } for (i=0; i %d", baddata[i], DEFAULT_PORT, ret); if (ret==0) { testDiag(" IP=0x%lx, port=%d", (unsigned long) ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port)); } } osiSockRelease(); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsSpinTest.c0000664000577000060420000001414413557101274020254 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 Helmholtz-Zentrum Berlin * fuer Materialien und Energie GmbH. * Copyright (c) 2012 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Ralph Lange * * based on epicsMutexTest by Marty Kraimer and Jeff Hill * */ #include #include #include #include #include #include #include "epicsTime.h" #include "epicsThread.h" #include "epicsAtomic.h" #include "epicsSpin.h" #include "epicsEvent.h" #include "errlog.h" #include "epicsUnitTest.h" #include "testMain.h" typedef struct info { int threadnum; epicsSpinId spin; int *counter; int rounds; epicsEventId done; } info; #define spinDelay 0.016667 void spinThread(void *arg) { info *pinfo = (info *) arg; testDiag("spinThread %d starting", pinfo->threadnum); epicsThreadSleep(0.1); /* Try to align threads */ while (pinfo->rounds--) { epicsThreadSleep(spinDelay); epicsSpinLock(pinfo->spin); pinfo->counter[0]++; epicsSpinUnlock(pinfo->spin); } testDiag("spinThread %d exiting", pinfo->threadnum); epicsEventSignal(pinfo->done); } static void lockPair(struct epicsSpin *spin) { epicsSpinLock(spin); epicsSpinUnlock(spin); } static void tenLockPairs(struct epicsSpin *spin) { lockPair(spin); lockPair(spin); lockPair(spin); lockPair(spin); lockPair(spin); lockPair(spin); lockPair(spin); lockPair(spin); lockPair(spin); lockPair(spin); } static void tenLockPairsSquared(struct epicsSpin *spin) { tenLockPairs(spin); tenLockPairs(spin); tenLockPairs(spin); tenLockPairs(spin); tenLockPairs(spin); tenLockPairs(spin); tenLockPairs(spin); tenLockPairs(spin); tenLockPairs(spin); tenLockPairs(spin); } void epicsSpinPerformance () { static const unsigned N = 10000; unsigned i; epicsSpinId spin; epicsTimeStamp begin; epicsTimeStamp end; double delay; /* Initialize spinlock */ spin = epicsSpinCreate(); if (!spin) testAbort("epicsSpinCreate() returned NULL"); /* test a single lock pair */ epicsTimeGetCurrent(&begin); for ( i = 0; i < N; i++ ) { tenLockPairsSquared(spin); } epicsTimeGetCurrent(&end); delay = epicsTimeDiffInSeconds(&end, &begin); delay /= N * 100u; /* convert to delay per lock pair */ delay *= 1e6; /* convert to micro seconds */ testDiag("lock()*1/unlock()*1 takes %f microseconds", delay); epicsSpinDestroy(spin); } struct verifyTryLock; struct verifyTryLockEnt { epicsEventId done; struct verifyTryLock *main; }; struct verifyTryLock { epicsSpinId spin; int flag; struct verifyTryLockEnt *ents; }; static void verifyTryLockThread(void *pArg) { struct verifyTryLockEnt *pVerify = (struct verifyTryLockEnt *) pArg; while(epicsAtomicGetIntT(&pVerify->main->flag)==0) { int ret = epicsSpinTryLock(pVerify->main->spin); if(ret!=0) { epicsAtomicCmpAndSwapIntT(&pVerify->main->flag, 0, ret); break; } else epicsSpinUnlock(pVerify->main->spin); } epicsEventSignal(pVerify->done); } /* Start one thread per CPU which will all try lock * the same spinlock. They break as soon as one * fails to take the lock. */ static void verifyTryLock() { int N, i; struct verifyTryLock verify; N = epicsThreadGetCPUs(); if(N==1) { testSkip(1, "verifyTryLock() only for SMP systems"); return; } verify.flag = 0; verify.spin = epicsSpinMustCreate(); testDiag("Starting %d spinners", N); verify.ents = calloc(N, sizeof(*verify.ents)); for(i=0; ithreadnum = i; pinfo[i]->spin = spin; pinfo[i]->counter = &counter; pinfo[i]->rounds = nrounds; pinfo[i]->done = epicsEventMustCreate(epicsEventEmpty); id[i] = epicsThreadCreate(name[i], 40, stackSize, spinThread, pinfo[i]); } for (i = 0; i < nthreads; i++) { epicsEventMustWait(pinfo[i]->done); epicsEventDestroy(pinfo[i]->done); free(name[i]); free(pinfo[i]); } testOk(counter == nthreads * nrounds, "Loops run = %d (expecting %d)", counter, nthreads * nrounds); free(pinfo); free(name); free(id); epicsSpinDestroy(spin); epicsSpinPerformance(); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsStackTraceTest.c0000664000577000060420000001340213557101274021363 0ustar anjaesctl/* * Copyright: Stanford University / SLAC National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. * * Author: Till Straumann , 2014 */ /* * Check stack trace functionality */ #include "epicsStackTrace.h" #include "errlog.h" #include "epicsUnitTest.h" #include "testMain.h" #include #include #include #define TST_BUFSZ 10000 #define MAXP 10 /* estimated size of (compiled) epicsStackTraceRecurseGbl */ #define WINDOW_SZ 400 typedef struct TestDataRec_ { char buf[TST_BUFSZ]; int pos; } TestDataRec, *TestData; typedef void (*RecFn)(int); /* We want a stack trace and need a few nested routines. * The whole magic here is intended to prevent a compiler * from optimizing the call stack away: * - call via a volatile pointer * - add a call to a no-op function at the end so that * tail-call optimization doesn't eliminate the call * stack. * * We use a local (static) and a global routine to test * if the stacktrace supports either flavor. */ void epicsStackTraceRecurseGbl(int lvl); static void epicsStackTraceRecurseLcl(int lvl); void nopFn(int lvl) { } RecFn volatile lfp = epicsStackTraceRecurseLcl; RecFn volatile gfp = epicsStackTraceRecurseGbl; RecFn volatile nfp = nopFn; static void epicsStackTraceRecurseLcl(int lvl) { if ( lvl ) gfp(lvl-1); else epicsStackTrace(); /* call something so that the call through gfp() doesn't * get optimized into a jump (tail-call optimization) */ nfp(0); } void epicsStackTraceRecurseGbl(int lvl) { if ( lvl ) lfp(lvl-1); else epicsStackTrace(); /* call something so that the call through gfp() doesn't * get optimized into a jump (tail-call optimization) */ nfp(0); } static void logClient(void *ptr, const char *msg) { TestData td = ptr; size_t sz = strlen(msg); size_t mx = sizeof(td->buf) - td->pos - 1; if ( sz > mx ) sz = mx; strncpy( td->buf+td->pos, msg, sz ); td->pos += sz; } static int findStringOcc(const char *buf, const char *what) { int rval = 0; size_t l = strlen(what); int ch; while ( (buf = strstr(buf, what)) ) { /* Is it just a prefix? */ ch = buf[l]; if ( ! isalnum(ch) && '_' != ch && '.' != ch ) { rval++; } buf += l; } testDiag("found %i x %s\n", rval, what); return rval; } static int findNumOcc(const char *buf) { void *ptrs[MAXP]; int n_ptrs = 0; int i,j; int rval = 0; while ( n_ptrs < sizeof(ptrs)/sizeof(ptrs[0]) && (buf=strchr(buf,'[')) ) { if ( 1 == sscanf(buf+1,"%p", &ptrs[n_ptrs]) ) n_ptrs++; buf++; } /* We should find an address close to epicsStackTraceRecurseGbl twice */ for (i=0; i= (char*)epicsStackTraceRecurseGbl && (char*)ptrs[i] < (char*)epicsStackTraceRecurseGbl + WINDOW_SZ ) { rval ++; testDiag("found address %p again\n", ptrs[i]); } } j++; } } return rval; } MAIN(epicsStackTraceTest) { int features, all_features; TestDataRec testData; int gblFound, lclFound, numFound, dynFound; char *nl, *p; testData.pos = 0; testPlan(5); features = epicsStackTraceGetFeatures(); all_features = EPICS_STACKTRACE_LCL_SYMBOLS | EPICS_STACKTRACE_GBL_SYMBOLS | EPICS_STACKTRACE_DYN_SYMBOLS | EPICS_STACKTRACE_ADDRESSES; if ( ! testOk( (features & ~all_features) == 0, "epicsStackTraceGetFeatures() obtains features") ) testAbort("epicsStackTraceGetFeatures() not working as expected"); testData.pos = 0; testDiag("calling a few nested routines and eventually dump a stack trace"); eltc(0); errlogAddListener( logClient, &testData ); gfp(3); errlogRemoveListeners( logClient, &testData ); eltc(1); /* ensure there's a terminating NUL -- we have reserved space for it */ testData.buf[testData.pos] = 0; testDiag("now scan the result for what we expect"); dynFound = findStringOcc( testData.buf, "epicsStackTrace" ); gblFound = findStringOcc( testData.buf, "epicsStackTraceRecurseGbl" ); lclFound = findStringOcc( testData.buf, "epicsStackTraceRecurseLcl" ); numFound = findNumOcc ( testData.buf ); if ( (features & EPICS_STACKTRACE_DYN_SYMBOLS) ) { testOk( dynFound == 1, "dumping symbol from library" ); } else { testSkip(1, "no support for dumping library symbols on this platform"); } if ( (features & EPICS_STACKTRACE_GBL_SYMBOLS) ) { testOk( gblFound == 2, "dumping global symbols" ); } else { testSkip(1, "no support for dumping global symbols on this platform"); } if ( (features & EPICS_STACKTRACE_LCL_SYMBOLS) ) { testOk( lclFound == 2, "dumping local symbols" ); } else { testSkip(1, "no support for dumping local symbols on this platform"); } if ( (features & EPICS_STACKTRACE_ADDRESSES) ) { testOk( numFound > 0, "dumping addresses" ); } else { testSkip(1 , "no support for dumping addresses on this platform"); } p = testData.buf; while ( (nl = strchr(p,'\n')) ) { *nl = 0; testDiag("%s",p); *nl = '\n'; p = nl+1; } testDiag("%s", p); testDone(); return 0; } base-7.0.3.1/modules/libcom/test/epicsStdioTest.c0000664000577000060420000001011213557101274020414 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsStdioTest.c * * Author Marty Kraimer */ #include #include #include #include #include #include #include #include #include "epicsStdio.h" #include "epicsUnitTest.h" #include "testMain.h" #define LINE_1 "# This is first line of sample report\n" #define LINE_2 "# This is second and last line of sample report\n" static void testEpicsSnprintf(void) { char exbuffer[80], buffer[80]; const int ivalue = 1234; const float fvalue = 1.23e4f; const char *svalue = "OneTwoThreeFour"; const char *format = "int %d float %8.2e string %s"; const char *expected = exbuffer; int size; int rtn, rlen; #ifdef _WIN32 #if (defined(_MSC_VER) && _MSC_VER < 1900) || \ (defined(_MINGW) && defined(_TWO_DIGIT_EXPONENT)) _set_output_format(_TWO_DIGIT_EXPONENT); #endif #endif sprintf(exbuffer, format, ivalue, fvalue, svalue); rlen = strlen(expected)+1; strcpy(buffer, "AAAA"); for (size = 1; size < strlen(expected) + 5; ++size) { rtn = epicsSnprintf(buffer, size, format, ivalue, fvalue, svalue); testOk(rtn <= rlen-1, "epicsSnprintf(size=%d) = %d", size, rtn); if (rtn != rlen-1) testDiag("Return value does not indicate buffer size needed"); testOk(strncmp(buffer, expected, size - 1) == 0, "buffer = '%s'", buffer); rtn = strlen(buffer); testOk(rtn == (size < rlen ? size : rlen) - 1, "length = %d", rtn); } } void testStdoutRedir (const char *report) { FILE *realStdout = stdout; FILE *stream = 0; char linebuf[80]; size_t buflen = sizeof linebuf; testOk1(epicsGetStdout() == stdout); errno = 0; if (!testOk1((stream = fopen(report, "w")) != NULL)) { testDiag("'%s' could not be opened for writing: %s", report, strerror(errno)); testSkip(11, "Can't create stream file"); return; } epicsSetThreadStdout(stream); testOk1(stdout == stream); printf(LINE_1); printf(LINE_2); epicsSetThreadStdout(0); testOk1(epicsGetStdout() == realStdout); testOk1(stdout == realStdout); errno = 0; if (!testOk1(!fclose(stream))) { testDiag("fclose error: %s", strerror(errno)); #ifdef vxWorks testDiag("The above test fails if you don't cd to a writable directory"); testDiag("before running the test. The next test will also fail..."); #endif } if (!testOk1((stream = fopen(report, "r")) != NULL)) { testDiag("'%s' could not be opened for reading: %s", report, strerror(errno)); testSkip(6, "Can't reopen stream file."); return; } if (!testOk1(fgets(linebuf, buflen, stream) != NULL)) { testDiag("File read error: %s", strerror(errno)); testSkip(5, "Read failed."); fclose(stream); return; } testOk(strcmp(linebuf, LINE_1) == 0, "First line correct"); if (!testOk1(fgets(linebuf, buflen, stream) != NULL)) { testDiag("File read error: %s", strerror(errno)); testSkip(1, "No line to compare."); } else testOk(strcmp(linebuf, LINE_2) == 0, "Second line"); testOk(!fgets(linebuf, buflen, stream), "File ends"); if (!testOk1(!fclose(stream))) testDiag("fclose error: %s\n", strerror(errno)); } MAIN(epicsStdioTest) { testPlan(163); testEpicsSnprintf(); #ifdef __rtems__ /* ensure there is a writeable area */ mkdir( "/tmp", S_IRWXU ); testStdoutRedir("/tmp/report"); #else testStdoutRedir("report"); #endif return testDone(); } base-7.0.3.1/modules/libcom/test/epicsStdlibTest.c0000664000577000060420000006204613557101274020570 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsStdlibTest.c * * Author Andrew Johnson */ #include #include #include #include #include "epicsTypes.h" #include "epicsStdlib.h" #include "epicsMath.h" #include "epicsUnitTest.h" #include "testMain.h" /* Implement the epicsParseDouble() API for checking the native stdtod() * so we can tell the user if our epicsStrtod() wrapper is unnecessary. */ int parseStrtod(const char *str, double *to, char **units) { int c; char *endp; double value; while ((c = *str) && isspace(c)) ++str; errno = 0; value = strtod(str, &endp); if (endp == str) return S_stdlib_noConversion; if (errno == ERANGE) return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow; while ((c = *endp) && isspace(c)) ++endp; if (c && !units) return S_stdlib_extraneous; *to = value; if (units) *units = endp; return 0; } #define scanStrtod(str, to) !parseStrtod(str, to, NULL) MAIN(epicsStdlibTest) { unsigned long u; long l; unsigned long long ull; long long ll; double d; float f; char *endp; epicsInt8 i8; epicsUInt8 u8; epicsInt16 i16; epicsUInt16 u16; epicsInt32 i32; epicsUInt32 u32; epicsInt64 i64; epicsUInt64 u64; testPlan(199); testOk(epicsParseLong("", &l, 0, NULL) == S_stdlib_noConversion, "Long '' => noConversion"); testOk(epicsParseULong("", &u, 0, NULL) == S_stdlib_noConversion, "ULong '' => noConversion"); testOk(epicsParseLLong("", &ll, 0, NULL) == S_stdlib_noConversion, "LLong '' => noConversion"); testOk(epicsParseULLong("", &ull, 0, NULL) == S_stdlib_noConversion, "ULLong '' => noConversion"); testOk(epicsParseFloat("", &f, NULL) == S_stdlib_noConversion, "Float '' => noConversion"); testOk(epicsParseDouble("", &d, NULL) == S_stdlib_noConversion, "Double '' => noConversion"); testOk(epicsParseLong("\t \n", &l, 0, NULL) == S_stdlib_noConversion, "Long '\\t 1\\n' => noConversion"); testOk(epicsParseULong("\t \n", &u, 0, NULL) == S_stdlib_noConversion, "ULong '\\t 1\\n' => noConversion"); testOk(epicsParseLLong("\t \n", &ll, 0, NULL) == S_stdlib_noConversion, "LLong '\\t 1\\n' => noConversion"); testOk(epicsParseULLong("\t \n", &ull, 0, NULL) == S_stdlib_noConversion, "ULLong '\\t 1\\n' => noConversion"); testOk(epicsParseFloat("\t \n", &f, NULL) == S_stdlib_noConversion, "Float '\\t 1\\n' => noConversion"); testOk(epicsParseDouble("\t \n", &d, NULL) == S_stdlib_noConversion, "Double '\\t 1\\n' => noConversion"); testOk(epicsParseLong("!", &l, 0, NULL) == S_stdlib_noConversion, "Long '!' => noConversion"); testOk(epicsParseULong("!", &u, 0, NULL) == S_stdlib_noConversion, "ULong '!' => noConversion"); testOk(epicsParseLLong("!", &ll, 0, NULL) == S_stdlib_noConversion, "LLong '!' => noConversion"); testOk(epicsParseULLong("!", &ull, 0, NULL) == S_stdlib_noConversion, "ULLong '!' => noConversion"); testOk(epicsParseFloat("!", &f, NULL) == S_stdlib_noConversion, "Float '!' => noConversion"); testOk(epicsParseDouble("!", &d, NULL) == S_stdlib_noConversion, "Double '!' => noConversion"); testOk(epicsScanLong("0", &l, 0) && l == 0, "Long '0'"); testOk(epicsScanULong("0", &u, 0) && u == 0, "ULong '0'"); testOk(epicsScanLLong("0", &ll, 0) && ll == 0, "LLong '0'"); testOk(epicsScanULLong("0", &ull, 0) && ull == 0, "ULLong '0'"); testOk(epicsScanFloat("0", &f) && f == 0, "Float '0'"); testOk(epicsScanDouble("0", &d) && d == 0, "Double '0'"); testOk(epicsScanLong("\t 1\n", &l, 0) && l == 1, "Long '\\t 1\\n'"); testOk(epicsScanULong("\t 1\n", &u, 0) && u == 1, "ULong '\\t 1\\n'"); testOk(epicsScanLLong("\t 1\n", &ll, 0) && ll == 1, "LLong '\\t 1\\n'"); testOk(epicsScanULLong("\t 1\n", &ull, 0) && ull == 1, "ULLong '\\t 1\\n'"); testOk(epicsScanFloat("\t 1\n", &f) && f == 1, "Float '\\t 1\\n'"); testOk(epicsScanDouble("\t 1\n", &d) && d == 1, "Double '\\t 1\\n'"); testOk(epicsScanLong("-1", &l, 0) && l == -1, "Long '-1'"); testOk(epicsScanULong("-1", &u, 0) && u + 1 == 0, "ULong '-1'"); testOk(epicsScanLLong("-1", &ll, 0) && ll == -1, "LLong '-1'"); testOk(epicsScanULLong("-1", &ull, 0) && ull + 1 == 0, "ULLong '-1'"); testOk(epicsScanFloat("-1", &f) && f == -1, "Float '-1'"); testOk(epicsScanDouble("-1", &d) && d == -1, "Double '-1'"); testOk(epicsParseLong("2!", &l, 0, NULL) == S_stdlib_extraneous, "Long '2!' => extraneous"); testOk(epicsParseULong("2!", &u, 0, NULL) == S_stdlib_extraneous, "ULong '2!' => extraneous"); testOk(epicsParseLLong("2!", &ll, 0, NULL) == S_stdlib_extraneous, "LLong '2!' => extraneous"); testOk(epicsParseULLong("2!", &ull, 0, NULL) == S_stdlib_extraneous, "ULLong '2!' => extraneous"); testOk(epicsParseFloat("2!", &f, NULL) == S_stdlib_extraneous, "Float '2!' => extraneous"); testOk(epicsParseDouble("2!", &d, NULL) == S_stdlib_extraneous, "Double '2!' => extraneous"); testOk(epicsParseLong("3 \n\t!", &l, 0, NULL) == S_stdlib_extraneous, "Long '3 \\n\\t!' => extraneous"); testOk(epicsParseULong("3 \n\t!", &u, 0, NULL) == S_stdlib_extraneous, "ULong '3 \\n\\t!' => extraneous"); testOk(epicsParseLLong("3 \n\t!", &ll, 0, NULL) == S_stdlib_extraneous, "LLong '3 \\n\\t!' => extraneous"); testOk(epicsParseULLong("3 \n\t!", &ull, 0, NULL) == S_stdlib_extraneous, "ULLong '3 \\n\\t!' => extraneous"); testOk(epicsParseFloat("3 \n\t!", &f, NULL) == S_stdlib_extraneous, "Float '3 \\n\\t!' => extraneous"); testOk(epicsParseDouble("3 \n\t!", &d, NULL) == S_stdlib_extraneous, "Double '3 \\n\\t!' => extraneous"); testOk(!epicsParseLong("2!", &l, 0, &endp) && *endp == '!', "Long '2!' => units='!'"); testOk(!epicsParseULong("2!", &u, 0, &endp) && *endp == '!', "ULong '2!' => units='!'"); testOk(!epicsParseLLong("2!", &ll, 0, &endp) && *endp == '!', "LLong '2!' => units='!'"); testOk(!epicsParseULLong("2!", &ull, 0, &endp) && *endp == '!', "ULLong '2!' => units='!'"); testOk(!epicsParseFloat("2!", &f, &endp) && *endp == '!', "Float '2!' => units='!'"); testOk(!epicsParseDouble("2!", &d, &endp) && *endp == '!', "Double '2!' => units='!'"); testOk(!epicsParseLong("3 \n\t!", &l, 0, &endp) && *endp == '!', "Long '3 \\n\\t!' => units='!'"); testOk(!epicsParseULong("3 \n\t!", &u, 0, &endp) && *endp == '!', "ULong '3 \\n\\t!' => units='!'"); testOk(!epicsParseLLong("3 \n\t!", &ll, 0, &endp) && *endp == '!', "LLong '3 \\n\\t!' => units='!'"); testOk(!epicsParseULLong("3 \n\t!", &ull, 0, &endp) && *endp == '!', "ULLong '3 \\n\\t!' => units='!'"); testOk(!epicsParseFloat("3 \n\t!", &f, &endp) && *endp == '!', "Float '3 \\n\\t!' => units='!'"); testOk(!epicsParseDouble("3 \n\t!", &d, &endp) && *endp == '!', "Double '3 \\n\\t!' => units='!'"); testOk(epicsScanLong("0x0", &l, 0) && l == 0, "Long '0x0'"); testOk(epicsScanULong("0x0", &u, 0) && u == 0, "ULong '0x0'"); testOk(epicsScanLLong("0x0", &ll, 0) && ll == 0, "LLong '0x0'"); testOk(epicsScanULLong("0x0", &ull, 0) && ull == 0, "ULLong '0x0'"); testOk(epicsScanFloat("0x0", &f) && f == 0, "Float '0x0'"); testOk(epicsScanDouble("0x0", &d) && d == 0, "Double '0x0'"); testOk(epicsScanLong("0x1", &l, 0) && l == 1, "Long '0x1'"); testOk(epicsScanULong("0x1", &u, 0) && u == 1, "ULong '0x1'"); testOk(epicsScanLLong("0x1", &ll, 0) && ll == 1, "LLong '0x1'"); testOk(epicsScanULLong("0x1", &ull, 0) && ull == 1, "ULLong '0x1'"); testOk(epicsScanFloat("0x1", &f) && f == 1, "Float '0x1'"); testOk(epicsScanDouble("0x1", &d) && d == 1, "Double '0x1'"); testOk(epicsScanLong("+0x1", &l, 0) && l == 1, "Long '+0x1'"); testOk(epicsScanULong("+0x1", &u, 0) && u == 1, "ULong '+0x1'"); testOk(epicsScanLLong("+0x1", &ll, 0) && ll == 1, "LLong '+0x1'"); testOk(epicsScanULLong("+0x1", &ull, 0) && ull == 1, "ULLong '+0x1'"); testOk(epicsScanFloat("+0x1", &f) && f == 1, "Float '+0x1'"); testOk(epicsScanDouble("+0x1", &d) && d == 1, "Double '+0x1'"); testOk(epicsScanLong("-0x1", &l, 0) && l == -1, "Long '-0x1'"); testOk(epicsScanULong("-0x1", &u, 0) && u == -1, "ULong '-0x1'"); testOk(epicsScanLLong("-0x1", &ll, 0) && ll == -1, "LLong '-0x1'"); testOk(epicsScanULLong("-0x1", &ull, 0) && ull == -1, "ULLong '-0x1'"); testOk(epicsScanFloat("-0x1", &f) && f == -1, "Float '-0x1'"); testOk(epicsScanDouble("-0x1", &d) && d == -1, "Double '-0x1'"); testOk(epicsScanLong("0xf", &l, 0) && l == 15, "Long '0xf'"); testOk(epicsScanULong("0xf", &u, 0) && u == 15, "ULong '0xf'"); testOk(epicsScanLLong("0xf", &ll, 0) && ll == 15, "LLong '0xf'"); testOk(epicsScanULLong("0xf", &ull, 0) && ull == 15, "ULLong '0xf'"); testOk(epicsScanFloat("0xf", &f) && f == 15, "Float '0xf'"); testOk(epicsScanDouble("0xf", &d) && d == 15, "Double '0xf'"); testOk(epicsScanLong("0XF", &l, 0) && l == 15, "Long '0XF'"); testOk(epicsScanULong("0XF", &u, 0) && u == 15, "ULong '0XF'"); testOk(epicsScanLLong("0XF", &ll, 0) && ll == 15, "LLong '0XF'"); testOk(epicsScanULLong("0XF", &ull, 0) && ull == 15, "ULLong '0XF'"); testOk(epicsScanFloat("0XF", &f) && f == 15, "Float '0XF'"); testOk(epicsScanDouble("0XF", &d) && d == 15, "Double '0XF'"); testOk(epicsParseLong("0x0", &l, 10, NULL) == S_stdlib_extraneous, "Long '0x0' in base 10 => extraneous"); testOk(epicsParseULong("0x0", &u, 10, NULL) == S_stdlib_extraneous, "ULong '0x0' in base 10 => extraneous"); testOk(epicsParseLLong("0x0", &ll, 10, NULL) == S_stdlib_extraneous, "LLong '0x0' in base 10 => extraneous"); testOk(epicsParseULLong("0x0", &ull, 10, NULL) == S_stdlib_extraneous, "ULLong '0x0' in base 10 => extraneous"); testOk(epicsScanLong("0x10", &l, 0) && l == 0x10, "Long '0x10' in base 0"); testOk(epicsScanULong("0x10", &u, 0) && u == 0x10, "ULong '0x10' in base 0"); testOk(epicsScanLong("0x10", &l, 16) && l == 0x10, "Long '0x10' in base 16"); testOk(epicsScanULong("0x10", &u, 16) && u == 0x10, "ULong '0x10' in base 16"); testOk(epicsScanLong("10", &l, 16) && l == 0x10, "Long '10' in base 16"); testOk(epicsScanULong("10", &u, 16) && u == 0x10, "ULong '10' in base 16"); testOk(epicsScanLong("0x7fffffff", &l, 0) && l == 0x7fffffff, "Long '0x7fffffff'"); testOk(epicsScanULong("0xffffffff", &u, 0) && u == 0xffffffff, "ULong '0xffffffff'"); testOk(epicsScanLLong("0x7fffffffffffffff", &ll, 0) && ll == 0x7fffffffffffffffLL, "LLong '0x7fffffffffffffff'"); testOk(epicsScanULLong("0xffffffffffffffff", &ull, 0) && ull == 0xffffffffffffffffULL, "ULLong '0xffffffffffffffff'"); testOk(epicsScanFloat("0xffffff", &f) && f == 0xffffff, "Float '0xffffff'"); testOk(epicsScanDouble("0xffffffff", &d) && d == 0xffffffff, "Double '0xffffffff'"); testOk(epicsScanLong("-0x7fffffff", &l, 0) && l == -0x7fffffff, "Long '-0x7fffffff'"); testOk(epicsScanULong("-0x7fffffff", &u, 0) && u == -0x7fffffff, "ULong '-0x7fffffff'"); testOk(epicsScanLLong("-0x7fffffffffffffff", &ll, 0) && ll == -0x7fffffffffffffffLL, "LLong '-0x7fffffffffffffff'"); testOk(epicsScanULLong("-0x7fffffffffffffff", &ull, 0) && ull == -0x7fffffffffffffffULL, "ULLong '-0x7fffffffffffffff'"); testOk(epicsScanFloat("-0xffffff", &f) && f == -0xffffff, "Float '-0xffffff'"); testOk(epicsScanDouble("-0x7fffffff", &d) && d == -0x7fffffff, "Double '-0x7fffffff'"); testOk(!epicsParseInt8("0x7f", &i8, 0, NULL) && i8 == 0x7f, "Int8 '0x7f'"); testOk(!epicsParseInt8("-0x80", &i8, 0, NULL) && ((i8 + 0x80) & 0xff) == 0, "Int8 '-0x80'"); testOk(!epicsParseUInt8("0xff", &u8, 0, NULL) && u8 == 0xff, "UInt8 '0xff'"); testOk(!epicsParseUInt8("-1", &u8, 0, NULL) && u8 == 0xff, "UInt8 '-1'"); testOk(epicsParseInt8("0x80", &i8, 0, NULL) == S_stdlib_overflow, "Int8 '0x80' => overflow"); testOk(epicsParseInt8("-0x81", &i8, 0, NULL) == S_stdlib_overflow, "Int8 '-0x81' => overflow"); testOk(epicsParseUInt8("0x100", &u8, 0, NULL) == S_stdlib_overflow, "UInt8 '0x100' => overflow"); testOk(epicsParseUInt8("-0x100", &u8, 0, NULL) == S_stdlib_overflow, "UInt8 '-0x100' => overflow"); testOk(!epicsParseInt16("0x7fff", &i16, 0, NULL) && i16 == 0x7fff, "Int16 '0x7fff'"); testOk(!epicsParseInt16("-0x8000", &i16, 0, NULL) && i16 == -0x8000, "Int16 '-0x8000'"); testOk(!epicsParseUInt16("0xffff", &u16, 0, NULL) && u16 == 0xffff, "UInt16 '0xffff'"); testOk(!epicsParseUInt16("-1", &u16, 0, NULL) && u16 == 0xffff, "UInt16 '-1'"); testOk(epicsParseInt16("0x8000", &i16, 0, NULL) == S_stdlib_overflow, "Int16 '0x8000' => overflow"); testOk(epicsParseInt16("-0x8001", &i16, 0, NULL) == S_stdlib_overflow, "Int16 '-0x8001' => overflow"); testOk(epicsParseUInt16("0x10000", &u16, 0, NULL) == S_stdlib_overflow, "UInt16 '0x10000' => overflow"); testOk(epicsParseUInt16("-0x10000", &u16, 0, NULL) == S_stdlib_overflow, "UInt16 '-0x10000' => overflow"); testOk(!epicsParseInt32("0x7fffffff", &i32, 0, NULL) && i32 == 0x7fffffff, "Int32 '0x7fffffff'"); testOk(!epicsParseInt32("-0x80000000", &i32, 0, NULL) && i32 == -0x80000000L, "Int32 '-0x80000000'"); testOk(!epicsParseUInt32("0xffffffff", &u32, 0, NULL) && u32 == 0xffffffff, "UInt32 '0xffffffff'"); testOk(!epicsParseUInt32("-1", &u32, 0, NULL) && u32 == 0xffffffffU, "UInt32 '-1'"); testOk(epicsParseInt32("0x80000000", &i32, 0, NULL) == S_stdlib_overflow, "Int32 '0x80000000' => overflow"); testOk(epicsParseInt32("-0x80000001", &i32, 0, NULL) == S_stdlib_overflow, "Int32 '-0x80000001' => overflow"); testOk(epicsParseUInt32("0x100000000", &u32, 0, NULL) == S_stdlib_overflow, "UInt32 '0x100000000' => overflow"); testOk(epicsParseUInt32("-0x100000000", &u32, 0, NULL) == S_stdlib_overflow, "UInt32 '-0x100000000' => overflow"); testOk(!epicsParseInt64("0x7fffffffffffffff", &i64, 0, NULL) && i64 == 0x7fffffffffffffffLL, "Int64 '0x7fffffffffffffff'"); testOk(!epicsParseInt64("-0x8000000000000000", &i64, 0, NULL) && i64 == -0x8000000000000000LL, "Int64 '-0x8000000000000000'"); testOk(!epicsParseUInt64("0xffffffffffffffff", &u64, 0, NULL) && u64 == 0xffffffffffffffffULL, "UInt64 '0xffffffffffffffff'"); testOk(!epicsParseUInt64("-1", &u64, 0, NULL) && u64 == 0xffffffffffffffffULL, "UInt64 '-1'"); testOk(epicsParseInt64("0x8000000000000000", &i64, 0, NULL) == S_stdlib_overflow, "Int64 '0x8000000000000000' => overflow"); testOk(epicsParseInt64("-0x8000000000000001", &i64, 0, NULL) == S_stdlib_overflow, "Int64 '-0x8000000000000001' => overflow"); testOk(epicsParseUInt64("0x10000000000000000", &u64, 0, NULL) == S_stdlib_overflow, "UInt64 '0x10000000000000000' => overflow"); testOk(epicsParseUInt64("-0x10000000000000000", &u64, 0, NULL) == S_stdlib_overflow, "UInt64 '-0x10000000000000000' => overflow"); testOk(epicsScanFloat(".1", &f) && fabs(f - 0.1) < 1e-7, "Float '.1'"); testOk(epicsScanDouble(".1", &d) && fabs(d - 0.1) < 1e-15, "Double '.1'"); testOk(epicsScanFloat("0.1", &f) && fabs(f - 0.1) < 1e-7, "Float '0.1'"); testOk(epicsScanDouble("0.1", &d) && fabs(d - 0.1) < 1e-15, "Double '0.1'"); testOk(epicsScanFloat("1e-1", &f) && fabs(f - 1e-1) < 1e-7, "Float '1e-1'"); testOk(epicsScanDouble("1e-1", &d) && fabs(d - 1e-1) < 1e-15, "Double '1e-1'"); testOk(epicsScanFloat("-.1", &f) && fabs(f + 0.1) < 1e-7, "Float '-.1'"); testOk(epicsScanDouble("-.1", &d) && fabs(d + 0.1) < 1e-15, "Double '-.1'"); testOk(epicsScanFloat("-0.1", &f) && fabs(f + 0.1) < 1e-7, "Float '-0.1'"); testOk(epicsScanDouble("-0.1", &d) && fabs(d + 0.1) < 1e-15, "Double '-0.1'"); testOk(epicsScanFloat("-1e-1", &f) && fabs(f + 1e-1) < 1e-7, "Float '-1e-1'"); testOk(epicsScanDouble("-1e-1", &d) && fabs(d + 1e-1) < 1e-15, "Double '-1e-1'"); testOk(epicsScanFloat("1e-30", &f) && fabs(f - 1e-30) < 1e-36, "Float '1e-30'"); testOk(epicsScanDouble("1e-300", &d) && fabs(d - 1e-300) < 1e-306, "Double '1e-300'"); testOk(epicsParseFloat("1e-40", &f, NULL) == S_stdlib_underflow, "Float '1e-40' => underflow"); #ifdef vxWorks testTodoBegin("Not detected on VxWorks"); #endif testOk(epicsParseDouble("1e-330", &d, NULL) == S_stdlib_underflow, "Double '1e-330' => underflow"); #ifdef vxWorks testTodoEnd(); #endif testOk(epicsScanFloat("1e30", &f) && fabs(f - 1e30) < 1e24, "Float '1e30'"); testOk(epicsScanDouble("1e300", &d) && fabs(d - 1e300) < 1e285, "Double '1e300'"); testOk(epicsParseFloat("1e40", &f, NULL) == S_stdlib_overflow, "Float '1e40' => overflow"); testOk(epicsParseDouble("1e310", &d, NULL) == S_stdlib_overflow, "Double '1e330' => overflow"); testOk(epicsScanLong("2147483647", &l, 0) && l == 2147483647, "Long '2147483647'"); testOk(epicsScanLong("-2147483647", &l, 0) && l == -2147483647, "Long '-2147483647'"); testOk(epicsScanULong("4294967295", &u, 0) && u == 4294967295u, "ULong '4294967295'"); testOk(epicsScanFloat("16777214", &f) && f == 16777214.0, "Float '16777214'"); testOk(epicsScanFloat("16777215", &f) && f == 16777215.0, "Float '16777215'"); testOk(epicsScanFloat("-16777215", &f) && f == -16777215.0, "Float '-16777215'"); testOk(epicsScanFloat("-16777216", &f) && f == -16777216.0, "Float '-16777216'"); testOk(epicsScanDouble("4294967294", &d) && d == 4294967294.0, "Double '4294967294'"); testOk(epicsScanDouble("4294967295", &d) && d == 4294967295.0, "Double '4294967295'"); testOk(epicsScanDouble("-4294967295", &d) && d == -4294967295.0, "Double '-4294967295'"); testOk(epicsScanDouble("-4294967296", &d) && d == -4294967296.0, "Double '-4294967296'"); testOk(epicsScanFloat("NAN", &f) && isnan(f), "Float 'NAN'"); testOk(epicsScanDouble("NAN", &d) && isnan(d), "Double 'NAN'"); testOk(epicsScanFloat("Nan", &f) && isnan(f), "Float 'Nan'"); testOk(epicsScanDouble("Nan", &d) && isnan(d), "Double 'Nan'"); #ifdef __rtems__ testTodoBegin("RTEMS (newlib) parser doesn't recognise 'nan()'"); #endif testOk(epicsScanFloat("nan()", &f) && isnan(f), "Float 'nan()'"); testOk(epicsScanDouble("nan()", &d) && isnan(d), "Double 'nan()'"); #ifdef __rtems__ testTodoEnd(); #endif testOk(epicsScanFloat("INF", &f) && f == epicsINF, "Float 'INF'"); testOk(epicsScanDouble("INF", &d) && d == epicsINF, "Double 'INF'"); testOk(epicsScanFloat("Infinity", &f) && f == epicsINF, "Float 'Infinity'"); testOk(epicsScanDouble("Infinity", &d) && d == epicsINF, "Double 'Infinity'"); testOk(epicsScanFloat("+INF", &f) && f == epicsINF, "Float '+INF'"); testOk(epicsScanDouble("+INF", &d) && d == epicsINF, "Double '+INF'"); testOk(epicsScanFloat("+Infinity", &f) && f == epicsINF, "Float '+Infinity'"); testOk(epicsScanDouble("+Infinity", &d) && d == epicsINF, "Double '+Infinity'"); testOk(epicsScanFloat("-INF", &f) && f == -epicsINF, "Float '-INF'"); testOk(epicsScanDouble("-INF", &d) && d == -epicsINF, "Double '-INF'"); testOk(epicsScanFloat("-Infinity", &f) && f == -epicsINF, "Float '-Infinity'"); testOk(epicsScanDouble("-Infinity", &d) && d == -epicsINF, "Double '-Infinity'"); #ifdef epicsStrtod #define CHECK_STRTOD epicsStrtod != strtod if (epicsStrtod == strtod) testDiag("This target defines epicsStrtod as strtod"); else testDiag("This target defines its own epicsStrtod macro"); #else #define CHECK_STRTOD 1 testDiag("This target compiles epicsStrtod()"); #endif if (CHECK_STRTOD) { int pass = 0, fail = 0; #define CHECK(ok, msg) if (ok) pass++; else fail++, testDiag(msg) testDiag("Checking the native strtod(), only failures shown:"); CHECK(parseStrtod("", &d, NULL) == S_stdlib_noConversion, " not ok - strtod('') => noConversion"); CHECK(parseStrtod("\t \n", &d, NULL) == S_stdlib_noConversion, " not ok - strtod('\\t 1\\n') => noConversion"); CHECK(parseStrtod("!", &d, NULL) == S_stdlib_noConversion, " not ok - strtod('!') => noConversion"); CHECK(scanStrtod("0", &d) && d == 0, " not ok - strtod('0')"); CHECK(scanStrtod("\t 1\n", &d) && d == 1, " not ok - strtod('\\t 1\\n')"); CHECK(parseStrtod("2!", &d, NULL) == S_stdlib_extraneous, " not ok - strtod('2!') => extraneous"); CHECK(parseStrtod("3 !", &d, NULL) == S_stdlib_extraneous, " not ok - strtod('3 !') => extraneous"); CHECK(!parseStrtod("2!", &d, &endp) && *endp == '!', " not ok - strtod('2!') => units='!'"); CHECK(!parseStrtod("3 \n\t!", &d, &endp) && *endp == '!', " not ok - strtod('3 \\n\\t!') => units='!'"); CHECK(scanStrtod("0x0", &d) && d == 0, " not ok - strtod('0x0')"); CHECK(scanStrtod("0x1", &d) && d == 1, " not ok - strtod('0x1')"); CHECK(scanStrtod("+0x1", &d) && d == 1, " not ok - strtod('+0x1')"); CHECK(scanStrtod("-0x1", &d) && d == -1, " not ok - strtod('-0x1')"); CHECK(scanStrtod("0xf", &d) && d == 15, " not ok - strtod('0xf')"); CHECK(scanStrtod("0XF", &d) && d == 15, " not ok - strtod('0XF')"); CHECK(scanStrtod("0xffffffff", &d) && d == 0xffffffff, " not ok - strtod('0xffffffff')"); CHECK(scanStrtod("-0x7fffffff", &d) && d == -0x7fffffffl, " not ok - strtod('-0x7fffffff')"); CHECK(scanStrtod(".1", &d) && fabs(d - 0.1) < 1e-15, " not ok - strtod('.1')"); CHECK(scanStrtod("0.1", &d) && fabs(d - 0.1) < 1e-15, " not ok - strtod('0.1')"); CHECK(scanStrtod("1e-1", &d) && fabs(d - 1e-1) < 1e-15, " not ok - strtod('1e-1')"); CHECK(scanStrtod("-.1", &d) && fabs(d + 0.1) < 1e-15, " not ok - strtod('-.1')"); CHECK(scanStrtod("-0.1", &d) && fabs(d + 0.1) < 1e-15, " not ok - strtod('-0.1')"); CHECK(scanStrtod("-1e-1", &d) && fabs(d + 1e-1) < 1e-15, " not ok - strtod('-1e-1')"); CHECK(scanStrtod("1e-300", &d) && fabs(d - 1e-300) < 1e-306, " not ok - strtod('1e-300')"); CHECK(parseStrtod("1e-400", &d, NULL) == S_stdlib_underflow, " not ok - strtod('1e-400') => underflow"); CHECK(scanStrtod("4294967294", &d) && d == 4294967294.0, " not ok - strtod('4294967294')"); CHECK(scanStrtod("4294967295", &d) && d == 4294967295.0, " not ok - strtod('4294967295')"); CHECK(scanStrtod("-4294967295", &d) && d == -4294967295.0, " not ok - strtod('-4294967295')"); CHECK(scanStrtod("-4294967296", &d) && d == -4294967296.0, " not ok - strtod('-4294967296')"); CHECK(scanStrtod("NAN", &d) && isnan(d), " not ok - strtod('NAN')"); CHECK(scanStrtod("Nan", &d) && isnan(d), " not ok - strtod('Nan')"); CHECK(scanStrtod("nan()", &d) && isnan(d), " not ok - strtod('nan()')"); CHECK(scanStrtod("INF", &d) && d == epicsINF, " not ok - strtod('INF')"); CHECK(scanStrtod("Infinity", &d) && d == epicsINF, " not ok - strtod('Infinity')"); CHECK(scanStrtod("+INF", &d) && d == epicsINF, " not ok - strtod('+INF')"); CHECK(scanStrtod("+Infinity", &d) && d == epicsINF, " not ok - strtod('+Infinity')"); CHECK(scanStrtod("-INF", &d) && d == -epicsINF, " not ok - strtod('-INF')"); CHECK(scanStrtod("-Infinity", &d) && d == -epicsINF, " not ok - strtod('-Infinity')"); if (fail) testDiag("The native strtod() passed %d and failed %d tests", pass, fail); else { testDiag("The native strtod() passed all %d tests", pass); #ifndef epicsStrtod testDiag("The compiled epicsStrtod() should not be needed"); #endif } } return testDone(); } base-7.0.3.1/modules/libcom/test/epicsStringTest.c0000664000577000060420000003316113557101274020611 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Marty Kraimer */ #include #include #include #include "epicsUnitTest.h" #include "epicsString.h" #include "testMain.h" static void testChars(void) { int i; char input[2] = {0, 0}; char escaped[20]; char result[20]; size_t s, t, needed; for (i = 255; i >= 0; --i) { input[0] = i; needed = epicsStrnEscapedFromRawSize(input, 1); s = epicsStrnEscapedFromRaw(escaped, sizeof(escaped), input, 1); t = epicsStrnRawFromEscaped(result, sizeof(result), escaped, s); testOk(needed == s && t == 1 && result[0] == input[0] && result[1] == 0, "escaped char 0x%2.2x -> \"%s\" (%d) -> 0x%2.2x", input[0] & 0xff, escaped, (int) needed, result[0] & 0xff); } } static void testGlob(void) { testOk1(epicsStrGlobMatch("xyz","xyz")); testOk1(!epicsStrGlobMatch("xyz","xyzm")); testOk1(!epicsStrGlobMatch("xyzm","xyz")); testOk1(!epicsStrGlobMatch("","xyzm")); testOk1(!epicsStrGlobMatch("xyz","")); testOk1(epicsStrGlobMatch("","")); testOk1(epicsStrGlobMatch("","*")); testOk1(!epicsStrGlobMatch("","?")); testOk1(!epicsStrGlobMatch("","?*")); testOk1(epicsStrGlobMatch("hello","h*o")); testOk1(!epicsStrGlobMatch("hello","h*x")); testOk1(!epicsStrGlobMatch("hellx","h*o")); testOk1(epicsStrGlobMatch("hello","he?lo")); testOk1(!epicsStrGlobMatch("hello","he?xo")); testOk1(epicsStrGlobMatch("hello","he??o")); testOk1(!epicsStrGlobMatch("helllo","he?lo")); testOk1(epicsStrGlobMatch("hello world","he*o w*d")); testOk1(!epicsStrGlobMatch("hello_world","he*o w*d")); testOk1(epicsStrGlobMatch("hello world","he**d")); testOk1(epicsStrGlobMatch("hello hello world","he*o w*d")); testOk1(!epicsStrGlobMatch("hello hello xorld","he*o w*d")); testOk1(epicsStrGlobMatch("hello","he*")); testOk1(epicsStrGlobMatch("hello","*lo")); testOk1(epicsStrGlobMatch("hello","*")); } MAIN(epicsStringTest) { const char * const empty = ""; const char * const space = " "; const char * const A = "A"; const char * const ABCD = "ABCD"; const char * const ABCDE = "ABCDE"; const char * const a = "a"; const char * const abcd = "abcd"; const char * const abcde = "abcde"; char result[8]; char *s; int status; testPlan(406); testChars(); testOk1(epicsStrnCaseCmp(empty, "", 0) == 0); testOk1(epicsStrnCaseCmp(empty, "", 1) == 0); testOk1(epicsStrnCaseCmp(space, empty, 1) > 0); testOk1(epicsStrnCaseCmp(empty, space, 1) < 0); testOk1(epicsStrnCaseCmp(a, A, 1) == 0); testOk1(epicsStrnCaseCmp(a, A, 2) == 0); testOk1(epicsStrnCaseCmp(abcd, ABCD, 2) == 0); testOk1(epicsStrnCaseCmp(abcd, ABCD, 4) == 0); testOk1(epicsStrnCaseCmp(abcd, ABCD, 1000) == 0); testOk1(epicsStrnCaseCmp(abcd, ABCDE, 2) == 0); testOk1(epicsStrnCaseCmp(abcd, ABCDE, 4) == 0); testOk1(epicsStrnCaseCmp(abcd, ABCDE, 1000) < 0); testOk1(epicsStrnCaseCmp(abcde, ABCD, 2) == 0); testOk1(epicsStrnCaseCmp(abcde, ABCD, 4) == 0); testOk1(epicsStrnCaseCmp(abcde, ABCD, 1000) > 0); testOk1(epicsStrCaseCmp(empty, "") == 0); testOk1(epicsStrCaseCmp(a, A) == 0); testOk1(epicsStrCaseCmp(abcd, ABCD) == 0); testOk1(epicsStrCaseCmp(abcd, ABCDE) < 0); testOk1(epicsStrCaseCmp(abcde, ABCD) > 0); testOk1(epicsStrCaseCmp(abcde, "ABCDF") < 0); s = epicsStrDup(abcd); testOk(strcmp(s, abcd) == 0 && s != abcd, "epicsStrDup"); free(s); testOk1(epicsStrHash(abcd, 0) != epicsStrHash("bacd", 0)); testOk1(epicsStrHash(abcd, 0) == epicsMemHash(abcde, 4, 0)); testOk1(epicsStrHash(abcd, 0) != epicsMemHash("abcd\0", 5, 0)); testOk1(epicsStrnLen("abcd", 5)==4); testOk1(epicsStrnLen("abcd", 4)==4); testOk1(epicsStrnLen("abcd", 3)==3); testOk1(epicsStrnLen("abcd", 0)==0); testGlob(); memset(result, 'x', sizeof(result)); status = epicsStrnEscapedFromRaw(result, 0, ABCD, 4); testOk(status == 4, "epicsStrnEscapedFromRaw(out, 0) -> %d (exp. 4)", status); testOk(result[0] == 'x', " No output"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 0, ABCD, 4); testOk(status == 0, "epicsStrnRawFromEscaped(out, 0) -> %d (exp. 0)", status); testOk(result[0] == 'x', " No output"); memset(result, 'x', sizeof(result)); status = epicsStrnEscapedFromRaw(result, 1, ABCD, 4); testOk(status == 4, "epicsStrnEscapedFromRaw(out, 1) -> %d (exp. 4)", status); testOk(result[0] == 0, " 0-terminated"); testOk(result[1] == 'x', " No overrun"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 1, ABCD, 4); testOk(status == 0, "epicsStrnRawFromEscaped(out, 1) -> %d (exp. 0)", status); testOk(result[0] == 0, " 0-terminated"); testOk(result[1] == 'x', " No overrun"); testDiag("Testing size = epicsStrnEscapedFromRawSize"); status = epicsStrnEscapedFromRawSize(ABCD, 3); testOk(status == 3, "size(\"ABCD\", 3) -> %d (exp. 3)", status); status = epicsStrnEscapedFromRawSize(ABCD, 4); testOk(status == 4, "size(\"ABCD\", 4) -> %d (exp. 4)", status); status = epicsStrnEscapedFromRawSize(ABCD, 5); testOk(status == 8, "size(\"ABCD\", 5) -> %d (exp. 8)", status); testDiag("Testing esc = epicsStrnEscapedFromRaw(out, 4, ...)"); memset(result, 'x', sizeof(result)); status = epicsStrnEscapedFromRaw(result, 4, ABCD, 3); testOk(status == 3, "esc(\"ABCD\", 3) -> %d (exp. 3)", status); testOk(result[3] == 0, " 0-terminated"); testOk(result[4] == 'x', " No overrun"); memset(result, 'x', sizeof(result)); status = epicsStrnEscapedFromRaw(result, 4, ABCD, 4); testOk(status == 4, "esc(\"ABCD\", 4) -> %d (exp. 4)", status); testOk(result[3] == 0, " 0-terminated"); testOk(result[4] == 'x', " No overrun"); memset(result, 'x', sizeof(result)); status = epicsStrnEscapedFromRaw(result, 4, ABCD, 5); testOk(status == 8, "esc(\"ABCD\", 5) -> %d (exp. 8)", status); testOk(result[3] == 0, " 0-terminated"); testOk(result[4] == 'x', " No overrun"); testDiag("Testing raw = epicsStrnRawFromEscaped(out, 4, ...)"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, ABCD, 0); testOk(status == 0, "raw(\"ABCD\", 0) -> %d (exp. 0)", status); testOk(result[0] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, ABCD, 1); testOk(status == 1, "raw(\"ABCD\", 1) -> %d (exp. 1)", status); testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]); testOk(result[1] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, ABCD, 2); testOk(status == 2, "raw(\"ABCD\", 2) -> %d (exp. 2)", status); testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]); testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]); testOk(result[2] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, ABCD, 3); testOk(status == 3, "raw(\"ABCD\", 3) -> %d (exp. 3)", status); testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]); testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]); testOk(result[2] == 'C', " Char '%c' (exp. 'C')", result[2]); testOk(result[3] == 0, " 0-terminated"); testOk(result[4] == 'x', " No write outside buffer"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, ABCD, 4); testOk(status == 3, "raw(\"ABCD\", 4) -> %d (exp. 3)", status); testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]); testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]); testOk(result[2] == 'C', " Char '%c' (exp. 'C')", result[2]); testOk(result[3] == 0, " 0-terminated"); testOk(result[4] == 'x', " No write outside buffer"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, ABCDE, 5); testOk(status == 3, "raw(\"ABCDE\", 5) -> %d (exp. 3)", status); testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]); testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]); testOk(result[2] == 'C', " Char '%c' (exp. 'C')", result[2]); testOk(result[3] == 0, " 0-terminated"); testOk(result[4] == 'x', " No write outside buffer"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "A", 2); testOk(status == 1, "raw(\"A\", 2) -> %d (exp. 1)", status); testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\123", 1); testOk(status == 0, "raw(\"\\123\", 1) -> %d (exp. 0)", status); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\123", 2); testOk(status == 1, "raw(\"\\123\", 2) -> %d (exp. 1)", status); testOk(result[0] == 1, " Octal escape (got \\%03o)", result[0]); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\123", 3); testOk(status == 1, "raw(\"\\123\", 3) -> %d (exp. 1)", status); testOk(result[0] == 012, " Octal escape (got \\%03o)", result[0]); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\123", 4); testOk(status == 1, "raw(\"\\123\", 4) -> %d (exp. 1)", status); testOk(result[0] == 0123, " Octal escape (got \\%03o)", result[0]); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\812", 2); testOk(status == 1, "raw(\"\\812\", 2) -> %d (exp. 1)", status); testOk(result[0] == '8', " Escaped '%c')", result[0]); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\182", 3); testOk(status == 2, "raw(\"\\182\", 3) -> %d (exp. 2)", status); testOk(result[0] == 1, " Octal escape (got \\%03o)", result[0]); testOk(result[1] == '8', " Terminated with '%c'", result[1]); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\128", 4); testOk(status == 2, "raw(\"\\128\", 4) -> %d (exp. 2)", status); testOk(result[0] == 012, " Octal escape (got \\%03o)", result[0]); testOk(result[1] == '8', " Terminator char got '%c'", result[1]); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\x12", 1); testOk(status == 0, "raw(\"\\x12\", 1) -> %d (exp. 0)", status); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\x12", 2); testOk(status == 0, "raw(\"\\x12\", 2) -> %d (exp. 0)", status); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\x12", 3); testOk(status == 1, "raw(\"\\x12\", 3) -> %d (exp. 1)", status); testOk(result[0] == 1, " Hex escape (got \\x%x)", result[0]); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\x12", 4); testOk(status == 1, "raw(\"\\x12\", 4) -> %d (exp. 1)", status); testOk(result[0] == 0x12," Hex escape (got \\x%x)", result[0]); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\xaF", 4); testOk(status == 1, "raw(\"\\xaF\", 4) -> %d (exp. 1)", status); testOk(result[0] == '\xaF'," Hex escape (got \\x%x)", result[0] & 0xFF); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\x012", 5); testOk(status == 1, "raw(\"\\x012\", 5) -> %d (exp. 1)", status); testOk(result[0] == 0x12," Hex escape (got \\x%x)", result[0]); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\x0012", 6); testOk(status == 1, "raw(\"\\x0012\", 6) -> %d (exp. 1)", status); testOk(result[0] == 0x12," Hex escape (got \\x%x)", result[0]); testOk(result[status] == 0, " 0-terminated"); memset(result, 'x', sizeof(result)); status = epicsStrnRawFromEscaped(result, 4, "\\x1g", 4); testOk(status == 2, "raw(\"\\x1g\", 4) -> %d (exp. 2)", status); testOk(result[0] == 1, " Hex escape (got \\x%x)", result[0]); testOk(result[1] == 'g', " Terminator char got '%c'", result[1]); testOk(result[status] == 0, " 0-terminated"); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsThreadHooksTest.c0000664000577000060420000000764313557101274021564 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2012 ITER Organization * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsThreadHooksTest.c */ #include #include "epicsThread.h" #include "epicsExit.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "errlog.h" #include "epicsUnitTest.h" #include "testMain.h" #define MAX_THREADS 16 #define TEST_THREADS 8 #define NUM_HOOKS 4 static int order[MAX_THREADS][NUM_HOOKS]; static int cnt[MAX_THREADS]; static int mine[MAX_THREADS]; static int mapped[MAX_THREADS]; static epicsThreadId tid[MAX_THREADS]; static epicsMutexId tidLock; static epicsEventId shutdown[TEST_THREADS]; static int newThreadIndex(epicsThreadId id) { int i = 0; if (epicsMutexLock(tidLock)) testAbort("newThreadIndex: Locking problem"); while (i < MAX_THREADS && tid[i] != 0) i++; if (i < MAX_THREADS) tid[i] = id; else testAbort("newThreadIndex: Too many threads!"); epicsMutexUnlock(tidLock); return i; } static int findThreadIndex(epicsThreadId id) { int i = 0; while (i < MAX_THREADS && tid[i] != id) i++; return i; } static void atExitHook1 (void *arg) { int no = findThreadIndex(epicsThreadGetIdSelf()); if (no < MAX_THREADS) order[no][3] = cnt[no]++; } static void atExitHook2 (void *arg) { int no = findThreadIndex(epicsThreadGetIdSelf()); if (no < MAX_THREADS) order[no][2] = cnt[no]++; } static void startHook1 (epicsThreadId id) { int no = newThreadIndex(id); if (no < MAX_THREADS) order[no][0] = cnt[no]++; epicsAtThreadExit(atExitHook1, NULL); } static void startHook2 (epicsThreadId id) { int no = findThreadIndex(id); if (no < MAX_THREADS) order[no][1] = cnt[no]++; epicsAtThreadExit(atExitHook2, NULL); } static void my_thread (void *arg) { int no = findThreadIndex(epicsThreadGetIdSelf()); if (no < MAX_THREADS) mine[no] = 1; epicsEventMustWait((epicsEventId) arg); } static void mapper (epicsThreadId id) { int no = findThreadIndex(id); if (no < MAX_THREADS) mapped[no]++; } MAIN(epicsThreadHooksTest) { int i; int ok; testPlan(TEST_THREADS + 1); tidLock = epicsMutexMustCreate(); if (epicsThreadHookAdd(startHook1)) testAbort("startHook1 registration failed"); if (epicsThreadHookAdd(startHook2)) testAbort("startHook2 registration failed"); for (i = 0; i < TEST_THREADS; i++) { char name[10]; shutdown[i] = epicsEventCreate(epicsEventEmpty); sprintf(name, "t%d", (int) i); epicsThreadCreate(name, epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), my_thread, shutdown[i]); } epicsThreadSleep(1.0); epicsThreadMap(mapper); ok = 1; for (i = 0; i < MAX_THREADS; i++) { if (!mine[i]) continue; if (mapped[i] != 1) { ok = 0; testDiag("mapped[%d] = %d", i, mapped[i]); } } testOk(ok, "All my tasks covered once by epicsThreadMap"); for (i = 0; i < TEST_THREADS; i++) { epicsEventSignal(shutdown[i]); } epicsThreadSleep(1.0); for (i = 0; i < MAX_THREADS; i++) { int j; if (!mine[i]) continue; ok = 1; for (j = 0; j < NUM_HOOKS; j++) { if (order[i][j] != j) { ok = 0; testDiag("order[%d][%d] = %d", i, j, order[i][j]); } } testOk(ok, "All hooks for task %d called in correct order", i); } epicsThreadHookDelete(startHook1); epicsThreadHookDelete(startHook2); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsThreadOnceTest.c0000664000577000060420000000537613557101274021366 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include "errlog.h" #include "epicsEvent.h" #include "epicsExit.h" #include "epicsMutex.h" #include "epicsThread.h" #include "epicsUnitTest.h" #include "testMain.h" #define NUM_ONCE_THREADS 8 epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT; epicsThreadOnceId twiceFlag = EPICS_THREAD_ONCE_INIT; epicsMutexId lock; epicsEventId go; epicsEventId done; int runCount = 0; int initCount = 0; char initBy[20]; int doneCount = 0; void onceInit(void *ctx) { initCount++; strcpy(initBy, epicsThreadGetNameSelf()); } void onceThread(void *ctx) { epicsMutexMustLock(lock); runCount++; epicsMutexUnlock(lock); epicsEventMustWait(go); epicsEventSignal(go); epicsThreadOnce(&onceFlag, onceInit, ctx); testOk(initCount == 1, "%s: initCount = %d", epicsThreadGetNameSelf(), initCount); epicsMutexMustLock(lock); doneCount++; if (doneCount == runCount) epicsEventSignal(done); epicsMutexUnlock(lock); } void recurseInit(void); void onceRecurse(void *ctx) { recurseInit(); } void recurseInit(void) { epicsThreadOnce(&twiceFlag, onceRecurse, 0); } void recurseThread(void *ctx) { recurseInit(); testFail("Recursive epicsThreadOnce() not detected"); } MAIN(epicsThreadOnceTest) { int i; epicsThreadId tid; testPlan(3 + NUM_ONCE_THREADS); go = epicsEventMustCreate(epicsEventEmpty); done = epicsEventMustCreate(epicsEventEmpty); lock = epicsMutexMustCreate(); for (i = 0; i < NUM_ONCE_THREADS; i++) { char name[20]; sprintf(name, "once-%d", i); epicsThreadCreate(name, epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackSmall), onceThread, 0); } epicsThreadSleep(0.1); testOk(runCount == NUM_ONCE_THREADS, "runCount = %d", runCount); epicsEventSignal(go); /* Use epicsEventBroadcast(go) when available */ epicsEventMustWait(done); testOk(doneCount == NUM_ONCE_THREADS, "doneCount = %d", doneCount); testDiag("init was run by %s", initBy); eltc(0); tid = epicsThreadCreate("recurse", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackSmall), recurseThread, 0); do { epicsThreadSleep(0.1); } while (!epicsThreadIsSuspended(tid)); testPass("Recursive epicsThreadOnce() detected"); eltc(1); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsThreadPerform.cpp0000664000577000060420000001501613557101274021604 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsThreadPerform.cpp */ /* sleep accuracy and sleep quantum tests by Jeff Hill */ /* epicsThreadGetIdSelf performance by Jeff Hill */ #include #include #include #include #include #include #include #include #include "epicsThread.h" #include "epicsTime.h" #include "errlog.h" #include "testMain.h" static void testPriority(const char *who) { epicsThreadId id; unsigned int oldPriority,newPriority; id = epicsThreadGetIdSelf(); oldPriority = epicsThreadGetPriority(id); epicsThreadSetPriority(id,epicsThreadPriorityMax); newPriority = epicsThreadGetPriority(id); epicsThreadSetPriority(id,oldPriority); printf("testPriority %s\n id %p old %u new %u\n", who,id,oldPriority,newPriority); } extern "C" void testPriorityThread(void *arg) { testPriority("thread"); } static void epicsThreadPriorityTest() { testPriority("main error expected from epicsThreadSetPriority"); epicsThreadCreate("testPriorityThread",epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium),testPriorityThread,0); epicsThreadSleep(0.5); } static double threadSleepMeasureDelayError ( const double & delay ) { epicsTime beg = epicsTime::getMonotonic(); epicsThreadSleep ( delay ); epicsTime end = epicsTime::getMonotonic(); double meas = end - beg; double error = fabs ( delay - meas ); return error; } static double measureSleepQuantum ( const unsigned iterations, const double testInterval ) { double errorSum = 0.0; printf ( "Estimating sleep quantum" ); fflush ( stdout ); for ( unsigned i = 0u; i < iterations; i++ ) { // try to guarantee a uniform probability density function // by intentionally burning some CPU until we are less // likely to be aligned with the schedualing clock double interval = rand (); interval /= RAND_MAX; interval *= testInterval; epicsTime start = epicsTime::getMonotonic (); epicsTime current = start; while ( current - start < interval ) { current = epicsTime::getMonotonic (); } errorSum += threadSleepMeasureDelayError ( testInterval ); if ( i % ( iterations / 10 ) == 0 ) { printf ( "." ); fflush ( stdout ); } } printf ( "done\n" ); // with a uniform probability density function the // sleep delay error mean is one half of the quantum double quantumEstimate = 2 * errorSum / iterations; return quantumEstimate; } static void threadSleepQuantumTest () { const double quantum = epicsThreadSleepQuantum (); double quantumEstimate = measureSleepQuantum ( 10, 10 * quantum ); quantumEstimate = measureSleepQuantum ( 100, 2 * quantumEstimate ); double quantumError = fabs ( quantumEstimate - quantum ) / quantum; const char * pTol = 0; if ( quantumError > 0.1 ) { pTol = "10%"; } else if ( quantumError > 0.01 ) { pTol = "1%"; } else if ( quantumError > 0.001 ) { pTol = "0.1%"; } if ( pTol ) { printf ( "The epicsThreadSleepQuantum() call returns %f sec.\n", quantum ); printf ( "This doesnt match the quantum estimate " "of %f sec within %s.\n", quantumEstimate, pTol ); } } static void threadSleepTest () { static const int iterations = 20; const double quantum = epicsThreadSleepQuantum (); double errorSum = threadSleepMeasureDelayError ( 0.0 ); for ( int i = 0u; i < iterations; i++ ) { double delay = ldexp ( 1.0 , -i ); double error = threadSleepMeasureDelayError ( delay ); errorSum += error; if ( error > quantum + quantum / 8.0 ) { printf ( "epicsThreadSleep ( %10f ) delay err %10f sec\n", delay, error ); } } double averageError = errorSum / ( iterations + 1 ); if ( averageError > quantum ) { printf ( "Average sleep delay error was %f sec\n", averageError ); } } static void epicsThreadGetIdSelfPerfTest () { static const unsigned N = 10000; static const double microSecPerSec = 1e6; epicsTime begin = epicsTime::getMonotonic (); for ( unsigned i = 0u; i < N; i++ ) { epicsThreadGetIdSelf (); epicsThreadGetIdSelf (); epicsThreadGetIdSelf (); epicsThreadGetIdSelf (); epicsThreadGetIdSelf (); epicsThreadGetIdSelf (); epicsThreadGetIdSelf (); epicsThreadGetIdSelf (); epicsThreadGetIdSelf (); epicsThreadGetIdSelf (); }; epicsTime end = epicsTime::getMonotonic (); printf ( "It takes %f micro sec to call epicsThreadGetIdSelf ()\n", microSecPerSec * ( end - begin ) / (10 * N) ); } static epicsThreadPrivate < bool > priv; static inline void callItTenTimes () { (void) priv.get (); (void) priv.get (); (void) priv.get (); (void) priv.get (); (void) priv.get (); (void) priv.get (); (void) priv.get (); (void) priv.get (); (void) priv.get (); (void) priv.get (); } static inline void callItTenTimesSquared () { callItTenTimes (); callItTenTimes (); callItTenTimes (); callItTenTimes (); callItTenTimes (); callItTenTimes (); callItTenTimes (); callItTenTimes (); callItTenTimes (); callItTenTimes (); } static void timeEpicsThreadPrivateGet () { priv.set ( 0 ); epicsTime begin = epicsTime::getMonotonic (); static const unsigned N = 1000u; for ( unsigned i = 0u; i < N; i++ ) { callItTenTimesSquared (); } double delay = epicsTime::getMonotonic() - begin; delay /= N * 100u; // convert to sec per call delay *= 1e6; // convert to micro sec printf("epicsThreadPrivateGet() takes %f microseconds\n", delay); } MAIN(epicsThreadPerform) { epicsThreadPriorityTest (); threadSleepQuantumTest (); threadSleepTest (); epicsThreadGetIdSelfPerfTest (); timeEpicsThreadPrivateGet (); return 0; } base-7.0.3.1/modules/libcom/test/epicsThreadPoolTest.c0000664000577000060420000002646613557101274021416 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Associates, as Operator of * Brookhaven National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include "epicsThreadPool.h" /* included to allow tests to peek */ #include "../../src/pool/poolPriv.h" #include "testMain.h" #include "epicsUnitTest.h" #include "cantProceed.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "epicsThread.h" /* Do nothing */ static void nullop(void) { epicsThreadPool *pool; testDiag("nullop()"); { epicsThreadPoolConfig conf; epicsThreadPoolConfigDefaults(&conf); testOk1(conf.maxThreads>0); testOk1((pool=epicsThreadPoolCreate(&conf))!=NULL); if(!pool) return; } epicsThreadPoolDestroy(pool); } /* Just create and destroy worker threads */ static void oneop(void) { epicsThreadPool *pool; testDiag("oneop()"); { epicsThreadPoolConfig conf; epicsThreadPoolConfigDefaults(&conf); conf.initialThreads=2; testOk1(conf.maxThreads>0); testOk1((pool=epicsThreadPoolCreate(&conf))!=NULL); if(!pool) return; } epicsThreadPoolDestroy(pool); } /* Test that Bursts of jobs will create enough threads to * run all in parallel */ typedef struct { epicsMutexId guard; unsigned int count; epicsEventId allrunning; epicsEventId done; epicsJob **job; } countPriv; static void countjob(void *param, epicsJobMode mode) { countPriv *cnt=param; testOk1(mode==epicsJobModeRun||mode==epicsJobModeCleanup); if(mode==epicsJobModeCleanup) return; epicsMutexMustLock(cnt->guard); testDiag("Job %lu", (unsigned long)cnt->count); cnt->count--; if(cnt->count==0) { testDiag("All jobs running"); epicsEventSignal(cnt->allrunning); } epicsMutexUnlock(cnt->guard); epicsEventMustWait(cnt->done); epicsEventSignal(cnt->done); /* pass along to next thread */ } /* Starts "mcnt" jobs in a pool with initial and max * thread counts "icnt" and "mcnt". * The test ensures that all jobs run in parallel. * "cork" checks the function of pausing the run queue * with epicsThreadPoolQueueRun */ static void postjobs(size_t icnt, size_t mcnt, int cork) { size_t i; epicsThreadPool *pool; countPriv *priv=callocMustSucceed(1, sizeof(*priv), "postjobs priv alloc"); priv->guard=epicsMutexMustCreate(); priv->done=epicsEventMustCreate(epicsEventEmpty); priv->allrunning=epicsEventMustCreate(epicsEventEmpty); priv->count=mcnt; priv->job=callocMustSucceed(mcnt, sizeof(*priv->job), "postjobs job array"); testDiag("postjobs(%lu,%lu)", (unsigned long)icnt, (unsigned long)mcnt); { epicsThreadPoolConfig conf; epicsThreadPoolConfigDefaults(&conf); conf.initialThreads=icnt; conf.maxThreads=mcnt; testOk1((pool=epicsThreadPoolCreate(&conf))!=NULL); if(!pool) return; } if(cork) epicsThreadPoolControl(pool, epicsThreadPoolQueueRun, 0); for(i=0; ijob[i] = epicsJobCreate(pool, &countjob, priv); testOk1(priv->job[i]!=NULL); testOk1(epicsJobQueue(priv->job[i])==0); } if(cork) { /* no jobs should have run */ epicsMutexMustLock(priv->guard); testOk1(priv->count==mcnt); epicsMutexUnlock(priv->guard); epicsThreadPoolControl(pool, epicsThreadPoolQueueRun, 1); } testDiag("Waiting for all jobs to start"); epicsEventMustWait(priv->allrunning); testDiag("Stop all"); epicsEventSignal(priv->done); for(i=0; ijob[i]); } epicsThreadPoolDestroy(pool); epicsMutexDestroy(priv->guard); epicsEventDestroy(priv->allrunning); epicsEventDestroy(priv->done); free(priv->job); free(priv); } static unsigned int flag0 = 0; /* Test cancel from job (no-op) * and destroy from job (lazy free) */ static void cleanupjob0(void* arg, epicsJobMode mode) { epicsJob *job=arg; testOk1(mode==epicsJobModeRun||mode==epicsJobModeCleanup); if(mode==epicsJobModeCleanup) return; assert(flag0==0); flag0=1; testOk1(epicsJobQueue(job)==0); epicsJobDestroy(job); /* delete while job is running */ } static void cleanupjob1(void* arg, epicsJobMode mode) { epicsJob *job=arg; testOk1(mode==epicsJobModeRun||mode==epicsJobModeCleanup); if(mode==epicsJobModeCleanup) return; testOk1(epicsJobQueue(job)==0); testOk1(epicsJobUnqueue(job)==0); /* delete later after job finishes, but before pool is destroyed */ } static void cleanupjob2(void* arg, epicsJobMode mode) { epicsJob *job=arg; testOk1(mode==epicsJobModeRun||mode==epicsJobModeCleanup); if(mode==epicsJobModeCleanup) epicsJobDestroy(job); /* delete when threadpool is destroyed */ else if(mode==epicsJobModeRun) testOk1(epicsJobUnqueue(job)==S_pool_jobIdle); } static epicsJobFunction cleanupjobs[3] = {&cleanupjob0,&cleanupjob1,&cleanupjob2}; /* Tests three methods for job cleanup. * 1. destroy which running * 2. deferred cleanup after pool destroyed * 3. immediate cleanup when pool destroyed */ static void testcleanup(void) { int i=0; epicsThreadPool *pool; epicsJob *job[3]; testDiag("testcleanup()"); testOk1((pool=epicsThreadPoolCreate(NULL))!=NULL); if(!pool) return; /* unrolled so that valgrind can show which methods leaks */ testOk1((job[0]=epicsJobCreate(pool, cleanupjobs[0], EPICSJOB_SELF))!=NULL); testOk1((job[1]=epicsJobCreate(pool, cleanupjobs[1], EPICSJOB_SELF))!=NULL); testOk1((job[2]=epicsJobCreate(pool, cleanupjobs[2], EPICSJOB_SELF))!=NULL); for(i=0; i<3; i++) { testOk1(epicsJobQueue(job[i])==0); } epicsThreadPoolWait(pool, -1); epicsJobDestroy(job[1]); epicsThreadPoolDestroy(pool); } /* Test re-add from inside job */ typedef struct { unsigned int count; epicsEventId done; epicsJob *job; unsigned int inprogress; } readdPriv; static void readdjob(void *arg, epicsJobMode mode) { readdPriv *priv=arg; testOk1(mode==epicsJobModeRun||mode==epicsJobModeCleanup); if(mode==epicsJobModeCleanup) return; testOk1(priv->inprogress==0); testDiag("count==%u", priv->count); if(priv->count--) { priv->inprogress=1; epicsJobQueue(priv->job); epicsThreadSleep(0.05); priv->inprogress=0; }else{ epicsEventSignal(priv->done); epicsJobDestroy(priv->job); } } /* Test re-queueing a job while it is running. * Check that a single job won't run concurrently. */ static void testreadd(void) { epicsThreadPoolConfig conf; epicsThreadPool *pool; readdPriv *priv=callocMustSucceed(1, sizeof(*priv), "testcleanup priv"); readdPriv *priv2=callocMustSucceed(1, sizeof(*priv), "testcleanup priv"); testDiag("testreadd"); priv->done=epicsEventMustCreate(epicsEventEmpty); priv->count=5; priv2->done=epicsEventMustCreate(epicsEventEmpty); priv2->count=5; epicsThreadPoolConfigDefaults(&conf); conf.maxThreads = 2; testOk1((pool=epicsThreadPoolCreate(&conf))!=NULL); if(!pool) return; testOk1((priv->job=epicsJobCreate(pool, &readdjob, priv))!=NULL); testOk1((priv2->job=epicsJobCreate(pool, &readdjob, priv2))!=NULL); testOk1(epicsJobQueue(priv->job)==0); testOk1(epicsJobQueue(priv2->job)==0); epicsEventMustWait(priv->done); epicsEventMustWait(priv2->done); testOk1(epicsThreadPoolNThreads(pool)==2); testDiag("epicsThreadPoolNThreads = %d", epicsThreadPoolNThreads(pool)); epicsThreadPoolDestroy(pool); epicsEventDestroy(priv->done); epicsEventDestroy(priv2->done); free(priv); free(priv2); } static int shouldneverrun = 0; static int numtoolate = 0; /* test job canceling */ static void neverrun(void *arg, epicsJobMode mode) { epicsJob *job=arg; testOk1(mode==epicsJobModeCleanup); if(mode==epicsJobModeCleanup) epicsJobDestroy(job); else shouldneverrun++; } static epicsEventId cancel[2]; static void toolate(void *arg, epicsJobMode mode) { epicsJob *job=arg; if(mode==epicsJobModeCleanup){ epicsJobDestroy(job); return; } testPass("Job runs"); numtoolate++; epicsEventSignal(cancel[0]); epicsEventMustWait(cancel[1]); } static void testcancel(void) { epicsJob *job[2]; epicsThreadPool *pool; testOk1((pool=epicsThreadPoolCreate(NULL))!=NULL); if(!pool) return; cancel[0]=epicsEventCreate(epicsEventEmpty); cancel[1]=epicsEventCreate(epicsEventEmpty); testOk1((job[0]=epicsJobCreate(pool, &neverrun, EPICSJOB_SELF))!=NULL); testOk1((job[1]=epicsJobCreate(pool, &toolate, EPICSJOB_SELF))!=NULL); /* freeze */ epicsThreadPoolControl(pool, epicsThreadPoolQueueRun, 0); testOk1(epicsJobUnqueue(job[0])==S_pool_jobIdle); /* not queued yet */ epicsJobQueue(job[0]); testOk1(epicsJobUnqueue(job[0])==0); testOk1(epicsJobUnqueue(job[0])==S_pool_jobIdle); epicsThreadSleep(0.01); epicsJobQueue(job[0]); testOk1(epicsJobUnqueue(job[0])==0); testOk1(epicsJobUnqueue(job[0])==S_pool_jobIdle); epicsThreadPoolControl(pool, epicsThreadPoolQueueRun, 1); epicsJobQueue(job[1]); /* actually let it run this time */ epicsEventMustWait(cancel[0]); testOk1(epicsJobUnqueue(job[0])==S_pool_jobIdle); epicsEventSignal(cancel[1]); epicsThreadPoolDestroy(pool); epicsEventDestroy(cancel[0]); epicsEventDestroy(cancel[1]); testOk1(shouldneverrun==0); testOk1(numtoolate==1); } static unsigned int sharedWasDeleted=0; static void lastjob(void *arg, epicsJobMode mode) { epicsJob *job=arg; if(mode==epicsJobModeCleanup) { sharedWasDeleted=1; epicsJobDestroy(job); } } static void testshared(void) { epicsThreadPool *poolA, *poolB; epicsThreadPoolConfig conf; epicsJob *job; epicsThreadPoolConfigDefaults(&conf); testDiag("Check reference counting of shared pools"); testOk1((poolA=epicsThreadPoolGetShared(&conf))!=NULL); testOk1(poolA->sharedCount==1); testOk1((poolB=epicsThreadPoolGetShared(&conf))!=NULL); testOk1(poolA==poolB); testOk1(poolA->sharedCount==2); epicsThreadPoolReleaseShared(poolA); testOk1(poolB->sharedCount==1); testOk1((job=epicsJobCreate(poolB, &lastjob, EPICSJOB_SELF))!=NULL); epicsThreadPoolReleaseShared(poolB); testOk1(sharedWasDeleted==1); testOk1((poolA=epicsThreadPoolGetShared(&conf))!=NULL); testOk1(poolA->sharedCount==1); epicsThreadPoolReleaseShared(poolA); } MAIN(epicsThreadPoolTest) { testPlan(171); nullop(); oneop(); testDiag("Queue with delayed start"); postjobs(1,1,1); postjobs(0,1,1); postjobs(4,4,1); postjobs(0,4,1); postjobs(2,4,1); testDiag("Queue with immediate start"); postjobs(1,1,0); postjobs(0,1,0); postjobs(4,4,0); postjobs(0,4,0); postjobs(2,4,0); testcleanup(); testreadd(); testcancel(); testshared(); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsThreadPriorityTest.cpp0000664000577000060420000000761213557101274022656 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsdThreadPriorityTest.cpp */ /* Author: Marty Kraimer Date: 21NOV2005 */ #include #include #include #include #include #include #include #include "epicsThread.h" #include "epicsEvent.h" #include "epicsExit.h" #include "epicsUnitTest.h" #include "testMain.h" typedef struct info { epicsEventId waitForMaster; epicsEventId waitForClient; }info; extern "C" { static void client(void *arg) { info *pinfo = (info *)arg; epicsThreadId idSelf = epicsThreadGetIdSelf(); int pass; for (pass = 0 ; pass < 3 ; pass++) { epicsEventWaitStatus status; status = epicsEventWait(pinfo->waitForMaster); testOk(status == epicsEventWaitOK, "task %p epicsEventWait returned %d", idSelf, status); epicsThreadSleep(0.01); epicsEventSignal(pinfo->waitForClient); } } static void runThreadPriorityTest(void *arg) { epicsEventId testComplete = (epicsEventId) arg; unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); epicsThreadId myId = epicsThreadGetIdSelf(); epicsThreadSetPriority(myId, 50); info *pinfo = (info *)calloc(1, sizeof(info)); pinfo->waitForMaster = epicsEventMustCreate(epicsEventEmpty); pinfo->waitForClient = epicsEventMustCreate(epicsEventEmpty); epicsThreadId clientId = epicsThreadCreate("client", 50, stackSize, client, pinfo); epicsEventSignal(pinfo->waitForMaster); epicsEventWaitStatus status; status = epicsEventWaitWithTimeout(pinfo->waitForClient, 0.1); if (!testOk(status == epicsEventWaitOK, "epicsEventWaitWithTimeout returned %d", status)) { testSkip(2, "epicsEventWaitWithTimeout failed"); goto done; } epicsThreadSetPriority(clientId, 20); /* expect that client will not be able to run */ epicsEventSignal(pinfo->waitForMaster); status = epicsEventTryWait(pinfo->waitForClient); if (status != epicsEventWaitTimeout) { testFail("epicsEventTryWait returned %d", status); } else { status = epicsEventWaitWithTimeout(pinfo->waitForClient, 0.1); if (!testOk(status == epicsEventWaitOK, "epicsEventWaitWithTimeout returned %d", status)) { testSkip(1, "epicsEventWaitWithTimeout failed"); goto done; } } epicsThreadSetPriority(clientId, 80); /* expect that client will be able to run */ epicsEventSignal(pinfo->waitForMaster); status = epicsEventTryWait(pinfo->waitForClient); if (status==epicsEventWaitOK) { testPass("Strict priority scheduler"); } else { testDiag("No strict priority scheduler"); status = epicsEventWaitWithTimeout(pinfo->waitForClient,.1); testOk(status == epicsEventWaitOK, "epicsEventWaitWithTimeout returned %d", status); } done: epicsThreadSleep(1.0); epicsEventSignal(testComplete); } } /* extern "C" */ MAIN(epicsThreadPriorityTest) { testPlan(7); epicsEventId testComplete = epicsEventMustCreate(epicsEventEmpty); epicsThreadMustCreate("threadPriorityTest", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), runThreadPriorityTest, testComplete); epicsEventWaitStatus status = epicsEventWait(testComplete); testOk(status == epicsEventWaitOK, "epicsEventWait returned %d", status); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsThreadPrivateTest.cpp0000664000577000060420000000250313557101274022441 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Jeff Hill Date: March 28 2001 */ #include #include "epicsTime.h" #include "epicsThread.h" #include "epicsUnitTest.h" #include "testMain.h" static epicsThreadPrivate < bool > priv; extern "C" void epicsThreadPrivateTestThread ( void * ) { testOk1 ( NULL == priv.get () ); bool var = true; priv.set ( &var ); testOk1 ( &var == priv.get () ); } MAIN(epicsThreadPrivateTest) { testPlan(5); bool var = false; priv.set ( &var ); testOk1 ( &var == priv.get() ); epicsThreadCreate ( "epicsThreadPrivateTest", epicsThreadPriorityMax, epicsThreadGetStackSize ( epicsThreadStackSmall ), epicsThreadPrivateTestThread, 0 ); epicsThreadSleep ( 1.0 ); testOk1 ( &var == priv.get() ); priv.set ( NULL ); testOk1 ( NULL == priv.get() ); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsThreadTest.cpp0000664000577000060420000001265513557101274021117 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsThreadTest.cpp */ #include #include #include #include #include #include #include #include #include "epicsThread.h" #include "epicsEvent.h" #include "epicsTime.h" #include "errlog.h" #include "epicsUnitTest.h" #include "testMain.h" namespace { static epicsThreadPrivate privateKey; class myThread: public epicsThreadRunable { public: myThread(int arg,const char *name); virtual ~myThread(); virtual void run(); epicsThread thread; epicsEvent startEvt; private: int *argvalue; }; myThread::myThread(int arg,const char *name) : thread(*this,name,epicsThreadStackSmall,50+arg), argvalue(0) { argvalue = new int; *argvalue = arg; thread.start(); } myThread::~myThread() {delete argvalue;} void myThread::run() { startEvt.signal(); int *pset = argvalue; privateKey.set(argvalue); int *pget = privateKey.get(); testOk1(pget == pset); epicsThreadId self = epicsThreadGetIdSelf(); testOk1(thread.getPriority() == epicsThreadGetPriority(self)); } void testMyThread() { const int ntasks = 3; myThread *myThreads[ntasks]; int startPriority = 0; for (int i = 0; i < ntasks; i++) { char name[10]; sprintf(name, "t%d", i); myThreads[i] = new myThread(i, name); if (i == 0) startPriority = myThreads[i]->thread.getPriority(); myThreads[i]->thread.setPriority(startPriority + i); } for (int i = 0; i < ntasks; i++) { myThreads[i]->startEvt.wait(); myThreads[i]->thread.exitWait(); delete myThreads[i]; } } struct joinStuff { epicsThreadOpts *opts; epicsEvent *trigger; epicsEvent *finished; }; void donothing(void *arg) {} void dowait(void *arg) { epicsEvent *trigger = (epicsEvent *) arg; trigger->wait(); epicsThreadSleep(0.1); } void dodelay(void *arg) { epicsThreadSleep(2.0); } void joinTests(void *arg) { struct joinStuff *stuff = (struct joinStuff *) arg; // Task finishes before parent joins epicsThreadId tid = epicsThreadCreateOpt("nothing", &donothing, 0, stuff->opts); epicsThreadSleep(0.1); epicsThreadMustJoin(tid); // Parent joins before task finishes tid = epicsThreadCreateOpt("await", &dowait, stuff->trigger, stuff->opts); stuff->trigger->signal(); epicsThreadMustJoin(tid); // Parent gets delayed until task finishes epicsTime start, end; start = epicsTime::getCurrent(); tid = epicsThreadCreateOpt("delay", &dodelay, 0, stuff->opts); epicsThreadMustJoin(tid); end = epicsTime::getCurrent(); double duration = end - start; #ifndef EPICS_THREAD_CAN_JOIN testTodoBegin("Thread join doesn't work"); #endif testOk(duration > 1.0, "Join delayed parent (%g seconds)", duration); testTodoEnd(); // This is a no-op epicsThreadId self = epicsThreadGetIdSelf(); epicsThreadMustJoin(self); // This is a no-op as well, except for a warning. eltc(0); epicsThreadMustJoin(self); eltc(1); stuff->finished->signal(); } void testJoining() { epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; epicsEvent finished, trigger; struct joinStuff stuff = { &opts, &trigger, &finished }; opts.priority = 50; opts.joinable = 1; epicsThreadCreateOpt("parent", &joinTests, &stuff, &opts); // as selfjoin joins itself, we can't. testOk(finished.wait(10.0), "Join tests completed"); } } // namespace typedef struct info { int isOkToBlock; int didSomething; } info; extern "C" { static void thread(void *arg) { info *pinfo = (info *)arg; epicsThreadSetOkToBlock(pinfo->isOkToBlock); testOk(epicsThreadIsOkToBlock() == pinfo->isOkToBlock, "%s epicsThreadIsOkToBlock() = %d", epicsThreadGetNameSelf(), pinfo->isOkToBlock); pinfo->didSomething = 1; } } static void testOkToBlock() { epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; opts.priority = 50; opts.joinable = 1; info infoA = {0, 0}; epicsThreadId threadA = epicsThreadCreateOpt("threadA", thread, &infoA, &opts); info infoB = {1, 0}; epicsThreadId threadB = epicsThreadCreateOpt("threadB", thread, &infoB, &opts); // join B first to better our chance of detecting if it never runs. epicsThreadMustJoin(threadB); testOk1(infoB.didSomething); epicsThreadMustJoin(threadA); testOk1(infoA.didSomething); } MAIN(epicsThreadTest) { testPlan(13); unsigned int ncpus = epicsThreadGetCPUs(); testDiag("System has %u CPUs", ncpus); testOk1(ncpus > 0); testDiag("main() thread %p", epicsThreadGetIdSelf()); testJoining(); // Do this first, ~epicsThread() uses it... testMyThread(); testOkToBlock(); // attempt to self-join from a non-EPICS thread // to make sure it does nothing as expected eltc(0); epicsThreadMustJoin(epicsThreadGetIdSelf()); eltc(1); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsTimeTest.cpp0000664000577000060420000002207113557101274020577 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Authors: Jeff Hill, Marty Kraimer and Andrew Johnson */ #include #include #include #include #include #include #include "epicsTime.h" #include "epicsThread.h" #include "errlog.h" #include "epicsUnitTest.h" #include "testMain.h" using namespace std; /* The functionality of the old invalidFormatTest () and badNanosecTest () * routines is incorporated into epicsTimeTest () below. */ struct l_fp { /* NTP time stamp */ epicsUInt32 l_ui; /* sec past NTP epoch */ epicsUInt32 l_uf; /* fractional seconds */ }; static const unsigned mSecPerSec = 1000u; static const unsigned uSecPerSec = 1000u * mSecPerSec; static const unsigned nSecPerSec = 1000u * uSecPerSec; static const double precisionEPICS = 1.0 / nSecPerSec; static void crossCheck(double delay) { double mindelta = 2*epicsMonotonicResolution()*1e-9, tres = epicsThreadSleepQuantum(); epicsUInt64 A = epicsMonotonicGet(); epicsThreadSleep(delay); epicsUInt64 B = epicsMonotonicGet(); double actual = (B-A)*1e-9, percent; if(mindelta") == 0, "undefined => '%s'", buf); // This is Noon GMT, when all timezones have the same date const epicsTimeStamp tTS = {12*60*60, 98765432}; et = tTS; pFormat = "%Y-%m-%d %S.%09f"; // %H and %M change with timezone et.strftime(buf, sizeof(buf), pFormat); testOk(strcmp(buf, "1990-01-01 00.098765432") == 0, "'%s' => '%s'", pFormat, buf); pFormat = "%S.%03f"; et.strftime(buf, sizeof(buf), pFormat); testOk(strcmp(buf, "00.099") == 0, "'%s' => '%s'", pFormat, buf); pFormat = "%S.%04f"; et.strftime(buf, sizeof(buf), pFormat); testOk(strcmp(buf, "00.0988") == 0, "'%s' => '%s'", pFormat, buf); pFormat = "%S.%05f"; et.strftime(buf, sizeof(buf), pFormat); testOk(strcmp(buf, "00.09877") == 0, "'%s' => '%s'", pFormat, buf); pFormat = "%S.%05f %S.%05f"; et.strftime(buf, sizeof(buf), pFormat); testOk(strcmp(buf, "00.09877 00.09877") == 0, "'%s' => '%s'", pFormat, buf); char smbuf[5]; pFormat = "%S.%05f"; et.strftime(smbuf, sizeof(smbuf), pFormat); testOk(strcmp(smbuf, "00.*") == 0, "'%s' => '%s'", pFormat, smbuf); pFormat = "%S.%03f"; (et + 0.9).strftime(buf, sizeof(buf), pFormat); testOk(strcmp(buf, "00.999") == 0, "0.998765 => '%s'", buf); pFormat = "%S.%03f"; (et + 0.901).strftime(buf, sizeof(buf), pFormat); testOk(strcmp(buf, "00.999") == 0, "0.999765 => '%s'", buf); pFormat = "%%S.%%05f"; et.strftime(buf, sizeof(buf), pFormat); testOk(strcmp(buf, "%S.%05f") == 0, "'%s' => '%s'", pFormat, buf); char bigBuf [512]; memset(bigBuf, '\a', sizeof(bigBuf)); bigBuf[ sizeof(bigBuf) - 1] = '\0'; et.strftime(buf, sizeof(buf), bigBuf); testOk(strcmp(buf, "") == 0, "bad format => '%s'", buf); } epicsTime now; try { now = epicsTime::getCurrent(); testPass("default time provider"); } catch ( ... ) { testFail("epicsTime::getCurrent() throws"); testAbort("Can't continue, check your time provider"); } { l_fp ntp = now; epicsTime tsf = ntp; const double diff = fabs(tsf - now); // the difference in the precision of the two time formats static const double precisionNTP = 1.0 / (1.0 + 0xffffffff); testOk1(diff <= precisionEPICS + precisionNTP); } testDiag("Running %d loops", nTimes); const epicsTime begin = epicsTime::getCurrent(); for (int loop = 0; loop < nTimes; ++loop) { for (int i = 0; i < wasteTime; ++i) { now = epicsTime::getCurrent(); } const double diff = now - begin; if (loop == 0) { testDiag ("%d calls to epicsTime::getCurrent() " "averaged %6.3f usec each", wasteTime, diff * 1e6 / wasteTime); } epicsTime copy = now; testOk1(copy == now); testOk1(copy <= now); testOk1(copy >= now); testOk1(now > begin); testOk1(now >= begin); testOk1(begin != now); testOk1(begin < now); testOk1(begin <= now); testOk1(now - now == 0); testOk(fabs((now - begin) - diff) < precisionEPICS * 0.01, "now - begin ~= diff"); testOk1(begin + 0 == begin); testOk1(begin + diff == now); testOk1(now - 0 == now); testOk1(now - diff == begin); epicsTime end = begin; end += diff; testOk(end == now, "(begin += diff) == now"); end = now; end -= diff; testOk(end == begin, "(now -= diff) == begin"); // test struct tm round-trip conversion local_tm_nano_sec ansiDate = begin; epicsTime beginANSI = ansiDate; testOk1(beginANSI + diff == now); // test struct gmtm round-trip conversion gm_tm_nano_sec ansiGmDate = begin; epicsTime beginGMANSI = ansiGmDate; testOk1(beginGMANSI + diff == now); // test struct timespec round-trip conversion struct timespec ts = begin; epicsTime beginTS = ts; testOk1(beginTS + diff == now); } epicsTime ten_years_hence; try { now = epicsTime::getCurrent(); ten_years_hence = now + 60 * 60 * 24 * 3652.5; testPass("epicsTime can represent 10 years hence"); } catch ( ... ) { testFail("epicsTime exception for value 10 years hence"); } try { /* This test exists because in libCom/osi/os/posix/osdTime.cpp * the convertDoubleToWakeTime() routine limits the timeout delay * to 10 years. libCom/timer/timerQueue.cpp returns DBL_MAX for * queues with no timers present, and convertDoubleToWakeTime() * has to return an absolute Posix timestamp. On 2028-01-19 any * systems that still implement time_t as a signed 32-bit integer * will be unable to represent that timestamp, so this will fail. */ time_t_wrapper os_time_t = ten_years_hence; epicsTime then = os_time_t; // No fractional seconds double delta = ten_years_hence - then; testOk(delta >= 0 && delta < 1.0, "OS time_t can represent 10 years hence"); } catch ( ... ) { testFail("OS time_t conversion exception for value 10 years hence"); } testMonotonic(); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsTimeZoneTest.c0000664000577000060420000000770713557101274021104 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2015 Michael Davidsaver * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include "envDefs.h" #include "epicsTime.h" #include "epicsUnitTest.h" #include "testMain.h" #if defined(_WIN32) # define tzset _tzset #endif static void setTZ(const char *tz) { testDiag("TZ = \"%s\"", tz); epicsEnvSet("TZ", tz); tzset(); } static void test_localtime(time_t T, int sec, int min, int hour, int mday, int mon, int year, int wday, int yday, int isdst) { struct tm B; testDiag("test_localtime(%ld, ...)", (long)T); if(epicsTime_localtime(&T, &B)!=epicsTimeOK) { testFail("epicsTime_localtime() error"); testSkip(9, "epicsTime_localtime() failed"); } else { B.tm_year += 1900; /* for readability */ testPass("epicsTime_localtime() success"); #define TEST(FLD) testOk(B.tm_##FLD==FLD, "%s %d==%d", #FLD, B.tm_##FLD, FLD) TEST(sec); TEST(min); TEST(hour); TEST(mday); TEST(mon); TEST(year); TEST(wday); TEST(yday); TEST(isdst); #undef TEST } } static void test_gmtime(time_t T, int sec, int min, int hour, int mday, int mon, int year, int wday, int yday, int isdst) { struct tm B; testDiag("test_gmtime(%ld, ...)", (long)T); if(epicsTime_gmtime(&T, &B)!=epicsTimeOK) { testFail("epicsTime_localtime() error"); testSkip(9, "epicsTime_localtime() failed"); } else { B.tm_year += 1900; /* for readability */ testPass("epicsTime_localtime() success"); #define TEST(FLD) testOk(B.tm_##FLD==FLD, "%s %d==%d", #FLD, B.tm_##FLD, FLD) TEST(sec); TEST(min); TEST(hour); TEST(mday); TEST(mon); TEST(year); TEST(wday); TEST(yday); TEST(isdst); #undef TEST } } MAIN(epicsTimeZoneTest) { testPlan(160); /* 1445259616 * Mon Oct 19 09:00:16 2015 EDT * Mon Oct 19 08:00:16 2015 CDT * Mon Oct 19 03:00:16 2015 HST (no dst) * Mon Oct 19 13:00:16 2015 UTC */ testDiag("POSIX 1445259616"); setTZ("EST5EDT"); test_localtime(1445259616ul, 16, 0, 9, 19, 9, 2015, 1, 291, 1); test_gmtime (1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0); setTZ("CST6CDT"); test_localtime(1445259616ul, 16, 0, 8, 19, 9, 2015, 1, 291, 1); test_gmtime (1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0); #if defined(__rtems__) setTZ("HST10HST10"); #else setTZ("HST10"); #endif test_localtime(1445259616ul, 16, 0, 3, 19, 9, 2015, 1, 291, 0); test_gmtime (1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0); setTZ("UTC0"); test_localtime(1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0); test_gmtime (1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0); /* 1421244931 * Wed Jan 14 09:15:31 2015 EST * Wed Jan 14 08:15:31 2015 CST * Wed Jan 14 04:15:31 2015 HST * Wed Jan 14 14:15:31 2015 UTC */ testDiag("POSIX 1421244931"); setTZ("EST5EDT"); test_localtime(1421244931ul, 31, 15, 9, 14, 0, 2015, 3, 13, 0); test_gmtime (1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0); setTZ("CST6CDT"); test_localtime(1421244931ul, 31, 15, 8, 14, 0, 2015, 3, 13, 0); test_gmtime (1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0); #if defined(__rtems__) setTZ("HST10HST10"); #else setTZ("HST10"); #endif test_localtime(1421244931ul, 31, 15, 4, 14, 0, 2015, 3, 13, 0); test_gmtime (1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0); setTZ("UTC0"); test_localtime(1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0); test_gmtime (1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsTimerTest.cpp0000664000577000060420000002744713557101274020775 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include #include #include "epicsTimer.h" #include "epicsEvent.h" #include "epicsAssert.h" #include "epicsGuard.h" #include "tsFreeList.h" #include "epicsUnitTest.h" #include "testMain.h" #define verify(exp) ((exp) ? (void)0 : \ epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) class notified : public epicsTimerNotify { public: bool done; notified() : epicsTimerNotify(), done(false) {} expireStatus expire(const epicsTime ¤tTime) {done=true; return expireStatus(noRestart);} }; void testRefCount() { notified action; epicsTimerQueueActive *Q1, *Q2; epicsTimer *T1, *T2; Q1 = &epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); T1 = &Q1->createTimer(); //timer->start(action, 0.0); Q2 = &epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); testOk1(Q1==Q2); T2 = &Q2->createTimer(); T2->destroy(); Q2->release(); T1->destroy(); Q1->release(); } static const double delayVerifyOffset = 1.0; // sec class delayVerify : public epicsTimerNotify { public: delayVerify ( double expectedDelay, epicsTimerQueue & ); void start ( const epicsTime &expireTime ); void setBegin ( const epicsTime & ); double delay () const; double checkError () const; protected: virtual ~delayVerify (); private: epicsTimer &timer; epicsTime beginStamp; epicsTime expireStamp; double expectedDelay; expireStatus expire ( const epicsTime & ); delayVerify ( const delayVerify & ); delayVerify & operator = ( const delayVerify & ); }; static volatile unsigned expireCount; static epicsEvent expireEvent; delayVerify::delayVerify ( double expectedDelayIn, epicsTimerQueue &queueIn ) : timer ( queueIn.createTimer() ), expectedDelay ( expectedDelayIn ) { } delayVerify::~delayVerify () { this->timer.destroy (); } inline void delayVerify::setBegin ( const epicsTime &beginIn ) { this->beginStamp = beginIn; } inline double delayVerify::delay () const { return this->expectedDelay; } double delayVerify::checkError () const { const double messageThresh = 5.0; // percent double actualDelay = this->expireStamp - this->beginStamp; double measuredError = actualDelay - this->expectedDelay; double percentError = 100.0 * fabs ( measuredError ) / this->expectedDelay; if ( ! testOk ( percentError < messageThresh, "%f < %f", percentError, messageThresh ) ) { testDiag ( "delay = %f s, error = %f s (%.1f %%)", this->expectedDelay, measuredError, percentError ); } return measuredError; } inline void delayVerify::start ( const epicsTime &expireTime ) { this->timer.start ( *this, expireTime ); } epicsTimerNotify::expireStatus delayVerify::expire ( const epicsTime ¤tTime ) { this->expireStamp = currentTime; if ( --expireCount == 0u ) { expireEvent.signal (); } return noRestart; } // // verify reasonable timer interval accuracy // void testAccuracy () { static const unsigned nTimers = 25u; delayVerify *pTimers[nTimers]; unsigned i; unsigned timerCount = 0; testDiag ( "Testing timer accuracy" ); epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMax ); for ( i = 0u; i < nTimers; i++ ) { pTimers[i] = new delayVerify ( i * 0.1 + delayVerifyOffset, queue ); timerCount += pTimers[i] ? 1 : 0; } testOk1 ( timerCount == nTimers ); expireCount = nTimers; for ( i = 0u; i < nTimers; i++ ) { epicsTime cur = epicsTime::getMonotonic (); pTimers[i]->setBegin ( cur ); pTimers[i]->start ( cur + pTimers[i]->delay () ); } while ( expireCount != 0u ) { expireEvent.wait (); } double averageMeasuredError = 0.0; for ( i = 0u; i < nTimers; i++ ) { averageMeasuredError += pTimers[i]->checkError (); } averageMeasuredError /= nTimers; testDiag ("average timer delay error %f ms", averageMeasuredError * 1000 ); queue.release (); } class cancelVerify : public epicsTimerNotify { public: cancelVerify ( epicsTimerQueue & ); void start ( const epicsTime &expireTime ); void cancel (); static unsigned cancelCount; static unsigned expireCount; protected: virtual ~cancelVerify (); private: epicsTimer &timer; expireStatus expire ( const epicsTime & ); cancelVerify ( const cancelVerify & ); cancelVerify & operator = ( const cancelVerify & ); }; unsigned cancelVerify::cancelCount; unsigned cancelVerify::expireCount; cancelVerify::cancelVerify ( epicsTimerQueue &queueIn ) : timer ( queueIn.createTimer () ) { } cancelVerify::~cancelVerify () { this->timer.destroy (); } inline void cancelVerify::start ( const epicsTime &expireTime ) { this->timer.start ( *this, expireTime ); } inline void cancelVerify::cancel () { this->timer.cancel (); ++cancelVerify::cancelCount; } epicsTimerNotify::expireStatus cancelVerify::expire ( const epicsTime & ) { ++cancelVerify::expireCount; double root = 3.14159; for ( unsigned i = 0u; i < 1000; i++ ) { root = sqrt ( root ); } return noRestart; } // // verify that expire() won't be called after the timer is cancelled // void testCancel () { static const unsigned nTimers = 25u; cancelVerify *pTimers[nTimers]; unsigned i; unsigned timerCount = 0; cancelVerify::cancelCount = 0; cancelVerify::expireCount = 0; testDiag ( "Testing timer cancellation" ); epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); for ( i = 0u; i < nTimers; i++ ) { pTimers[i] = new cancelVerify ( queue ); timerCount += pTimers[i] ? 1 : 0; } testOk1 ( timerCount == nTimers ); if ( ! testOk1 ( cancelVerify::expireCount == 0 ) ) testDiag ( "expireCount = %u", cancelVerify::expireCount ); if ( ! testOk1 ( cancelVerify::cancelCount == 0 ) ) testDiag ( "cancelCount = %u", cancelVerify::cancelCount ); testDiag ( "starting %d timers", nTimers ); epicsTime exp = epicsTime::getMonotonic () + 4.0; for ( i = 0u; i < nTimers; i++ ) { pTimers[i]->start ( exp ); } testOk1 ( cancelVerify::expireCount == 0 ); testOk1 ( cancelVerify::cancelCount == 0 ); testDiag ( "cancelling timers" ); for ( i = 0u; i < nTimers; i++ ) { pTimers[i]->cancel (); } testOk1 ( cancelVerify::expireCount == 0 ); testOk1 ( cancelVerify::cancelCount == nTimers ); testDiag ( "waiting until after timers should have expired" ); epicsThreadSleep ( 5.0 ); testOk1 ( cancelVerify::expireCount == 0 ); testOk1 ( cancelVerify::cancelCount == nTimers ); epicsThreadSleep ( 1.0 ); queue.release (); } class expireDestroyVerify : public epicsTimerNotify { public: expireDestroyVerify ( epicsTimerQueue & ); void start ( const epicsTime &expireTime ); static unsigned destroyCount; protected: virtual ~expireDestroyVerify (); private: epicsTimer & timer; expireStatus expire ( const epicsTime & ); expireDestroyVerify ( const expireDestroyVerify & ); expireDestroyVerify & operator = ( const expireDestroyVerify & ); }; unsigned expireDestroyVerify::destroyCount; expireDestroyVerify::expireDestroyVerify ( epicsTimerQueue & queueIn ) : timer ( queueIn.createTimer () ) { } expireDestroyVerify::~expireDestroyVerify () { this->timer.destroy (); ++expireDestroyVerify::destroyCount; } inline void expireDestroyVerify::start ( const epicsTime & expireTime ) { this->timer.start ( *this, expireTime ); } epicsTimerNotify::expireStatus expireDestroyVerify::expire ( const epicsTime & ) { delete this; return noRestart; } // // verify that a timer can be destroyed in expire // void testExpireDestroy () { static const unsigned nTimers = 25u; expireDestroyVerify *pTimers[nTimers]; unsigned i; unsigned timerCount = 0; expireDestroyVerify::destroyCount = 0; testDiag ( "Testing timer destruction in expire()" ); epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); for ( i = 0u; i < nTimers; i++ ) { pTimers[i] = new expireDestroyVerify ( queue ); timerCount += pTimers[i] ? 1 : 0; } testOk1 ( timerCount == nTimers ); testOk1 ( expireDestroyVerify::destroyCount == 0 ); testDiag ( "starting %d timers", nTimers ); epicsTime cur = epicsTime::getMonotonic (); for ( i = 0u; i < nTimers; i++ ) { pTimers[i]->start ( cur ); } testDiag ( "waiting until all timers should have expired" ); epicsThreadSleep ( 5.0 ); testOk1 ( expireDestroyVerify::destroyCount == nTimers ); queue.release (); } class periodicVerify : public epicsTimerNotify { public: periodicVerify ( epicsTimerQueue & ); void start ( const epicsTime &expireTime ); void cancel (); bool verifyCount (); protected: virtual ~periodicVerify (); private: epicsTimer &timer; unsigned nExpire; bool cancelCalled; expireStatus expire ( const epicsTime & ); periodicVerify ( const periodicVerify & ); periodicVerify & operator = ( const periodicVerify & ); }; periodicVerify::periodicVerify ( epicsTimerQueue & queueIn ) : timer ( queueIn.createTimer () ), nExpire ( 0u ), cancelCalled ( false ) { } periodicVerify::~periodicVerify () { this->timer.destroy (); } inline void periodicVerify::start ( const epicsTime &expireTime ) { this->timer.start ( *this, expireTime ); } inline void periodicVerify::cancel () { this->timer.cancel (); this->cancelCalled = true; } inline bool periodicVerify::verifyCount () { return ( this->nExpire > 1u ); } epicsTimerNotify::expireStatus periodicVerify::expire ( const epicsTime & ) { this->nExpire++; double root = 3.14159; for ( unsigned i = 0u; i < 1000; i++ ) { root = sqrt ( root ); } verify ( ! this->cancelCalled ); double delay = rand (); delay = delay / RAND_MAX; delay /= 10.0; return expireStatus ( restart, delay ); } // // verify periodic timers // void testPeriodic () { static const unsigned nTimers = 25u; periodicVerify *pTimers[nTimers]; unsigned i; unsigned timerCount = 0; testDiag ( "Testing periodic timers" ); epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); for ( i = 0u; i < nTimers; i++ ) { pTimers[i] = new periodicVerify ( queue ); timerCount += pTimers[i] ? 1 : 0; } testOk1 ( timerCount == nTimers ); testDiag ( "starting %d timers", nTimers ); epicsTime cur = epicsTime::getMonotonic (); for ( i = 0u; i < nTimers; i++ ) { pTimers[i]->start ( cur ); } testDiag ( "waiting until all timers should have expired" ); epicsThreadSleep ( 5.0 ); bool notWorking = false; for ( i = 0u; i < nTimers; i++ ) { notWorking |= ! pTimers[i]->verifyCount (); pTimers[i]->cancel (); } testOk( ! notWorking, "All timers expiring" ); epicsThreadSleep ( 1.0 ); queue.release (); } MAIN(epicsTimerTest) { testPlan(41); testRefCount(); testAccuracy (); testCancel (); testExpireDestroy (); testPeriodic (); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsTypesTest.c0000664000577000060420000000267013557101274020450 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsTypesTest.c * * Size-check epicsTypes * */ #include "epicsUnitTest.h" #include "epicsTypes.h" #include "testMain.h" #include "epicsAssert.h" /* * Might as well check at compile-time too, since we can. */ STATIC_ASSERT(sizeof(epicsInt8) == 1); STATIC_ASSERT(sizeof(epicsUInt8) == 1); STATIC_ASSERT(sizeof(epicsInt16) == 2); STATIC_ASSERT(sizeof(epicsUInt16) == 2); STATIC_ASSERT(sizeof(epicsInt32) == 4); STATIC_ASSERT(sizeof(epicsUInt32) == 4); STATIC_ASSERT(sizeof(epicsInt64) == 8); STATIC_ASSERT(sizeof(epicsUInt64) == 8); STATIC_ASSERT(sizeof(epicsFloat32) == 4); STATIC_ASSERT(sizeof(epicsFloat64) == 8); MAIN(epicsTypesTest) { testPlan(10); testOk1(sizeof(epicsInt8) == 1); testOk1(sizeof(epicsUInt8) == 1); testOk1(sizeof(epicsInt16) == 2); testOk1(sizeof(epicsUInt16) == 2); testOk1(sizeof(epicsInt32) == 4); testOk1(sizeof(epicsUInt32) == 4); testOk1(sizeof(epicsInt64) == 8); testOk1(sizeof(epicsUInt64) == 8); testOk1(sizeof(epicsFloat32) == 4); testOk1(sizeof(epicsFloat64) == 8); return testDone(); } base-7.0.3.1/modules/libcom/test/epicsUnitTestTest.c0000664000577000060420000000175513557101274021126 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 The University of Chicago, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Andrew Johnson * * Test for the unit test module... */ #include "epicsUnitTest.h" #define testOk1_success 1 #define testOk1_failure 0 int main (void) { testPlan(11); testOk(1, "testOk(1)"); testOk(0, "testOk(0)"); testPass("testPass()"); testFail("testFail()"); testSkip(2, "Skipping two"); testTodoBegin("Testing Todo"); testOk(1, "Todo pass"); testOk(0, "Todo fail"); testSkip(1, "Todo skip"); testTodoEnd(); testOk1(testOk1_success); testOk1(testOk1_failure); testDiag("Diagnostic"); /* testAbort("testAbort"); */ return testDone(); } base-7.0.3.1/modules/libcom/test/epicsUnitTestTest.plt0000664000577000060420000000105413557101274021473 0ustar anjaesctl#!/usr/bin/perl use strict; use Test; BEGIN {plan tests => 1} my $prog = "./$0"; $prog =~ s/\.t$//; my $expected = join '', ; $ENV{HARNESS_ACTIVE} = 1; my $result = `$prog`; ok($result, $expected); # test output matches __DATA__ 1..11 ok 1 - testOk(1) not ok 2 - testOk(0) ok 3 - testPass() not ok 4 - testFail() ok 5 # SKIP Skipping two ok 6 # SKIP Skipping two ok 7 - Todo pass # TODO Testing Todo not ok 8 - Todo fail # TODO Testing Todo ok 9 # SKIP Todo skip ok 10 - testOk1_success not ok 11 - testOk1_failure # Diagnostic base-7.0.3.1/modules/libcom/test/fdmgrTest.c0000664000577000060420000000665413557101274017425 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include "fdmgr.h" #include "epicsTime.h" #include "epicsAssert.h" #include "cadef.h" #define verify(exp) ((exp) ? (void)0 : \ epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) static const unsigned uSecPerSec = 1000000; typedef struct cbStructCreateDestroyFD { fdctx *pfdm; int trig; } cbStructCreateDestroyFD; void fdHandler (void *pArg) { cbStructCreateDestroyFD *pCBFD = (cbStructCreateDestroyFD *) pArg; printf ("triggered\n"); pCBFD->trig = 1; } void fdCreateDestroyHandler (void *pArg, int fd, int open) { cbStructCreateDestroyFD *pCBFD = (cbStructCreateDestroyFD *) pArg; int status; if (open) { printf ("new fd = %d\n", fd); status = fdmgr_add_callback (pCBFD->pfdm, fd, fdi_read, fdHandler, pArg); verify (status==0); } else { printf ("terminated fd = %d\n", fd); status = fdmgr_clear_callback (pCBFD->pfdm, fd, fdi_read); verify (status==0); } } typedef struct cbStuctTimer { epicsTimeStamp time; int done; } cbStruct; void alarmCB (void *parg) { cbStruct *pCBS = (cbStruct *) parg; epicsTimeGetCurrent (&pCBS->time); pCBS->done = 1; } void testTimer (fdctx *pfdm, double delay) { int status; fdmgrAlarmId aid; struct timeval tmo; epicsTimeStamp begin; cbStruct cbs; double measuredDelay; double measuredError; epicsTimeGetCurrent (&begin); cbs.done = 0; tmo.tv_sec = (time_t) delay; tmo.tv_usec = (unsigned long) ((delay - tmo.tv_sec) * uSecPerSec); aid = fdmgr_add_timeout (pfdm, &tmo, alarmCB, &cbs); verify (aid!=fdmgrNoAlarm); while (!cbs.done) { tmo.tv_sec = (time_t) delay; tmo.tv_usec = (unsigned long) ((delay - tmo.tv_sec) * uSecPerSec); status = fdmgr_pend_event (pfdm, &tmo); verify (status==0); } measuredDelay = epicsTimeDiffInSeconds (&cbs.time, &begin); measuredError = fabs (measuredDelay-delay); printf ("measured delay for %lf sec was off by %lf sec (%lf %%)\n", delay, measuredError, 100.0*measuredError/delay); } int main (int argc, char **argv) { int status; fdctx *pfdm; cbStructCreateDestroyFD cbsfd; struct timeval tmo; chid chan; pfdm = fdmgr_init (); verify (pfdm); SEVCHK (ca_task_initialize(), NULL); cbsfd.pfdm = pfdm; SEVCHK (ca_add_fd_registration (fdCreateDestroyHandler, &cbsfd), NULL); /* * timer test */ testTimer (pfdm, 0.001); testTimer (pfdm, 0.01); testTimer (pfdm, 0.1); testTimer (pfdm, 1.0); if (argc==2) { SEVCHK(ca_search (argv[1], &chan), NULL); } while (1) { tmo.tv_sec = 0; tmo.tv_usec = 100000; cbsfd.trig = 0; status = fdmgr_pend_event (pfdm, &tmo); verify (status==0); ca_poll (); } status = fdmgr_delete (pfdm); verify (status==0); printf ( "Test Complete\n" ); return 0; } base-7.0.3.1/modules/libcom/test/iocshTest.cpp0000664000577000060420000000700413557101274017761 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2019 Michael Davidsaver * EPICS BASE 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 namespace { void findTestData() { const char *locations[] = { ".", "..", ".." OSI_PATH_LIST_SEPARATOR "O.Common", "O.Common", }; for(size_t i=0; i %d", expect ? "ran" : "expected error from", fname, err); } void testCmd(const char *cmd, bool expect=true) { testDiag("eval \"%s\"", cmd); int err = iocshCmd(cmd); testOk((err==0) ^ (!expect), "%s \"%s\" -> %d", expect ? "eval'd" : "expected error from", cmd, err); } std::set reached; const iocshArg positionArg0 = {"position", iocshArgString}; const iocshArg * const positionArgs[1] = { &positionArg0 }; const iocshFuncDef positionFuncDef = {"position",1,positionArgs}; void positionCallFunc(const iocshArgBuf *args) { testDiag("Reaching \"%s\"", args[0].sval); reached.insert(args[0].sval); } void testPosition(const std::string& pos, bool expect=true) { testOk((reached.find(pos)!=reached.end()) ^ !expect, "%sreached position %s", expect ? "" : "not ", pos.c_str()); } const iocshArg assertArg0 = {"condition", iocshArgInt}; const iocshArg * const assertArgs[1] = {&assertArg0}; const iocshFuncDef assertFuncDef = {"assert",1,assertArgs}; void assertCallFunc(const iocshArgBuf *args) { iocshSetError(args[0].ival); } } // namespace MAIN(iocshTest) { testPlan(19); libComRegister(); iocshRegister(&positionFuncDef, &positionCallFunc); iocshRegister(&assertFuncDef, &assertCallFunc); findTestData(); testFile("iocshTestSuccess.cmd"); testPosition("success"); reached.clear(); testPosition("success", false); testCmd(" #include #define EPICS_PRIVATE_API #include "epicsMutex.h" #include "epicsGuard.h" #include "epicsThread.h" #include "epicsEvent.h" #include "ipAddrToAsciiAsynchronous.h" #include "epicsUnitTest.h" #include "testMain.h" namespace { typedef epicsGuard Guard; typedef epicsGuardRelease UnGuard; struct CB : public ipAddrToAsciiCallBack { const char *name; epicsMutex mutex; epicsEvent starter, blocker, complete; bool started, cont, done; CB(const char *name) : name(name), started(false), cont(false), done(false) {} virtual ~CB() {} virtual void transactionComplete ( const char * pHostName ) { Guard G(mutex); started = true; starter.signal(); testDiag("In transactionComplete(%s) for %s", pHostName, name); while(!cont) { UnGuard U(G); if(!blocker.wait(2.0)) break; } done = true; complete.signal(); } void waitStart() { Guard G(mutex); while(!started) { UnGuard U(G); if(!starter.wait(2.0)) break; } } void poke() { testDiag("Poke"); Guard G(mutex); cont = true; blocker.signal(); } void finish() { testDiag("Finish"); Guard G(mutex); while(!done) { UnGuard U(G); if(!complete.wait(2.0)) break; } testDiag("Finished"); } }; // ensure that lookup of 127.0.0.1 works void doLookup(ipAddrToAsciiEngine& engine) { testDiag("In doLookup"); ipAddrToAsciiTransaction& trn(engine.createTransaction()); CB cb("cb"); osiSockAddr addr; addr.ia.sin_family = AF_INET; addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.ia.sin_port = htons(42); testDiag("Start lookup"); trn.ipAddrToAscii(addr, cb); cb.poke(); cb.finish(); testOk1(cb.cont); testOk1(cb.done); trn.release(); } // Test cancel of pending transaction void doCancel() { testDiag("In doCancel"); ipAddrToAsciiEngine& engine1(ipAddrToAsciiEngine::allocate()); ipAddrToAsciiEngine& engine2(ipAddrToAsciiEngine::allocate()); ipAddrToAsciiTransaction& trn1(engine1.createTransaction()), & trn2(engine2.createTransaction()); testOk1(&trn1!=&trn2); CB cb1("cb1"), cb2("cb2"); osiSockAddr addr; addr.ia.sin_family = AF_INET; addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.ia.sin_port = htons(42); // ensure that the worker thread is blocked with a transaction from engine1 testDiag("Start lookup1"); trn1.ipAddrToAscii(addr, cb1); testDiag("Wait start1"); cb1.waitStart(); testDiag("Start lookup2"); trn2.ipAddrToAscii(addr, cb2); testDiag("release engine2, implicitly cancels lookup2"); engine2.release(); cb2.poke(); testDiag("Wait for lookup2 timeout"); cb2.finish(); testOk1(!cb2.done); testDiag("Complete lookup1"); cb1.poke(); cb1.finish(); testOk1(cb1.done); engine1.release(); trn1.release(); trn2.release(); } } // namespace MAIN(ipAddrToAsciiTest) { testPlan(5); { ipAddrToAsciiEngine& engine(ipAddrToAsciiEngine::allocate()); doLookup(engine); engine.release(); } doCancel(); // TODO: somehow test cancel of in-progress callback // allow time for any un-canceled transcations to crash us... epicsThreadSleep(1.0); #ifdef __linux__ ipAddrToAsciiEngine::cleanup(); #endif return testDone(); } base-7.0.3.1/modules/libcom/test/macDefExpandTest.c0000664000577000060420000001705413557101274020641 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include #include #include "macLib.h" #include "envDefs.h" #include "errlog.h" #include "epicsUnitTest.h" #include "testMain.h" static void checkMac(MAC_HANDLE *handle, const char *name, const char *value) { char buf[20]; buf[19]='\0'; if(macGetValue(handle, name, buf, 19)<0) { if(value) testFail("Macro %s undefined, expected %s", name, value); else testPass("Macro %s undefined", name); } else { if(!value) testFail("Macro %s is %s, expected undefined", name, buf); else if(strcmp(value, buf)==0) testPass("Macro %s is %s", name, value); else testFail("Macro %s is %s, expected %s", name, buf, value); } } static void macEnvScope(void) { MAC_HANDLE *handle; char **defines; static const char *pairs[] = { "", "environ", NULL, NULL }; epicsEnvSet("C","3"); epicsEnvSet("D","4"); epicsEnvSet("E","5"); macCreateHandle(&handle, pairs); macParseDefns(NULL, "A=1,B=2,E=15", &defines); macInstallMacros(handle, defines); checkMac(handle, "A", "1"); checkMac(handle, "B", "2"); checkMac(handle, "C", "3"); checkMac(handle, "D", "4"); checkMac(handle, "E", "15"); checkMac(handle, "F", NULL); { macPushScope(handle); macParseDefns(NULL, "A=11,C=13,D=14,G=7", &defines); macInstallMacros(handle, defines); checkMac(handle, "A", "11"); checkMac(handle, "B", "2"); checkMac(handle, "C", "13"); checkMac(handle, "D", "14"); checkMac(handle, "E", "15"); checkMac(handle, "F", NULL); checkMac(handle, "G", "7"); epicsEnvSet("D", "24"); macPutValue(handle, "D", NULL); /* implicit when called through in iocshBody */ epicsEnvSet("F", "6"); macPutValue(handle, "F", NULL); /* implicit */ epicsEnvSet("G", "17"); macPutValue(handle, "G", NULL); /* implicit */ checkMac(handle, "D", "24"); checkMac(handle, "F", "6"); checkMac(handle, "G", "17"); macPopScope(handle); } checkMac(handle, "A", "1"); checkMac(handle, "B", "2"); checkMac(handle, "C", "3"); checkMac(handle, "D", "24"); checkMac(handle, "E", "15"); checkMac(handle, "F", "6"); checkMac(handle, "G", "17"); { macPushScope(handle); macParseDefns(NULL, "D=34,G=27", &defines); macInstallMacros(handle, defines); checkMac(handle, "D", "34"); checkMac(handle, "G", "27"); macPopScope(handle); } checkMac(handle, "D", "24"); macDeleteHandle(handle); } static void check(const char *str, const char *macros, const char *expect) { MAC_HANDLE *handle; char **defines; static const char *pairs[] = { "", "environ", NULL, NULL }; char *got; int pass = 1; macCreateHandle(&handle, pairs); macParseDefns(NULL, macros, &defines); macInstallMacros(handle, defines); got = macDefExpand(str, handle); if (expect && !got) { testDiag("Got NULL, expected \"%s\".\n", expect); pass = 0; } else if (!expect && got) { testDiag("Got \"%s\", expected NULL.\n", got); pass = 0; } else if (expect && got && strcmp(got, expect)) { testDiag("Got \"%s\", expected \"%s\".\n", got, expect); pass = 0; } testOk(pass, "%s", str); macDeleteHandle(handle); } MAIN(macDefExpandTest) { eltc(0); testPlan(97); check("FOO", "", "FOO"); check("${FOO}", "", NULL); check("${FOO,BAR}", "", NULL); check("${FOO,BAR=baz}", "", NULL); check("${FOO,BAR=$(FOO)}", "", NULL); check("${FOO,FOO}", "", NULL); check("${FOO,FOO=$(FOO)}", "", NULL); check("${FOO,BAR=baz,FUM}", "", NULL); check("${=}", "", ""); check("x${=}y", "", "xy"); check("${,=}", "", ""); check("x${,=}y", "", "xy"); check("${FOO=}", "", ""); check("x${FOO=}y", "", "xy"); check("${FOO=,}", "", ""); check("x${FOO=,}y", "", "xy"); check("${FOO,FOO=}", "", ""); check("x${FOO,FOO=}y", "", "xy"); check("${FOO=,BAR}", "", ""); check("x${FOO=,BAR}y", "", "xy"); check("${FOO=$(BAR=)}", "", ""); check("x${FOO=$(BAR=)}y", "", "xy"); check("${FOO=,BAR=baz}", "", ""); check("x${FOO=,BAR=baz}y", "", "xy"); check("${FOO=$(BAR),BAR=}", "", ""); check("x${FOO=$(BAR),BAR=}y", "", "xy"); check("${=BAR}", "", "BAR"); check("x${=BAR}y", "", "xBARy"); check("${FOO=BAR}", "", "BAR"); check("x${FOO=BAR}y", "", "xBARy"); epicsEnvSet("FOO","BLETCH"); check("${FOO}", "", "BLETCH"); check("${FOO,FOO}", "", "BLETCH"); check("x${FOO}y", "", "xBLETCHy"); check("x${FOO}y${FOO}z", "", "xBLETCHyBLETCHz"); check("${FOO=BAR}", "", "BLETCH"); check("x${FOO=BAR}y", "", "xBLETCHy"); check("${FOO=${BAZ}}", "", "BLETCH"); check("${FOO=${BAZ},BAR=$(BAZ)}", "", "BLETCH"); check("x${FOO=${BAZ}}y", "", "xBLETCHy"); check("x${FOO=${BAZ},BAR=$(BAZ)}y", "", "xBLETCHy"); check("${BAR=${FOO}}", "", "BLETCH"); check("x${BAR=${FOO}}y", "", "xBLETCHy"); check("w${BAR=x${FOO}y}z", "", "wxBLETCHyz"); check("${FOO,FOO=BAR}", "", "BAR"); check("x${FOO,FOO=BAR}y", "", "xBARy"); check("${BAR,BAR=$(FOO)}", "", "BLETCH"); check("x${BAR,BAR=$(FOO)}y", "", "xBLETCHy"); check("${BAR,BAR=$($(FOO)),BLETCH=GRIBBLE}", "", "GRIBBLE"); check("x${BAR,BAR=$($(FOO)),BLETCH=GRIBBLE}y", "", "xGRIBBLEy"); check("${$(BAR,BAR=$(FOO)),BLETCH=GRIBBLE}", "", "GRIBBLE"); check("x${$(BAR,BAR=$(FOO)),BLETCH=GRIBBLE}y", "", "xGRIBBLEy"); check("${FOO}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); check("x${FOO}/${BAR}y", "BAR=GLEEP", "xBLETCH/GLEEPy"); check("${FOO,BAR}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); check("${FOO,BAR=x}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); check("${BAZ=BLETCH,BAR}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); check("${BAZ=BLETCH,BAR=x}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); check("${${FOO}}", "BAR=GLEEP,BLETCH=BAR", "BAR"); check("x${${FOO}}y", "BAR=GLEEP,BLETCH=BAR", "xBARy"); check("${${FOO}=GRIBBLE}", "BAR=GLEEP,BLETCH=BAR", "BAR"); check("x${${FOO}=GRIBBLE}y", "BAR=GLEEP,BLETCH=BAR", "xBARy"); check("${${FOO}}", "BAR=GLEEP,BLETCH=${BAR}", "GLEEP"); epicsEnvSet("FOO","${BAR}"); check("${FOO}", "BAR=GLEEP,BLETCH=${BAR}" ,"GLEEP"); check("${FOO}", "BAR=${BAZ},BLETCH=${BAR}", NULL); check("${FOO}", "BAR=${BAZ=GRIBBLE},BLETCH=${BAR}", "GRIBBLE"); check("${FOO}", "BAR=${STR1},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", "VAL1"); check("${FOO}", "BAR=${STR2},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", "VAL2"); check("${FOO}", "BAR=${FOO},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", NULL); check("${FOO,FOO=$(FOO)}", "BAR=${FOO},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", NULL); check("${FOO=$(FOO)}", "BAR=${FOO},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", NULL); check("${FOO=$(BAR),BAR=$(FOO)}", "BAR=${FOO},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", NULL); macEnvScope(); errlogFlush(); eltc(1); return testDone(); } base-7.0.3.1/modules/libcom/test/macLib.plt0000664000577000060420000000476113557101274017227 0ustar anjaesctl#!/usr/bin/perl use lib '@TOP@/lib/perl'; use Test::More tests => 35; use EPICS::macLib; use Data::Dumper; my $m = EPICS::macLib->new; isa_ok $m, 'EPICS::macLib'; is $m->expandString(''), '', 'Empty string'; { local *STDERR; my $output; open STDERR, '>', \$output; is $m->expandString('$(undef)'), undef, 'Warning $(undef)'; chomp $output; is $output, q/macLib: macro 'undef' is undefined (expanding string '$(undef)')/, 'macLib error message'; } $m->suppressWarning(1); is $m->expandString('$(undef)'), '$(undef)', 'Suppressed $(undef)'; $m->putValue('a', 'foo'); is $m->expandString('$(a)'), 'foo', '$(a)'; is $m->expandString('${a}'), 'foo', '${a}'; is $m->expandString('$(a=bar)'), 'foo', '$(a=bar)'; is $m->expandString('${a=bar}'), 'foo', '${a=bar}'; is $m->expandString('$(undef)'), '$(undef)', '$(undef) again'; is $m->expandString('${undef}'), '$(undef)', '${undef} again'; $m->suppressWarning(0); is $m->expandString('$(undef=$(a))'), 'foo', '$(undef=$(a))'; is $m->expandString('${undef=${a}}'), 'foo', '${undef=${a}}'; is $m->expandString('${undef=$(a)}'), 'foo', '${undef=$(a)}'; is $m->expandString('$(undef=${a})'), 'foo', '$(undef=${a})'; is $m->expandString('$(a=$(undef))'), 'foo', '$(a=$(undef))'; $m->putValue('b', 'baz'); is $m->expandString('$(b)'), 'baz', '$(b)'; is $m->expandString('$(a)'), 'foo', '$(a)'; is $m->expandString('$(a)$(b)'), 'foobaz', '$(a)$(b)'; is $m->expandString('$(a)/$(b)'), 'foo/baz', '$(a)/$(b)'; is $m->expandString('$(a)\$(b)'), 'foo\$(b)', '$(a)\$(b)'; is $m->expandString('$(a)$$(b)'), 'foo$baz', '$(a)$$(b)'; $m->putValue('c', '$(a)'); is $m->expandString('$(c)'), 'foo', '$(c)'; is $m->expandString('$(undef=$(c))'), 'foo', '$(undef=$(c))'; $m->putValue('d', 'c'); is $m->expandString('$(d)'), 'c', '$(d)'; is $m->expandString('$($(d))'), 'foo', '$($(d))'; is $m->expandString('$($(b)=$(a))'), 'foo', '$($(b)=$(a))'; $m->suppressWarning(1); $m->putValue('c', undef); is $m->expandString('$(c)'), '$(c)', '$(c) deleted'; $m->installMacros('c=fum,d'); is $m->expandString('$(c)'), 'fum', 'installMacros, $(c)'; is $m->expandString('$(d)'), '$(d)', 'installMacros deletion'; $m->pushScope; is $m->expandString('$(a)'), 'foo', 'pushScope, $(a)'; $m->putValue('a', 'grinch'); is $m->expandString('$(a)'), 'grinch', 'new $(a) in child'; $m->putValue('b', undef); is $m->expandString('$(b)'), '$(b)', '$(b) deleted in child'; $m->popScope; is $m->expandString('$(a)'), 'foo', 'popScope, $(a) restored'; is $m->expandString('$(b)'), 'baz', '$(b) restored'; base-7.0.3.1/modules/libcom/test/macLibTest.c0000664000577000060420000001626613557101274017515 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include #include #include "macLib.h" #include "dbDefs.h" #include "envDefs.h" #include "errlog.h" #include "epicsUnitTest.h" #include "testMain.h" MAC_HANDLE *h; static void check(const char *str, const char *expect) { char output[MAC_SIZE] = {'\0'}; long status = macExpandString(h, str, output, MAC_SIZE); long expect_len = strlen(expect+1); int expect_error = (expect[0] == '!'); int statBad = expect_error ^ (status < 0); int strBad = strcmp(output, expect+1); testOk(!statBad && !strBad, "%s => %s", str, output); if (strBad) { testDiag("Got \"%s\", expected \"%s\"", output, expect+1); } if (statBad) { testDiag("Return status was %ld, expected %ld", status, expect_error ? -expect_len : expect_len); } } static void ovcheck(void) { char output[54]; long status; macPutValue(h, "OVVAR","abcdefghijklmnopqrstuvwxyz"); memset(output, '~', sizeof output); status = macExpandString(h, "abcdefghijklmnopqrstuvwxyz$(OVVAR)", output, 52); testOk(status == 51, "expansion returned %ld, expected 51", status); testOk(output[50] == 'y', "final character %x, expect 79 (y)", output[50]); testOk(output[51] == '\0', "terminator character %x, expect 0", output[51]); testOk(output[52] == '~', "sentinel character %x, expect 7e, (~)", output[52]); memset(output, '~', sizeof output); status = macExpandString(h, "abcdefghijklmnopqrstuvwxyz$(OVVAR)", output, 53); testOk(status == 52, "expansion returned %ld, expected 52", status); testOk(output[51] == 'z', "final character %x, expect 7a (z)", output[51]); testOk(output[52] == '\0', "terminator character %x, expect 0", output[52]); testOk(output[53] == '~', "sentinel character %x, expect 7e, (~)", output[53]); } MAIN(macLibTest) { testPlan(93); if (macCreateHandle(&h, NULL)) testAbort("macCreateHandle() failed"); eltc(0); check("FOO", " FOO"); check("$(FOO)", "!$(FOO,undefined)"); check("${FOO}", "!$(FOO,undefined)"); check("${FOO=${FOO}}", "!$(FOO,undefined)"); check("${FOO,FOO}", "!$(FOO,undefined)"); check("${FOO,FOO=${FOO}}", "!$(FOO,recursive)"); check("${FOO,BAR}", "!$(FOO,undefined)"); check("${FOO,BAR=baz}", "!$(FOO,undefined)"); check("${FOO,BAR=${FOO}}", "!$(FOO,undefined)"); check("${FOO,BAR=baz,FUM}", "!$(FOO,undefined)"); check("${FOO=${BAR},BAR=${FOO}}", "!$(FOO,undefined)"); check("${FOO,FOO=${BAR},BAR=${FOO}}", "!$(BAR,recursive)"); check("${=}", " "); check("x${=}y", " xy"); check("${,=}", " "); check("x${,=}y", " xy"); check("${FOO=}", " "); check("x${FOO=}y", " xy"); check("${FOO=,}", " "); check("x${FOO=,}y", " xy"); check("${FOO,FOO=}", " "); check("x${FOO,FOO=}y", " xy"); check("${FOO=,BAR}", " "); check("x${FOO=,BAR}y", " xy"); check("${FOO=${BAR=}}", " "); check("x${FOO=${BAR=}}y", " xy"); check("${FOO=,BAR=baz}", " "); check("x${FOO=,BAR=baz}y", " xy"); check("${FOO=${BAR},BAR=}", " "); check("x${FOO=${BAR},BAR=}y", " xy"); check("${=BAR}", " BAR"); check("x${=BAR}y", " xBARy"); check("${FOO=BAR}", " BAR"); check("x${FOO=BAR}y", " xBARy"); macPutValue(h, "FOO", "BLETCH"); /* FOO = "BLETCH" */ check("${FOO}", " BLETCH"); check("${FOO,FOO}", " BLETCH"); check("x${FOO}y", " xBLETCHy"); check("x${FOO}y${FOO}z", " xBLETCHyBLETCHz"); check("${FOO=BAR}", " BLETCH"); check("x${FOO=BAR}y", " xBLETCHy"); check("${FOO=${BAZ}}", " BLETCH"); check("${FOO=${BAZ},BAR=${BAZ}}", " BLETCH"); check("x${FOO=${BAZ}}y", " xBLETCHy"); check("x${FOO=${BAZ},BAR=${BAZ}}y", " xBLETCHy"); check("${BAR=${FOO}}", " BLETCH"); check("x${BAR=${FOO}}y", " xBLETCHy"); check("w${BAR=x${FOO}y}z", " wxBLETCHyz"); check("${FOO,FOO=BAR}", " BAR"); check("x${FOO,FOO=BAR}y", " xBARy"); check("${BAR,BAR=${FOO}}", " BLETCH"); check("x${BAR,BAR=${FOO}}y", " xBLETCHy"); check("${BAR,BAR=${${FOO}},BLETCH=GRIBBLE}", " GRIBBLE"); check("x${BAR,BAR=${${FOO}},BLETCH=GRIBBLE}y", " xGRIBBLEy"); check("${${BAR,BAR=${FOO}},BLETCH=GRIBBLE}", " GRIBBLE"); check("x${${BAR,BAR=${FOO}},BLETCH=GRIBBLE}y", " xGRIBBLEy"); check("${N=${FOO}/${BAR},BAR=GLEEP}", " BLETCH/GLEEP"); macPutValue(h, "BAR","GLEEP"); /* FOO = "BLETCH" */ /* BAR = "GLEEP" */ check("${FOO}/${BAR}", " BLETCH/GLEEP"); check("x${FOO}/${BAR}y", " xBLETCH/GLEEPy"); check("${FOO,BAR}/${BAR}", " BLETCH/GLEEP"); check("${FOO,BAR=x}/${BAR}", " BLETCH/GLEEP"); check("${BAZ=BLETCH,BAR}/${BAR}", " BLETCH/GLEEP"); check("${BAZ=BLETCH,BAR=x}/${BAR}", " BLETCH/GLEEP"); check("${N=${FOO}/${BAR}}", " BLETCH/GLEEP"); macPutValue(h, "BLETCH","BAR"); /* FOO = "BLETCH" */ /* BLETCH = "BAR" */ check("${${FOO}}", " BAR"); check("x${${FOO}}y", " xBARy"); check("${${FOO}=GRIBBLE}", " BAR"); check("x${${FOO}=GRIBBLE}y", " xBARy"); macPutValue(h, "BLETCH","${BAR}"); /* FOO = "BLETCH" */ /* BLETCH = "${BAR}" */ /* BAR = "GLEEP" */ check("${${FOO}}", " GLEEP"); check("${BLETCH=${FOO}}", " GLEEP"); macPutValue(h, "FOO","${BAR}"); /* FOO = "${BAR}" */ /* BAR = "GLEEP" */ check("${FOO}", " GLEEP"); check("${FOO=BLETCH,BAR=BAZ}", " BAZ"); macPutValue(h, "BAR","${BAZ}"); /* FOO = "${BAR}" */ /* BAR = "${BAZ}" */ check("${FOO}", "!$(BAZ,undefined)"); macPutValue(h, "BAR","${BAZ=GRIBBLE}"); /* FOO = "${BAR}" */ /* BAR = "${BAZ=GRIBBLE}" */ check("${FOO}", " GRIBBLE"); check("${FOO,BAZ=GEEK}", " GEEK"); macPutValue(h, "BAR","${STR1}"); macPutValue(h, "STR1","VAL1"); macPutValue(h, "STR2","VAL2"); /* FOO = "${BAR}" */ /* BAR = "${STR1}" */ /* STR1 = "VAL1" */ /* STR2 = "VAL2" */ check("${FOO}", " VAL1"); macPutValue(h, "BAR","${STR2}"); /* FOO = "${BAR}" */ /* BAR = "${STR2}" */ /* STR1 = "VAL1" */ /* STR2 = "VAL2" */ check("${FOO}", " VAL2"); check("$(FOO)$(FOO1)", "!VAL2$(FOO1,undefined)"); check("$(FOO1)$(FOO)", "!$(FOO1,undefined)VAL2"); macPutValue(h, "BAR","${FOO}"); /* FOO = "${BAR}" */ /* BAR = "${FOO}" */ check("${FOO}", "!$(BAR,recursive)"); check("${FOO=GRIBBLE}", "!$(BAR,recursive)"); check("${FOO=GRIBBLE,BAR=${FOO}}", "!$(BAR,recursive)"); check("${FOO,FOO=${FOO}}", "!$(FOO,recursive)"); check("${FOO=GRIBBLE,FOO=${FOO}}", "!$(FOO,recursive)"); macSuppressWarning(h, TRUE); check("$(CRUX)", "!$(CRUX)"); check("${FOO}", "!$(BAR)"); ovcheck(); return testDone(); } base-7.0.3.1/modules/libcom/test/nonEpicsThreadPriorityTest.cpp0000664000577000060420000000524313557101274023327 0ustar anjaesctl#include #include "epicsUnitTest.h" #include "testMain.h" #include #include #ifdef __rtems__ /* RTEMS is posix but currently does not use the pthread API */ MAIN(nonEpicsThreadPriorityTest) { testPlan(1); testSkip(1, "Platform does not use pthread API"); return testDone(); } #else static void *nonEpicsTestFunc(void *arg) { unsigned int pri; // epicsThreadGetIdSelf() creates an EPICS context // verify that the priority computed by epics context // is OK pri = epicsThreadGetPriority( epicsThreadGetIdSelf() ); if ( ! testOk( 0 == pri, "'createImplicit' assigned correct priority (%d) to non-EPICS thread", pri) ) { return 0; } return (void*)1; } static void testFunc(void *arg) { epicsEventId ev = (epicsEventId)arg; int policy; struct sched_param param; int status; pthread_t tid; void *rval; pthread_attr_t attr; status = pthread_getschedparam(pthread_self(), &policy,¶m); if ( status ) { testSkip(1, "pthread_getschedparam failed"); goto done; } if ( SCHED_FIFO != policy ) { testSkip(1, "nonEpicsThreadPriorityTest must be executed with privileges to use SCHED_FIFO"); goto done; } if ( pthread_attr_init( &attr ) ) { testSkip(1, "pthread_attr_init failed"); goto done; } if ( pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ) ) { testSkip(1, "pthread_attr_setinheritsched failed"); goto done; } if ( pthread_attr_setschedpolicy ( &attr, SCHED_OTHER ) ) { testSkip(1, "pthread_attr_setschedpolicy failed"); goto done; } param.sched_priority = 0; if ( pthread_attr_setschedparam ( &attr, ¶m ) ) { testSkip(1, "pthread_attr_setschedparam failed"); goto done; } if ( pthread_create( &tid, &attr, nonEpicsTestFunc, 0 ) ) { testSkip(1, "pthread_create failed"); goto done; } if ( pthread_join( tid, &rval ) ) { testSkip(1, "pthread_join failed"); goto done; } done: epicsEventSignal( ev ); } MAIN(nonEpicsThreadPriorityTest) { testPlan(2); epicsEventId testComplete = epicsEventMustCreate(epicsEventEmpty); epicsThreadMustCreate("nonEpicsThreadPriorityTest", epicsThreadPriorityLow, epicsThreadGetStackSize(epicsThreadStackMedium), testFunc, testComplete); epicsEventWaitStatus status = epicsEventWait(testComplete); testOk(status == epicsEventWaitOK, "epicsEventWait returned %d", status); return testDone(); } #endif base-7.0.3.1/modules/libcom/test/osiSockTest.c0000664000577000060420000000472213557101274017732 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include "osiSock.h" #include "epicsUnitTest.h" #include "testMain.h" /* This could easily be generalized to test more options */ void udpBroadcast(SOCKET s, int put) { int status; int flag = put; osiSocklen_t len = sizeof(flag); status = setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&flag, len); testOk(status >= 0, "setsockopt BROADCAST := %d", put); status = getsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&flag, &len); testOk(status >= 0 && len == sizeof(flag) && !flag == !put, "getsockopt BROADCAST => %d", flag); } void multiCastLoop(SOCKET s, int put) { int status; osiSockOptMcastLoop_t flag = put; osiSocklen_t len = sizeof(flag); status = setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&flag, len); testOk(status >= 0, "setsockopt MULTICAST_LOOP := %d", put); status = getsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&flag, &len); testOk(status >= 0 && len == sizeof(flag) && !flag == !put, "getsockopt MULTICAST_LOOP => %d", (int) flag); } void multiCastTTL(SOCKET s, int put) { int status; osiSockOptMcastTTL_t flag = put; osiSocklen_t len = sizeof(flag); status = setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&flag, len); testOk(status >= 0, "setsockopt IP_MULTICAST_TTL := %d", put); status = getsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&flag, &len); testOk(status >= 0 && len == sizeof(flag) && !flag == !put, "getsockopt IP_MULTICAST_TTL => %d", (int) flag); } void udpSockTest(void) { SOCKET s; s = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); testOk(s != INVALID_SOCKET, "epicsSocketCreate INET, DGRAM, 0"); udpBroadcast(s, 1); udpBroadcast(s, 0); multiCastLoop(s, 1); multiCastLoop(s, 0); multiCastTTL(s, 1); multiCastTTL(s, 0); epicsSocketDestroy(s); } MAIN(osiSockTest) { int status; testPlan(14); status = osiSockAttach(); testOk(status, "osiSockAttach"); udpSockTest(); osiSockRelease(); return testDone(); } base-7.0.3.1/modules/libcom/test/ringBytesTest.c0000664000577000060420000001020113557101274020253 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* ringBytesTest.c */ #include #include #include #include #include #include #include #include "epicsThread.h" #include "epicsRingBytes.h" #include "errlog.h" #include "epicsEvent.h" #include "epicsUnitTest.h" #include "testMain.h" #define RINGSIZE 10 typedef struct info { epicsEventId consumerEvent; epicsRingBytesId ring; }info; static void check(epicsRingBytesId ring, int expectedFree, int expectedHighWaterMark) { int expectedUsed = RINGSIZE - expectedFree; int expectedEmpty = (expectedUsed == 0); int expectedFull = (expectedFree == 0); int nFree = epicsRingBytesFreeBytes(ring); int nUsed = epicsRingBytesUsedBytes(ring); int isEmpty = epicsRingBytesIsEmpty(ring); int isFull = epicsRingBytesIsFull(ring); int highWaterMark = epicsRingBytesHighWaterMark(ring); testOk(nFree == expectedFree, "Free: %d == %d", nFree, expectedFree); testOk(nUsed == expectedUsed, "Used: %d == %d", nUsed, expectedUsed); testOk(isEmpty == expectedEmpty, "Empty: %d == %d", isEmpty, expectedEmpty); testOk(isFull == expectedFull, "Full: %d == %d", isFull, expectedFull); testOk(highWaterMark == expectedHighWaterMark, "HighWaterMark: %d == %d", highWaterMark, expectedHighWaterMark); } MAIN(ringBytesTest) { int i, n; info *pinfo; epicsEventId consumerEvent; char put[RINGSIZE+1]; char get[RINGSIZE+1]; epicsRingBytesId ring; testPlan(292); pinfo = calloc(1,sizeof(info)); if (!pinfo) { testAbort("calloc failed"); } pinfo->consumerEvent = consumerEvent = epicsEventCreate(epicsEventEmpty); if (!consumerEvent) { testAbort("epicsEventCreate failed"); } pinfo->ring = ring = epicsRingBytesCreate(RINGSIZE); if (!ring) { testAbort("epicsRingBytesCreate failed"); } check(ring, RINGSIZE, 0); for (i = 0 ; i < sizeof(put) ; i++) put[i] = i; for(i = 0 ; i < RINGSIZE ; i++) { n = epicsRingBytesPut(ring, put, i); testOk(n==i, "ring put %d", i); check(ring, RINGSIZE-i, i); n = epicsRingBytesGet(ring, get, i); testOk(n==i, "ring get %d", i); check(ring, RINGSIZE, i); testOk(memcmp(put,get,i)==0, "get matches write"); } epicsRingBytesResetHighWaterMark(ring); for(i = 0 ; i < RINGSIZE ; i++) { n = epicsRingBytesPut(ring, put+i, 1); testOk(n==1, "ring put 1, %d", i); check(ring, RINGSIZE-1-i, i + 1); } n = epicsRingBytesPut(ring, put+RINGSIZE, 1); testOk(n==0, "put to full ring"); check(ring, 0, RINGSIZE); for(i = 0 ; i < RINGSIZE ; i++) { n = epicsRingBytesGet(ring, get+i, 1); testOk(n==1, "ring get 1, %d", i); check(ring, 1+i, RINGSIZE); } testOk(memcmp(put,get,RINGSIZE)==0, "get matches write"); n = epicsRingBytesGet(ring, get+RINGSIZE, 1); testOk(n==0, "get from empty ring"); check(ring, RINGSIZE, RINGSIZE); epicsRingBytesResetHighWaterMark(ring); n = epicsRingBytesPut(ring, put, RINGSIZE+1); testOk(n==0, "ring put beyond ring capacity (%d, expected 0)",n); check(ring, RINGSIZE, 0); n = epicsRingBytesPut(ring, put, 1); testOk(n==1, "ring put %d", 1); check(ring, RINGSIZE-1, 1); n = epicsRingBytesPut(ring, put, RINGSIZE); testOk(n==0, "ring put beyond ring capacity (%d, expected 0)",n); check(ring, RINGSIZE-1, 1); n = epicsRingBytesGet(ring, get, 1); testOk(n==1, "ring get %d", 1); check(ring, RINGSIZE, 1); epicsRingBytesDelete(ring); epicsEventDestroy(consumerEvent); free(pinfo); return testDone(); } base-7.0.3.1/modules/libcom/test/ringPointerTest.c0000664000577000060420000001565013557101274020622 0ustar anjaesctl/*************************************************************************\ * Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * Copyright (c) 2013 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* ringPointerTest.c */ /* Author: Marty Kraimer Date: 13OCT2000 */ #include #include #include #include #include #include #include #include "epicsThread.h" #include "epicsRingPointer.h" #include "errlog.h" #include "epicsEvent.h" #include "epicsUnitTest.h" #include "testMain.h" static void *int2ptr(size_t i) { char *zero = 0; i = i|(i<<16); return zero+i; } static int foundCorruption; static size_t ptr2int(void *p) { char *zero = 0, *p2 = p; size_t i = p2-zero; if((i&0xffff)!=((i>>16)&0xffff)) { testDiag("Pointer value corruption %p", p); foundCorruption = 1; } return i&0xffff; } static void testSingle(void) { int i; const int rsize = 100; void *addr = 0; epicsRingPointerId ring = epicsRingPointerCreate(rsize); foundCorruption = 0; testDiag("Testing operations w/o threading"); testOk1(epicsRingPointerIsEmpty(ring)); testOk1(!epicsRingPointerIsFull(ring)); testOk1(epicsRingPointerGetFree(ring)==rsize); testOk1(epicsRingPointerGetSize(ring)==rsize); testOk1(epicsRingPointerGetUsed(ring)==0); testOk1(epicsRingPointerGetHighWaterMark(ring)==0); testOk1(epicsRingPointerPop(ring)==NULL); addr = int2ptr(1); testOk1(epicsRingPointerPush(ring, addr)==1); testOk1(!epicsRingPointerIsEmpty(ring)); testOk1(!epicsRingPointerIsFull(ring)); testOk1(epicsRingPointerGetFree(ring)==rsize-1); testOk1(epicsRingPointerGetSize(ring)==rsize); testOk1(epicsRingPointerGetUsed(ring)==1); testOk1(epicsRingPointerGetHighWaterMark(ring)==1); epicsRingPointerResetHighWaterMark(ring); testOk1(epicsRingPointerGetHighWaterMark(ring)==1); testDiag("Fill it up"); for(i=2; i<2*rsize; i++) { int ret; addr = int2ptr(i); ret = epicsRingPointerPush(ring, addr); if(!ret) break; } /* Note: +1 because we started with 1 */ testOk(i==rsize+1, "%d == %d", i, rsize+1); testOk1(!epicsRingPointerIsEmpty(ring)); testOk1(epicsRingPointerIsFull(ring)); testOk1(epicsRingPointerGetFree(ring)==0); testOk1(epicsRingPointerGetSize(ring)==rsize); testOk1(epicsRingPointerGetUsed(ring)==rsize); testOk1(epicsRingPointerGetHighWaterMark(ring)==rsize); testDiag("Drain it out"); for(i=1; i<2*rsize; i++) { addr = epicsRingPointerPop(ring); if(addr==NULL || ptr2int(addr)!=i) break; } testOk1(!foundCorruption); testOk(i==rsize+1, "%d == %d", i, rsize+1); testOk1(epicsRingPointerIsEmpty(ring)); testOk1(!epicsRingPointerIsFull(ring)); testOk1(epicsRingPointerGetFree(ring)==rsize); testOk1(epicsRingPointerGetSize(ring)==rsize); testOk1(epicsRingPointerGetUsed(ring)==0); testOk1(epicsRingPointerGetHighWaterMark(ring)==rsize); testDiag("Fill it up again"); for(i=2; i<2*rsize; i++) { int ret; addr = int2ptr(i); ret = epicsRingPointerPush(ring, addr); if(!ret) break; } testDiag("flush"); testOk1(epicsRingPointerIsFull(ring)); epicsRingPointerFlush(ring); testOk1(epicsRingPointerIsEmpty(ring)); testOk1(!epicsRingPointerIsFull(ring)); testOk1(epicsRingPointerGetFree(ring)==rsize); testOk1(epicsRingPointerGetSize(ring)==rsize); testOk1(epicsRingPointerGetUsed(ring)==0); epicsRingPointerDelete(ring); } typedef struct { epicsRingPointerId ring; epicsEventId sync, wait; int stop; void *lastaddr; } pairPvt; static void pairConsumer(void *raw) { pairPvt *pvt = raw; void *prev = pvt->lastaddr; epicsEventMustTrigger(pvt->sync); while(1) { size_t c,p; epicsEventMustWait(pvt->wait); if(pvt->stop) break; pvt->lastaddr = epicsRingPointerPop(pvt->ring); c = ptr2int(pvt->lastaddr); p = ptr2int(prev); if(p+1!=c) { testFail("consumer skip %p %p", prev, pvt->lastaddr); break; } prev = pvt->lastaddr; } pvt->stop = 1; epicsEventMustTrigger(pvt->sync); } static void testPair(int locked) { unsigned int myprio = epicsThreadGetPrioritySelf(), consumerprio; pairPvt pvt; const int rsize = 100; int i, expect; epicsRingPointerId ring; if(locked) ring = epicsRingPointerLockedCreate(rsize); else ring = epicsRingPointerCreate(rsize); pvt.ring = ring; pvt.sync = epicsEventCreate(epicsEventEmpty); pvt.wait = epicsEventCreate(epicsEventEmpty); pvt.stop = 0; pvt.lastaddr = 0; foundCorruption = 0; testDiag("single producer, single consumer with%s locking", locked?"":"out"); /* give the consumer thread a slightly higher priority so that * it can preempt us on RTOS targets. On non-RTOS targets * we expect to be preempted at some random time */ if(!epicsThreadLowestPriorityLevelAbove(myprio, &consumerprio)) testAbort("Can't run test from thread with highest priority"); epicsThreadMustCreate("pair", consumerprio, epicsThreadGetStackSize(epicsThreadStackSmall), &pairConsumer, &pvt); /* wait for worker to start */ epicsEventMustWait(pvt.sync); i=1; while(i #include #include "taskwd.h" #include "errlog.h" #include "epicsThread.h" #include "epicsUnitTest.h" #include "testMain.h" /* This is a unique prefix used for the names of the test threads */ #define baseName "testTask" void monInsert(void *usr, epicsThreadId tid) { char tname[32]; epicsThreadGetName(tid, tname, sizeof(tname)); if (strncmp(tname, baseName, strlen(baseName)) == 0) testPass("monInsert(thread='%s')", tname); else testDiag("monInsert(thread='%s')", tname); } void monNotify(void *usr, epicsThreadId tid, int suspended) { char tname[32]; epicsThreadGetName(tid, tname, sizeof(tname)); testPass("monNotify(thread='%s', suspended=%d)", tname, suspended); epicsThreadResume(tid); } void monRemove(void *usr, epicsThreadId tid) { char tname[32]; epicsThreadGetName(tid, tname, sizeof(tname)); if (strncmp(tname, baseName, strlen(baseName)) == 0) testPass("monRemove(thread='%s')", tname); else testDiag("monRemove(thread='%s')", tname); } taskwdMonitor monFuncs = {monInsert, monNotify, monRemove}; void anyNotify(void *usr, epicsThreadId tid) { char tname[32]; epicsThreadGetName(tid, tname, sizeof(tname)); if (strncmp(tname, baseName, strlen(baseName)) == 0) testPass("anyNotify(thread='%s')", tname); else testDiag("anyNotify(thread='%s')", tname); } void taskNotify(void *usr) { const char *id = (const char *) usr; testPass("taskNotify id='%s'", id); } void testTask1(void *arg) { taskwdInsert(0, taskNotify, "1"); epicsThreadSleep(14.0); testDiag("Task 1 cleaning up"); taskwdRemove(0); } void testTask2(void *arg) { taskwdInsert(0, taskNotify, "2"); epicsThreadSleep(1.0); testDiag("Task 2 suspending"); epicsThreadSuspendSelf(); epicsThreadSleep(1.0); testDiag("Task 2 alive again"); epicsThreadSleep(6.0); testDiag("Task 2 cleaning up"); taskwdRemove(0); } MAIN(taskwdTest) { eltc(0); testPlan(8); taskwdInit(); taskwdMonitorAdd(&monFuncs, NULL); taskwdAnyInsert(NULL, anyNotify, NULL); epicsThreadCreate(baseName "1", epicsThreadPriorityMax, epicsThreadGetStackSize(epicsThreadStackSmall), testTask1, NULL); epicsThreadSleep(1.0); epicsThreadCreate(baseName "2", epicsThreadPriorityMax, epicsThreadGetStackSize(epicsThreadStackSmall), testTask2, NULL); /* taskwd checks tasks every 6 seconds */ epicsThreadSleep(18.0); taskwdMonitorDel(&monFuncs, NULL); taskwdAnyRemove(NULL); eltc(1); return testDone(); } base-7.0.3.1/modules/libcom/test/testexecname.c0000664000577000060420000000071213557101274020140 0ustar anjaesctl #include #include #include #include MAIN(testexecname) { testPlan(1); { char *buf = epicsGetExecName(); if(!buf) { testSkip(1, "epicsGetExecName() not available for this target"); } else { char *loc = strstr(buf, "testexecname"); testOk(!!loc, "Find \"testexecname\" in \"%s\"", buf); } } return testDone(); } base-7.0.3.1/modules/libcom/test/yajlTest.plt0000664000577000060420000000237213557101274017633 0ustar anjaesctl#!/usr/bin/perl # # This is a test director for running yajl JSON parser tests. # The tests are actually defined in the yajlTestCases.pm module, # which is generated from the yajl cases by yajlTestConverter.pl use strict; use Test::More; use IO::Handle; use IPC::Open3; # Load test cases use lib ".."; use yajlTestCases; my @cases = cases(); plan tests => scalar @cases; # The yajl_test program reads JSON from stdin and sends a description # of what it got to stdout, with errors going to stderr. We merge the # two output streams for the purpose of checking the test results. my $prog = './yajl_test'; $prog .= '.exe' if ($^O eq 'MSWin32') || ($^O eq 'cygwin'); foreach my $case (@cases) { my $name = $case->{name}; my @opts = @{$case->{opts}}; my @input = @{$case->{input}}; my @gives = @{$case->{gives}}; my ($rx, $tx); my $pid = open3($tx, $rx, 0, $prog, @opts); # Send the test case, then EOF print $tx join "\n", @input; close $tx; # Receive the result my @result; while (!$rx->eof) { chomp(my $line = <$rx>); push @result, $line; } close $rx; # Clean up the child process waitpid $pid, 0; # Report the result of this test case is_deeply(\@result, \@gives, $name); } base-7.0.3.1/modules/libcom/test/yajlTestCases.pm0000664000577000060420000023547513557101274020443 0ustar anjaesctl# Parser test cases from https://github.com/lloyd/yajl # # This file is generated, DO NOT EDIT! # # See comments in yajlTestConverter.pl for instructions on # how to regenerate this file from the original yajl sources. sub cases { my $VAR1 = [ { name => "difficult_json_c_test_case_with_comments", opts => [ "-c" ], input => [ "{ \"glossary\": { /* you */ \"title\": /**/ \"example glossary\", /*should*/\"GlossDiv\": { \"title\": /*never*/\"S\", /*ever*/\"GlossList\": [ { \"ID\": \"SGML\", \"SortAs\": \"SGML\", \"GlossTerm\": \"Standard Generalized Markup Language\", \"Acronym\": \"SGML\", \"Abbrev\": \"ISO 8879:1986\", \"GlossDef\": \"A meta-markup language, used to create markup languages such as DocBook.\", /*see*/\"GlossSeeAlso\"/*this*/:/*coming*/[/*out*/\"GML\"/*of*/,/*the*/\"XML\"/*parser!*/, \"markup\"] /*hey*/}/*ho*/]/*hey*/}/*ho*/} } // and the parser won't even get this far, so chill. /* hah!", "" ], gives => [ "map open '{'", "key: 'glossary'", "map open '{'", "key: 'title'", "string: 'example glossary'", "key: 'GlossDiv'", "map open '{'", "key: 'title'", "string: 'S'", "key: 'GlossList'", "array open '['", "map open '{'", "key: 'ID'", "string: 'SGML'", "key: 'SortAs'", "string: 'SGML'", "key: 'GlossTerm'", "string: 'Standard Generalized Markup Language'", "key: 'Acronym'", "string: 'SGML'", "key: 'Abbrev'", "string: 'ISO 8879:1986'", "key: 'GlossDef'", "string: 'A meta-markup language, used to create markup languages such as DocBook.'", "key: 'GlossSeeAlso'", "array open '['", "string: 'GML'", "string: 'XML'", "string: 'markup'", "array close ']'", "map close '}'", "array close ']'", "map close '}'", "map close '}'", "map close '}'", "memory leaks:\t0" ] }, { name => "simple_with_comments", opts => [ "-c" ], input => [ "{", " \"this\": \"is\", // ignore this", " \"really\": \"simple\",", " /* ignore", "this", "too * / ", "** //", "(/", "******/", " \"json\": \"right?\"", "}", "" ], gives => [ "map open '{'", "key: 'this'", "string: 'is'", "key: 'really'", "string: 'simple'", "key: 'json'", "string: 'right?'", "map close '}'", "memory leaks:\t0" ] }, { name => "false_then_garbage", opts => [ "-g" ], input => [ "falsex" ], gives => [ "bool: false", "memory leaks:\t0" ] }, { name => "null_then_garbage", opts => [ "-g" ], input => [ "nullx", "" ], gives => [ "null", "memory leaks:\t0" ] }, { name => "true_then_garbage", opts => [ "-g" ], input => [ "truex" ], gives => [ "bool: true", "memory leaks:\t0" ] }, { name => "eof", opts => [ "-m" ], input => [ "{ \"123\":", "" ], gives => [ "map open '{'", "key: '123'", "parse error: premature EOF", "memory leaks:\t0" ] }, { name => "integers", opts => [ "-m" ], input => [ "1221 21", "" ], gives => [ "integer: 1221", "integer: 21", "memory leaks:\t0" ] }, { name => "multiple", opts => [ "-m" ], input => [ "", "{}", "{}", "" ], gives => [ "map open '{'", "map close '}'", "map open '{'", "map close '}'", "memory leaks:\t0" ] }, { name => "stuff", opts => [ "-m" ], input => [ "{}", "[]", "[]", "\"sdfasd\"", 123, "{ \"123\" : 123 }", "3.1e124", "" ], gives => [ "map open '{'", "map close '}'", "array open '['", "array close ']'", "array open '['", "array close ']'", "string: 'sdfasd'", "integer: 123", "map open '{'", "key: '123'", "integer: 123", "map close '}'", "double: 3.1e+124", "memory leaks:\t0" ] }, { name => "array_open", opts => [ "-p" ], input => [ "[", "" ], gives => [ "array open '['", "memory leaks:\t0" ] }, { name => "eof_str", opts => [ "-p" ], input => [ "\"abc" ], gives => [ "memory leaks:\t0" ] }, { name => "map_open", opts => [ "-p" ], input => [ "{", "" ], gives => [ "map open '{'", "memory leaks:\t0" ] }, { name => "partial_ok", opts => [ "-p" ], input => [ "[ \"foo\", \"bar\"", "" ], gives => [ "array open '['", "string: 'foo'", "string: 'bar'", "memory leaks:\t0" ] }, { name => "array", opts => [], input => [ "[\"foo\",", " \"bar\", \"baz\",", " true,false,null,{\"key\":\"value\"},", " [null,null,null,[]],", " \"\\n\\r\\\\\"", "]", "" ], gives => [ "array open '['", "string: 'foo'", "string: 'bar'", "string: 'baz'", "bool: true", "bool: false", "null", "map open '{'", "key: 'key'", "string: 'value'", "map close '}'", "array open '['", "null", "null", "null", "array open '['", "array close ']'", "array close ']'", "string: '", "\r\\'", "array close ']'", "memory leaks:\t0" ] }, { name => "array_close", opts => [], input => [ "]", "" ], gives => [ "parse error: unallowed token at this point in JSON text", "memory leaks:\t0" ] }, { name => "bignums", opts => [], input => [ "[ 9223372036854775807, -9223372036854775807 ]", "" ], gives => [ "array open '['", "integer: 9223372036854775807", "integer: -9223372036854775807", "array close ']'", "memory leaks:\t0" ] }, { name => "bogus_char", opts => [], input => [ "[\"this\",\"is\",\"what\",\"should\",\"be\",", " \"a happy bit of json\",", " \"but someone, misspelled \\\"true\\\"\", ture,", " \"who says JSON is easy for humans to generate?\"]", "" ], gives => [ "array open '['", "string: 'this'", "string: 'is'", "string: 'what'", "string: 'should'", "string: 'be'", "string: 'a happy bit of json'", "string: 'but someone, misspelled \"true\"'", "lexical error: invalid string in json text.", "memory leaks:\t0" ] }, { name => "codepoints_from_unicode_org", opts => [], input => [ "\"\\u004d\\u0430\\u4e8c\\ud800\\udf02\"", "" ], gives => [ "string: 'M\320\260\344\272\214\360\220\214\202'", "memory leaks:\t0" ] }, { name => "deep_arrays", opts => [], input => [ "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" ], gives => [ "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array open '['", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "array close ']'", "memory leaks:\t0" ] }, { name => "difficult_json_c_test_case", opts => [], input => [ "{ \"glossary\": { \"title\": \"example glossary\", \"GlossDiv\": { \"title\": \"S\", \"GlossList\": [ { \"ID\": \"SGML\", \"SortAs\": \"SGML\", \"GlossTerm\": \"Standard Generalized Markup Language\", \"Acronym\": \"SGML\", \"Abbrev\": \"ISO 8879:1986\", \"GlossDef\": \"A meta-markup language, used to create markup languages such as DocBook.\", \"GlossSeeAlso\": [\"GML\", \"XML\", \"markup\"] } ] } } }", "" ], gives => [ "map open '{'", "key: 'glossary'", "map open '{'", "key: 'title'", "string: 'example glossary'", "key: 'GlossDiv'", "map open '{'", "key: 'title'", "string: 'S'", "key: 'GlossList'", "array open '['", "map open '{'", "key: 'ID'", "string: 'SGML'", "key: 'SortAs'", "string: 'SGML'", "key: 'GlossTerm'", "string: 'Standard Generalized Markup Language'", "key: 'Acronym'", "string: 'SGML'", "key: 'Abbrev'", "string: 'ISO 8879:1986'", "key: 'GlossDef'", "string: 'A meta-markup language, used to create markup languages such as DocBook.'", "key: 'GlossSeeAlso'", "array open '['", "string: 'GML'", "string: 'XML'", "string: 'markup'", "array close ']'", "map close '}'", "array close ']'", "map close '}'", "map close '}'", "map close '}'", "memory leaks:\t0" ] }, { name => "doubles", opts => [], input => [ "[ 0.1e2, 1e1, 3.141569, 10000000000000e-10]", "" ], gives => [ "array open '['", "double: 10", "double: 10", "double: 3.14157", "double: 1000", "array close ']'", "memory leaks:\t0" ] }, { name => "doubles_in_array", opts => [], input => [ "[0.00011999999999999999, 6E-06, 6E-06, 1E-06, 1E-06]", "" ], gives => [ "array open '['", "double: 0.00012", "double: 6e-06", "double: 6e-06", "double: 1e-06", "double: 1e-06", "array close ']'", "memory leaks:\t0" ] }, { name => "empty_array", opts => [], input => [ "[]" ], gives => [ "array open '['", "array close ']'", "memory leaks:\t0" ] }, { name => "empty_string", opts => [], input => [ "\"\"", "" ], gives => [ "string: ''", "memory leaks:\t0" ] }, { name => "escaped_bulgarian", opts => [], input => [ "[\"\\u0414\\u0430\",", " \"\\u041c\\u0443\",", " \"\\u0415\\u0431\\u0430\",", " \"\\u041c\\u0430\\u0439\\u043a\\u0430\\u0442\\u0430\"]", "" ], gives => [ "array open '['", "string: '\320\224\320\260'", "string: '\320\234\321\203'", "string: '\320\225\320\261\320\260'", "string: '\320\234\320\260\320\271\320\272\320\260\321\202\320\260'", "array close ']'", "memory leaks:\t0" ] }, { name => "escaped_foobar", opts => [], input => [ "\"\\u0066\\u006f\\u006f\\u0062\\u0061\\u0072\"", "" ], gives => [ "string: 'foobar'", "memory leaks:\t0" ] }, { name => "false", opts => [], input => [ "false", "" ], gives => [ "bool: false", "memory leaks:\t0" ] }, { name => "false_then_garbage", opts => [], input => [ "falsex" ], gives => [ "bool: false", "parse error: trailing garbage", "memory leaks:\t0" ] }, { name => "issue_7", opts => [], input => [ "2009-10-20\@20:38:21.539575", "" ], gives => [ "integer: 2009", "parse error: trailing garbage", "memory leaks:\t0" ] }, { name => "null_then_garbage", opts => [], input => [ "nullx", "" ], gives => [ "null", "parse error: trailing garbage", "memory leaks:\t0" ] }, { name => "true_then_garbage", opts => [], input => [ "truex", "" ], gives => [ "bool: true", "parse error: trailing garbage", "memory leaks:\t0" ] }, { name => "four_byte_utf8", opts => [], input => [ "{ \"U+10ABCD\": \"\364\212\257\215\" }", "" ], gives => [ "map open '{'", "key: 'U+10ABCD'", "string: '\364\212\257\215'", "map close '}'", "memory leaks:\t0" ] }, { name => "high_overflow", opts => [], input => [ "9223372036854775808" ], gives => [ "parse error: integer overflow", "memory leaks:\t0" ] }, { name => "integers", opts => [], input => [ "[ 1,2,3,4,5,6,7,", " 123456789 , -123456789,", " 2147483647, -2147483647 ]", "" ], gives => [ "array open '['", "integer: 1", "integer: 2", "integer: 3", "integer: 4", "integer: 5", "integer: 6", "integer: 7", "integer: 123456789", "integer: -123456789", "integer: 2147483647", "integer: -2147483647", "array close ']'", "memory leaks:\t0" ] }, { name => "invalid_utf8", opts => [], input => [ "[\"\320\224\320\260 \320\234\321 \320\225\320\261\320\260 \320\234\320\260\320\271\320\272\320\260\321\202\320\260\"]", "" ], gives => [ "array open '['", "lexical error: invalid bytes in UTF8 string.", "memory leaks:\t0" ] }, { name => "isolated_surrogate_marker", opts => [], input => [ "\"\\ud800\"", "" ], gives => [ "string: '?'", "memory leaks:\t0" ] }, { name => "leading_zero_in_number", opts => [], input => [ "{ \"bad thing\": 01 }", "" ], gives => [ "map open '{'", "key: 'bad thing'", "integer: 0", "parse error: after key and value, inside map, I expect ',' or '}'", "memory leaks:\t0" ] }, { name => "lonely_minus_sign", opts => [], input => [ "[", "\t\t\"foo\", true,", "\t\ttrue, \"blue\",", "\t\t\"baby where are you?\", \"oh boo hoo!\",", " - ", "]", "" ], gives => [ "array open '['", "string: 'foo'", "bool: true", "bool: true", "string: 'blue'", "string: 'baby where are you?'", "string: 'oh boo hoo!'", "lexical error: malformed number, a digit is required after the minus sign.", "memory leaks:\t0" ] }, { name => "lonely_number", opts => [], input => [ 123456789 ], gives => [ "integer: 123456789", "memory leaks:\t0" ] }, { name => "low_overflow", opts => [], input => [ "-9223372036854775808" ], gives => [ "parse error: integer overflow", "memory leaks:\t0" ] }, { name => "map_close", opts => [], input => [ "}", "" ], gives => [ "parse error: unallowed token at this point in JSON text", "memory leaks:\t0" ] }, { name => "missing_integer_after_decimal_point", opts => [], input => [ "10.e2", "" ], gives => [ "lexical error: malformed number, a digit is required after the decimal point.", "memory leaks:\t0" ] }, { name => "missing_integer_after_exponent", opts => [], input => [ "10e", "" ], gives => [ "lexical error: malformed number, a digit is required after the exponent.", "memory leaks:\t0" ] }, { name => "multiple", opts => [], input => [ "", "{}", "{}", "" ], gives => [ "map open '{'", "map close '}'", "parse error: trailing garbage", "memory leaks:\t0" ] }, { name => "non_utf8_char_in_string", opts => [], input => [ "{\"CoreletAPIVersion\":2,\"CoreletType\":\"standalone\",\"documentation\":\"A corelet that provides the capability to upload a folder\222s contents into a user\222s locker.\",\"functions\":[{\"documentation\":\"Displays a dialog box that allows user to select a folder on the local system.\",\"name\":\"ShowBrowseDialog\",\"parameters\":[{\"documentation\":\"The callback function for results.\",\"name\":\"callback\",\"required\":true,\"type\":\"callback\"}]},{\"documentation\":\"Uploads all mp3 files in the folder provided.\",\"name\":\"UploadFolder\",\"parameters\":[{\"documentation\":\"The path to upload mp3 files from.\",\"name\":\"path\",\"required\":true,\"type\":\"string\"},{\"documentation\":\"The callback function for progress.\",\"name\":\"callback\",\"required\":true,\"type\":\"callback\"}]},{\"documentation\":\"Returns the server name to the current locker service.\",\"name\":\"GetLockerService\",\"parameters\":[]},{\"documentation\":\"Changes the name of the locker service.\",\"name\":\"SetLockerService\",\"parameters\":[{\"documentation\":\"The value of the locker service to set active.\",\"name\":\"LockerService\",\"required\":true,\"type\":\"string\"}]},{\"documentation\":\"Downloads locker files to the suggested folder.\",\"name\":\"DownloadFile\",\"parameters\":[{\"documentation\":\"The origin path of the locker file.\",\"name\":\"path\",\"required\":true,\"type\":\"string\"},{\"documentation\":\"The Window destination path of the locker file.\",\"name\":\"destination\",\"required\":true,\"type\":\"integer\"},{\"documentation\":\"The callback function for progress.\",\"name\":\"callback\",\"required\":true,\"type\":\"callback\"}]}],\"name\":\"LockerUploader\",\"version\":{\"major\":0,\"micro\":1,\"minor\":0},\"versionString\":\"0.0.1\"}" ], gives => [ "map open '{'", "key: 'CoreletAPIVersion'", "integer: 2", "key: 'CoreletType'", "string: 'standalone'", "key: 'documentation'", "lexical error: invalid bytes in UTF8 string.", "memory leaks:\t0" ] }, { name => "partial_bad", opts => [], input => [ "[ \"foo\", \"bar\"", "" ], gives => [ "array open '['", "string: 'foo'", "string: 'bar'", "parse error: premature EOF", "memory leaks:\t0" ] }, { name => "null", opts => [], input => [ "null", "" ], gives => [ "null", "memory leaks:\t0" ] }, { name => "nulls_and_bools", opts => [], input => [ "{", "\t\"boolean, true\": true,", "\t\"boolean, false\": false,", "\t\"null\": null", "}", "" ], gives => [ "map open '{'", "key: 'boolean, true'", "bool: true", "key: 'boolean, false'", "bool: false", "key: 'null'", "null", "map close '}'", "memory leaks:\t0" ] }, { name => "simple", opts => [], input => [ "{", " \"this\": \"is\",", " \"really\": \"simple\",", " \"json\": \"right?\"", "}", "" ], gives => [ "map open '{'", "key: 'this'", "string: 'is'", "key: 'really'", "string: 'simple'", "key: 'json'", "string: 'right?'", "map close '}'", "memory leaks:\t0" ] }, { name => "simple_with_comments", opts => [], input => [ "{", " \"this\": \"is\", // ignore this", " \"really\": \"simple\",", " /* ignore", "this", "too * / ", "** //", "(/", "******/", " \"json\": \"right?\"", "}", "" ], gives => [ "map open '{'", "key: 'this'", "string: 'is'", "lexical error: probable comment found in input text, comments are not enabled.", "memory leaks:\t0" ] }, { name => "string_invalid_escape", opts => [], input => [ "[\"\\n foo \\/ bar \\r\\f\\\\\\uffff\\t\\b\\\"\\\\ and you can't escape thi\\s\"]", "" ], gives => [ "array open '['", "lexical error: inside a string, '\\' occurs before a character which it may not.", "memory leaks:\t0" ] }, { name => "string_invalid_hex_char", opts => [], input => [ "\"foo foo, blah blah \\u0123 \\u4567 \\u89ab \\uc/ef \\uABCD \\uEFFE bar baz bing\"", "" ], gives => [ "lexical error: invalid (non-hex) character occurs after '\\u' inside string.", "memory leaks:\t0" ] }, { name => "string_with_escapes", opts => [], input => [ "[\"\\n foo \\/ bar \\r\\f\\\\\\uffff\\t\\b\\\"\\\\\",", " \"\\\"and this string has an escape at the beginning\",", " \"and this string has no escapes\" ]", "" ], gives => [ "array open '['", "string: '", " foo / bar \r\f\\\357\277\277\t\b\"\\'", "string: '\"and this string has an escape at the beginning'", "string: 'and this string has no escapes'", "array close ']'", "memory leaks:\t0" ] }, { name => "string_with_invalid_newline", opts => [], input => [ "\"la di dah. this is a string, and I can do this, \\n, but not this", "\"", "" ], gives => [ "lexical error: invalid character inside string.", "memory leaks:\t0" ] }, { name => "three_byte_utf8", opts => [], input => [ "{\"matzue\": \"\346\235\276\346\261\237\", \"asakusa\": \"\346\265\205\350\215\211\"}", "" ], gives => [ "map open '{'", "key: 'matzue'", "string: '\346\235\276\346\261\237'", "key: 'asakusa'", "string: '\346\265\205\350\215\211'", "map close '}'", "memory leaks:\t0" ] }, { name => "trailing_commas", opts => [], input => [ "{\"array\":[1,2,],\"map\":{\"a\":1,},}", "" ], gives => [ "map open '{'", "key: 'array'", "array open '['", "integer: 1", "integer: 2", "array close ']'", "key: 'map'", "map open '{'", "key: 'a'", "integer: 1", "map close '}'", "map close '}'", "memory leaks:\t0" ] }, { name => "true", opts => [], input => [ "true", "" ], gives => [ "bool: true", "memory leaks:\t0" ] }, { name => "unescaped_bulgarian", opts => [], input => [ "[\"\320\224\320\260 \320\234\321\203 \320\225\320\261\320\260 \320\234\320\260\320\271\320\272\320\260\321\202\320\260\"]", "" ], gives => [ "array open '['", "string: '\320\224\320\260 \320\234\321\203 \320\225\320\261\320\260 \320\234\320\260\320\271\320\272\320\260\321\202\320\260'", "array close ']'", "memory leaks:\t0" ] }, { name => "zerobyte", opts => [], input => [ "\"\\u0000\"", "" ], gives => [ "string: '\0'", "memory leaks:\t0" ] } ]; return @{$VAR1}; } 1; base-7.0.3.1/modules/libcom/test/yajlTestConverter.pl0000775000577000060420000000434513557101274021344 0ustar anjaesctl#!/usr/bin/perl # # This script converts the parsing test cases from the yajl release tree # into the yajlTestCases module as used by libCom/test/yajlTest.plt # # Re-do this conversion and commit after checking out a new version of yajl # from https://github.com/lloyd/yajl as follows: # $ cd /src/libCom/test # $ perl yajlTestConverter.pl /path/to/yajl # The tests are saved into the file yajlTestCases.pm in the src/libCom/test # directory which will be read by the yajlTest.t test script. use Data::Dumper; my $yajl = shift @ARGV or die "Usage: $0 /path/to/yajl\n"; my @files = glob "$yajl/test/parsing/cases/*.json"; my $caseFile = 'yajlTestCases.pm'; my @cases; for my $file (@files) { $file =~ m|/([afn][cgmp]_)?([^/]*)\.json$|; my $allow = $1; my $name = $2; next if $name eq ''; my $case = { name => $name }; if ($allow eq 'ac_') { $case->{opts} = ['-c']; } elsif ($allow eq 'ag_') { $case->{opts} = ['-g']; } elsif ($allow eq 'am_') { $case->{opts} = ['-m']; } elsif ($allow eq 'ap_') { $case->{opts} = ['-p']; } else { $case->{opts} = []; } my $input = slurp($file); my @input = split "\n", $input; push @input, '' if $input =~ m/\n$/; $case->{input} = \@input; my @gives = split "\n", slurp("$file.gold"); $case->{gives} = \@gives; push @cases, $case; } # Configure Dumper() output $Data::Dumper::Pad = ' '; $Data::Dumper::Indent = 1; $Data::Dumper::Useqq = 1; $Data::Dumper::Quotekeys = 0; $Data::Dumper::Sortkeys = sub { return ['name', 'opts', 'input', 'gives'] }; my $data = Dumper(\@cases); open my $out, '>', $caseFile or die "Can't open/create $caseFile: $@\n"; print $out <<"EOF"; # Parser test cases from https://github.com/lloyd/yajl # # This file is generated, DO NOT EDIT! # # See comments in yajlTestConverter.pl for instructions on # how to regenerate this file from the original yajl sources. sub cases { my$data return \@{\$VAR1}; } 1; EOF close $out or die "Problem writing $caseFile: $@\n"; exit 0; sub slurp { my ($file) = @_; open my $in, '<', $file or die "Can't open file $file: $!\n"; my $contents = do { local $/; <$in> }; return $contents; } base-7.0.3.1/modules/libcom/test/yajl_test.c0000664000577000060420000001670413557101274017461 0ustar anjaesctl/* * Copyright (c) 2007-2014, Lloyd Hilaiel * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include /* memory debugging routines */ typedef struct { unsigned int numFrees; unsigned int numMallocs; /* XXX: we really need a hash table here with per-allocation * information */ } yajlTestMemoryContext; /* cast void * into context */ #define TEST_CTX(vptr) ((yajlTestMemoryContext *) (vptr)) static void yajlTestFree(void * ctx, void * ptr) { assert(ptr != NULL); TEST_CTX(ctx)->numFrees++; free(ptr); } static void * yajlTestMalloc(void * ctx, size_t sz) { assert(sz != 0); TEST_CTX(ctx)->numMallocs++; return malloc(sz); } static void * yajlTestRealloc(void * ctx, void * ptr, size_t sz) { if (ptr == NULL) { assert(sz != 0); TEST_CTX(ctx)->numMallocs++; } else if (sz == 0) { TEST_CTX(ctx)->numFrees++; } return realloc(ptr, sz); } /* begin parsing callback routines */ #define BUF_SIZE 2048 static int test_yajl_null(void *ctx) { printf("null\n"); return 1; } static int test_yajl_boolean(void * ctx, int boolVal) { printf("bool: %s\n", boolVal ? "true" : "false"); return 1; } static int test_yajl_integer(void *ctx, long long integerVal) { printf("integer: %lld\n", integerVal); return 1; } static int test_yajl_double(void *ctx, double doubleVal) { printf("double: %g\n", doubleVal); return 1; } static int test_yajl_string(void *ctx, const unsigned char * stringVal, size_t stringLen) { printf("string: '"); fwrite(stringVal, 1, stringLen, stdout); printf("'\n"); return 1; } static int test_yajl_map_key(void *ctx, const unsigned char * stringVal, size_t stringLen) { char * str = (char *) malloc(stringLen + 1); str[stringLen] = 0; memcpy(str, stringVal, stringLen); printf("key: '%s'\n", str); free(str); return 1; } static int test_yajl_start_map(void *ctx) { printf("map open '{'\n"); return 1; } static int test_yajl_end_map(void *ctx) { printf("map close '}'\n"); return 1; } static int test_yajl_start_array(void *ctx) { printf("array open '['\n"); return 1; } static int test_yajl_end_array(void *ctx) { printf("array close ']'\n"); return 1; } static yajl_callbacks callbacks = { test_yajl_null, test_yajl_boolean, test_yajl_integer, test_yajl_double, NULL, test_yajl_string, test_yajl_start_map, test_yajl_map_key, test_yajl_end_map, test_yajl_start_array, test_yajl_end_array }; static void usage(const char * progname) { fprintf(stderr, "usage: %s [options]\n" "Parse input from stdin as JSON and ouput parsing details " "to stdout\n" " -b set the read buffer size\n" " -c allow comments\n" " -g allow *g*arbage after valid JSON text\n" " -h print this help message\n" " -m allows the parser to consume multiple JSON values\n" " from a single string separated by whitespace\n" " -p partial JSON documents should not cause errors\n", progname); exit(1); } int main(int argc, char ** argv) { yajl_handle hand; const char * fileName = NULL; static unsigned char * fileData = NULL; FILE *file; size_t bufSize = BUF_SIZE; yajl_status stat; size_t rd; int i, j; /* memory allocation debugging: allocate a structure which collects * statistics */ yajlTestMemoryContext memCtx = { 0,0 }; /* memory allocation debugging: allocate a structure which holds * allocation routines */ yajl_alloc_funcs allocFuncs = { yajlTestMalloc, yajlTestRealloc, yajlTestFree, (void *) NULL }; allocFuncs.ctx = (void *) &memCtx; /* allocate the parser */ hand = yajl_alloc(&callbacks, &allocFuncs, NULL); /* check arguments. We expect exactly one! */ for (i=1;i= argc) usage(argv[0]); /* validate integer */ for (j=0;j<(int)strlen(argv[i]);j++) { if (argv[i][j] <= '9' && argv[i][j] >= '0') continue; fprintf(stderr, "-b requires an integer argument. '%s' " "is invalid\n", argv[i]); usage(argv[0]); } bufSize = atoi(argv[i]); if (!bufSize) { fprintf(stderr, "%zu is an invalid buffer size\n", bufSize); } } else if (!strcmp("-g", argv[i])) { yajl_config(hand, yajl_allow_trailing_garbage, 1); } else if (!strcmp("-h", argv[i])) { usage(argv[0]); } else if (!strcmp("-m", argv[i])) { yajl_config(hand, yajl_allow_multiple_values, 1); } else if (!strcmp("-p", argv[i])) { yajl_config(hand, yajl_allow_partial_values, 1); } else { fileName = argv[i]; break; } } fileData = (unsigned char *) malloc(bufSize); if (fileData == NULL) { fprintf(stderr, "failed to allocate read buffer of %zu bytes, exiting.", bufSize); yajl_free(hand); exit(2); } if (fileName) { file = fopen(fileName, "r"); } else { file = stdin; } for (;;) { rd = fread((void *) fileData, 1, bufSize, file); if (rd == 0) { if (!feof(file)) { fprintf(stderr, "error reading from '%s'\n", fileName); } break; } /* read file data, now pass to parser */ stat = yajl_parse(hand, fileData, rd); if (stat != yajl_status_ok) break; } stat = yajl_complete_parse(hand); if (stat != yajl_status_ok) { unsigned char * str = yajl_get_error(hand, 0, fileData, rd); fflush(stdout); fprintf(stderr, "%s", (char *) str); yajl_free_error(hand, str); } yajl_free(hand); free(fileData); if (fileName) { fclose(file); } /* finally, print out some memory statistics */ /* (lth) only print leaks here, as allocations and frees may vary depending * on read buffer size, causing false failures. * * printf("allocations:\t%u\n", memCtx.numMallocs); * printf("frees:\t\t%u\n", memCtx.numFrees); */ fflush(stderr); fflush(stdout); printf("memory leaks:\t%u\n", memCtx.numMallocs - memCtx.numFrees); return 0; } base-7.0.3.1/modules/libcom/vxWorks/Makefile0000664000577000060420000000540013557101274017452 0ustar anjaesctl#************************************************************************* # Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* TOP = ../../.. include $(TOP)/configure/CONFIG # Install Boost smart_ptr headers needed by VxWorks 6.x INC_vxWorks += boost/assert.hpp INC_vxWorks += boost/checked_delete.hpp INC_vxWorks += boost/config/auto_link.hpp INC_vxWorks += boost/config/compiler/gcc.hpp INC_vxWorks += boost/config.hpp INC_vxWorks += boost/config/no_tr1/memory.hpp INC_vxWorks += boost/config/no_tr1/utility.hpp INC_vxWorks += boost/config/platform/vxworks.hpp INC_vxWorks += boost/config/posix_features.hpp INC_vxWorks += boost/config/select_compiler_config.hpp INC_vxWorks += boost/config/select_platform_config.hpp INC_vxWorks += boost/config/select_stdlib_config.hpp INC_vxWorks += boost/config/stdlib/dinkumware.hpp INC_vxWorks += boost/config/suffix.hpp INC_vxWorks += boost/config/user.hpp INC_vxWorks += boost/current_function.hpp INC_vxWorks += boost/detail/sp_typeinfo.hpp INC_vxWorks += boost/detail/workaround.hpp INC_vxWorks += boost/enable_shared_from_this.hpp INC_vxWorks += boost/exception/detail/attribute_noreturn.hpp INC_vxWorks += boost/exception/exception.hpp INC_vxWorks += boost/memory_order.hpp INC_vxWorks += boost/shared_ptr.hpp INC_vxWorks += boost/smart_ptr/bad_weak_ptr.hpp INC_vxWorks += boost/smart_ptr/detail/operator_bool.hpp INC_vxWorks += boost/smart_ptr/detail/shared_count.hpp INC_vxWorks += boost/smart_ptr/detail/sp_convertible.hpp INC_vxWorks += boost/smart_ptr/detail/sp_counted_base_gcc_ppc.hpp INC_vxWorks += boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp INC_vxWorks += boost/smart_ptr/detail/sp_counted_base.hpp INC_vxWorks += boost/smart_ptr/detail/sp_counted_impl.hpp INC_vxWorks += boost/smart_ptr/detail/sp_has_sync.hpp INC_vxWorks += boost/smart_ptr/detail/spinlock.hpp INC_vxWorks += boost/smart_ptr/detail/spinlock_pool.hpp INC_vxWorks += boost/smart_ptr/detail/spinlock_pt.hpp INC_vxWorks += boost/smart_ptr/detail/spinlock_sync.hpp INC_vxWorks += boost/smart_ptr/detail/sp_nullptr_t.hpp INC_vxWorks += boost/smart_ptr/detail/yield_k.hpp INC_vxWorks += boost/smart_ptr/enable_shared_from_this.hpp INC_vxWorks += boost/smart_ptr/shared_ptr.hpp INC_vxWorks += boost/smart_ptr/weak_ptr.hpp INC_vxWorks += boost/throw_exception.hpp INC_vxWorks += boost/tr1/detail/config.hpp INC_vxWorks += boost/tr1/memory.hpp INC_vxWorks += boost/type_traits/is_signed.hpp INC_vxWorks += boost/weak_ptr.hpp # Base doesn't have a vpath for .hpp files vpath %.hpp $(USR_VPATH) $(ALL_SRC_DIRS) include $(TOP)/configure/RULES base-7.0.3.1/modules/libcom/vxWorks/boost/assert.hpp0000664000577000060420000001003013557101274021145 0ustar anjaesctl// // boost/assert.hpp - BOOST_ASSERT(expr) // BOOST_ASSERT_MSG(expr, msg) // BOOST_VERIFY(expr) // // Copyright (c) 2001, 2002 Peter Dimov and Multi Media Ltd. // Copyright (c) 2007 Peter Dimov // Copyright (c) Beman Dawes 2011 // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // Note: There are no include guards. This is intentional. // // See http://www.boost.org/libs/utility/assert.html for documentation. // // // Stop inspect complaining about use of 'assert': // // boostinspect:naassert_macro // //--------------------------------------------------------------------------------------// // BOOST_ASSERT // //--------------------------------------------------------------------------------------// #undef BOOST_ASSERT #if defined(BOOST_DISABLE_ASSERTS) # define BOOST_ASSERT(expr) ((void)0) #elif defined(BOOST_ENABLE_ASSERT_HANDLER) #include namespace boost { void assertion_failed(char const * expr, char const * function, char const * file, long line); // user defined } // namespace boost #define BOOST_ASSERT(expr) ((expr) \ ? ((void)0) \ : ::boost::assertion_failed(#expr, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__)) #else # include // .h to support old libraries w/o - effect is the same # define BOOST_ASSERT(expr) assert(expr) #endif //--------------------------------------------------------------------------------------// // BOOST_ASSERT_MSG // //--------------------------------------------------------------------------------------// # undef BOOST_ASSERT_MSG #if defined(BOOST_DISABLE_ASSERTS) || defined(NDEBUG) #define BOOST_ASSERT_MSG(expr, msg) ((void)0) #elif defined(BOOST_ENABLE_ASSERT_HANDLER) #include namespace boost { void assertion_failed_msg(char const * expr, char const * msg, char const * function, char const * file, long line); // user defined } // namespace boost #define BOOST_ASSERT_MSG(expr, msg) ((expr) \ ? ((void)0) \ : ::boost::assertion_failed_msg(#expr, msg, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__)) #else #ifndef BOOST_ASSERT_HPP #define BOOST_ASSERT_HPP #include #include #include // IDE's like Visual Studio perform better if output goes to std::cout or // some other stream, so allow user to configure output stream: #ifndef BOOST_ASSERT_MSG_OSTREAM # define BOOST_ASSERT_MSG_OSTREAM std::cerr #endif namespace boost { namespace assertion { namespace detail { inline void assertion_failed_msg(char const * expr, char const * msg, char const * function, char const * file, long line) { BOOST_ASSERT_MSG_OSTREAM << "***** Internal Program Error - assertion (" << expr << ") failed in " << function << ":\n" << file << '(' << line << "): " << msg << std::endl; std::abort(); } } // detail } // assertion } // detail #endif #define BOOST_ASSERT_MSG(expr, msg) ((expr) \ ? ((void)0) \ : ::boost::assertion::detail::assertion_failed_msg(#expr, msg, \ BOOST_CURRENT_FUNCTION, __FILE__, __LINE__)) #endif //--------------------------------------------------------------------------------------// // BOOST_VERIFY // //--------------------------------------------------------------------------------------// #undef BOOST_VERIFY #if defined(BOOST_DISABLE_ASSERTS) || ( !defined(BOOST_ENABLE_ASSERT_HANDLER) && defined(NDEBUG) ) # define BOOST_VERIFY(expr) ((void)(expr)) #else # define BOOST_VERIFY(expr) BOOST_ASSERT(expr) #endif base-7.0.3.1/modules/libcom/vxWorks/boost/checked_delete.hpp0000664000577000060420000000306513557101274022566 0ustar anjaesctl#ifndef BOOST_CHECKED_DELETE_HPP_INCLUDED #define BOOST_CHECKED_DELETE_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // boost/checked_delete.hpp // // Copyright (c) 2002, 2003 Peter Dimov // Copyright (c) 2003 Daniel Frey // Copyright (c) 2003 Howard Hinnant // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // See http://www.boost.org/libs/utility/checked_delete.html for documentation. // namespace boost { // verify that types are complete for increased safety template inline void checked_delete(T * x) { // intentionally complex - simplification causes regressions typedef char type_must_be_complete[ sizeof(T)? 1: -1 ]; (void) sizeof(type_must_be_complete); delete x; } template inline void checked_array_delete(T * x) { typedef char type_must_be_complete[ sizeof(T)? 1: -1 ]; (void) sizeof(type_must_be_complete); delete [] x; } template struct checked_deleter { typedef void result_type; typedef T * argument_type; void operator()(T * x) const { // boost:: disables ADL boost::checked_delete(x); } }; template struct checked_array_deleter { typedef void result_type; typedef T * argument_type; void operator()(T * x) const { boost::checked_array_delete(x); } }; } // namespace boost #endif // #ifndef BOOST_CHECKED_DELETE_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/config.hpp0000664000577000060420000000403213557101274021116 0ustar anjaesctl// Boost config.hpp configuration header file ------------------------------// // (C) Copyright John Maddock 2002. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // See http://www.boost.org/libs/config for most recent version. // Boost config.hpp policy and rationale documentation has been moved to // http://www.boost.org/libs/config // // CAUTION: This file is intended to be completely stable - // DO NOT MODIFY THIS FILE! // #ifndef BOOST_CONFIG_HPP #define BOOST_CONFIG_HPP // if we don't have a user config, then use the default location: #if !defined(BOOST_USER_CONFIG) && !defined(BOOST_NO_USER_CONFIG) # define BOOST_USER_CONFIG #endif // include it first: #ifdef BOOST_USER_CONFIG # include BOOST_USER_CONFIG #endif // if we don't have a compiler config set, try and find one: #if !defined(BOOST_COMPILER_CONFIG) && !defined(BOOST_NO_COMPILER_CONFIG) && !defined(BOOST_NO_CONFIG) # include #endif // if we have a compiler config, include it now: #ifdef BOOST_COMPILER_CONFIG # include BOOST_COMPILER_CONFIG #endif // if we don't have a std library config set, try and find one: #if !defined(BOOST_STDLIB_CONFIG) && !defined(BOOST_NO_STDLIB_CONFIG) && !defined(BOOST_NO_CONFIG) && defined(__cplusplus) # include #endif // if we have a std library config, include it now: #ifdef BOOST_STDLIB_CONFIG # include BOOST_STDLIB_CONFIG #endif // if we don't have a platform config set, try and find one: #if !defined(BOOST_PLATFORM_CONFIG) && !defined(BOOST_NO_PLATFORM_CONFIG) && !defined(BOOST_NO_CONFIG) # include #endif // if we have a platform config, include it now: #ifdef BOOST_PLATFORM_CONFIG # include BOOST_PLATFORM_CONFIG #endif // get config suffix code: #include #endif // BOOST_CONFIG_HPP base-7.0.3.1/modules/libcom/vxWorks/boost/config/auto_link.hpp0000664000577000060420000003367413557101274023121 0ustar anjaesctl// (C) Copyright John Maddock 2003. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) /* * LOCATION: see http://www.boost.org for most recent version. * FILE auto_link.hpp * VERSION see * DESCRIPTION: Automatic library inclusion for Borland/Microsoft compilers. */ /************************************************************************* USAGE: ~~~~~~ Before including this header you must define one or more of define the following macros: BOOST_LIB_NAME: Required: A string containing the basename of the library, for example boost_regex. BOOST_LIB_TOOLSET: Optional: the base name of the toolset. BOOST_DYN_LINK: Optional: when set link to dll rather than static library. BOOST_LIB_DIAGNOSTIC: Optional: when set the header will print out the name of the library selected (useful for debugging). BOOST_AUTO_LINK_NOMANGLE: Specifies that we should link to BOOST_LIB_NAME.lib, rather than a mangled-name version. BOOST_AUTO_LINK_TAGGED: Specifies that we link to libraries built with the --layout=tagged option. This is essentially the same as the default name-mangled version, but without the compiler name and version, or the Boost version. Just the build options. These macros will be undef'ed at the end of the header, further this header has no include guards - so be sure to include it only once from your library! Algorithm: ~~~~~~~~~~ Libraries for Borland and Microsoft compilers are automatically selected here, the name of the lib is selected according to the following formula: BOOST_LIB_PREFIX + BOOST_LIB_NAME + "_" + BOOST_LIB_TOOLSET + BOOST_LIB_THREAD_OPT + BOOST_LIB_RT_OPT "-" + BOOST_LIB_VERSION These are defined as: BOOST_LIB_PREFIX: "lib" for static libraries otherwise "". BOOST_LIB_NAME: The base name of the lib ( for example boost_regex). BOOST_LIB_TOOLSET: The compiler toolset name (vc6, vc7, bcb5 etc). BOOST_LIB_THREAD_OPT: "-mt" for multithread builds, otherwise nothing. BOOST_LIB_RT_OPT: A suffix that indicates the runtime library used, contains one or more of the following letters after a hyphen: s static runtime (dynamic if not present). g debug/diagnostic runtime (release if not present). y Python debug/diagnostic runtime (release if not present). d debug build (release if not present). p STLport build. n STLport build without its IOStreams. BOOST_LIB_VERSION: The Boost version, in the form x_y, for Boost version x.y. ***************************************************************************/ #ifdef __cplusplus # ifndef BOOST_CONFIG_HPP # include # endif #elif defined(_MSC_VER) && !defined(__MWERKS__) && !defined(__EDG_VERSION__) // // C language compatability (no, honestly) // # define BOOST_MSVC _MSC_VER # define BOOST_STRINGIZE(X) BOOST_DO_STRINGIZE(X) # define BOOST_DO_STRINGIZE(X) #X #endif // // Only include what follows for known and supported compilers: // #if defined(BOOST_MSVC) \ || defined(__BORLANDC__) \ || (defined(__MWERKS__) && defined(_WIN32) && (__MWERKS__ >= 0x3000)) \ || (defined(__ICL) && defined(_MSC_EXTENSIONS) && (_MSC_VER >= 1200)) #ifndef BOOST_VERSION_HPP # include #endif #ifndef BOOST_LIB_NAME # error "Macro BOOST_LIB_NAME not set (internal error)" #endif // // error check: // #if defined(__MSVC_RUNTIME_CHECKS) && !defined(_DEBUG) # pragma message("Using the /RTC option without specifying a debug runtime will lead to linker errors") # pragma message("Hint: go to the code generation options and switch to one of the debugging runtimes") # error "Incompatible build options" #endif // // select toolset if not defined already: // #ifndef BOOST_LIB_TOOLSET # if defined(BOOST_MSVC) && (BOOST_MSVC < 1200) // Note: no compilers before 1200 are supported # elif defined(BOOST_MSVC) && (BOOST_MSVC < 1300) # ifdef UNDER_CE // eVC4: # define BOOST_LIB_TOOLSET "evc4" # else // vc6: # define BOOST_LIB_TOOLSET "vc6" # endif # elif defined(BOOST_MSVC) && (BOOST_MSVC < 1310) // vc7: # define BOOST_LIB_TOOLSET "vc7" # elif defined(BOOST_MSVC) && (BOOST_MSVC < 1400) // vc71: # define BOOST_LIB_TOOLSET "vc71" # elif defined(BOOST_MSVC) && (BOOST_MSVC < 1500) // vc80: # define BOOST_LIB_TOOLSET "vc80" # elif defined(BOOST_MSVC) && (BOOST_MSVC < 1600) // vc90: # define BOOST_LIB_TOOLSET "vc90" # elif defined(BOOST_MSVC) && (BOOST_MSVC < 1700) // vc10: # define BOOST_LIB_TOOLSET "vc100" # elif defined(BOOST_MSVC) // vc11: # define BOOST_LIB_TOOLSET "vc110" # elif defined(__BORLANDC__) // CBuilder 6: # define BOOST_LIB_TOOLSET "bcb" # elif defined(__ICL) // Intel C++, no version number: # define BOOST_LIB_TOOLSET "iw" # elif defined(__MWERKS__) && (__MWERKS__ <= 0x31FF ) // Metrowerks CodeWarrior 8.x # define BOOST_LIB_TOOLSET "cw8" # elif defined(__MWERKS__) && (__MWERKS__ <= 0x32FF ) // Metrowerks CodeWarrior 9.x # define BOOST_LIB_TOOLSET "cw9" # endif #endif // BOOST_LIB_TOOLSET // // select thread opt: // #if defined(_MT) || defined(__MT__) # define BOOST_LIB_THREAD_OPT "-mt" #else # define BOOST_LIB_THREAD_OPT #endif #if defined(_MSC_VER) || defined(__MWERKS__) # ifdef _DLL # if (defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION)) && (defined(_STLP_OWN_IOSTREAMS) || defined(__STL_OWN_IOSTREAMS)) # if defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG))\ && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) # define BOOST_LIB_RT_OPT "-gydp" # elif defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG)) # define BOOST_LIB_RT_OPT "-gdp" # elif defined(_DEBUG)\ && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) # define BOOST_LIB_RT_OPT "-gydp" # pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") # error "Build options aren't compatible with pre-built libraries" # elif defined(_DEBUG) # define BOOST_LIB_RT_OPT "-gdp" # pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") # error "Build options aren't compatible with pre-built libraries" # else # define BOOST_LIB_RT_OPT "-p" # endif # elif defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION) # if defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG))\ && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) # define BOOST_LIB_RT_OPT "-gydpn" # elif defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG)) # define BOOST_LIB_RT_OPT "-gdpn" # elif defined(_DEBUG)\ && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) # define BOOST_LIB_RT_OPT "-gydpn" # pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") # error "Build options aren't compatible with pre-built libraries" # elif defined(_DEBUG) # define BOOST_LIB_RT_OPT "-gdpn" # pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") # error "Build options aren't compatible with pre-built libraries" # else # define BOOST_LIB_RT_OPT "-pn" # endif # else # if defined(_DEBUG) && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) # define BOOST_LIB_RT_OPT "-gyd" # elif defined(_DEBUG) # define BOOST_LIB_RT_OPT "-gd" # else # define BOOST_LIB_RT_OPT # endif # endif # else # if (defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION)) && (defined(_STLP_OWN_IOSTREAMS) || defined(__STL_OWN_IOSTREAMS)) # if defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG))\ && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) # define BOOST_LIB_RT_OPT "-sgydp" # elif defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG)) # define BOOST_LIB_RT_OPT "-sgdp" # elif defined(_DEBUG)\ && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) # define BOOST_LIB_RT_OPT "-sgydp" # pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") # error "Build options aren't compatible with pre-built libraries" # elif defined(_DEBUG) # define BOOST_LIB_RT_OPT "-sgdp" # pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") # error "Build options aren't compatible with pre-built libraries" # else # define BOOST_LIB_RT_OPT "-sp" # endif # elif defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION) # if defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG))\ && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) # define BOOST_LIB_RT_OPT "-sgydpn" # elif defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG)) # define BOOST_LIB_RT_OPT "-sgdpn" # elif defined(_DEBUG)\ && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) # define BOOST_LIB_RT_OPT "-sgydpn" # pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") # error "Build options aren't compatible with pre-built libraries" # elif defined(_DEBUG) # define BOOST_LIB_RT_OPT "-sgdpn" # pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") # error "Build options aren't compatible with pre-built libraries" # else # define BOOST_LIB_RT_OPT "-spn" # endif # else # if defined(_DEBUG)\ && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) # define BOOST_LIB_RT_OPT "-sgyd" # elif defined(_DEBUG) # define BOOST_LIB_RT_OPT "-sgd" # else # define BOOST_LIB_RT_OPT "-s" # endif # endif # endif #elif defined(__BORLANDC__) // // figure out whether we want the debug builds or not: // #if __BORLANDC__ > 0x561 #pragma defineonoption BOOST_BORLAND_DEBUG -v #endif // // sanity check: // #if defined(__STL_DEBUG) || defined(_STLP_DEBUG) #error "Pre-built versions of the Boost libraries are not provided in STLport-debug form" #endif # ifdef _RTLDLL # if defined(BOOST_BORLAND_DEBUG)\ && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) # define BOOST_LIB_RT_OPT "-yd" # elif defined(BOOST_BORLAND_DEBUG) # define BOOST_LIB_RT_OPT "-d" # elif defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) # define BOOST_LIB_RT_OPT -y # else # define BOOST_LIB_RT_OPT # endif # else # if defined(BOOST_BORLAND_DEBUG)\ && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) # define BOOST_LIB_RT_OPT "-syd" # elif defined(BOOST_BORLAND_DEBUG) # define BOOST_LIB_RT_OPT "-sd" # elif defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) # define BOOST_LIB_RT_OPT "-sy" # else # define BOOST_LIB_RT_OPT "-s" # endif # endif #endif // // select linkage opt: // #if (defined(_DLL) || defined(_RTLDLL)) && defined(BOOST_DYN_LINK) # define BOOST_LIB_PREFIX #elif defined(BOOST_DYN_LINK) # error "Mixing a dll boost library with a static runtime is a really bad idea..." #else # define BOOST_LIB_PREFIX "lib" #endif // // now include the lib: // #if defined(BOOST_LIB_NAME) \ && defined(BOOST_LIB_PREFIX) \ && defined(BOOST_LIB_TOOLSET) \ && defined(BOOST_LIB_THREAD_OPT) \ && defined(BOOST_LIB_RT_OPT) \ && defined(BOOST_LIB_VERSION) #ifdef BOOST_AUTO_LINK_TAGGED # pragma comment(lib, BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) BOOST_LIB_THREAD_OPT BOOST_LIB_RT_OPT ".lib") # ifdef BOOST_LIB_DIAGNOSTIC # pragma message ("Linking to lib file: " BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) BOOST_LIB_THREAD_OPT BOOST_LIB_RT_OPT ".lib") # endif #elif defined(BOOST_AUTO_LINK_NOMANGLE) # pragma comment(lib, BOOST_STRINGIZE(BOOST_LIB_NAME) ".lib") # ifdef BOOST_LIB_DIAGNOSTIC # pragma message ("Linking to lib file: " BOOST_STRINGIZE(BOOST_LIB_NAME) ".lib") # endif #else # pragma comment(lib, BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) "-" BOOST_LIB_TOOLSET BOOST_LIB_THREAD_OPT BOOST_LIB_RT_OPT "-" BOOST_LIB_VERSION ".lib") # ifdef BOOST_LIB_DIAGNOSTIC # pragma message ("Linking to lib file: " BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) "-" BOOST_LIB_TOOLSET BOOST_LIB_THREAD_OPT BOOST_LIB_RT_OPT "-" BOOST_LIB_VERSION ".lib") # endif #endif #else # error "some required macros where not defined (internal logic error)." #endif #endif // _MSC_VER || __BORLANDC__ // // finally undef any macros we may have set: // #ifdef BOOST_LIB_PREFIX # undef BOOST_LIB_PREFIX #endif #if defined(BOOST_LIB_NAME) # undef BOOST_LIB_NAME #endif // Don't undef this one: it can be set by the user and should be the // same for all libraries: //#if defined(BOOST_LIB_TOOLSET) //# undef BOOST_LIB_TOOLSET //#endif #if defined(BOOST_LIB_THREAD_OPT) # undef BOOST_LIB_THREAD_OPT #endif #if defined(BOOST_LIB_RT_OPT) # undef BOOST_LIB_RT_OPT #endif #if defined(BOOST_LIB_LINK_OPT) # undef BOOST_LIB_LINK_OPT #endif #if defined(BOOST_LIB_DEBUG_OPT) # undef BOOST_LIB_DEBUG_OPT #endif #if defined(BOOST_DYN_LINK) # undef BOOST_DYN_LINK #endif base-7.0.3.1/modules/libcom/vxWorks/boost/config/compiler/gcc.hpp0000664000577000060420000002061713557101274023473 0ustar anjaesctl// (C) Copyright John Maddock 2001 - 2003. // (C) Copyright Darin Adler 2001 - 2002. // (C) Copyright Jens Maurer 2001 - 2002. // (C) Copyright Beman Dawes 2001 - 2003. // (C) Copyright Douglas Gregor 2002. // (C) Copyright David Abrahams 2002 - 2003. // (C) Copyright Synge Todo 2003. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // See http://www.boost.org for most recent version. // GNU C++ compiler setup: #if __GNUC__ < 3 # if __GNUC_MINOR__ == 91 // egcs 1.1 won't parse shared_ptr.hpp without this: # define BOOST_NO_AUTO_PTR # endif # if __GNUC_MINOR__ < 95 // // Prior to gcc 2.95 member templates only partly // work - define BOOST_MSVC6_MEMBER_TEMPLATES // instead since inline member templates mostly work. // # define BOOST_NO_MEMBER_TEMPLATES # if __GNUC_MINOR__ >= 9 # define BOOST_MSVC6_MEMBER_TEMPLATES # endif # endif # if __GNUC_MINOR__ < 96 # define BOOST_NO_SFINAE # endif # if __GNUC_MINOR__ <= 97 # define BOOST_NO_MEMBER_TEMPLATE_FRIENDS # define BOOST_NO_OPERATORS_IN_NAMESPACE # endif # define BOOST_NO_USING_DECLARATION_OVERLOADS_FROM_TYPENAME_BASE # define BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL # define BOOST_NO_IS_ABSTRACT # define BOOST_NO_CXX11_EXTERN_TEMPLATE // Variadic macros do not exist for gcc versions before 3.0 # define BOOST_NO_CXX11_VARIADIC_MACROS #elif __GNUC__ == 3 # if defined (__PATHSCALE__) # define BOOST_NO_TWO_PHASE_NAME_LOOKUP # define BOOST_NO_IS_ABSTRACT # endif // // gcc-3.x problems: // // Bug specific to gcc 3.1 and 3.2: // # if ((__GNUC_MINOR__ == 1) || (__GNUC_MINOR__ == 2)) # define BOOST_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS # endif # if __GNUC_MINOR__ < 4 # define BOOST_NO_IS_ABSTRACT # endif # define BOOST_NO_CXX11_EXTERN_TEMPLATE #endif #if __GNUC__ < 4 // // All problems to gcc-3.x and earlier here: // #define BOOST_NO_TWO_PHASE_NAME_LOOKUP # ifdef __OPEN64__ # define BOOST_NO_IS_ABSTRACT # endif #endif #if __GNUC__ < 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ < 4 ) // Previous versions of GCC did not completely implement value-initialization: // GCC Bug 30111, "Value-initialization of POD base class doesn't initialize // members", reported by Jonathan Wakely in 2006, // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30111 (fixed for GCC 4.4) // GCC Bug 33916, "Default constructor fails to initialize array members", // reported by Michael Elizabeth Chastain in 2007, // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33916 (fixed for GCC 4.2.4) // See also: http://www.boost.org/libs/utility/value_init.htm#compiler_issues #define BOOST_NO_COMPLETE_VALUE_INITIALIZATION #endif #if !defined(__EXCEPTIONS) && !defined(BOOST_NO_EXCEPTIONS) # define BOOST_NO_EXCEPTIONS #endif // // Threading support: Turn this on unconditionally here (except for // those platforms where we can know for sure). It will get turned off again // later if no threading API is detected. // #if !defined(__MINGW32__) && !defined(linux) && !defined(__linux) && !defined(__linux__) # define BOOST_HAS_THREADS #endif // // gcc has "long long" // #define BOOST_HAS_LONG_LONG // // gcc implements the named return value optimization since version 3.1 // #if __GNUC__ > 3 || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) #define BOOST_HAS_NRVO #endif // // Dynamic shared object (DSO) and dynamic-link library (DLL) support // #if __GNUC__ >= 4 # if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) && !defined(__CYGWIN__) // All Win32 development environments, including 64-bit Windows and MinGW, define // _WIN32 or one of its variant spellings. Note that Cygwin is a POSIX environment, // so does not define _WIN32 or its variants. # define BOOST_HAS_DECLSPEC # define BOOST_SYMBOL_EXPORT __attribute__((dllexport)) # define BOOST_SYMBOL_IMPORT __attribute__((dllimport)) # else # define BOOST_SYMBOL_EXPORT __attribute__((visibility("default"))) # define BOOST_SYMBOL_IMPORT # endif # define BOOST_SYMBOL_VISIBLE __attribute__((visibility("default"))) #else // config/platform/win32.hpp will define BOOST_SYMBOL_EXPORT, etc., unless already defined # define BOOST_SYMBOL_EXPORT #endif // // RTTI and typeinfo detection is possible post gcc-4.3: // #if __GNUC__ * 100 + __GNUC_MINOR__ >= 403 # ifndef __GXX_RTTI # ifndef BOOST_NO_TYPEID # define BOOST_NO_TYPEID # endif # ifndef BOOST_NO_RTTI # define BOOST_NO_RTTI # endif # endif #endif // // Recent GCC versions have __int128 when in 64-bit mode: // #if defined(__SIZEOF_INT128__) # define BOOST_HAS_INT128 #endif // C++0x features in 4.3.n and later // #if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 2)) && defined(__GXX_EXPERIMENTAL_CXX0X__) // C++0x features are only enabled when -std=c++0x or -std=gnu++0x are // passed on the command line, which in turn defines // __GXX_EXPERIMENTAL_CXX0X__. # define BOOST_HAS_DECLTYPE # define BOOST_HAS_RVALUE_REFS # define BOOST_HAS_STATIC_ASSERT # define BOOST_HAS_VARIADIC_TMPL #else # define BOOST_NO_CXX11_DECLTYPE # define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS # define BOOST_NO_CXX11_RVALUE_REFERENCES # define BOOST_NO_CXX11_STATIC_ASSERT // Variadic templates compiler: // http://www.generic-programming.org/~dgregor/cpp/variadic-templates.html # if defined(__VARIADIC_TEMPLATES) || (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4) && defined(__GXX_EXPERIMENTAL_CXX0X__)) # define BOOST_HAS_VARIADIC_TMPL # else # define BOOST_NO_CXX11_VARIADIC_TEMPLATES # endif #endif // C++0x features in 4.4.n and later // #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 4) || !defined(__GXX_EXPERIMENTAL_CXX0X__) # define BOOST_NO_CXX11_AUTO_DECLARATIONS # define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS # define BOOST_NO_CXX11_CHAR16_T # define BOOST_NO_CXX11_CHAR32_T # define BOOST_NO_CXX11_HDR_INITIALIZER_LIST # define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS # define BOOST_NO_CXX11_DELETED_FUNCTIONS #endif #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5) # define BOOST_NO_SFINAE_EXPR #endif // C++0x features in 4.5.0 and later // #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5) || !defined(__GXX_EXPERIMENTAL_CXX0X__) # define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS # define BOOST_NO_CXX11_LAMBDAS # define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS # define BOOST_NO_CXX11_RAW_LITERALS # define BOOST_NO_CXX11_UNICODE_LITERALS #endif // C++0x features in 4.5.1 and later // #if (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__ < 40501) || !defined(__GXX_EXPERIMENTAL_CXX0X__) // scoped enums have a serious bug in 4.4.0, so define BOOST_NO_CXX11_SCOPED_ENUMS before 4.5.1 // See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38064 # define BOOST_NO_CXX11_SCOPED_ENUMS #endif // C++0x features in 4.6.n and later // #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6) || !defined(__GXX_EXPERIMENTAL_CXX0X__) #define BOOST_NO_CXX11_CONSTEXPR #define BOOST_NO_CXX11_NOEXCEPT #define BOOST_NO_CXX11_NULLPTR #define BOOST_NO_CXX11_RANGE_BASED_FOR #define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX #endif #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7) || !defined(__GXX_EXPERIMENTAL_CXX0X__) # define BOOST_NO_CXX11_TEMPLATE_ALIASES #endif // C++0x features not supported at all yet // #define BOOST_NO_CXX11_DECLTYPE_N3276 #ifndef BOOST_COMPILER # define BOOST_COMPILER "GNU C++ version " __VERSION__ #endif // ConceptGCC compiler: // http://www.generic-programming.org/software/ConceptGCC/ #ifdef __GXX_CONCEPTS__ # define BOOST_HAS_CONCEPTS # define BOOST_COMPILER "ConceptGCC version " __VERSION__ #endif // versions check: // we don't know gcc prior to version 2.90: #if (__GNUC__ == 2) && (__GNUC_MINOR__ < 90) # error "Compiler not configured - please reconfigure" #endif // // last known and checked version is 4.6 (Pre-release): #if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 6)) # if defined(BOOST_ASSERT_CONFIG) # error "Unknown compiler version - please run the configure tests and report the results" # else // we don't emit warnings here anymore since there are no defect macros defined for // gcc post 3.4, so any failures are gcc regressions... //# warning "Unknown compiler version - please run the configure tests and report the results" # endif #endif base-7.0.3.1/modules/libcom/vxWorks/boost/config/no_tr1/memory.hpp0000664000577000060420000000151613557101274023634 0ustar anjaesctl// (C) Copyright John Maddock 2005. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // The aim of this header is just to include but to do // so in a way that does not result in recursive inclusion of // the Boost TR1 components if boost/tr1/tr1/memory is in the // include search path. We have to do this to avoid circular // dependencies: // #ifndef BOOST_CONFIG_MEMORY # define BOOST_CONFIG_MEMORY # ifndef BOOST_TR1_NO_RECURSION # define BOOST_TR1_NO_RECURSION # define BOOST_CONFIG_NO_MEMORY_RECURSION # endif # include # ifdef BOOST_CONFIG_NO_MEMORY_RECURSION # undef BOOST_TR1_NO_RECURSION # undef BOOST_CONFIG_NO_MEMORY_RECURSION # endif #endif base-7.0.3.1/modules/libcom/vxWorks/boost/config/no_tr1/utility.hpp0000664000577000060420000000152613557101274024030 0ustar anjaesctl// (C) Copyright John Maddock 2005. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // The aim of this header is just to include but to do // so in a way that does not result in recursive inclusion of // the Boost TR1 components if boost/tr1/tr1/utility is in the // include search path. We have to do this to avoid circular // dependencies: // #ifndef BOOST_CONFIG_UTILITY # define BOOST_CONFIG_UTILITY # ifndef BOOST_TR1_NO_RECURSION # define BOOST_TR1_NO_RECURSION # define BOOST_CONFIG_NO_UTILITY_RECURSION # endif # include # ifdef BOOST_CONFIG_NO_UTILITY_RECURSION # undef BOOST_TR1_NO_RECURSION # undef BOOST_CONFIG_NO_UTILITY_RECURSION # endif #endif base-7.0.3.1/modules/libcom/vxWorks/boost/config/platform/vxworks.hpp0000664000577000060420000000146313557101274024472 0ustar anjaesctl// (C) Copyright Dustin Spicuzza 2009. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // See http://www.boost.org for most recent version. // vxWorks specific config options: #define BOOST_PLATFORM "vxWorks" #define BOOST_NO_CWCHAR #define BOOST_NO_INTRINSIC_WCHAR_T #if defined(__GNUC__) && defined(__STRICT_ANSI__) #define BOOST_NO_INT64_T #endif #define BOOST_HAS_UNISTD_H // these allow posix_features to work, since vxWorks doesn't // define them itself #define _POSIX_TIMERS 1 #define _POSIX_THREADS 1 // vxworks doesn't work with asio serial ports #define BOOST_ASIO_DISABLE_SERIAL_PORT // boilerplate code: #include base-7.0.3.1/modules/libcom/vxWorks/boost/config/posix_features.hpp0000664000577000060420000000723013557101274024161 0ustar anjaesctl// (C) Copyright John Maddock 2001 - 2003. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // See http://www.boost.org for most recent version. // All POSIX feature tests go in this file, // Note that we test _POSIX_C_SOURCE and _XOPEN_SOURCE as well // _POSIX_VERSION and _XOPEN_VERSION: on some systems POSIX API's // may be present but none-functional unless _POSIX_C_SOURCE and // _XOPEN_SOURCE have been defined to the right value (it's up // to the user to do this *before* including any header, although // in most cases the compiler will do this for you). # if defined(BOOST_HAS_UNISTD_H) # include // XOpen has , but is this the correct version check? # if defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 3) # define BOOST_HAS_NL_TYPES_H # endif // POSIX version 6 requires # if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 200100) # define BOOST_HAS_STDINT_H # endif // POSIX version 2 requires # if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 199009L) # define BOOST_HAS_DIRENT_H # endif // POSIX version 3 requires to have sigaction: # if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 199506L) # define BOOST_HAS_SIGACTION # endif // POSIX defines _POSIX_THREADS > 0 for pthread support, // however some platforms define _POSIX_THREADS without // a value, hence the (_POSIX_THREADS+0 >= 0) check. // Strictly speaking this may catch platforms with a // non-functioning stub , but such occurrences should // occur very rarely if at all. # if defined(_POSIX_THREADS) && (_POSIX_THREADS+0 >= 0) && !defined(BOOST_HAS_WINTHREADS) && !defined(BOOST_HAS_MPTASKS) # define BOOST_HAS_PTHREADS # endif // BOOST_HAS_NANOSLEEP: // This is predicated on _POSIX_TIMERS or _XOPEN_REALTIME: # if (defined(_POSIX_TIMERS) && (_POSIX_TIMERS+0 >= 0)) \ || (defined(_XOPEN_REALTIME) && (_XOPEN_REALTIME+0 >= 0)) # define BOOST_HAS_NANOSLEEP # endif // BOOST_HAS_CLOCK_GETTIME: // This is predicated on _POSIX_TIMERS (also on _XOPEN_REALTIME // but at least one platform - linux - defines that flag without // defining clock_gettime): # if (defined(_POSIX_TIMERS) && (_POSIX_TIMERS+0 >= 0)) # define BOOST_HAS_CLOCK_GETTIME # endif // BOOST_HAS_SCHED_YIELD: // This is predicated on _POSIX_PRIORITY_SCHEDULING or // on _POSIX_THREAD_PRIORITY_SCHEDULING or on _XOPEN_REALTIME. # if defined(_POSIX_PRIORITY_SCHEDULING) && (_POSIX_PRIORITY_SCHEDULING+0 > 0)\ || (defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING+0 > 0))\ || (defined(_XOPEN_REALTIME) && (_XOPEN_REALTIME+0 >= 0)) # define BOOST_HAS_SCHED_YIELD # endif // BOOST_HAS_GETTIMEOFDAY: // BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE: // These are predicated on _XOPEN_VERSION, and appears to be first released // in issue 4, version 2 (_XOPEN_VERSION > 500). // Likewise for the functions log1p and expm1. # if defined(_XOPEN_VERSION) && (_XOPEN_VERSION+0 >= 500) # define BOOST_HAS_GETTIMEOFDAY # if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE+0 >= 500) # define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE # endif # ifndef BOOST_HAS_LOG1P # define BOOST_HAS_LOG1P # endif # ifndef BOOST_HAS_EXPM1 # define BOOST_HAS_EXPM1 # endif # endif # endif base-7.0.3.1/modules/libcom/vxWorks/boost/config/select_compiler_config.hpp0000664000577000060420000000716513557101274025626 0ustar anjaesctl// Boost compiler configuration selection header file // (C) Copyright John Maddock 2001 - 2003. // (C) Copyright Martin Wille 2003. // (C) Copyright Guillaume Melquiond 2003. // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // See http://www.boost.org/ for most recent version. // locate which compiler we are using and define // BOOST_COMPILER_CONFIG as needed: #if defined(__GCCXML__) // GCC-XML emulates other compilers, it has to appear first here! # define BOOST_COMPILER_CONFIG "boost/config/compiler/gcc_xml.hpp" #elif defined(_CRAYC) // EDG based Cray compiler: # define BOOST_COMPILER_CONFIG "boost/config/compiler/cray.hpp" #elif defined __CUDACC__ // NVIDIA CUDA C++ compiler for GPU # define BOOST_COMPILER_CONFIG "boost/config/compiler/nvcc.hpp" #elif defined __COMO__ // Comeau C++ # define BOOST_COMPILER_CONFIG "boost/config/compiler/comeau.hpp" #elif defined(__PATHSCALE__) && (__PATHCC__ >= 4) // PathScale EKOPath compiler (has to come before clang and gcc) # define BOOST_COMPILER_CONFIG "boost/config/compiler/pathscale.hpp" #elif defined __clang__ // Clang C++ emulates GCC, so it has to appear early. # define BOOST_COMPILER_CONFIG "boost/config/compiler/clang.hpp" #elif defined __DMC__ // Digital Mars C++ # define BOOST_COMPILER_CONFIG "boost/config/compiler/digitalmars.hpp" #elif defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC) // Intel # define BOOST_COMPILER_CONFIG "boost/config/compiler/intel.hpp" # elif defined __GNUC__ // GNU C++: # define BOOST_COMPILER_CONFIG "boost/config/compiler/gcc.hpp" #elif defined __KCC // Kai C++ # define BOOST_COMPILER_CONFIG "boost/config/compiler/kai.hpp" #elif defined __sgi // SGI MIPSpro C++ # define BOOST_COMPILER_CONFIG "boost/config/compiler/sgi_mipspro.hpp" #elif defined __DECCXX // Compaq Tru64 Unix cxx # define BOOST_COMPILER_CONFIG "boost/config/compiler/compaq_cxx.hpp" #elif defined __ghs // Greenhills C++ # define BOOST_COMPILER_CONFIG "boost/config/compiler/greenhills.hpp" #elif defined __CODEGEARC__ // CodeGear - must be checked for before Borland # define BOOST_COMPILER_CONFIG "boost/config/compiler/codegear.hpp" #elif defined __BORLANDC__ // Borland # define BOOST_COMPILER_CONFIG "boost/config/compiler/borland.hpp" #elif defined __MWERKS__ // Metrowerks CodeWarrior # define BOOST_COMPILER_CONFIG "boost/config/compiler/metrowerks.hpp" #elif defined __SUNPRO_CC // Sun Workshop Compiler C++ # define BOOST_COMPILER_CONFIG "boost/config/compiler/sunpro_cc.hpp" #elif defined __HP_aCC // HP aCC # define BOOST_COMPILER_CONFIG "boost/config/compiler/hp_acc.hpp" #elif defined(__MRC__) || defined(__SC__) // MPW MrCpp or SCpp # define BOOST_COMPILER_CONFIG "boost/config/compiler/mpw.hpp" #elif defined(__IBMCPP__) // IBM Visual Age # define BOOST_COMPILER_CONFIG "boost/config/compiler/vacpp.hpp" #elif defined(__PGI) // Portland Group Inc. # define BOOST_COMPILER_CONFIG "boost/config/compiler/pgi.hpp" #elif defined _MSC_VER // Microsoft Visual C++ // // Must remain the last #elif since some other vendors (Metrowerks, for // example) also #define _MSC_VER # define BOOST_COMPILER_CONFIG "boost/config/compiler/visualc.hpp" #elif defined (BOOST_ASSERT_CONFIG) // this must come last - generate an error if we don't // recognise the compiler: # error "Unknown compiler - please configure (http://www.boost.org/libs/config/config.htm#configuring) and report the results to the main boost mailing list (http://www.boost.org/more/mailing_lists.htm#main)" #endif base-7.0.3.1/modules/libcom/vxWorks/boost/config/select_platform_config.hpp0000664000577000060420000000622713557101274025636 0ustar anjaesctl// Boost compiler configuration selection header file // (C) Copyright John Maddock 2001 - 2002. // (C) Copyright Jens Maurer 2001. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // See http://www.boost.org for most recent version. // locate which platform we are on and define BOOST_PLATFORM_CONFIG as needed. // Note that we define the headers to include using "header_name" not // in order to prevent macro expansion within the header // name (for example "linux" is a macro on linux systems). #if (defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) && !defined(_CRAYC) // linux, also other platforms (Hurd etc) that use GLIBC, should these really have their own config headers though? # define BOOST_PLATFORM_CONFIG "boost/config/platform/linux.hpp" #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) // BSD: # define BOOST_PLATFORM_CONFIG "boost/config/platform/bsd.hpp" #elif defined(sun) || defined(__sun) // solaris: # define BOOST_PLATFORM_CONFIG "boost/config/platform/solaris.hpp" #elif defined(__sgi) // SGI Irix: # define BOOST_PLATFORM_CONFIG "boost/config/platform/irix.hpp" #elif defined(__hpux) // hp unix: # define BOOST_PLATFORM_CONFIG "boost/config/platform/hpux.hpp" #elif defined(__CYGWIN__) // cygwin is not win32: # define BOOST_PLATFORM_CONFIG "boost/config/platform/cygwin.hpp" #elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) // win32: # define BOOST_PLATFORM_CONFIG "boost/config/platform/win32.hpp" #elif defined(__BEOS__) // BeOS # define BOOST_PLATFORM_CONFIG "boost/config/platform/beos.hpp" #elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) // MacOS # define BOOST_PLATFORM_CONFIG "boost/config/platform/macos.hpp" #elif defined(__IBMCPP__) || defined(_AIX) // IBM # define BOOST_PLATFORM_CONFIG "boost/config/platform/aix.hpp" #elif defined(__amigaos__) // AmigaOS # define BOOST_PLATFORM_CONFIG "boost/config/platform/amigaos.hpp" #elif defined(__QNXNTO__) // QNX: # define BOOST_PLATFORM_CONFIG "boost/config/platform/qnxnto.hpp" #elif defined(__VXWORKS__) // vxWorks: # define BOOST_PLATFORM_CONFIG "boost/config/platform/vxworks.hpp" #elif defined(__SYMBIAN32__) // Symbian: # define BOOST_PLATFORM_CONFIG "boost/config/platform/symbian.hpp" #elif defined(_CRAYC) // Cray: # define BOOST_PLATFORM_CONFIG "boost/config/platform/cray.hpp" #elif defined(__VMS) // VMS: # define BOOST_PLATFORM_CONFIG "boost/config/platform/vms.hpp" #else # if defined(unix) \ || defined(__unix) \ || defined(_XOPEN_SOURCE) \ || defined(_POSIX_SOURCE) // generic unix platform: # ifndef BOOST_HAS_UNISTD_H # define BOOST_HAS_UNISTD_H # endif # include # endif # if defined (BOOST_ASSERT_CONFIG) // this must come last - generate an error if we don't // recognise the platform: # error "Unknown platform - please configure and report the results to boost.org" # endif #endif base-7.0.3.1/modules/libcom/vxWorks/boost/config/select_stdlib_config.hpp0000664000577000060420000000565013557101274025272 0ustar anjaesctl// Boost compiler configuration selection header file // (C) Copyright John Maddock 2001 - 2003. // (C) Copyright Jens Maurer 2001 - 2002. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // See http://www.boost.org for most recent version. // locate which std lib we are using and define BOOST_STDLIB_CONFIG as needed: // First include to determine if some version of STLport is in use as the std lib // (do not rely on this header being included since users can short-circuit this header // if they know whose std lib they are using.) #ifdef __cplusplus # include #else # include #endif #if defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION) // STLPort library; this _must_ come first, otherwise since // STLport typically sits on top of some other library, we // can end up detecting that first rather than STLport: # define BOOST_STDLIB_CONFIG "boost/config/stdlib/stlport.hpp" #else // If our std lib was not some version of STLport, then include as it is about // the smallest of the std lib headers that includes real C++ stuff. (Some std libs do not // include their C++-related macros in so this additional include makes sure // we get those definitions) // (again do not rely on this header being included since users can short-circuit this // header if they know whose std lib they are using.) #include #if defined(__LIBCOMO__) // Comeau STL: #define BOOST_STDLIB_CONFIG "boost/config/stdlib/libcomo.hpp" #elif defined(__STD_RWCOMPILER_H__) || defined(_RWSTD_VER) // Rogue Wave library: # define BOOST_STDLIB_CONFIG "boost/config/stdlib/roguewave.hpp" #elif defined(_LIBCPP_VERSION) // libc++ # define BOOST_STDLIB_CONFIG "boost/config/stdlib/libcpp.hpp" #elif defined(__GLIBCPP__) || defined(__GLIBCXX__) // GNU libstdc++ 3 # define BOOST_STDLIB_CONFIG "boost/config/stdlib/libstdcpp3.hpp" #elif defined(__STL_CONFIG_H) // generic SGI STL # define BOOST_STDLIB_CONFIG "boost/config/stdlib/sgi.hpp" #elif defined(__MSL_CPP__) // MSL standard lib: # define BOOST_STDLIB_CONFIG "boost/config/stdlib/msl.hpp" #elif defined(__IBMCPP__) // take the default VACPP std lib # define BOOST_STDLIB_CONFIG "boost/config/stdlib/vacpp.hpp" #elif defined(MSIPL_COMPILE_H) // Modena C++ standard library # define BOOST_STDLIB_CONFIG "boost/config/stdlib/modena.hpp" #elif (defined(_YVALS) && !defined(__IBMCPP__)) || defined(_CPPLIB_VER) // Dinkumware Library (this has to appear after any possible replacement libraries): # define BOOST_STDLIB_CONFIG "boost/config/stdlib/dinkumware.hpp" #elif defined (BOOST_ASSERT_CONFIG) // this must come last - generate an error if we don't // recognise the library: # error "Unknown standard library - please configure and report the results to boost.org" #endif #endif base-7.0.3.1/modules/libcom/vxWorks/boost/config/stdlib/dinkumware.hpp0000664000577000060420000001212413557101274024546 0ustar anjaesctl// (C) Copyright John Maddock 2001 - 2003. // (C) Copyright Jens Maurer 2001. // (C) Copyright Peter Dimov 2001. // (C) Copyright David Abrahams 2002. // (C) Copyright Guillaume Melquiond 2003. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // See http://www.boost.org for most recent version. // Dinkumware standard library config: #if !defined(_YVALS) && !defined(_CPPLIB_VER) #include #if !defined(_YVALS) && !defined(_CPPLIB_VER) #error This is not the Dinkumware lib! #endif #endif #if defined(_CPPLIB_VER) && (_CPPLIB_VER >= 306) // full dinkumware 3.06 and above // fully conforming provided the compiler supports it: # if !(defined(_GLOBAL_USING) && (_GLOBAL_USING+0 > 0)) && !defined(__BORLANDC__) && !defined(_STD) && !(defined(__ICC) && (__ICC >= 700)) // can be defined in yvals.h # define BOOST_NO_STDC_NAMESPACE # endif # if !(defined(_HAS_MEMBER_TEMPLATES_REBIND) && (_HAS_MEMBER_TEMPLATES_REBIND+0 > 0)) && !(defined(_MSC_VER) && (_MSC_VER > 1300)) && defined(BOOST_MSVC) # define BOOST_NO_STD_ALLOCATOR # endif # define BOOST_HAS_PARTIAL_STD_ALLOCATOR # if defined(BOOST_MSVC) && (BOOST_MSVC < 1300) // if this lib version is set up for vc6 then there is no std::use_facet: # define BOOST_NO_STD_USE_FACET # define BOOST_HAS_TWO_ARG_USE_FACET // C lib functions aren't in namespace std either: # define BOOST_NO_STDC_NAMESPACE // and nor is # define BOOST_NO_EXCEPTION_STD_NAMESPACE # endif // There's no numeric_limits support unless _LONGLONG is defined: # if !defined(_LONGLONG) && (_CPPLIB_VER <= 310) # define BOOST_NO_MS_INT64_NUMERIC_LIMITS # endif // 3.06 appears to have (non-sgi versions of) & , // and no at all #else # define BOOST_MSVC_STD_ITERATOR 1 # define BOOST_NO_STD_ITERATOR # define BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS # define BOOST_NO_STD_ALLOCATOR # define BOOST_NO_STDC_NAMESPACE # define BOOST_NO_STD_USE_FACET # define BOOST_NO_STD_OUTPUT_ITERATOR_ASSIGN # define BOOST_HAS_MACRO_USE_FACET # ifndef _CPPLIB_VER // Updated Dinkum library defines this, and provides // its own min and max definitions, as does MTA version. # ifndef __MTA__ # define BOOST_NO_STD_MIN_MAX # endif # define BOOST_NO_MS_INT64_NUMERIC_LIMITS # endif #endif // // std extension namespace is stdext for vc7.1 and later, // the same applies to other compilers that sit on top // of vc7.1 (Intel and Comeau): // #if defined(_MSC_VER) && (_MSC_VER >= 1310) && !defined(__BORLANDC__) # define BOOST_STD_EXTENSION_NAMESPACE stdext #endif #if (defined(_MSC_VER) && (_MSC_VER <= 1300) && !defined(__BORLANDC__)) || !defined(_CPPLIB_VER) || (_CPPLIB_VER < 306) // if we're using a dinkum lib that's // been configured for VC6/7 then there is // no iterator traits (true even for icl) # define BOOST_NO_STD_ITERATOR_TRAITS #endif #if defined(__ICL) && (__ICL < 800) && defined(_CPPLIB_VER) && (_CPPLIB_VER <= 310) // Intel C++ chokes over any non-trivial use of // this may be an overly restrictive define, but regex fails without it: # define BOOST_NO_STD_LOCALE #endif #include #if ( (!_HAS_EXCEPTIONS && !defined(__ghs__)) || (!_HAS_NAMESPACE && defined(__ghs__)) ) # define BOOST_NO_STD_TYPEINFO #endif // C++0x headers implemented in 520 (as shipped by Microsoft) // #if !defined(_CPPLIB_VER) || _CPPLIB_VER < 520 # define BOOST_NO_CXX11_HDR_ARRAY # define BOOST_NO_CXX11_HDR_CODECVT # define BOOST_NO_CXX11_HDR_FORWARD_LIST # define BOOST_NO_CXX11_HDR_INITIALIZER_LIST # define BOOST_NO_CXX11_HDR_RANDOM # define BOOST_NO_CXX11_HDR_REGEX # define BOOST_NO_CXX11_HDR_SYSTEM_ERROR # define BOOST_NO_CXX11_HDR_UNORDERED_MAP # define BOOST_NO_CXX11_HDR_UNORDERED_SET # define BOOST_NO_CXX11_HDR_TUPLE # define BOOST_NO_CXX11_HDR_TYPEINDEX # define BOOST_NO_CXX11_HDR_FUNCTIONAL # define BOOST_NO_CXX11_NUMERIC_LIMITS # define BOOST_NO_CXX11_SMART_PTR #endif #if (!defined(_HAS_TR1_IMPORTS) || (_HAS_TR1_IMPORTS+0 == 0)) && !defined(BOOST_NO_CXX11_HDR_TUPLE) # define BOOST_NO_CXX11_HDR_TUPLE #endif // C++0x headers implemented in 540 (as shipped by Microsoft) // #if !defined(_CPPLIB_VER) || _CPPLIB_VER < 540 # define BOOST_NO_CXX11_HDR_TYPE_TRAITS # define BOOST_NO_CXX11_HDR_CHRONO # define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE # define BOOST_NO_CXX11_HDR_FUTURE # define BOOST_NO_CXX11_HDR_MUTEX # define BOOST_NO_CXX11_HDR_RATIO # define BOOST_NO_CXX11_HDR_THREAD # define BOOST_NO_CXX11_ALLOCATOR # define BOOST_NO_CXX11_ATOMIC_SMART_PTR #endif // // C++0x headers not yet (fully) implemented: // # define BOOST_NO_CXX11_HDR_INITIALIZER_LIST #ifdef _CPPLIB_VER # define BOOST_DINKUMWARE_STDLIB _CPPLIB_VER #else # define BOOST_DINKUMWARE_STDLIB 1 #endif #ifdef _CPPLIB_VER # define BOOST_STDLIB "Dinkumware standard library version " BOOST_STRINGIZE(_CPPLIB_VER) #else # define BOOST_STDLIB "Dinkumware standard library version 1.x" #endif base-7.0.3.1/modules/libcom/vxWorks/boost/config/suffix.hpp0000664000577000060420000007730413557101274022436 0ustar anjaesctl// Boost config.hpp configuration header file ------------------------------// // boostinspect:ndprecated_macros -- tell the inspect tool to ignore this file // Copyright (c) 2001-2003 John Maddock // Copyright (c) 2001 Darin Adler // Copyright (c) 2001 Peter Dimov // Copyright (c) 2002 Bill Kempf // Copyright (c) 2002 Jens Maurer // Copyright (c) 2002-2003 David Abrahams // Copyright (c) 2003 Gennaro Prota // Copyright (c) 2003 Eric Friedman // Copyright (c) 2010 Eric Jourdanneau, Joel Falcou // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // See http://www.boost.org/ for most recent version. // Boost config.hpp policy and rationale documentation has been moved to // http://www.boost.org/libs/config/ // // This file is intended to be stable, and relatively unchanging. // It should contain boilerplate code only - no compiler specific // code unless it is unavoidable - no changes unless unavoidable. #ifndef BOOST_CONFIG_SUFFIX_HPP #define BOOST_CONFIG_SUFFIX_HPP #if defined(__GNUC__) && (__GNUC__ >= 4) // // Some GCC-4.x versions issue warnings even when __extension__ is used, // so use this as a workaround: // #pragma GCC system_header #endif // // ensure that visibility macros are always defined, thus symplifying use // #ifndef BOOST_SYMBOL_EXPORT # define BOOST_SYMBOL_EXPORT #endif #ifndef BOOST_SYMBOL_IMPORT # define BOOST_SYMBOL_IMPORT #endif #ifndef BOOST_SYMBOL_VISIBLE # define BOOST_SYMBOL_VISIBLE #endif // // look for long long by looking for the appropriate macros in . // Note that we use limits.h rather than climits for maximal portability, // remember that since these just declare a bunch of macros, there should be // no namespace issues from this. // #if !defined(BOOST_HAS_LONG_LONG) && !defined(BOOST_NO_LONG_LONG) \ && !defined(BOOST_MSVC) && !defined(__BORLANDC__) # include # if (defined(ULLONG_MAX) || defined(ULONG_LONG_MAX) || defined(ULONGLONG_MAX)) # define BOOST_HAS_LONG_LONG # else # define BOOST_NO_LONG_LONG # endif #endif // GCC 3.x will clean up all of those nasty macro definitions that // BOOST_NO_CTYPE_FUNCTIONS is intended to help work around, so undefine // it under GCC 3.x. #if defined(__GNUC__) && (__GNUC__ >= 3) && defined(BOOST_NO_CTYPE_FUNCTIONS) # undef BOOST_NO_CTYPE_FUNCTIONS #endif // // Assume any extensions are in namespace std:: unless stated otherwise: // # ifndef BOOST_STD_EXTENSION_NAMESPACE # define BOOST_STD_EXTENSION_NAMESPACE std # endif // // If cv-qualified specializations are not allowed, then neither are cv-void ones: // # if defined(BOOST_NO_CV_SPECIALIZATIONS) \ && !defined(BOOST_NO_CV_VOID_SPECIALIZATIONS) # define BOOST_NO_CV_VOID_SPECIALIZATIONS # endif // // If there is no numeric_limits template, then it can't have any compile time // constants either! // # if defined(BOOST_NO_LIMITS) \ && !defined(BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS) # define BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS # define BOOST_NO_MS_INT64_NUMERIC_LIMITS # define BOOST_NO_LONG_LONG_NUMERIC_LIMITS # endif // // if there is no long long then there is no specialisation // for numeric_limits either: // #if !defined(BOOST_HAS_LONG_LONG) && !defined(BOOST_NO_LONG_LONG_NUMERIC_LIMITS) # define BOOST_NO_LONG_LONG_NUMERIC_LIMITS #endif // // if there is no __int64 then there is no specialisation // for numeric_limits<__int64> either: // #if !defined(BOOST_HAS_MS_INT64) && !defined(BOOST_NO_MS_INT64_NUMERIC_LIMITS) # define BOOST_NO_MS_INT64_NUMERIC_LIMITS #endif // // if member templates are supported then so is the // VC6 subset of member templates: // # if !defined(BOOST_NO_MEMBER_TEMPLATES) \ && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) # define BOOST_MSVC6_MEMBER_TEMPLATES # endif // // Without partial specialization, can't test for partial specialisation bugs: // # if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ && !defined(BOOST_BCB_PARTIAL_SPECIALIZATION_BUG) # define BOOST_BCB_PARTIAL_SPECIALIZATION_BUG # endif // // Without partial specialization, we can't have array-type partial specialisations: // # if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ && !defined(BOOST_NO_ARRAY_TYPE_SPECIALIZATIONS) # define BOOST_NO_ARRAY_TYPE_SPECIALIZATIONS # endif // // Without partial specialization, std::iterator_traits can't work: // # if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ && !defined(BOOST_NO_STD_ITERATOR_TRAITS) # define BOOST_NO_STD_ITERATOR_TRAITS # endif // // Without partial specialization, partial // specialization with default args won't work either: // # if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ && !defined(BOOST_NO_PARTIAL_SPECIALIZATION_IMPLICIT_DEFAULT_ARGS) # define BOOST_NO_PARTIAL_SPECIALIZATION_IMPLICIT_DEFAULT_ARGS # endif // // Without member template support, we can't have template constructors // in the standard library either: // # if defined(BOOST_NO_MEMBER_TEMPLATES) \ && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) \ && !defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS) # define BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS # endif // // Without member template support, we can't have a conforming // std::allocator template either: // # if defined(BOOST_NO_MEMBER_TEMPLATES) \ && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) \ && !defined(BOOST_NO_STD_ALLOCATOR) # define BOOST_NO_STD_ALLOCATOR # endif // // without ADL support then using declarations will break ADL as well: // #if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) && !defined(BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL) # define BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL #endif // // Without typeid support we have no dynamic RTTI either: // #if defined(BOOST_NO_TYPEID) && !defined(BOOST_NO_RTTI) # define BOOST_NO_RTTI #endif // // If we have a standard allocator, then we have a partial one as well: // #if !defined(BOOST_NO_STD_ALLOCATOR) # define BOOST_HAS_PARTIAL_STD_ALLOCATOR #endif // // We can't have a working std::use_facet if there is no std::locale: // # if defined(BOOST_NO_STD_LOCALE) && !defined(BOOST_NO_STD_USE_FACET) # define BOOST_NO_STD_USE_FACET # endif // // We can't have a std::messages facet if there is no std::locale: // # if defined(BOOST_NO_STD_LOCALE) && !defined(BOOST_NO_STD_MESSAGES) # define BOOST_NO_STD_MESSAGES # endif // // We can't have a working std::wstreambuf if there is no std::locale: // # if defined(BOOST_NO_STD_LOCALE) && !defined(BOOST_NO_STD_WSTREAMBUF) # define BOOST_NO_STD_WSTREAMBUF # endif // // We can't have a if there is no : // # if defined(BOOST_NO_CWCHAR) && !defined(BOOST_NO_CWCTYPE) # define BOOST_NO_CWCTYPE # endif // // We can't have a swprintf if there is no : // # if defined(BOOST_NO_CWCHAR) && !defined(BOOST_NO_SWPRINTF) # define BOOST_NO_SWPRINTF # endif // // If Win32 support is turned off, then we must turn off // threading support also, unless there is some other // thread API enabled: // #if defined(BOOST_DISABLE_WIN32) && defined(_WIN32) \ && !defined(BOOST_DISABLE_THREADS) && !defined(BOOST_HAS_PTHREADS) # define BOOST_DISABLE_THREADS #endif // // Turn on threading support if the compiler thinks that it's in // multithreaded mode. We put this here because there are only a // limited number of macros that identify this (if there's any missing // from here then add to the appropriate compiler section): // #if (defined(__MT__) || defined(_MT) || defined(_REENTRANT) \ || defined(_PTHREADS) || defined(__APPLE__) || defined(__DragonFly__)) \ && !defined(BOOST_HAS_THREADS) # define BOOST_HAS_THREADS #endif // // Turn threading support off if BOOST_DISABLE_THREADS is defined: // #if defined(BOOST_DISABLE_THREADS) && defined(BOOST_HAS_THREADS) # undef BOOST_HAS_THREADS #endif // // Turn threading support off if we don't recognise the threading API: // #if defined(BOOST_HAS_THREADS) && !defined(BOOST_HAS_PTHREADS)\ && !defined(BOOST_HAS_WINTHREADS) && !defined(BOOST_HAS_BETHREADS)\ && !defined(BOOST_HAS_MPTASKS) # undef BOOST_HAS_THREADS #endif // // Turn threading detail macros off if we don't (want to) use threading // #ifndef BOOST_HAS_THREADS # undef BOOST_HAS_PTHREADS # undef BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE # undef BOOST_HAS_PTHREAD_YIELD # undef BOOST_HAS_PTHREAD_DELAY_NP # undef BOOST_HAS_WINTHREADS # undef BOOST_HAS_BETHREADS # undef BOOST_HAS_MPTASKS #endif // // If the compiler claims to be C99 conformant, then it had better // have a : // # if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901) # define BOOST_HAS_STDINT_H # ifndef BOOST_HAS_LOG1P # define BOOST_HAS_LOG1P # endif # ifndef BOOST_HAS_EXPM1 # define BOOST_HAS_EXPM1 # endif # endif // // Define BOOST_NO_SLIST and BOOST_NO_HASH if required. // Note that this is for backwards compatibility only. // # if !defined(BOOST_HAS_SLIST) && !defined(BOOST_NO_SLIST) # define BOOST_NO_SLIST # endif # if !defined(BOOST_HAS_HASH) && !defined(BOOST_NO_HASH) # define BOOST_NO_HASH # endif // // Set BOOST_SLIST_HEADER if not set already: // #if defined(BOOST_HAS_SLIST) && !defined(BOOST_SLIST_HEADER) # define BOOST_SLIST_HEADER #endif // // Set BOOST_HASH_SET_HEADER if not set already: // #if defined(BOOST_HAS_HASH) && !defined(BOOST_HASH_SET_HEADER) # define BOOST_HASH_SET_HEADER #endif // // Set BOOST_HASH_MAP_HEADER if not set already: // #if defined(BOOST_HAS_HASH) && !defined(BOOST_HASH_MAP_HEADER) # define BOOST_HASH_MAP_HEADER #endif // BOOST_HAS_ABI_HEADERS // This macro gets set if we have headers that fix the ABI, // and prevent ODR violations when linking to external libraries: #if defined(BOOST_ABI_PREFIX) && defined(BOOST_ABI_SUFFIX) && !defined(BOOST_HAS_ABI_HEADERS) # define BOOST_HAS_ABI_HEADERS #endif #if defined(BOOST_HAS_ABI_HEADERS) && defined(BOOST_DISABLE_ABI_HEADERS) # undef BOOST_HAS_ABI_HEADERS #endif // BOOST_NO_STDC_NAMESPACE workaround --------------------------------------// // Because std::size_t usage is so common, even in boost headers which do not // otherwise use the C library, the workaround is included here so // that ugly workaround code need not appear in many other boost headers. // NOTE WELL: This is a workaround for non-conforming compilers; // must still be #included in the usual places so that inclusion // works as expected with standard conforming compilers. The resulting // double inclusion of is harmless. # if defined(BOOST_NO_STDC_NAMESPACE) && defined(__cplusplus) # include namespace std { using ::ptrdiff_t; using ::size_t; } # endif // Workaround for the unfortunate min/max macros defined by some platform headers #define BOOST_PREVENT_MACRO_SUBSTITUTION #ifndef BOOST_USING_STD_MIN # define BOOST_USING_STD_MIN() using std::min #endif #ifndef BOOST_USING_STD_MAX # define BOOST_USING_STD_MAX() using std::max #endif // BOOST_NO_STD_MIN_MAX workaround -----------------------------------------// # if defined(BOOST_NO_STD_MIN_MAX) && defined(__cplusplus) namespace std { template inline const _Tp& min BOOST_PREVENT_MACRO_SUBSTITUTION (const _Tp& __a, const _Tp& __b) { return __b < __a ? __b : __a; } template inline const _Tp& max BOOST_PREVENT_MACRO_SUBSTITUTION (const _Tp& __a, const _Tp& __b) { return __a < __b ? __b : __a; } } # endif // BOOST_STATIC_CONSTANT workaround --------------------------------------- // // On compilers which don't allow in-class initialization of static integral // constant members, we must use enums as a workaround if we want the constants // to be available at compile-time. This macro gives us a convenient way to // declare such constants. # ifdef BOOST_NO_INCLASS_MEMBER_INITIALIZATION # define BOOST_STATIC_CONSTANT(type, assignment) enum { assignment } # else # define BOOST_STATIC_CONSTANT(type, assignment) static const type assignment # endif // BOOST_USE_FACET / HAS_FACET workaround ----------------------------------// // When the standard library does not have a conforming std::use_facet there // are various workarounds available, but they differ from library to library. // The same problem occurs with has_facet. // These macros provide a consistent way to access a locale's facets. // Usage: // replace // std::use_facet(loc); // with // BOOST_USE_FACET(Type, loc); // Note do not add a std:: prefix to the front of BOOST_USE_FACET! // Use for BOOST_HAS_FACET is analogous. #if defined(BOOST_NO_STD_USE_FACET) # ifdef BOOST_HAS_TWO_ARG_USE_FACET # define BOOST_USE_FACET(Type, loc) std::use_facet(loc, static_cast(0)) # define BOOST_HAS_FACET(Type, loc) std::has_facet(loc, static_cast(0)) # elif defined(BOOST_HAS_MACRO_USE_FACET) # define BOOST_USE_FACET(Type, loc) std::_USE(loc, Type) # define BOOST_HAS_FACET(Type, loc) std::_HAS(loc, Type) # elif defined(BOOST_HAS_STLP_USE_FACET) # define BOOST_USE_FACET(Type, loc) (*std::_Use_facet(loc)) # define BOOST_HAS_FACET(Type, loc) std::has_facet< Type >(loc) # endif #else # define BOOST_USE_FACET(Type, loc) std::use_facet< Type >(loc) # define BOOST_HAS_FACET(Type, loc) std::has_facet< Type >(loc) #endif // BOOST_NESTED_TEMPLATE workaround ------------------------------------------// // Member templates are supported by some compilers even though they can't use // the A::template member syntax, as a workaround replace: // // typedef typename A::template rebind binder; // // with: // // typedef typename A::BOOST_NESTED_TEMPLATE rebind binder; #ifndef BOOST_NO_MEMBER_TEMPLATE_KEYWORD # define BOOST_NESTED_TEMPLATE template #else # define BOOST_NESTED_TEMPLATE #endif // BOOST_UNREACHABLE_RETURN(x) workaround -------------------------------------// // Normally evaluates to nothing, unless BOOST_NO_UNREACHABLE_RETURN_DETECTION // is defined, in which case it evaluates to return x; Use when you have a return // statement that can never be reached. #ifdef BOOST_NO_UNREACHABLE_RETURN_DETECTION # define BOOST_UNREACHABLE_RETURN(x) return x; #else # define BOOST_UNREACHABLE_RETURN(x) #endif // BOOST_DEDUCED_TYPENAME workaround ------------------------------------------// // // Some compilers don't support the use of `typename' for dependent // types in deduced contexts, e.g. // // template void f(T, typename T::type); // ^^^^^^^^ // Replace these declarations with: // // template void f(T, BOOST_DEDUCED_TYPENAME T::type); #ifndef BOOST_NO_DEDUCED_TYPENAME # define BOOST_DEDUCED_TYPENAME typename #else # define BOOST_DEDUCED_TYPENAME #endif #ifndef BOOST_NO_TYPENAME_WITH_CTOR # define BOOST_CTOR_TYPENAME typename #else # define BOOST_CTOR_TYPENAME #endif // long long workaround ------------------------------------------// // On gcc (and maybe other compilers?) long long is alway supported // but it's use may generate either warnings (with -ansi), or errors // (with -pedantic -ansi) unless it's use is prefixed by __extension__ // #if defined(BOOST_HAS_LONG_LONG) && defined(__cplusplus) namespace boost{ # ifdef __GNUC__ __extension__ typedef long long long_long_type; __extension__ typedef unsigned long long ulong_long_type; # else typedef long long long_long_type; typedef unsigned long long ulong_long_type; # endif } #endif // same again for __int128: #if defined(BOOST_HAS_INT128) && defined(__cplusplus) namespace boost{ # ifdef __GNUC__ __extension__ typedef __int128 int128_type; __extension__ typedef unsigned __int128 uint128_type; # else typedef __int128 int128_type; typedef unsigned __int128 uint128_type; # endif } #endif // BOOST_[APPEND_]EXPLICIT_TEMPLATE_[NON_]TYPE macros --------------------------// // // Some compilers have problems with function templates whose template // parameters don't appear in the function parameter list (basically // they just link one instantiation of the template in the final // executable). These macros provide a uniform way to cope with the // problem with no effects on the calling syntax. // Example: // // #include // #include // #include // // template // void f() { std::cout << n << ' '; } // // template // void g() { std::cout << typeid(T).name() << ' '; } // // int main() { // f<1>(); // f<2>(); // // g(); // g(); // } // // With VC++ 6.0 the output is: // // 2 2 double double // // To fix it, write // // template // void f(BOOST_EXPLICIT_TEMPLATE_NON_TYPE(int, n)) { ... } // // template // void g(BOOST_EXPLICIT_TEMPLATE_TYPE(T)) { ... } // #if defined(BOOST_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS) && defined(__cplusplus) # include "boost/type.hpp" # include "boost/non_type.hpp" # define BOOST_EXPLICIT_TEMPLATE_TYPE(t) boost::type* = 0 # define BOOST_EXPLICIT_TEMPLATE_TYPE_SPEC(t) boost::type* # define BOOST_EXPLICIT_TEMPLATE_NON_TYPE(t, v) boost::non_type* = 0 # define BOOST_EXPLICIT_TEMPLATE_NON_TYPE_SPEC(t, v) boost::non_type* # define BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(t) \ , BOOST_EXPLICIT_TEMPLATE_TYPE(t) # define BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE_SPEC(t) \ , BOOST_EXPLICIT_TEMPLATE_TYPE_SPEC(t) # define BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE(t, v) \ , BOOST_EXPLICIT_TEMPLATE_NON_TYPE(t, v) # define BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE_SPEC(t, v) \ , BOOST_EXPLICIT_TEMPLATE_NON_TYPE_SPEC(t, v) #else // no workaround needed: expand to nothing # define BOOST_EXPLICIT_TEMPLATE_TYPE(t) # define BOOST_EXPLICIT_TEMPLATE_TYPE_SPEC(t) # define BOOST_EXPLICIT_TEMPLATE_NON_TYPE(t, v) # define BOOST_EXPLICIT_TEMPLATE_NON_TYPE_SPEC(t, v) # define BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(t) # define BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE_SPEC(t) # define BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE(t, v) # define BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE_SPEC(t, v) #endif // defined BOOST_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS // When BOOST_NO_STD_TYPEINFO is defined, we can just import // the global definition into std namespace: #if defined(BOOST_NO_STD_TYPEINFO) && defined(__cplusplus) #include namespace std{ using ::type_info; } #endif // ---------------------------------------------------------------------------// // // Helper macro BOOST_STRINGIZE: // Converts the parameter X to a string after macro replacement // on X has been performed. // #define BOOST_STRINGIZE(X) BOOST_DO_STRINGIZE(X) #define BOOST_DO_STRINGIZE(X) #X // // Helper macro BOOST_JOIN: // The following piece of macro magic joins the two // arguments together, even when one of the arguments is // itself a macro (see 16.3.1 in C++ standard). The key // is that macro expansion of macro arguments does not // occur in BOOST_DO_JOIN2 but does in BOOST_DO_JOIN. // #define BOOST_JOIN( X, Y ) BOOST_DO_JOIN( X, Y ) #define BOOST_DO_JOIN( X, Y ) BOOST_DO_JOIN2(X,Y) #define BOOST_DO_JOIN2( X, Y ) X##Y // // Set some default values for compiler/library/platform names. // These are for debugging config setup only: // # ifndef BOOST_COMPILER # define BOOST_COMPILER "Unknown ISO C++ Compiler" # endif # ifndef BOOST_STDLIB # define BOOST_STDLIB "Unknown ISO standard library" # endif # ifndef BOOST_PLATFORM # if defined(unix) || defined(__unix) || defined(_XOPEN_SOURCE) \ || defined(_POSIX_SOURCE) # define BOOST_PLATFORM "Generic Unix" # else # define BOOST_PLATFORM "Unknown" # endif # endif // // Set some default values GPU support // # ifndef BOOST_GPU_ENABLED # define BOOST_GPU_ENABLED # endif // BOOST_FORCEINLINE ---------------------------------------------// // Macro to use in place of 'inline' to force a function to be inline #if !defined(BOOST_FORCEINLINE) # if defined(_MSC_VER) # define BOOST_FORCEINLINE __forceinline # elif defined(__GNUC__) && __GNUC__ > 3 # define BOOST_FORCEINLINE inline __attribute__ ((always_inline)) # else # define BOOST_FORCEINLINE inline # endif #endif // // Set BOOST_NO_DECLTYPE_N3276 when BOOST_NO_DECLTYPE is defined // #if defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_CXX11_DECLTYPE_N3276) #define BOOST_NO_CXX11_DECLTYPE_N3276 BOOST_NO_CXX11_DECLTYPE #endif // -------------------- Deprecated macros for 1.50 --------------------------- // These will go away in a future release // Use BOOST_NO_CXX11_HDR_UNORDERED_SET or BOOST_NO_CXX11_HDR_UNORDERED_MAP // instead of BOOST_NO_STD_UNORDERED #if defined(BOOST_NO_CXX11_HDR_UNORDERED_MAP) || defined (BOOST_NO_CXX11_HDR_UNORDERED_SET) # ifndef BOOST_NO_CXX11_STD_UNORDERED # define BOOST_NO_CXX11_STD_UNORDERED # endif #endif // Use BOOST_NO_CXX11_HDR_INITIALIZER_LIST instead of BOOST_NO_INITIALIZER_LISTS #if defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) && !defined(BOOST_NO_INITIALIZER_LISTS) # define BOOST_NO_INITIALIZER_LISTS #endif // Use BOOST_NO_CXX11_HDR_ARRAY instead of BOOST_NO_0X_HDR_ARRAY #if defined(BOOST_NO_CXX11_HDR_ARRAY) && !defined(BOOST_NO_BOOST_NO_0X_HDR_ARRAY) # define BOOST_NO_0X_HDR_ARRAY #endif // Use BOOST_NO_CXX11_HDR_CHRONO instead of BOOST_NO_0X_HDR_CHRONO #if defined(BOOST_NO_CXX11_HDR_CHRONO) && !defined(BOOST_NO_0X_HDR_CHRONO) # define BOOST_NO_0X_HDR_CHRONO #endif // Use BOOST_NO_CXX11_HDR_CODECVT instead of BOOST_NO_0X_HDR_CODECVT #if defined(BOOST_NO_CXX11_HDR_CODECVT) && !defined(BOOST_NO_0X_HDR_CODECVT) # define BOOST_NO_0X_HDR_CODECVT #endif // Use BOOST_NO_CXX11_HDR_CONDITION_VARIABLE instead of BOOST_NO_0X_HDR_CONDITION_VARIABLE #if defined(BOOST_NO_CXX11_HDR_CONDITION_VARIABLE) && !defined(BOOST_NO_0X_HDR_CONDITION_VARIABLE) # define BOOST_NO_0X_HDR_CONDITION_VARIABLE #endif // Use BOOST_NO_CXX11_HDR_FORWARD_LIST instead of BOOST_NO_0X_HDR_FORWARD_LIST #if defined(BOOST_NO_CXX11_HDR_FORWARD_LIST) && !defined(BOOST_NO_0X_HDR_FORWARD_LIST) # define BOOST_NO_0X_HDR_FORWARD_LIST #endif // Use BOOST_NO_CXX11_HDR_FUTURE instead of BOOST_NO_0X_HDR_FUTURE #if defined(BOOST_NO_CXX11_HDR_FUTURE) && !defined(BOOST_NO_0X_HDR_FUTURE) # define BOOST_NO_0X_HDR_FUTURE #endif // Use BOOST_NO_CXX11_HDR_INITIALIZER_LIST // instead of BOOST_NO_0X_HDR_INITIALIZER_LIST or BOOST_NO_INITIALIZER_LISTS #ifdef BOOST_NO_CXX11_HDR_INITIALIZER_LIST # ifndef BOOST_NO_0X_HDR_INITIALIZER_LIST # define BOOST_NO_0X_HDR_INITIALIZER_LIST # endif # ifndef BOOST_NO_INITIALIZER_LISTS # define BOOST_NO_INITIALIZER_LISTS # endif #endif // Use BOOST_NO_CXX11_HDR_MUTEX instead of BOOST_NO_0X_HDR_MUTEX #if defined(BOOST_NO_CXX11_HDR_MUTEX) && !defined(BOOST_NO_0X_HDR_MUTEX) # define BOOST_NO_0X_HDR_MUTEX #endif // Use BOOST_NO_CXX11_HDR_RANDOM instead of BOOST_NO_0X_HDR_RANDOM #if defined(BOOST_NO_CXX11_HDR_RANDOM) && !defined(BOOST_NO_0X_HDR_RANDOM) # define BOOST_NO_0X_HDR_RANDOM #endif // Use BOOST_NO_CXX11_HDR_RATIO instead of BOOST_NO_0X_HDR_RATIO #if defined(BOOST_NO_CXX11_HDR_RATIO) && !defined(BOOST_NO_0X_HDR_RATIO) # define BOOST_NO_0X_HDR_RATIO #endif // Use BOOST_NO_CXX11_HDR_REGEX instead of BOOST_NO_0X_HDR_REGEX #if defined(BOOST_NO_CXX11_HDR_REGEX) && !defined(BOOST_NO_0X_HDR_REGEX) # define BOOST_NO_0X_HDR_REGEX #endif // Use BOOST_NO_CXX11_HDR_SYSTEM_ERROR instead of BOOST_NO_0X_HDR_SYSTEM_ERROR #if defined(BOOST_NO_CXX11_HDR_SYSTEM_ERROR) && !defined(BOOST_NO_0X_HDR_SYSTEM_ERROR) # define BOOST_NO_0X_HDR_SYSTEM_ERROR #endif // Use BOOST_NO_CXX11_HDR_THREAD instead of BOOST_NO_0X_HDR_THREAD #if defined(BOOST_NO_CXX11_HDR_THREAD) && !defined(BOOST_NO_0X_HDR_THREAD) # define BOOST_NO_0X_HDR_THREAD #endif // Use BOOST_NO_CXX11_HDR_TUPLE instead of BOOST_NO_0X_HDR_TUPLE #if defined(BOOST_NO_CXX11_HDR_TUPLE) && !defined(BOOST_NO_0X_HDR_TUPLE) # define BOOST_NO_0X_HDR_TUPLE #endif // Use BOOST_NO_CXX11_HDR_TYPE_TRAITS instead of BOOST_NO_0X_HDR_TYPE_TRAITS #if defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS) && !defined(BOOST_NO_0X_HDR_TYPE_TRAITS) # define BOOST_NO_0X_HDR_TYPE_TRAITS #endif // Use BOOST_NO_CXX11_HDR_TYPEINDEX instead of BOOST_NO_0X_HDR_TYPEINDEX #if defined(BOOST_NO_CXX11_HDR_TYPEINDEX) && !defined(BOOST_NO_0X_HDR_TYPEINDEX) # define BOOST_NO_0X_HDR_TYPEINDEX #endif // Use BOOST_NO_CXX11_HDR_UNORDERED_MAP instead of BOOST_NO_0X_HDR_UNORDERED_MAP #if defined(BOOST_NO_CXX11_HDR_UNORDERED_MAP) && !defined(BOOST_NO_0X_HDR_UNORDERED_MAP) # define BOOST_NO_0X_HDR_UNORDERED_MAP #endif // Use BOOST_NO_CXX11_HDR_UNORDERED_SET instead of BOOST_NO_0X_HDR_UNORDERED_SET #if defined(BOOST_NO_CXX11_HDR_UNORDERED_SET) && !defined(BOOST_NO_0X_HDR_UNORDERED_SET) # define BOOST_NO_0X_HDR_UNORDERED_SET #endif // ------------------ End of deprecated macros for 1.50 --------------------------- // -------------------- Deprecated macros for 1.51 --------------------------- // These will go away in a future release // Use BOOST_NO_CXX11_AUTO_DECLARATIONS instead of BOOST_NO_AUTO_DECLARATIONS #if defined(BOOST_NO_CXX11_AUTO_DECLARATIONS) && !defined(BOOST_NO_AUTO_DECLARATIONS) # define BOOST_NO_AUTO_DECLARATIONS #endif // Use BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS instead of BOOST_NO_AUTO_MULTIDECLARATIONS #if defined(BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS) && !defined(BOOST_NO_AUTO_MULTIDECLARATIONS) # define BOOST_NO_AUTO_MULTIDECLARATIONS #endif // Use BOOST_NO_CXX11_CHAR16_T instead of BOOST_NO_CHAR16_T #if defined(BOOST_NO_CXX11_CHAR16_T) && !defined(BOOST_NO_CHAR16_T) # define BOOST_NO_CHAR16_T #endif // Use BOOST_NO_CXX11_CHAR32_T instead of BOOST_NO_CHAR32_T #if defined(BOOST_NO_CXX11_CHAR32_T) && !defined(BOOST_NO_CHAR32_T) # define BOOST_NO_CHAR32_T #endif // Use BOOST_NO_CXX11_TEMPLATE_ALIASES instead of BOOST_NO_TEMPLATE_ALIASES #if defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) && !defined(BOOST_NO_TEMPLATE_ALIASES) # define BOOST_NO_TEMPLATE_ALIASES #endif // Use BOOST_NO_CXX11_CONSTEXPR instead of BOOST_NO_CONSTEXPR #if defined(BOOST_NO_CXX11_CONSTEXPR) && !defined(BOOST_NO_CONSTEXPR) # define BOOST_NO_CONSTEXPR #endif // Use BOOST_NO_CXX11_DECLTYPE_N3276 instead of BOOST_NO_DECLTYPE_N3276 #if defined(BOOST_NO_CXX11_DECLTYPE_N3276) && !defined(BOOST_NO_DECLTYPE_N3276) # define BOOST_NO_DECLTYPE_N3276 #endif // Use BOOST_NO_CXX11_DECLTYPE instead of BOOST_NO_DECLTYPE #if defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_DECLTYPE) # define BOOST_NO_DECLTYPE #endif // Use BOOST_NO_CXX11_DEFAULTED_FUNCTIONS instead of BOOST_NO_DEFAULTED_FUNCTIONS #if defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) && !defined(BOOST_NO_DEFAULTED_FUNCTIONS) # define BOOST_NO_DEFAULTED_FUNCTIONS #endif // Use BOOST_NO_CXX11_DELETED_FUNCTIONS instead of BOOST_NO_DELETED_FUNCTIONS #if defined(BOOST_NO_CXX11_DELETED_FUNCTIONS) && !defined(BOOST_NO_DELETED_FUNCTIONS) # define BOOST_NO_DELETED_FUNCTIONS #endif // Use BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS instead of BOOST_NO_EXPLICIT_CONVERSION_OPERATORS #if defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) && !defined(BOOST_NO_EXPLICIT_CONVERSION_OPERATORS) # define BOOST_NO_EXPLICIT_CONVERSION_OPERATORS #endif // Use BOOST_NO_CXX11_EXTERN_TEMPLATE instead of BOOST_NO_EXTERN_TEMPLATE #if defined(BOOST_NO_CXX11_EXTERN_TEMPLATE) && !defined(BOOST_NO_EXTERN_TEMPLATE) # define BOOST_NO_EXTERN_TEMPLATE #endif // Use BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS instead of BOOST_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS #if defined(BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS) && !defined(BOOST_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS) # define BOOST_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS #endif // Use BOOST_NO_CXX11_LAMBDAS instead of BOOST_NO_LAMBDAS #if defined(BOOST_NO_CXX11_LAMBDAS) && !defined(BOOST_NO_LAMBDAS) # define BOOST_NO_LAMBDAS #endif // Use BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS instead of BOOST_NO_LOCAL_CLASS_TEMPLATE_PARAMETERS #if defined(BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS) && !defined(BOOST_NO_LOCAL_CLASS_TEMPLATE_PARAMETERS) # define BOOST_NO_LOCAL_CLASS_TEMPLATE_PARAMETERS #endif // Use BOOST_NO_CXX11_NOEXCEPT instead of BOOST_NO_NOEXCEPT #if defined(BOOST_NO_CXX11_NOEXCEPT) && !defined(BOOST_NO_NOEXCEPT) # define BOOST_NO_NOEXCEPT #endif // Use BOOST_NO_CXX11_NULLPTR instead of BOOST_NO_NULLPTR #if defined(BOOST_NO_CXX11_NULLPTR) && !defined(BOOST_NO_NULLPTR) # define BOOST_NO_NULLPTR #endif // Use BOOST_NO_CXX11_RAW_LITERALS instead of BOOST_NO_RAW_LITERALS #if defined(BOOST_NO_CXX11_RAW_LITERALS) && !defined(BOOST_NO_RAW_LITERALS) # define BOOST_NO_RAW_LITERALS #endif // Use BOOST_NO_CXX11_RVALUE_REFERENCES instead of BOOST_NO_RVALUE_REFERENCES #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_NO_RVALUE_REFERENCES) # define BOOST_NO_RVALUE_REFERENCES #endif // Use BOOST_NO_CXX11_SCOPED_ENUMS instead of BOOST_NO_SCOPED_ENUMS #if defined(BOOST_NO_CXX11_SCOPED_ENUMS) && !defined(BOOST_NO_SCOPED_ENUMS) # define BOOST_NO_SCOPED_ENUMS #endif // Use BOOST_NO_CXX11_STATIC_ASSERT instead of BOOST_NO_STATIC_ASSERT #if defined(BOOST_NO_CXX11_STATIC_ASSERT) && !defined(BOOST_NO_STATIC_ASSERT) # define BOOST_NO_STATIC_ASSERT #endif // Use BOOST_NO_CXX11_STD_UNORDERED instead of BOOST_NO_STD_UNORDERED #if defined(BOOST_NO_CXX11_STD_UNORDERED) && !defined(BOOST_NO_STD_UNORDERED) # define BOOST_NO_STD_UNORDERED #endif // Use BOOST_NO_CXX11_UNICODE_LITERALS instead of BOOST_NO_UNICODE_LITERALS #if defined(BOOST_NO_CXX11_UNICODE_LITERALS) && !defined(BOOST_NO_UNICODE_LITERALS) # define BOOST_NO_UNICODE_LITERALS #endif // Use BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX instead of BOOST_NO_UNIFIED_INITIALIZATION_SYNTAX #if defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX) && !defined(BOOST_NO_UNIFIED_INITIALIZATION_SYNTAX) # define BOOST_NO_UNIFIED_INITIALIZATION_SYNTAX #endif // Use BOOST_NO_CXX11_VARIADIC_TEMPLATES instead of BOOST_NO_VARIADIC_TEMPLATES #if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && !defined(BOOST_NO_VARIADIC_TEMPLATES) # define BOOST_NO_VARIADIC_TEMPLATES #endif // Use BOOST_NO_CXX11_VARIADIC_MACROS instead of BOOST_NO_VARIADIC_MACROS #if defined(BOOST_NO_CXX11_VARIADIC_MACROS) && !defined(BOOST_NO_VARIADIC_MACROS) # define BOOST_NO_VARIADIC_MACROS #endif // Use BOOST_NO_CXX11_NUMERIC_LIMITS instead of BOOST_NO_NUMERIC_LIMITS_LOWEST #if defined(BOOST_NO_CXX11_NUMERIC_LIMITS) && !defined(BOOST_NO_NUMERIC_LIMITS_LOWEST) # define BOOST_NO_NUMERIC_LIMITS_LOWEST #endif // ------------------ End of deprecated macros for 1.51 --------------------------- // // Helper macros BOOST_NOEXCEPT, BOOST_NOEXCEPT_IF, BOOST_NOEXCEPT_EXPR // These aid the transition to C++11 while still supporting C++03 compilers // #ifdef BOOST_NO_CXX11_NOEXCEPT # define BOOST_NOEXCEPT # define BOOST_NOEXCEPT_IF(Predicate) # define BOOST_NOEXCEPT_EXPR(Expression) false #else # define BOOST_NOEXCEPT noexcept # define BOOST_NOEXCEPT_IF(Predicate) noexcept((Predicate)) # define BOOST_NOEXCEPT_EXPR(Expression) noexcept((Expression)) #endif // // constexpr workarounds // #if defined(BOOST_NO_CXX11_CONSTEXPR) #define BOOST_CONSTEXPR #define BOOST_CONSTEXPR_OR_CONST const #else #define BOOST_CONSTEXPR constexpr #define BOOST_CONSTEXPR_OR_CONST constexpr #endif #define BOOST_STATIC_CONSTEXPR static BOOST_CONSTEXPR_OR_CONST // // Set BOOST_HAS_STATIC_ASSERT when BOOST_NO_CXX11_STATIC_ASSERT is not defined // #if !defined(BOOST_NO_CXX11_STATIC_ASSERT) && !defined(BOOST_HAS_STATIC_ASSERT) # define BOOST_HAS_STATIC_ASSERT #endif // // Set BOOST_HAS_RVALUE_REFS when BOOST_NO_CXX11_RVALUE_REFERENCES is not defined // #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_HAS_RVALUE_REFS) #define BOOST_HAS_RVALUE_REFS #endif // // Set BOOST_HAS_VARIADIC_TMPL when BOOST_NO_CXX11_VARIADIC_TEMPLATES is not defined // #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && !defined(BOOST_HAS_VARIADIC_TMPL) #define BOOST_HAS_VARIADIC_TMPL #endif #endif base-7.0.3.1/modules/libcom/vxWorks/boost/config/user.hpp0000664000577000060420000001204513557101274022077 0ustar anjaesctl// boost/config/user.hpp ---------------------------------------------------// // (C) Copyright John Maddock 2001. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // Do not check in modified versions of this file, // This file may be customized by the end user, but not by boost. // // Use this file to define a site and compiler specific // configuration policy: // // define this to locate a compiler config file: // #define BOOST_COMPILER_CONFIG // define this to locate a stdlib config file: // #define BOOST_STDLIB_CONFIG // define this to locate a platform config file: // #define BOOST_PLATFORM_CONFIG // define this to disable compiler config, // use if your compiler config has nothing to set: // #define BOOST_NO_COMPILER_CONFIG // define this to disable stdlib config, // use if your stdlib config has nothing to set: // #define BOOST_NO_STDLIB_CONFIG // define this to disable platform config, // use if your platform config has nothing to set: // #define BOOST_NO_PLATFORM_CONFIG // define this to disable all config options, // excluding the user config. Use if your // setup is fully ISO compliant, and has no // useful extensions, or for autoconf generated // setups: // #define BOOST_NO_CONFIG // define this to make the config "optimistic" // about unknown compiler versions. Normally // unknown compiler versions are assumed to have // all the defects of the last known version, however // setting this flag, causes the config to assume // that unknown compiler versions are fully conformant // with the standard: // #define BOOST_STRICT_CONFIG // define this to cause the config to halt compilation // with an #error if it encounters anything unknown -- // either an unknown compiler version or an unknown // compiler/platform/library: // #define BOOST_ASSERT_CONFIG // define if you want to disable threading support, even // when available: // #define BOOST_DISABLE_THREADS // define when you want to disable Win32 specific features // even when available: // #define BOOST_DISABLE_WIN32 // BOOST_DISABLE_ABI_HEADERS: Stops boost headers from including any // prefix/suffix headers that normally control things like struct // packing and alignment. // #define BOOST_DISABLE_ABI_HEADERS // BOOST_ABI_PREFIX: A prefix header to include in place of whatever // boost.config would normally select, any replacement should set up // struct packing and alignment options as required. // #define BOOST_ABI_PREFIX my-header-name // BOOST_ABI_SUFFIX: A suffix header to include in place of whatever // boost.config would normally select, any replacement should undo // the effects of the prefix header. // #define BOOST_ABI_SUFFIX my-header-name // BOOST_ALL_DYN_LINK: Forces all libraries that have separate source, // to be linked as dll's rather than static libraries on Microsoft Windows // (this macro is used to turn on __declspec(dllimport) modifiers, so that // the compiler knows which symbols to look for in a dll rather than in a // static library). Note that there may be some libraries that can only // be statically linked (Boost.Test for example) and others which may only // be dynamically linked (Boost.Threads for example), in these cases this // macro has no effect. // #define BOOST_ALL_DYN_LINK // BOOST_WHATEVER_DYN_LINK: Forces library "whatever" to be linked as a dll // rather than a static library on Microsoft Windows: replace the WHATEVER // part of the macro name with the name of the library that you want to // dynamically link to, for example use BOOST_DATE_TIME_DYN_LINK or // BOOST_REGEX_DYN_LINK etc (this macro is used to turn on __declspec(dllimport) // modifiers, so that the compiler knows which symbols to look for in a dll // rather than in a static library). // Note that there may be some libraries that can only be statically linked // (Boost.Test for example) and others which may only be dynamically linked // (Boost.Threads for example), in these cases this macro is unsupported. // #define BOOST_WHATEVER_DYN_LINK // BOOST_ALL_NO_LIB: Tells the config system not to automatically select // which libraries to link against. // Normally if a compiler supports #pragma lib, then the correct library // build variant will be automatically selected and linked against, // simply by the act of including one of that library's headers. // This macro turns that feature off. // #define BOOST_ALL_NO_LIB // BOOST_WHATEVER_NO_LIB: Tells the config system not to automatically // select which library to link against for library "whatever", // replace WHATEVER in the macro name with the name of the library; // for example BOOST_DATE_TIME_NO_LIB or BOOST_REGEX_NO_LIB. // Normally if a compiler supports #pragma lib, then the correct library // build variant will be automatically selected and linked against, simply // by the act of including one of that library's headers. This macro turns // that feature off. // #define BOOST_WHATEVER_NO_LIB base-7.0.3.1/modules/libcom/vxWorks/boost/current_function.hpp0000664000577000060420000000274713557101274023253 0ustar anjaesctl#ifndef BOOST_CURRENT_FUNCTION_HPP_INCLUDED #define BOOST_CURRENT_FUNCTION_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // boost/current_function.hpp - BOOST_CURRENT_FUNCTION // // Copyright (c) 2002 Peter Dimov and Multi Media Ltd. // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // http://www.boost.org/libs/utility/current_function.html // namespace boost { namespace detail { inline void current_function_helper() { #if defined(__GNUC__) || (defined(__MWERKS__) && (__MWERKS__ >= 0x3000)) || (defined(__ICC) && (__ICC >= 600)) || defined(__ghs__) # define BOOST_CURRENT_FUNCTION __PRETTY_FUNCTION__ #elif defined(__DMC__) && (__DMC__ >= 0x810) # define BOOST_CURRENT_FUNCTION __PRETTY_FUNCTION__ #elif defined(__FUNCSIG__) # define BOOST_CURRENT_FUNCTION __FUNCSIG__ #elif (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 600)) || (defined(__IBMCPP__) && (__IBMCPP__ >= 500)) # define BOOST_CURRENT_FUNCTION __FUNCTION__ #elif defined(__BORLANDC__) && (__BORLANDC__ >= 0x550) # define BOOST_CURRENT_FUNCTION __FUNC__ #elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901) # define BOOST_CURRENT_FUNCTION __func__ #else # define BOOST_CURRENT_FUNCTION "(unknown)" #endif } } // namespace detail } // namespace boost #endif // #ifndef BOOST_CURRENT_FUNCTION_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/detail/sp_typeinfo.hpp0000664000577000060420000000456713557101274023467 0ustar anjaesctl#ifndef BOOST_DETAIL_SP_TYPEINFO_HPP_INCLUDED #define BOOST_DETAIL_SP_TYPEINFO_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // detail/sp_typeinfo.hpp // // Copyright 2007 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #if defined( BOOST_NO_TYPEID ) #include #include namespace boost { namespace detail { class sp_typeinfo { private: sp_typeinfo( sp_typeinfo const& ); sp_typeinfo& operator=( sp_typeinfo const& ); char const * name_; public: explicit sp_typeinfo( char const * name ): name_( name ) { } bool operator==( sp_typeinfo const& rhs ) const { return this == &rhs; } bool operator!=( sp_typeinfo const& rhs ) const { return this != &rhs; } bool before( sp_typeinfo const& rhs ) const { return std::less< sp_typeinfo const* >()( this, &rhs ); } char const* name() const { return name_; } }; template struct sp_typeid_ { static sp_typeinfo ti_; static char const * name() { return BOOST_CURRENT_FUNCTION; } }; #if defined(__SUNPRO_CC) // see #4199, the Sun Studio compiler gets confused about static initialization // constructor arguments. But an assignment works just fine. template sp_typeinfo sp_typeid_< T >::ti_ = sp_typeid_< T >::name(); #else template sp_typeinfo sp_typeid_< T >::ti_(sp_typeid_< T >::name()); #endif template struct sp_typeid_< T & >: sp_typeid_< T > { }; template struct sp_typeid_< T const >: sp_typeid_< T > { }; template struct sp_typeid_< T volatile >: sp_typeid_< T > { }; template struct sp_typeid_< T const volatile >: sp_typeid_< T > { }; } // namespace detail } // namespace boost #define BOOST_SP_TYPEID(T) (boost::detail::sp_typeid_::ti_) #else #include namespace boost { namespace detail { #if defined( BOOST_NO_STD_TYPEINFO ) typedef ::type_info sp_typeinfo; #else typedef std::type_info sp_typeinfo; #endif } // namespace detail } // namespace boost #define BOOST_SP_TYPEID(T) typeid(T) #endif #endif // #ifndef BOOST_DETAIL_SP_TYPEINFO_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/detail/workaround.hpp0000664000577000060420000001616513557101274023320 0ustar anjaesctl// Copyright David Abrahams 2002. // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef WORKAROUND_DWA2002126_HPP # define WORKAROUND_DWA2002126_HPP // Compiler/library version workaround macro // // Usage: // // #if BOOST_WORKAROUND(BOOST_MSVC, < 1300) // // workaround for eVC4 and VC6 // ... // workaround code here // #endif // // When BOOST_STRICT_CONFIG is defined, expands to 0. Otherwise, the // first argument must be undefined or expand to a numeric // value. The above expands to: // // (BOOST_MSVC) != 0 && (BOOST_MSVC) < 1300 // // When used for workarounds that apply to the latest known version // and all earlier versions of a compiler, the following convention // should be observed: // // #if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1301)) // // The version number in this case corresponds to the last version in // which the workaround was known to have been required. When // BOOST_DETECT_OUTDATED_WORKAROUNDS is not the defined, the macro // BOOST_TESTED_AT(x) expands to "!= 0", which effectively activates // the workaround for any version of the compiler. When // BOOST_DETECT_OUTDATED_WORKAROUNDS is defined, a compiler warning or // error will be issued if the compiler version exceeds the argument // to BOOST_TESTED_AT(). This can be used to locate workarounds which // may be obsoleted by newer versions. # ifndef BOOST_STRICT_CONFIG #include #ifndef __BORLANDC__ #define __BORLANDC___WORKAROUND_GUARD 1 #else #define __BORLANDC___WORKAROUND_GUARD 0 #endif #ifndef __CODEGEARC__ #define __CODEGEARC___WORKAROUND_GUARD 1 #else #define __CODEGEARC___WORKAROUND_GUARD 0 #endif #ifndef _MSC_VER #define _MSC_VER_WORKAROUND_GUARD 1 #else #define _MSC_VER_WORKAROUND_GUARD 0 #endif #ifndef _MSC_FULL_VER #define _MSC_FULL_VER_WORKAROUND_GUARD 1 #else #define _MSC_FULL_VER_WORKAROUND_GUARD 0 #endif #ifndef BOOST_MSVC #define BOOST_MSVC_WORKAROUND_GUARD 1 #else #define BOOST_MSVC_WORKAROUND_GUARD 0 #endif #ifndef BOOST_MSVC_FULL_VER #define BOOST_MSVC_FULL_VER_WORKAROUND_GUARD 1 #else #define BOOST_MSVC_FULL_VER_WORKAROUND_GUARD 0 #endif #ifndef __GNUC__ #define __GNUC___WORKAROUND_GUARD 1 #else #define __GNUC___WORKAROUND_GUARD 0 #endif #ifndef __GNUC_MINOR__ #define __GNUC_MINOR___WORKAROUND_GUARD 1 #else #define __GNUC_MINOR___WORKAROUND_GUARD 0 #endif #ifndef __GNUC_PATCHLEVEL__ #define __GNUC_PATCHLEVEL___WORKAROUND_GUARD 1 #else #define __GNUC_PATCHLEVEL___WORKAROUND_GUARD 0 #endif #ifndef __IBMCPP__ #define __IBMCPP___WORKAROUND_GUARD 1 #else #define __IBMCPP___WORKAROUND_GUARD 0 #endif #ifndef __SUNPRO_CC #define __SUNPRO_CC_WORKAROUND_GUARD 1 #else #define __SUNPRO_CC_WORKAROUND_GUARD 0 #endif #ifndef __DECCXX_VER #define __DECCXX_VER_WORKAROUND_GUARD 1 #else #define __DECCXX_VER_WORKAROUND_GUARD 0 #endif #ifndef __MWERKS__ #define __MWERKS___WORKAROUND_GUARD 1 #else #define __MWERKS___WORKAROUND_GUARD 0 #endif #ifndef __EDG__ #define __EDG___WORKAROUND_GUARD 1 #else #define __EDG___WORKAROUND_GUARD 0 #endif #ifndef __EDG_VERSION__ #define __EDG_VERSION___WORKAROUND_GUARD 1 #else #define __EDG_VERSION___WORKAROUND_GUARD 0 #endif #ifndef __HP_aCC #define __HP_aCC_WORKAROUND_GUARD 1 #else #define __HP_aCC_WORKAROUND_GUARD 0 #endif #ifndef __hpxstd98 #define __hpxstd98_WORKAROUND_GUARD 1 #else #define __hpxstd98_WORKAROUND_GUARD 0 #endif #ifndef _CRAYC #define _CRAYC_WORKAROUND_GUARD 1 #else #define _CRAYC_WORKAROUND_GUARD 0 #endif #ifndef __DMC__ #define __DMC___WORKAROUND_GUARD 1 #else #define __DMC___WORKAROUND_GUARD 0 #endif #ifndef MPW_CPLUS #define MPW_CPLUS_WORKAROUND_GUARD 1 #else #define MPW_CPLUS_WORKAROUND_GUARD 0 #endif #ifndef __COMO__ #define __COMO___WORKAROUND_GUARD 1 #else #define __COMO___WORKAROUND_GUARD 0 #endif #ifndef __COMO_VERSION__ #define __COMO_VERSION___WORKAROUND_GUARD 1 #else #define __COMO_VERSION___WORKAROUND_GUARD 0 #endif #ifndef __INTEL_COMPILER #define __INTEL_COMPILER_WORKAROUND_GUARD 1 #else #define __INTEL_COMPILER_WORKAROUND_GUARD 0 #endif #ifndef __ICL #define __ICL_WORKAROUND_GUARD 1 #else #define __ICL_WORKAROUND_GUARD 0 #endif #ifndef _COMPILER_VERSION #define _COMPILER_VERSION_WORKAROUND_GUARD 1 #else #define _COMPILER_VERSION_WORKAROUND_GUARD 0 #endif #ifndef _RWSTD_VER #define _RWSTD_VER_WORKAROUND_GUARD 1 #else #define _RWSTD_VER_WORKAROUND_GUARD 0 #endif #ifndef BOOST_RWSTD_VER #define BOOST_RWSTD_VER_WORKAROUND_GUARD 1 #else #define BOOST_RWSTD_VER_WORKAROUND_GUARD 0 #endif #ifndef __GLIBCPP__ #define __GLIBCPP___WORKAROUND_GUARD 1 #else #define __GLIBCPP___WORKAROUND_GUARD 0 #endif #ifndef _GLIBCXX_USE_C99_FP_MACROS_DYNAMIC #define _GLIBCXX_USE_C99_FP_MACROS_DYNAMIC_WORKAROUND_GUARD 1 #else #define _GLIBCXX_USE_C99_FP_MACROS_DYNAMIC_WORKAROUND_GUARD 0 #endif #ifndef __SGI_STL_PORT #define __SGI_STL_PORT_WORKAROUND_GUARD 1 #else #define __SGI_STL_PORT_WORKAROUND_GUARD 0 #endif #ifndef _STLPORT_VERSION #define _STLPORT_VERSION_WORKAROUND_GUARD 1 #else #define _STLPORT_VERSION_WORKAROUND_GUARD 0 #endif #ifndef __LIBCOMO_VERSION__ #define __LIBCOMO_VERSION___WORKAROUND_GUARD 1 #else #define __LIBCOMO_VERSION___WORKAROUND_GUARD 0 #endif #ifndef _CPPLIB_VER #define _CPPLIB_VER_WORKAROUND_GUARD 1 #else #define _CPPLIB_VER_WORKAROUND_GUARD 0 #endif #ifndef BOOST_INTEL_CXX_VERSION #define BOOST_INTEL_CXX_VERSION_WORKAROUND_GUARD 1 #else #define BOOST_INTEL_CXX_VERSION_WORKAROUND_GUARD 0 #endif #ifndef BOOST_INTEL_WIN #define BOOST_INTEL_WIN_WORKAROUND_GUARD 1 #else #define BOOST_INTEL_WIN_WORKAROUND_GUARD 0 #endif #ifndef BOOST_DINKUMWARE_STDLIB #define BOOST_DINKUMWARE_STDLIB_WORKAROUND_GUARD 1 #else #define BOOST_DINKUMWARE_STDLIB_WORKAROUND_GUARD 0 #endif #ifndef BOOST_INTEL #define BOOST_INTEL_WORKAROUND_GUARD 1 #else #define BOOST_INTEL_WORKAROUND_GUARD 0 #endif // Always define to zero, if it's used it'll be defined my MPL: #define BOOST_MPL_CFG_GCC_WORKAROUND_GUARD 0 # define BOOST_WORKAROUND(symbol, test) \ ((symbol ## _WORKAROUND_GUARD + 0 == 0) && \ (symbol != 0) && (1 % (( (symbol test) ) + 1))) // ^ ^ ^ ^ // The extra level of parenthesis nesting above, along with the // BOOST_OPEN_PAREN indirection below, is required to satisfy the // broken preprocessor in MWCW 8.3 and earlier. // // The basic mechanism works as follows: // (symbol test) + 1 => if (symbol test) then 2 else 1 // 1 % ((symbol test) + 1) => if (symbol test) then 1 else 0 // // The complication with % is for cooperation with BOOST_TESTED_AT(). // When "test" is BOOST_TESTED_AT(x) and // BOOST_DETECT_OUTDATED_WORKAROUNDS is #defined, // // symbol test => if (symbol <= x) then 1 else -1 // (symbol test) + 1 => if (symbol <= x) then 2 else 0 // 1 % ((symbol test) + 1) => if (symbol <= x) then 1 else divide-by-zero // # ifdef BOOST_DETECT_OUTDATED_WORKAROUNDS # define BOOST_OPEN_PAREN ( # define BOOST_TESTED_AT(value) > value) ?(-1): BOOST_OPEN_PAREN 1 # else # define BOOST_TESTED_AT(value) != ((value)-(value)) # endif # else # define BOOST_WORKAROUND(symbol, test) 0 # endif #endif // WORKAROUND_DWA2002126_HPP base-7.0.3.1/modules/libcom/vxWorks/boost/enable_shared_from_this.hpp0000664000577000060420000000102213557101274024473 0ustar anjaesctl#ifndef BOOST_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED #define BOOST_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED // // enable_shared_from_this.hpp // // Copyright (c) 2002 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // // http://www.boost.org/libs/smart_ptr/enable_shared_from_this.html // #include #endif // #ifndef BOOST_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/exception/detail/attribute_noreturn.hpp0000664000577000060420000000101613557101274027047 0ustar anjaesctl//Copyright (c) 2009 Emil Dotchevski and Reverge Studios, Inc. //Distributed under the Boost Software License, Version 1.0. (See accompanying //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef UUID_61531AB0680611DEADD5846855D89593 #define UUID_61531AB0680611DEADD5846855D89593 #if defined(_MSC_VER) #define BOOST_ATTRIBUTE_NORETURN __declspec(noreturn) #elif defined(__GNUC__) #define BOOST_ATTRIBUTE_NORETURN __attribute__((__noreturn__)) #else #define BOOST_ATTRIBUTE_NORETURN #endif #endif base-7.0.3.1/modules/libcom/vxWorks/boost/exception/exception.hpp0000664000577000060420000002752313557101274023657 0ustar anjaesctl//Copyright (c) 2006-2009 Emil Dotchevski and Reverge Studios, Inc. //Distributed under the Boost Software License, Version 1.0. (See accompanying //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef UUID_274DA366004E11DCB1DDFE2E56D89593 #define UUID_274DA366004E11DCB1DDFE2E56D89593 #if defined(__GNUC__) && !defined(BOOST_EXCEPTION_ENABLE_WARNINGS) #pragma GCC system_header #endif #if defined(_MSC_VER) && !defined(BOOST_EXCEPTION_ENABLE_WARNINGS) #pragma warning(push,1) #endif namespace boost { namespace exception_detail { template class refcount_ptr { public: refcount_ptr(): px_(0) { } ~refcount_ptr() { release(); } refcount_ptr( refcount_ptr const & x ): px_(x.px_) { add_ref(); } refcount_ptr & operator=( refcount_ptr const & x ) { adopt(x.px_); return *this; } void adopt( T * px ) { release(); px_=px; add_ref(); } T * get() const { return px_; } private: T * px_; void add_ref() { if( px_ ) px_->add_ref(); } void release() { if( px_ && px_->release() ) px_=0; } }; } //////////////////////////////////////////////////////////////////////// template class error_info; typedef error_info throw_function; typedef error_info throw_file; typedef error_info throw_line; template <> class error_info { public: typedef char const * value_type; value_type v_; explicit error_info( value_type v ): v_(v) { } }; template <> class error_info { public: typedef char const * value_type; value_type v_; explicit error_info( value_type v ): v_(v) { } }; template <> class error_info { public: typedef int value_type; value_type v_; explicit error_info( value_type v ): v_(v) { } }; #if defined(__GNUC__) # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) # pragma GCC visibility push (default) # endif #endif class exception; #if defined(__GNUC__) # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) # pragma GCC visibility pop # endif #endif template class shared_ptr; namespace exception_detail { class error_info_base; struct type_info_; struct error_info_container { virtual char const * diagnostic_information( char const * ) const = 0; virtual shared_ptr get( type_info_ const & ) const = 0; virtual void set( shared_ptr const &, type_info_ const & ) = 0; virtual void add_ref() const = 0; virtual bool release() const = 0; virtual refcount_ptr clone() const = 0; protected: ~error_info_container() throw() { } }; template struct get_info; template <> struct get_info; template <> struct get_info; template <> struct get_info; char const * get_diagnostic_information( exception const &, char const * ); void copy_boost_exception( exception *, exception const * ); template E const & set_info( E const &, error_info const & ); template E const & set_info( E const &, throw_function const & ); template E const & set_info( E const &, throw_file const & ); template E const & set_info( E const &, throw_line const & ); } #if defined(__GNUC__) # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) # pragma GCC visibility push (default) # endif #endif class exception { protected: exception(): throw_function_(0), throw_file_(0), throw_line_(-1) { } #ifdef __HP_aCC //On HP aCC, this protected copy constructor prevents throwing boost::exception. //On all other platforms, the same effect is achieved by the pure virtual destructor. exception( exception const & x ) throw(): data_(x.data_), throw_function_(x.throw_function_), throw_file_(x.throw_file_), throw_line_(x.throw_line_) { } #endif virtual ~exception() throw() #ifndef __HP_aCC = 0 //Workaround for HP aCC, =0 incorrectly leads to link errors. #endif ; #if (defined(__MWERKS__) && __MWERKS__<=0x3207) || (defined(_MSC_VER) && _MSC_VER<=1310) public: #else private: template friend E const & exception_detail::set_info( E const &, throw_function const & ); template friend E const & exception_detail::set_info( E const &, throw_file const & ); template friend E const & exception_detail::set_info( E const &, throw_line const & ); template friend E const & exception_detail::set_info( E const &, error_info const & ); friend char const * exception_detail::get_diagnostic_information( exception const &, char const * ); template friend struct exception_detail::get_info; friend struct exception_detail::get_info; friend struct exception_detail::get_info; friend struct exception_detail::get_info; friend void exception_detail::copy_boost_exception( exception *, exception const * ); #endif mutable exception_detail::refcount_ptr data_; mutable char const * throw_function_; mutable char const * throw_file_; mutable int throw_line_; }; #if defined(__GNUC__) # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) # pragma GCC visibility pop # endif #endif inline exception:: ~exception() throw() { } namespace exception_detail { template E const & set_info( E const & x, throw_function const & y ) { x.throw_function_=y.v_; return x; } template E const & set_info( E const & x, throw_file const & y ) { x.throw_file_=y.v_; return x; } template E const & set_info( E const & x, throw_line const & y ) { x.throw_line_=y.v_; return x; } } //////////////////////////////////////////////////////////////////////// namespace exception_detail { #if defined(__GNUC__) # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) # pragma GCC visibility push (default) # endif #endif template struct error_info_injector: public T, public exception { explicit error_info_injector( T const & x ): T(x) { } ~error_info_injector() throw() { } }; #if defined(__GNUC__) # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) # pragma GCC visibility pop # endif #endif struct large_size { char c[256]; }; large_size dispatch_boost_exception( exception const * ); struct small_size { }; small_size dispatch_boost_exception( void const * ); template struct enable_error_info_helper; template struct enable_error_info_helper { typedef T type; }; template struct enable_error_info_helper { typedef error_info_injector type; }; template struct enable_error_info_return_type { typedef typename enable_error_info_helper(0)))>::type type; }; } template inline typename exception_detail::enable_error_info_return_type::type enable_error_info( T const & x ) { typedef typename exception_detail::enable_error_info_return_type::type rt; return rt(x); } //////////////////////////////////////////////////////////////////////// namespace exception_detail { #if defined(__GNUC__) # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) # pragma GCC visibility push (default) # endif #endif class clone_base { public: virtual clone_base const * clone() const = 0; virtual void rethrow() const = 0; virtual ~clone_base() throw() { } }; #if defined(__GNUC__) # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) # pragma GCC visibility pop # endif #endif inline void copy_boost_exception( exception * a, exception const * b ) { refcount_ptr data; if( error_info_container * d=b->data_.get() ) data = d->clone(); a->throw_file_ = b->throw_file_; a->throw_line_ = b->throw_line_; a->throw_function_ = b->throw_function_; a->data_ = data; } inline void copy_boost_exception( void *, void const * ) { } template class clone_impl: public T, public virtual clone_base { struct clone_tag { }; clone_impl( clone_impl const & x, clone_tag ): T(x) { copy_boost_exception(this,&x); } public: explicit clone_impl( T const & x ): T(x) { copy_boost_exception(this,&x); } ~clone_impl() throw() { } private: clone_base const * clone() const { return new clone_impl(*this,clone_tag()); } void rethrow() const { throw*this; } }; } template inline exception_detail::clone_impl enable_current_exception( T const & x ) { return exception_detail::clone_impl(x); } } #if defined(_MSC_VER) && !defined(BOOST_EXCEPTION_ENABLE_WARNINGS) #pragma warning(pop) #endif #endif base-7.0.3.1/modules/libcom/vxWorks/boost/memory_order.hpp0000664000577000060420000000237113557101274022360 0ustar anjaesctl#ifndef BOOST_MEMORY_ORDER_HPP_INCLUDED #define BOOST_MEMORY_ORDER_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // boost/memory_order.hpp // // Defines enum boost::memory_order per the C++0x working draft // // Copyright (c) 2008, 2009 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) namespace boost { // // Enum values are chosen so that code that needs to insert // a trailing fence for acquire semantics can use a single // test such as: // // if( mo & memory_order_acquire ) { ...fence... } // // For leading fences one can use: // // if( mo & memory_order_release ) { ...fence... } // // Architectures such as Alpha that need a fence on consume // can use: // // if( mo & ( memory_order_acquire | memory_order_consume ) ) { ...fence... } // enum memory_order { memory_order_relaxed = 0, memory_order_acquire = 1, memory_order_release = 2, memory_order_acq_rel = 3, // acquire | release memory_order_seq_cst = 7, // acq_rel | 4 memory_order_consume = 8 }; } // namespace boost #endif // #ifndef BOOST_MEMORY_ORDER_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/shared_ptr.hpp0000664000577000060420000000103313557101274022002 0ustar anjaesctl#ifndef BOOST_SHARED_PTR_HPP_INCLUDED #define BOOST_SHARED_PTR_HPP_INCLUDED // // shared_ptr.hpp // // (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. // Copyright (c) 2001-2008 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // See http://www.boost.org/libs/smart_ptr/shared_ptr.htm for documentation. // #include #endif // #ifndef BOOST_SHARED_PTR_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/bad_weak_ptr.hpp0000664000577000060420000000263013557101274024310 0ustar anjaesctl#ifndef BOOST_SMART_PTR_BAD_WEAK_PTR_HPP_INCLUDED #define BOOST_SMART_PTR_BAD_WEAK_PTR_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // boost/smart_ptr/bad_weak_ptr.hpp // // Copyright (c) 2001, 2002, 2003 Peter Dimov and Multi Media Ltd. // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // #include #ifdef __BORLANDC__ # pragma warn -8026 // Functions with excep. spec. are not expanded inline #endif namespace boost { // The standard library that comes with Borland C++ 5.5.1, 5.6.4 // defines std::exception and its members as having C calling // convention (-pc). When the definition of bad_weak_ptr // is compiled with -ps, the compiler issues an error. // Hence, the temporary #pragma option -pc below. #if defined(__BORLANDC__) && __BORLANDC__ <= 0x564 # pragma option push -pc #endif class bad_weak_ptr: public std::exception { public: virtual char const * what() const throw() { return "tr1::bad_weak_ptr"; } }; #if defined(__BORLANDC__) && __BORLANDC__ <= 0x564 # pragma option pop #endif } // namespace boost #ifdef __BORLANDC__ # pragma warn .8026 // Functions with excep. spec. are not expanded inline #endif #endif // #ifndef BOOST_SMART_PTR_BAD_WEAK_PTR_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/detail/operator_bool.hpp0000664000577000060420000000314513557101274026000 0ustar anjaesctl// This header intentionally has no include guards. // // Copyright (c) 2001-2009, 2012 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt #if !defined( BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS ) && !defined( BOOST_NO_CXX11_NULLPTR ) explicit operator bool () const BOOST_NOEXCEPT { return px != 0; } #elif ( defined(__SUNPRO_CC) && BOOST_WORKAROUND(__SUNPRO_CC, < 0x570) ) || defined(__CINT__) operator bool () const BOOST_NOEXCEPT { return px != 0; } #elif defined( _MANAGED ) static void unspecified_bool( this_type*** ) { } typedef void (*unspecified_bool_type)( this_type*** ); operator unspecified_bool_type() const BOOST_NOEXCEPT { return px == 0? 0: unspecified_bool; } #elif \ ( defined(__MWERKS__) && BOOST_WORKAROUND(__MWERKS__, < 0x3200) ) || \ ( defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 304) ) || \ ( defined(__SUNPRO_CC) && BOOST_WORKAROUND(__SUNPRO_CC, <= 0x590) ) typedef element_type * (this_type::*unspecified_bool_type)() const; operator unspecified_bool_type() const BOOST_NOEXCEPT { return px == 0? 0: &this_type::get; } #else typedef element_type * this_type::*unspecified_bool_type; operator unspecified_bool_type() const BOOST_NOEXCEPT { return px == 0? 0: &this_type::px; } #endif // operator! is redundant, but some compilers need it bool operator! () const BOOST_NOEXCEPT { return px == 0; } base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/detail/shared_count.hpp0000664000577000060420000003071713557101274025615 0ustar anjaesctl#ifndef BOOST_SMART_PTR_DETAIL_SHARED_COUNT_HPP_INCLUDED #define BOOST_SMART_PTR_DETAIL_SHARED_COUNT_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // detail/shared_count.hpp // // Copyright (c) 2001, 2002, 2003 Peter Dimov and Multi Media Ltd. // Copyright 2004-2005 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // #ifdef __BORLANDC__ # pragma warn -8027 // Functions containing try are not expanded inline #endif #include #include #include #include #include #include #include // In order to avoid circular dependencies with Boost.TR1 // we make sure that our include of doesn't try to // pull in the TR1 headers: that's why we use this header // rather than including directly: #include // std::auto_ptr #include // std::less #ifdef BOOST_NO_EXCEPTIONS # include // std::bad_alloc #endif #if !defined( BOOST_NO_CXX11_SMART_PTR ) # include #endif namespace boost { namespace detail { #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) int const shared_count_id = 0x2C35F101; int const weak_count_id = 0x298C38A4; #endif struct sp_nothrow_tag {}; template< class D > struct sp_inplace_tag { }; #if !defined( BOOST_NO_CXX11_SMART_PTR ) template< class T > class sp_reference_wrapper { public: explicit sp_reference_wrapper( T & t): t_( boost::addressof( t ) ) { } template< class Y > void operator()( Y * p ) const { (*t_)( p ); } private: T * t_; }; template< class D > struct sp_convert_reference { typedef D type; }; template< class D > struct sp_convert_reference< D& > { typedef sp_reference_wrapper< D > type; }; #endif class weak_count; class shared_count { private: sp_counted_base * pi_; #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) int id_; #endif friend class weak_count; public: shared_count(): pi_(0) // nothrow #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(shared_count_id) #endif { } template explicit shared_count( Y * p ): pi_( 0 ) #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(shared_count_id) #endif { #ifndef BOOST_NO_EXCEPTIONS try { pi_ = new sp_counted_impl_p( p ); } catch(...) { boost::checked_delete( p ); throw; } #else pi_ = new sp_counted_impl_p( p ); if( pi_ == 0 ) { boost::checked_delete( p ); boost::throw_exception( std::bad_alloc() ); } #endif } #if defined( BOOST_MSVC ) && BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) template shared_count( Y * p, D d ): pi_(0) #else template shared_count( P p, D d ): pi_(0) #endif #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(shared_count_id) #endif { #if defined( BOOST_MSVC ) && BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) typedef Y* P; #endif #ifndef BOOST_NO_EXCEPTIONS try { pi_ = new sp_counted_impl_pd(p, d); } catch(...) { d(p); // delete p throw; } #else pi_ = new sp_counted_impl_pd(p, d); if(pi_ == 0) { d(p); // delete p boost::throw_exception(std::bad_alloc()); } #endif } #if !defined( BOOST_NO_FUNCTION_TEMPLATE_ORDERING ) template< class P, class D > shared_count( P p, sp_inplace_tag ): pi_( 0 ) #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(shared_count_id) #endif { #ifndef BOOST_NO_EXCEPTIONS try { pi_ = new sp_counted_impl_pd< P, D >( p ); } catch( ... ) { D()( p ); // delete p throw; } #else pi_ = new sp_counted_impl_pd< P, D >( p ); if( pi_ == 0 ) { D()( p ); // delete p boost::throw_exception( std::bad_alloc() ); } #endif // #ifndef BOOST_NO_EXCEPTIONS } #endif // !defined( BOOST_NO_FUNCTION_TEMPLATE_ORDERING ) template shared_count( P p, D d, A a ): pi_( 0 ) #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(shared_count_id) #endif { typedef sp_counted_impl_pda impl_type; typedef typename A::template rebind< impl_type >::other A2; A2 a2( a ); #ifndef BOOST_NO_EXCEPTIONS try { pi_ = a2.allocate( 1, static_cast< impl_type* >( 0 ) ); new( static_cast< void* >( pi_ ) ) impl_type( p, d, a ); } catch(...) { d( p ); if( pi_ != 0 ) { a2.deallocate( static_cast< impl_type* >( pi_ ), 1 ); } throw; } #else pi_ = a2.allocate( 1, static_cast< impl_type* >( 0 ) ); if( pi_ != 0 ) { new( static_cast< void* >( pi_ ) ) impl_type( p, d, a ); } else { d( p ); boost::throw_exception( std::bad_alloc() ); } #endif } #if !defined( BOOST_NO_FUNCTION_TEMPLATE_ORDERING ) template< class P, class D, class A > shared_count( P p, sp_inplace_tag< D >, A a ): pi_( 0 ) #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(shared_count_id) #endif { typedef sp_counted_impl_pda< P, D, A > impl_type; typedef typename A::template rebind< impl_type >::other A2; A2 a2( a ); #ifndef BOOST_NO_EXCEPTIONS try { pi_ = a2.allocate( 1, static_cast< impl_type* >( 0 ) ); new( static_cast< void* >( pi_ ) ) impl_type( p, a ); } catch(...) { D()( p ); if( pi_ != 0 ) { a2.deallocate( static_cast< impl_type* >( pi_ ), 1 ); } throw; } #else pi_ = a2.allocate( 1, static_cast< impl_type* >( 0 ) ); if( pi_ != 0 ) { new( static_cast< void* >( pi_ ) ) impl_type( p, a ); } else { D()( p ); boost::throw_exception( std::bad_alloc() ); } #endif // #ifndef BOOST_NO_EXCEPTIONS } #endif // !defined( BOOST_NO_FUNCTION_TEMPLATE_ORDERING ) #ifndef BOOST_NO_AUTO_PTR // auto_ptr is special cased to provide the strong guarantee template explicit shared_count( std::auto_ptr & r ): pi_( new sp_counted_impl_p( r.get() ) ) #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(shared_count_id) #endif { #ifdef BOOST_NO_EXCEPTIONS if( pi_ == 0 ) { boost::throw_exception(std::bad_alloc()); } #endif r.release(); } #endif #if !defined( BOOST_NO_CXX11_SMART_PTR ) template explicit shared_count( std::unique_ptr & r ): pi_( 0 ) #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(shared_count_id) #endif { typedef typename sp_convert_reference::type D2; D2 d2( r.get_deleter() ); pi_ = new sp_counted_impl_pd< typename std::unique_ptr::pointer, D2 >( r.get(), d2 ); #ifdef BOOST_NO_EXCEPTIONS if( pi_ == 0 ) { boost::throw_exception( std::bad_alloc() ); } #endif r.release(); } #endif ~shared_count() // nothrow { if( pi_ != 0 ) pi_->release(); #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) id_ = 0; #endif } shared_count(shared_count const & r): pi_(r.pi_) // nothrow #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(shared_count_id) #endif { if( pi_ != 0 ) pi_->add_ref_copy(); } #if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) shared_count(shared_count && r): pi_(r.pi_) // nothrow #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(shared_count_id) #endif { r.pi_ = 0; } #endif explicit shared_count(weak_count const & r); // throws bad_weak_ptr when r.use_count() == 0 shared_count( weak_count const & r, sp_nothrow_tag ); // constructs an empty *this when r.use_count() == 0 shared_count & operator= (shared_count const & r) // nothrow { sp_counted_base * tmp = r.pi_; if( tmp != pi_ ) { if( tmp != 0 ) tmp->add_ref_copy(); if( pi_ != 0 ) pi_->release(); pi_ = tmp; } return *this; } void swap(shared_count & r) // nothrow { sp_counted_base * tmp = r.pi_; r.pi_ = pi_; pi_ = tmp; } long use_count() const // nothrow { return pi_ != 0? pi_->use_count(): 0; } bool unique() const // nothrow { return use_count() == 1; } bool empty() const // nothrow { return pi_ == 0; } friend inline bool operator==(shared_count const & a, shared_count const & b) { return a.pi_ == b.pi_; } friend inline bool operator<(shared_count const & a, shared_count const & b) { return std::less()( a.pi_, b.pi_ ); } void * get_deleter( sp_typeinfo const & ti ) const { return pi_? pi_->get_deleter( ti ): 0; } void * get_untyped_deleter() const { return pi_? pi_->get_untyped_deleter(): 0; } }; class weak_count { private: sp_counted_base * pi_; #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) int id_; #endif friend class shared_count; public: weak_count(): pi_(0) // nothrow #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(weak_count_id) #endif { } weak_count(shared_count const & r): pi_(r.pi_) // nothrow #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(weak_count_id) #endif { if(pi_ != 0) pi_->weak_add_ref(); } weak_count(weak_count const & r): pi_(r.pi_) // nothrow #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(weak_count_id) #endif { if(pi_ != 0) pi_->weak_add_ref(); } // Move support #if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) weak_count(weak_count && r): pi_(r.pi_) // nothrow #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(weak_count_id) #endif { r.pi_ = 0; } #endif ~weak_count() // nothrow { if(pi_ != 0) pi_->weak_release(); #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) id_ = 0; #endif } weak_count & operator= (shared_count const & r) // nothrow { sp_counted_base * tmp = r.pi_; if( tmp != pi_ ) { if(tmp != 0) tmp->weak_add_ref(); if(pi_ != 0) pi_->weak_release(); pi_ = tmp; } return *this; } weak_count & operator= (weak_count const & r) // nothrow { sp_counted_base * tmp = r.pi_; if( tmp != pi_ ) { if(tmp != 0) tmp->weak_add_ref(); if(pi_ != 0) pi_->weak_release(); pi_ = tmp; } return *this; } void swap(weak_count & r) // nothrow { sp_counted_base * tmp = r.pi_; r.pi_ = pi_; pi_ = tmp; } long use_count() const // nothrow { return pi_ != 0? pi_->use_count(): 0; } bool empty() const // nothrow { return pi_ == 0; } friend inline bool operator==(weak_count const & a, weak_count const & b) { return a.pi_ == b.pi_; } friend inline bool operator<(weak_count const & a, weak_count const & b) { return std::less()(a.pi_, b.pi_); } }; inline shared_count::shared_count( weak_count const & r ): pi_( r.pi_ ) #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(shared_count_id) #endif { if( pi_ == 0 || !pi_->add_ref_lock() ) { boost::throw_exception( boost::bad_weak_ptr() ); } } inline shared_count::shared_count( weak_count const & r, sp_nothrow_tag ): pi_( r.pi_ ) #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) , id_(shared_count_id) #endif { if( pi_ != 0 && !pi_->add_ref_lock() ) { pi_ = 0; } } } // namespace detail } // namespace boost #ifdef __BORLANDC__ # pragma warn .8027 // Functions containing try are not expanded inline #endif #endif // #ifndef BOOST_SMART_PTR_DETAIL_SHARED_COUNT_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_convertible.hpp0000664000577000060420000000412613557101274026150 0ustar anjaesctl#ifndef BOOST_SMART_PTR_DETAIL_SP_CONVERTIBLE_HPP_INCLUDED #define BOOST_SMART_PTR_DETAIL_SP_CONVERTIBLE_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // detail/sp_convertible.hpp // // Copyright 2008 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt #include #if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) && defined( BOOST_NO_SFINAE ) # define BOOST_SP_NO_SP_CONVERTIBLE #endif #if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) && defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ < 303 ) # define BOOST_SP_NO_SP_CONVERTIBLE #endif #if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) && defined( __BORLANDC__ ) && ( __BORLANDC__ < 0x630 ) # define BOOST_SP_NO_SP_CONVERTIBLE #endif #if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) namespace boost { namespace detail { template< class Y, class T > struct sp_convertible { typedef char (&yes) [1]; typedef char (&no) [2]; static yes f( T* ); static no f( ... ); enum _vt { value = sizeof( (f)( static_cast(0) ) ) == sizeof(yes) }; }; template< class Y, class T > struct sp_convertible< Y, T[] > { enum _vt { value = false }; }; template< class Y, class T > struct sp_convertible< Y[], T[] > { enum _vt { value = sp_convertible< Y[1], T[1] >::value }; }; template< class Y, std::size_t N, class T > struct sp_convertible< Y[N], T[] > { enum _vt { value = sp_convertible< Y[1], T[1] >::value }; }; struct sp_empty { }; template< bool > struct sp_enable_if_convertible_impl; template<> struct sp_enable_if_convertible_impl { typedef sp_empty type; }; template<> struct sp_enable_if_convertible_impl { }; template< class Y, class T > struct sp_enable_if_convertible: public sp_enable_if_convertible_impl< sp_convertible< Y, T >::value > { }; } // namespace detail } // namespace boost #endif // !defined( BOOST_SP_NO_SP_CONVERTIBLE ) #endif // #ifndef BOOST_SMART_PTR_DETAIL_SP_CONVERTIBLE_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_base.hpp0000664000577000060420000000556313557101274026275 0ustar anjaesctl#ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_HPP_INCLUDED #define BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // detail/sp_counted_base.hpp // // Copyright 2005, 2006 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // #include #include #if defined( BOOST_SP_DISABLE_THREADS ) # include #elif defined( BOOST_SP_USE_SPINLOCK ) # include #elif defined( BOOST_SP_USE_PTHREADS ) # include #elif defined( BOOST_DISABLE_THREADS ) && !defined( BOOST_SP_ENABLE_THREADS ) && !defined( BOOST_DISABLE_WIN32 ) # include #elif defined( __SNC__ ) # include #elif defined( __GNUC__ ) && ( defined( __i386__ ) || defined( __x86_64__ ) ) && !defined(__PATHSCALE__) # include #elif defined(__HP_aCC) && defined(__ia64) # include #elif defined( __GNUC__ ) && defined( __ia64__ ) && !defined( __INTEL_COMPILER ) && !defined(__PATHSCALE__) # include #elif defined( __IBMCPP__ ) && defined( __powerpc ) # include #elif defined( __MWERKS__ ) && defined( __POWERPC__ ) # include #elif defined( __GNUC__ ) && ( defined( __powerpc__ ) || defined( __ppc__ ) || defined( __ppc ) ) && !defined(__PATHSCALE__) && !defined( _AIX ) # include #elif defined( __GNUC__ ) && ( defined( __mips__ ) || defined( _mips ) ) && !defined(__PATHSCALE__) # include #elif defined( BOOST_SP_HAS_SYNC ) # include #elif defined(__GNUC__) && ( defined( __sparcv9 ) || ( defined( __sparcv8 ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 402 ) ) ) # include #elif defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || defined(__CYGWIN__) # include #elif defined( _AIX ) # include #elif !defined( BOOST_HAS_THREADS ) # include #else # include #endif #endif // #ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_base_gcc_ppc.hpp0000664000577000060420000000670113557101274027746 0ustar anjaesctl#ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_GCC_PPC_HPP_INCLUDED #define BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_GCC_PPC_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // detail/sp_counted_base_gcc_ppc.hpp - g++ on PowerPC // // Copyright (c) 2001, 2002, 2003 Peter Dimov and Multi Media Ltd. // Copyright 2004-2005 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // // Lock-free algorithm by Alexander Terekhov // // Thanks to Ben Hitchings for the #weak + (#shared != 0) // formulation // #include namespace boost { namespace detail { inline void atomic_increment( int * pw ) { // ++*pw; int tmp; __asm__ ( "0:\n\t" "lwarx %1, 0, %2\n\t" "addi %1, %1, 1\n\t" "stwcx. %1, 0, %2\n\t" "bne- 0b": "=m"( *pw ), "=&b"( tmp ): "r"( pw ), "m"( *pw ): "cc" ); } inline int atomic_decrement( int * pw ) { // return --*pw; int rv; __asm__ __volatile__ ( "sync\n\t" "0:\n\t" "lwarx %1, 0, %2\n\t" "addi %1, %1, -1\n\t" "stwcx. %1, 0, %2\n\t" "bne- 0b\n\t" "isync": "=m"( *pw ), "=&b"( rv ): "r"( pw ), "m"( *pw ): "memory", "cc" ); return rv; } inline int atomic_conditional_increment( int * pw ) { // if( *pw != 0 ) ++*pw; // return *pw; int rv; __asm__ ( "0:\n\t" "lwarx %1, 0, %2\n\t" "cmpwi %1, 0\n\t" "beq 1f\n\t" "addi %1, %1, 1\n\t" "1:\n\t" "stwcx. %1, 0, %2\n\t" "bne- 0b": "=m"( *pw ), "=&b"( rv ): "r"( pw ), "m"( *pw ): "cc" ); return rv; } class sp_counted_base { private: sp_counted_base( sp_counted_base const & ); sp_counted_base & operator= ( sp_counted_base const & ); int use_count_; // #shared int weak_count_; // #weak + (#shared != 0) public: sp_counted_base(): use_count_( 1 ), weak_count_( 1 ) { } virtual ~sp_counted_base() // nothrow { } // dispose() is called when use_count_ drops to zero, to release // the resources managed by *this. virtual void dispose() = 0; // nothrow // destroy() is called when weak_count_ drops to zero. virtual void destroy() // nothrow { delete this; } virtual void * get_deleter( sp_typeinfo const & ti ) = 0; virtual void * get_untyped_deleter() = 0; void add_ref_copy() { atomic_increment( &use_count_ ); } bool add_ref_lock() // true on success { return atomic_conditional_increment( &use_count_ ) != 0; } void release() // nothrow { if( atomic_decrement( &use_count_ ) == 0 ) { dispose(); weak_release(); } } void weak_add_ref() // nothrow { atomic_increment( &weak_count_ ); } void weak_release() // nothrow { if( atomic_decrement( &weak_count_ ) == 0 ) { destroy(); } } long use_count() const // nothrow { return static_cast( use_count_ ); } }; } // namespace detail } // namespace boost #endif // #ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_GCC_PPC_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp0000664000577000060420000000700013557101274027602 0ustar anjaesctl#ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_GCC_X86_HPP_INCLUDED #define BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_GCC_X86_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // detail/sp_counted_base_gcc_x86.hpp - g++ on 486+ or AMD64 // // Copyright (c) 2001, 2002, 2003 Peter Dimov and Multi Media Ltd. // Copyright 2004-2005 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // // Lock-free algorithm by Alexander Terekhov // // Thanks to Ben Hitchings for the #weak + (#shared != 0) // formulation // #include namespace boost { namespace detail { inline int atomic_exchange_and_add( int * pw, int dv ) { // int r = *pw; // *pw += dv; // return r; int r; __asm__ __volatile__ ( "lock\n\t" "xadd %1, %0": "=m"( *pw ), "=r"( r ): // outputs (%0, %1) "m"( *pw ), "1"( dv ): // inputs (%2, %3 == %1) "memory", "cc" // clobbers ); return r; } inline void atomic_increment( int * pw ) { //atomic_exchange_and_add( pw, 1 ); __asm__ ( "lock\n\t" "incl %0": "=m"( *pw ): // output (%0) "m"( *pw ): // input (%1) "cc" // clobbers ); } inline int atomic_conditional_increment( int * pw ) { // int rv = *pw; // if( rv != 0 ) ++*pw; // return rv; int rv, tmp; __asm__ ( "movl %0, %%eax\n\t" "0:\n\t" "test %%eax, %%eax\n\t" "je 1f\n\t" "movl %%eax, %2\n\t" "incl %2\n\t" "lock\n\t" "cmpxchgl %2, %0\n\t" "jne 0b\n\t" "1:": "=m"( *pw ), "=&a"( rv ), "=&r"( tmp ): // outputs (%0, %1, %2) "m"( *pw ): // input (%3) "cc" // clobbers ); return rv; } class sp_counted_base { private: sp_counted_base( sp_counted_base const & ); sp_counted_base & operator= ( sp_counted_base const & ); int use_count_; // #shared int weak_count_; // #weak + (#shared != 0) public: sp_counted_base(): use_count_( 1 ), weak_count_( 1 ) { } virtual ~sp_counted_base() // nothrow { } // dispose() is called when use_count_ drops to zero, to release // the resources managed by *this. virtual void dispose() = 0; // nothrow // destroy() is called when weak_count_ drops to zero. virtual void destroy() // nothrow { delete this; } virtual void * get_deleter( sp_typeinfo const & ti ) = 0; virtual void * get_untyped_deleter() = 0; void add_ref_copy() { atomic_increment( &use_count_ ); } bool add_ref_lock() // true on success { return atomic_conditional_increment( &use_count_ ) != 0; } void release() // nothrow { if( atomic_exchange_and_add( &use_count_, -1 ) == 1 ) { dispose(); weak_release(); } } void weak_add_ref() // nothrow { atomic_increment( &weak_count_ ); } void weak_release() // nothrow { if( atomic_exchange_and_add( &weak_count_, -1 ) == 1 ) { destroy(); } } long use_count() const // nothrow { return static_cast( use_count_ ); } }; } // namespace detail } // namespace boost #endif // #ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_GCC_X86_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_impl.hpp0000664000577000060420000001267613557101274026327 0ustar anjaesctl#ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_IMPL_HPP_INCLUDED #define BOOST_SMART_PTR_DETAIL_SP_COUNTED_IMPL_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // detail/sp_counted_impl.hpp // // Copyright (c) 2001, 2002, 2003 Peter Dimov and Multi Media Ltd. // Copyright 2004-2005 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // #include #if defined(BOOST_SP_USE_STD_ALLOCATOR) && defined(BOOST_SP_USE_QUICK_ALLOCATOR) # error BOOST_SP_USE_STD_ALLOCATOR and BOOST_SP_USE_QUICK_ALLOCATOR are incompatible. #endif #include #include #if defined(BOOST_SP_USE_QUICK_ALLOCATOR) #include #endif #if defined(BOOST_SP_USE_STD_ALLOCATOR) #include // std::allocator #endif #include // std::size_t namespace boost { #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) void sp_scalar_constructor_hook( void * px, std::size_t size, void * pn ); void sp_scalar_destructor_hook( void * px, std::size_t size, void * pn ); #endif namespace detail { template class sp_counted_impl_p: public sp_counted_base { private: X * px_; sp_counted_impl_p( sp_counted_impl_p const & ); sp_counted_impl_p & operator= ( sp_counted_impl_p const & ); typedef sp_counted_impl_p this_type; public: explicit sp_counted_impl_p( X * px ): px_( px ) { #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) boost::sp_scalar_constructor_hook( px, sizeof(X), this ); #endif } virtual void dispose() // nothrow { #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) boost::sp_scalar_destructor_hook( px_, sizeof(X), this ); #endif boost::checked_delete( px_ ); } virtual void * get_deleter( detail::sp_typeinfo const & ) { return 0; } virtual void * get_untyped_deleter() { return 0; } #if defined(BOOST_SP_USE_STD_ALLOCATOR) void * operator new( std::size_t ) { return std::allocator().allocate( 1, static_cast(0) ); } void operator delete( void * p ) { std::allocator().deallocate( static_cast(p), 1 ); } #endif #if defined(BOOST_SP_USE_QUICK_ALLOCATOR) void * operator new( std::size_t ) { return quick_allocator::alloc(); } void operator delete( void * p ) { quick_allocator::dealloc( p ); } #endif }; // // Borland's Codeguard trips up over the -Vx- option here: // #ifdef __CODEGUARD__ # pragma option push -Vx- #endif template class sp_counted_impl_pd: public sp_counted_base { private: P ptr; // copy constructor must not throw D del; // copy constructor must not throw sp_counted_impl_pd( sp_counted_impl_pd const & ); sp_counted_impl_pd & operator= ( sp_counted_impl_pd const & ); typedef sp_counted_impl_pd this_type; public: // pre: d(p) must not throw sp_counted_impl_pd( P p, D & d ): ptr( p ), del( d ) { } sp_counted_impl_pd( P p ): ptr( p ), del() { } virtual void dispose() // nothrow { del( ptr ); } virtual void * get_deleter( detail::sp_typeinfo const & ti ) { return ti == BOOST_SP_TYPEID(D)? &reinterpret_cast( del ): 0; } virtual void * get_untyped_deleter() { return &reinterpret_cast( del ); } #if defined(BOOST_SP_USE_STD_ALLOCATOR) void * operator new( std::size_t ) { return std::allocator().allocate( 1, static_cast(0) ); } void operator delete( void * p ) { std::allocator().deallocate( static_cast(p), 1 ); } #endif #if defined(BOOST_SP_USE_QUICK_ALLOCATOR) void * operator new( std::size_t ) { return quick_allocator::alloc(); } void operator delete( void * p ) { quick_allocator::dealloc( p ); } #endif }; template class sp_counted_impl_pda: public sp_counted_base { private: P p_; // copy constructor must not throw D d_; // copy constructor must not throw A a_; // copy constructor must not throw sp_counted_impl_pda( sp_counted_impl_pda const & ); sp_counted_impl_pda & operator= ( sp_counted_impl_pda const & ); typedef sp_counted_impl_pda this_type; public: // pre: d( p ) must not throw sp_counted_impl_pda( P p, D & d, A a ): p_( p ), d_( d ), a_( a ) { } sp_counted_impl_pda( P p, A a ): p_( p ), d_(), a_( a ) { } virtual void dispose() // nothrow { d_( p_ ); } virtual void destroy() // nothrow { typedef typename A::template rebind< this_type >::other A2; A2 a2( a_ ); this->~this_type(); a2.deallocate( this, 1 ); } virtual void * get_deleter( detail::sp_typeinfo const & ti ) { return ti == BOOST_SP_TYPEID( D )? &reinterpret_cast( d_ ): 0; } virtual void * get_untyped_deleter() { return &reinterpret_cast( d_ ); } }; #ifdef __CODEGUARD__ # pragma option pop #endif } // namespace detail } // namespace boost #endif // #ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_IMPL_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_has_sync.hpp0000664000577000060420000000277113557101274025447 0ustar anjaesctl#ifndef BOOST_SMART_PTR_DETAIL_SP_HAS_SYNC_HPP_INCLUDED #define BOOST_SMART_PTR_DETAIL_SP_HAS_SYNC_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // boost/smart_ptr/detail/sp_has_sync.hpp // // Copyright (c) 2008, 2009 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // Defines the BOOST_SP_HAS_SYNC macro if the __sync_* intrinsics // are available. // #ifndef BOOST_SP_NO_SYNC #if defined( __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 ) # define BOOST_SP_HAS_SYNC #elif defined( __IBMCPP__ ) && ( __IBMCPP__ >= 1210 ) # define BOOST_SP_HAS_SYNC #elif defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) #define BOOST_SP_HAS_SYNC #if defined( __arm__ ) || defined( __armel__ ) #undef BOOST_SP_HAS_SYNC #endif #if defined( __hppa ) || defined( __hppa__ ) #undef BOOST_SP_HAS_SYNC #endif #if defined( __m68k__ ) #undef BOOST_SP_HAS_SYNC #endif #if defined( __sh__ ) #undef BOOST_SP_HAS_SYNC #endif #if defined( __sparc__ ) #undef BOOST_SP_HAS_SYNC #endif #if defined( __INTEL_COMPILER ) && !defined( __ia64__ ) && ( __INTEL_COMPILER < 1110 ) #undef BOOST_SP_HAS_SYNC #endif #if defined(__PATHSCALE__) && ((__PATHCC__ == 4) && (__PATHCC_MINOR__ < 9)) #undef BOOST_SP_HAS_SYNC #endif #endif #endif // #ifndef BOOST_SP_NO_SYNC #endif // #ifndef BOOST_SMART_PTR_DETAIL_SP_HAS_SYNC_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_nullptr_t.hpp0000664000577000060420000000165113557101274025657 0ustar anjaesctl#ifndef BOOST_SMART_PTR_DETAIL_SP_NULLPTR_T_HPP_INCLUDED #define BOOST_SMART_PTR_DETAIL_SP_NULLPTR_T_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // detail/sp_nullptr_t.hpp // // Copyright 2013 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt #include #include #if !defined( BOOST_NO_CXX11_NULLPTR ) namespace boost { namespace detail { #if defined( __clang__ ) && !defined( _LIBCPP_VERSION ) && !defined( BOOST_NO_CXX11_DECLTYPE ) typedef decltype(nullptr) sp_nullptr_t; #else typedef std::nullptr_t sp_nullptr_t; #endif } // namespace detail } // namespace boost #endif // !defined( BOOST_NO_CXX11_NULLPTR ) #endif // #ifndef BOOST_SMART_PTR_DETAIL_SP_NULLPTR_T_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock.hpp0000664000577000060420000000267413557101274024762 0ustar anjaesctl#ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_HPP_INCLUDED #define BOOST_SMART_PTR_DETAIL_SPINLOCK_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // boost/detail/spinlock.hpp // // Copyright (c) 2008 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // struct spinlock // { // void lock(); // bool try_lock(); // void unlock(); // // class scoped_lock; // }; // // #define BOOST_DETAIL_SPINLOCK_INIT // #include #include #if defined( BOOST_SP_USE_PTHREADS ) # include #elif defined(__GNUC__) && defined( __arm__ ) && !defined( __thumb__ ) # include #elif defined( BOOST_SP_HAS_SYNC ) # include #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) # include #elif defined(BOOST_HAS_PTHREADS) # include #elif !defined(BOOST_HAS_THREADS) # include #else # error Unrecognized threading platform #endif #endif // #ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock_pool.hpp0000664000577000060420000000561213557101274026006 0ustar anjaesctl#ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_POOL_HPP_INCLUDED #define BOOST_SMART_PTR_DETAIL_SPINLOCK_POOL_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // boost/detail/spinlock_pool.hpp // // Copyright (c) 2008 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // spinlock_pool<0> is reserved for atomic<>, when/if it arrives // spinlock_pool<1> is reserved for shared_ptr reference counts // spinlock_pool<2> is reserved for shared_ptr atomic access // #include #include #include namespace boost { namespace detail { template< int I > class spinlock_pool { private: static spinlock pool_[ 41 ]; public: static spinlock & spinlock_for( void const * pv ) { #if defined(__VMS) && __INITIAL_POINTER_SIZE == 64 std::size_t i = reinterpret_cast< unsigned long long >( pv ) % 41; #else std::size_t i = reinterpret_cast< std::size_t >( pv ) % 41; #endif return pool_[ i ]; } class scoped_lock { private: spinlock & sp_; scoped_lock( scoped_lock const & ); scoped_lock & operator=( scoped_lock const & ); public: explicit scoped_lock( void const * pv ): sp_( spinlock_for( pv ) ) { sp_.lock(); } ~scoped_lock() { sp_.unlock(); } }; }; template< int I > spinlock spinlock_pool< I >::pool_[ 41 ] = { BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT }; } // namespace detail } // namespace boost #endif // #ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_POOL_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock_pt.hpp0000664000577000060420000000244213557101274025456 0ustar anjaesctl#ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_PT_HPP_INCLUDED #define BOOST_SMART_PTR_DETAIL_SPINLOCK_PT_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // Copyright (c) 2008 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // #include namespace boost { namespace detail { class spinlock { public: pthread_mutex_t v_; public: bool try_lock() { return pthread_mutex_trylock( &v_ ) == 0; } void lock() { pthread_mutex_lock( &v_ ); } void unlock() { pthread_mutex_unlock( &v_ ); } public: class scoped_lock { private: spinlock & sp_; scoped_lock( scoped_lock const & ); scoped_lock & operator=( scoped_lock const & ); public: explicit scoped_lock( spinlock & sp ): sp_( sp ) { sp.lock(); } ~scoped_lock() { sp_.unlock(); } }; }; } // namespace detail } // namespace boost #define BOOST_DETAIL_SPINLOCK_INIT { PTHREAD_MUTEX_INITIALIZER } #endif // #ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_PT_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock_sync.hpp0000664000577000060420000000272313557101274026011 0ustar anjaesctl#ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_SYNC_HPP_INCLUDED #define BOOST_SMART_PTR_DETAIL_SPINLOCK_SYNC_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // Copyright (c) 2008 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // #include #if defined( __ia64__ ) && defined( __INTEL_COMPILER ) # include #endif namespace boost { namespace detail { class spinlock { public: int v_; public: bool try_lock() { int r = __sync_lock_test_and_set( &v_, 1 ); return r == 0; } void lock() { for( unsigned k = 0; !try_lock(); ++k ) { boost::detail::yield( k ); } } void unlock() { __sync_lock_release( &v_ ); } public: class scoped_lock { private: spinlock & sp_; scoped_lock( scoped_lock const & ); scoped_lock & operator=( scoped_lock const & ); public: explicit scoped_lock( spinlock & sp ): sp_( sp ) { sp.lock(); } ~scoped_lock() { sp_.unlock(); } }; }; } // namespace detail } // namespace boost #define BOOST_DETAIL_SPINLOCK_INIT {0} #endif // #ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_SYNC_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/detail/yield_k.hpp0000664000577000060420000000460013557101274024547 0ustar anjaesctl#ifndef BOOST_SMART_PTR_DETAIL_YIELD_K_HPP_INCLUDED #define BOOST_SMART_PTR_DETAIL_YIELD_K_HPP_INCLUDED // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // yield_k.hpp // // Copyright (c) 2008 Peter Dimov // // void yield( unsigned k ); // // Typical use: // // for( unsigned k = 0; !try_lock(); ++k ) yield( k ); // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // #include // BOOST_SMT_PAUSE #if defined(_MSC_VER) && _MSC_VER >= 1310 && ( defined(_M_IX86) || defined(_M_X64) ) extern "C" void _mm_pause(); #pragma intrinsic( _mm_pause ) #define BOOST_SMT_PAUSE _mm_pause(); #elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) #define BOOST_SMT_PAUSE __asm__ __volatile__( "rep; nop" : : : "memory" ); #endif // #if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || defined( __CYGWIN__ ) #if defined( BOOST_USE_WINDOWS_H ) # include #endif namespace boost { namespace detail { #if !defined( BOOST_USE_WINDOWS_H ) extern "C" void __stdcall Sleep( unsigned long ms ); #endif inline void yield( unsigned k ) { if( k < 4 ) { } #if defined( BOOST_SMT_PAUSE ) else if( k < 16 ) { BOOST_SMT_PAUSE } #endif else if( k < 32 ) { Sleep( 0 ); } else { Sleep( 1 ); } } } // namespace detail } // namespace boost #elif defined( BOOST_HAS_PTHREADS ) #include #include namespace boost { namespace detail { inline void yield( unsigned k ) { if( k < 4 ) { } #if defined( BOOST_SMT_PAUSE ) else if( k < 16 ) { BOOST_SMT_PAUSE } #endif else if( k < 32 || k & 1 ) { sched_yield(); } else { // g++ -Wextra warns on {} or {0} struct timespec rqtp = { 0, 0 }; // POSIX says that timespec has tv_sec and tv_nsec // But it doesn't guarantee order or placement rqtp.tv_sec = 0; rqtp.tv_nsec = 1000; nanosleep( &rqtp, 0 ); } } } // namespace detail } // namespace boost #else namespace boost { namespace detail { inline void yield( unsigned ) { } } // namespace detail } // namespace boost #endif #endif // #ifndef BOOST_SMART_PTR_DETAIL_YIELD_K_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/enable_shared_from_this.hpp0000664000577000060420000000352213557101274026515 0ustar anjaesctl#ifndef BOOST_SMART_PTR_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED #define BOOST_SMART_PTR_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED // // enable_shared_from_this.hpp // // Copyright 2002, 2009 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // // http://www.boost.org/libs/smart_ptr/enable_shared_from_this.html // #include #include #include #include namespace boost { template class enable_shared_from_this { protected: enable_shared_from_this() BOOST_NOEXCEPT { } enable_shared_from_this(enable_shared_from_this const &) BOOST_NOEXCEPT { } enable_shared_from_this & operator=(enable_shared_from_this const &) BOOST_NOEXCEPT { return *this; } ~enable_shared_from_this() BOOST_NOEXCEPT // ~weak_ptr newer throws, so this call also must not throw { } public: shared_ptr shared_from_this() { shared_ptr p( weak_this_ ); BOOST_ASSERT( p.get() == this ); return p; } shared_ptr shared_from_this() const { shared_ptr p( weak_this_ ); BOOST_ASSERT( p.get() == this ); return p; } public: // actually private, but avoids compiler template friendship issues // Note: invoked automatically by shared_ptr; do not call template void _internal_accept_owner( shared_ptr const * ppx, Y * py ) const { if( weak_this_.expired() ) { weak_this_ = shared_ptr( *ppx, py ); } } private: mutable weak_ptr weak_this_; }; } // namespace boost #endif // #ifndef BOOST_SMART_PTR_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/shared_ptr.hpp0000664000577000060420000006262513557101274024033 0ustar anjaesctl#ifndef BOOST_SMART_PTR_SHARED_PTR_HPP_INCLUDED #define BOOST_SMART_PTR_SHARED_PTR_HPP_INCLUDED // // shared_ptr.hpp // // (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. // Copyright (c) 2001-2008 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // See http://www.boost.org/libs/smart_ptr/shared_ptr.htm for documentation. // #include // for broken compiler workarounds #if defined(BOOST_NO_MEMBER_TEMPLATES) && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) #include #else // In order to avoid circular dependencies with Boost.TR1 // we make sure that our include of doesn't try to // pull in the TR1 headers: that's why we use this header // rather than including directly: #include // std::auto_ptr #include #include #include #include #include #include #include #if !defined(BOOST_SP_NO_ATOMIC_ACCESS) #include #include #endif #include // for std::swap #include // for std::less #include // for std::bad_cast #include // for std::size_t #if !defined(BOOST_NO_IOSTREAM) #if !defined(BOOST_NO_IOSFWD) #include // for std::basic_ostream #else #include #endif #endif namespace boost { template class shared_ptr; template class weak_ptr; template class enable_shared_from_this; class enable_shared_from_raw; namespace detail { // sp_element, element_type template< class T > struct sp_element { typedef T type; }; #if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) template< class T > struct sp_element< T[] > { typedef T type; }; #if !defined( __BORLANDC__ ) || !BOOST_WORKAROUND( __BORLANDC__, < 0x600 ) template< class T, std::size_t N > struct sp_element< T[N] > { typedef T type; }; #endif #endif // !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) // sp_dereference, return type of operator* template< class T > struct sp_dereference { typedef T & type; }; template<> struct sp_dereference< void > { typedef void type; }; #if !defined(BOOST_NO_CV_VOID_SPECIALIZATIONS) template<> struct sp_dereference< void const > { typedef void type; }; template<> struct sp_dereference< void volatile > { typedef void type; }; template<> struct sp_dereference< void const volatile > { typedef void type; }; #endif // !defined(BOOST_NO_CV_VOID_SPECIALIZATIONS) #if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) template< class T > struct sp_dereference< T[] > { typedef void type; }; #if !defined( __BORLANDC__ ) || !BOOST_WORKAROUND( __BORLANDC__, < 0x600 ) template< class T, std::size_t N > struct sp_dereference< T[N] > { typedef void type; }; #endif #endif // !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) // sp_member_access, return type of operator-> template< class T > struct sp_member_access { typedef T * type; }; #if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) template< class T > struct sp_member_access< T[] > { typedef void type; }; #if !defined( __BORLANDC__ ) || !BOOST_WORKAROUND( __BORLANDC__, < 0x600 ) template< class T, std::size_t N > struct sp_member_access< T[N] > { typedef void type; }; #endif #endif // !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) // sp_array_access, return type of operator[] template< class T > struct sp_array_access { typedef void type; }; #if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) template< class T > struct sp_array_access< T[] > { typedef T & type; }; #if !defined( __BORLANDC__ ) || !BOOST_WORKAROUND( __BORLANDC__, < 0x600 ) template< class T, std::size_t N > struct sp_array_access< T[N] > { typedef T & type; }; #endif #endif // !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) // sp_extent, for operator[] index check template< class T > struct sp_extent { enum _vt { value = 0 }; }; #if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) template< class T, std::size_t N > struct sp_extent< T[N] > { enum _vt { value = N }; }; #endif // !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) // enable_shared_from_this support template< class X, class Y, class T > inline void sp_enable_shared_from_this( boost::shared_ptr const * ppx, Y const * py, boost::enable_shared_from_this< T > const * pe ) { if( pe != 0 ) { pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) ); } } template< class X, class Y > inline void sp_enable_shared_from_this( boost::shared_ptr * ppx, Y const * py, boost::enable_shared_from_raw const * pe ); #ifdef _MANAGED // Avoid C4793, ... causes native code generation struct sp_any_pointer { template sp_any_pointer( T* ) {} }; inline void sp_enable_shared_from_this( sp_any_pointer, sp_any_pointer, sp_any_pointer ) { } #else // _MANAGED inline void sp_enable_shared_from_this( ... ) { } #endif // _MANAGED #if !defined( BOOST_NO_SFINAE ) && !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) && !defined( BOOST_NO_AUTO_PTR ) // rvalue auto_ptr support based on a technique by Dave Abrahams template< class T, class R > struct sp_enable_if_auto_ptr { }; template< class T, class R > struct sp_enable_if_auto_ptr< std::auto_ptr< T >, R > { typedef R type; }; #endif // sp_assert_convertible template< class Y, class T > inline void sp_assert_convertible() { #if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) // static_assert( sp_convertible< Y, T >::value ); typedef char tmp[ sp_convertible< Y, T >::value? 1: -1 ]; (void)sizeof( tmp ); #else T* p = static_cast< Y* >( 0 ); (void)p; #endif } // pointer constructor helper template< class T, class Y > inline void sp_pointer_construct( boost::shared_ptr< T > * ppx, Y * p, boost::detail::shared_count & pn ) { boost::detail::shared_count( p ).swap( pn ); boost::detail::sp_enable_shared_from_this( ppx, p, p ); } #if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) template< class T, class Y > inline void sp_pointer_construct( boost::shared_ptr< T[] > * /*ppx*/, Y * p, boost::detail::shared_count & pn ) { sp_assert_convertible< Y[], T[] >(); boost::detail::shared_count( p, boost::checked_array_deleter< T >() ).swap( pn ); } template< class T, std::size_t N, class Y > inline void sp_pointer_construct( boost::shared_ptr< T[N] > * /*ppx*/, Y * p, boost::detail::shared_count & pn ) { sp_assert_convertible< Y[N], T[N] >(); boost::detail::shared_count( p, boost::checked_array_deleter< T >() ).swap( pn ); } #endif // !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) // deleter constructor helper template< class T, class Y > inline void sp_deleter_construct( boost::shared_ptr< T > * ppx, Y * p ) { boost::detail::sp_enable_shared_from_this( ppx, p, p ); } #if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) template< class T, class Y > inline void sp_deleter_construct( boost::shared_ptr< T[] > * /*ppx*/, Y * /*p*/ ) { sp_assert_convertible< Y[], T[] >(); } template< class T, std::size_t N, class Y > inline void sp_deleter_construct( boost::shared_ptr< T[N] > * /*ppx*/, Y * /*p*/ ) { sp_assert_convertible< Y[N], T[N] >(); } #endif // !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) } // namespace detail // // shared_ptr // // An enhanced relative of scoped_ptr with reference counted copy semantics. // The object pointed to is deleted when the last shared_ptr pointing to it // is destroyed or reset. // template class shared_ptr { private: // Borland 5.5.1 specific workaround typedef shared_ptr this_type; public: typedef typename boost::detail::sp_element< T >::type element_type; shared_ptr() BOOST_NOEXCEPT : px( 0 ), pn() // never throws in 1.30+ { } #if !defined( BOOST_NO_CXX11_NULLPTR ) shared_ptr( boost::detail::sp_nullptr_t ) BOOST_NOEXCEPT : px( 0 ), pn() // never throws { } #endif template explicit shared_ptr( Y * p ): px( p ), pn() // Y must be complete { boost::detail::sp_pointer_construct( this, p, pn ); } // // Requirements: D's copy constructor must not throw // // shared_ptr will release p by calling d(p) // template shared_ptr( Y * p, D d ): px( p ), pn( p, d ) { boost::detail::sp_deleter_construct( this, p ); } #if !defined( BOOST_NO_CXX11_NULLPTR ) template shared_ptr( boost::detail::sp_nullptr_t p, D d ): px( p ), pn( p, d ) { } #endif // As above, but with allocator. A's copy constructor shall not throw. template shared_ptr( Y * p, D d, A a ): px( p ), pn( p, d, a ) { boost::detail::sp_deleter_construct( this, p ); } #if !defined( BOOST_NO_CXX11_NULLPTR ) template shared_ptr( boost::detail::sp_nullptr_t p, D d, A a ): px( p ), pn( p, d, a ) { } #endif // generated copy constructor, destructor are fine... #if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) // ... except in C++0x, move disables the implicit copy shared_ptr( shared_ptr const & r ) BOOST_NOEXCEPT : px( r.px ), pn( r.pn ) { } #endif template explicit shared_ptr( weak_ptr const & r ): pn( r.pn ) // may throw { boost::detail::sp_assert_convertible< Y, T >(); // it is now safe to copy r.px, as pn(r.pn) did not throw px = r.px; } template shared_ptr( weak_ptr const & r, boost::detail::sp_nothrow_tag ) BOOST_NOEXCEPT : px( 0 ), pn( r.pn, boost::detail::sp_nothrow_tag() ) { if( !pn.empty() ) { px = r.px; } } template #if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) shared_ptr( shared_ptr const & r, typename boost::detail::sp_enable_if_convertible::type = boost::detail::sp_empty() ) #else shared_ptr( shared_ptr const & r ) #endif BOOST_NOEXCEPT : px( r.px ), pn( r.pn ) { boost::detail::sp_assert_convertible< Y, T >(); } // aliasing template< class Y > shared_ptr( shared_ptr const & r, element_type * p ) BOOST_NOEXCEPT : px( p ), pn( r.pn ) { } #ifndef BOOST_NO_AUTO_PTR template explicit shared_ptr( std::auto_ptr & r ): px(r.get()), pn() { boost::detail::sp_assert_convertible< Y, T >(); Y * tmp = r.get(); pn = boost::detail::shared_count( r ); boost::detail::sp_deleter_construct( this, tmp ); } #if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) template shared_ptr( std::auto_ptr && r ): px(r.get()), pn() { boost::detail::sp_assert_convertible< Y, T >(); Y * tmp = r.get(); pn = boost::detail::shared_count( r ); boost::detail::sp_deleter_construct( this, tmp ); } #elif !defined( BOOST_NO_SFINAE ) && !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) template explicit shared_ptr( Ap r, typename boost::detail::sp_enable_if_auto_ptr::type = 0 ): px( r.get() ), pn() { typedef typename Ap::element_type Y; boost::detail::sp_assert_convertible< Y, T >(); Y * tmp = r.get(); pn = boost::detail::shared_count( r ); boost::detail::sp_deleter_construct( this, tmp ); } #endif // BOOST_NO_SFINAE, BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION #endif // BOOST_NO_AUTO_PTR #if !defined( BOOST_NO_CXX11_SMART_PTR ) template< class Y, class D > shared_ptr( std::unique_ptr< Y, D > && r ): px( r.get() ), pn() { boost::detail::sp_assert_convertible< Y, T >(); typename std::unique_ptr< Y, D >::pointer tmp = r.get(); pn = boost::detail::shared_count( r ); boost::detail::sp_deleter_construct( this, tmp ); } #endif // assignment shared_ptr & operator=( shared_ptr const & r ) BOOST_NOEXCEPT { this_type(r).swap(*this); return *this; } #if !defined(BOOST_MSVC) || (BOOST_MSVC >= 1400) template shared_ptr & operator=(shared_ptr const & r) BOOST_NOEXCEPT { this_type(r).swap(*this); return *this; } #endif #ifndef BOOST_NO_AUTO_PTR template shared_ptr & operator=( std::auto_ptr & r ) { this_type( r ).swap( *this ); return *this; } #if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) template shared_ptr & operator=( std::auto_ptr && r ) { this_type( static_cast< std::auto_ptr && >( r ) ).swap( *this ); return *this; } #elif !defined( BOOST_NO_SFINAE ) && !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) template typename boost::detail::sp_enable_if_auto_ptr< Ap, shared_ptr & >::type operator=( Ap r ) { this_type( r ).swap( *this ); return *this; } #endif // BOOST_NO_SFINAE, BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION #endif // BOOST_NO_AUTO_PTR #if !defined( BOOST_NO_CXX11_SMART_PTR ) template shared_ptr & operator=( std::unique_ptr && r ) { this_type( static_cast< std::unique_ptr && >( r ) ).swap(*this); return *this; } #endif // Move support #if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) shared_ptr( shared_ptr && r ) BOOST_NOEXCEPT : px( r.px ), pn() { pn.swap( r.pn ); r.px = 0; } template #if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) shared_ptr( shared_ptr && r, typename boost::detail::sp_enable_if_convertible::type = boost::detail::sp_empty() ) #else shared_ptr( shared_ptr && r ) #endif BOOST_NOEXCEPT : px( r.px ), pn() { boost::detail::sp_assert_convertible< Y, T >(); pn.swap( r.pn ); r.px = 0; } shared_ptr & operator=( shared_ptr && r ) BOOST_NOEXCEPT { this_type( static_cast< shared_ptr && >( r ) ).swap( *this ); return *this; } template shared_ptr & operator=( shared_ptr && r ) BOOST_NOEXCEPT { this_type( static_cast< shared_ptr && >( r ) ).swap( *this ); return *this; } #endif #if !defined( BOOST_NO_CXX11_NULLPTR ) shared_ptr & operator=( boost::detail::sp_nullptr_t ) BOOST_NOEXCEPT // never throws { this_type().swap(*this); return *this; } #endif void reset() BOOST_NOEXCEPT // never throws in 1.30+ { this_type().swap(*this); } template void reset( Y * p ) // Y must be complete { BOOST_ASSERT( p == 0 || p != px ); // catch self-reset errors this_type( p ).swap( *this ); } template void reset( Y * p, D d ) { this_type( p, d ).swap( *this ); } template void reset( Y * p, D d, A a ) { this_type( p, d, a ).swap( *this ); } template void reset( shared_ptr const & r, element_type * p ) { this_type( r, p ).swap( *this ); } // never throws (but has a BOOST_ASSERT in it, so not marked with BOOST_NOEXCEPT) typename boost::detail::sp_dereference< T >::type operator* () const { BOOST_ASSERT( px != 0 ); return *px; } // never throws (but has a BOOST_ASSERT in it, so not marked with BOOST_NOEXCEPT) typename boost::detail::sp_member_access< T >::type operator-> () const { BOOST_ASSERT( px != 0 ); return px; } // never throws (but has a BOOST_ASSERT in it, so not marked with BOOST_NOEXCEPT) typename boost::detail::sp_array_access< T >::type operator[] ( std::ptrdiff_t i ) const { BOOST_ASSERT( px != 0 ); BOOST_ASSERT( i >= 0 && ( i < boost::detail::sp_extent< T >::value || boost::detail::sp_extent< T >::value == 0 ) ); return px[ i ]; } element_type * get() const BOOST_NOEXCEPT { return px; } // implicit conversion to "bool" #include bool unique() const BOOST_NOEXCEPT { return pn.unique(); } long use_count() const BOOST_NOEXCEPT { return pn.use_count(); } void swap( shared_ptr & other ) BOOST_NOEXCEPT { std::swap(px, other.px); pn.swap(other.pn); } template bool owner_before( shared_ptr const & rhs ) const BOOST_NOEXCEPT { return pn < rhs.pn; } template bool owner_before( weak_ptr const & rhs ) const BOOST_NOEXCEPT { return pn < rhs.pn; } void * _internal_get_deleter( boost::detail::sp_typeinfo const & ti ) const BOOST_NOEXCEPT { return pn.get_deleter( ti ); } void * _internal_get_untyped_deleter() const BOOST_NOEXCEPT { return pn.get_untyped_deleter(); } bool _internal_equiv( shared_ptr const & r ) const BOOST_NOEXCEPT { return px == r.px && pn == r.pn; } // Tasteless as this may seem, making all members public allows member templates // to work in the absence of member template friends. (Matthew Langston) #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS private: template friend class shared_ptr; template friend class weak_ptr; #endif element_type * px; // contained pointer boost::detail::shared_count pn; // reference counter }; // shared_ptr template inline bool operator==(shared_ptr const & a, shared_ptr const & b) BOOST_NOEXCEPT { return a.get() == b.get(); } template inline bool operator!=(shared_ptr const & a, shared_ptr const & b) BOOST_NOEXCEPT { return a.get() != b.get(); } #if __GNUC__ == 2 && __GNUC_MINOR__ <= 96 // Resolve the ambiguity between our op!= and the one in rel_ops template inline bool operator!=(shared_ptr const & a, shared_ptr const & b) BOOST_NOEXCEPT { return a.get() != b.get(); } #endif #if !defined( BOOST_NO_CXX11_NULLPTR ) template inline bool operator==( shared_ptr const & p, boost::detail::sp_nullptr_t ) BOOST_NOEXCEPT { return p.get() == 0; } template inline bool operator==( boost::detail::sp_nullptr_t, shared_ptr const & p ) BOOST_NOEXCEPT { return p.get() == 0; } template inline bool operator!=( shared_ptr const & p, boost::detail::sp_nullptr_t ) BOOST_NOEXCEPT { return p.get() != 0; } template inline bool operator!=( boost::detail::sp_nullptr_t, shared_ptr const & p ) BOOST_NOEXCEPT { return p.get() != 0; } #endif template inline bool operator<(shared_ptr const & a, shared_ptr const & b) BOOST_NOEXCEPT { return a.owner_before( b ); } template inline void swap(shared_ptr & a, shared_ptr & b) BOOST_NOEXCEPT { a.swap(b); } template shared_ptr static_pointer_cast( shared_ptr const & r ) BOOST_NOEXCEPT { (void) static_cast< T* >( static_cast< U* >( 0 ) ); typedef typename shared_ptr::element_type E; E * p = static_cast< E* >( r.get() ); return shared_ptr( r, p ); } template shared_ptr const_pointer_cast( shared_ptr const & r ) BOOST_NOEXCEPT { (void) const_cast< T* >( static_cast< U* >( 0 ) ); typedef typename shared_ptr::element_type E; E * p = const_cast< E* >( r.get() ); return shared_ptr( r, p ); } template shared_ptr dynamic_pointer_cast( shared_ptr const & r ) BOOST_NOEXCEPT { (void) dynamic_cast< T* >( static_cast< U* >( 0 ) ); typedef typename shared_ptr::element_type E; E * p = dynamic_cast< E* >( r.get() ); return p? shared_ptr( r, p ): shared_ptr(); } template shared_ptr reinterpret_pointer_cast( shared_ptr const & r ) BOOST_NOEXCEPT { (void) reinterpret_cast< T* >( static_cast< U* >( 0 ) ); typedef typename shared_ptr::element_type E; E * p = reinterpret_cast< E* >( r.get() ); return shared_ptr( r, p ); } // get_pointer() enables boost::mem_fn to recognize shared_ptr template inline typename shared_ptr::element_type * get_pointer(shared_ptr const & p) BOOST_NOEXCEPT { return p.get(); } // operator<< #if !defined(BOOST_NO_IOSTREAM) #if defined(BOOST_NO_TEMPLATED_IOSTREAMS) || ( defined(__GNUC__) && (__GNUC__ < 3) ) template std::ostream & operator<< (std::ostream & os, shared_ptr const & p) { os << p.get(); return os; } #else // in STLport's no-iostreams mode no iostream symbols can be used #ifndef _STLP_NO_IOSTREAMS # if defined(BOOST_MSVC) && BOOST_WORKAROUND(BOOST_MSVC, < 1300 && __SGI_STL_PORT) // MSVC6 has problems finding std::basic_ostream through the using declaration in namespace _STL using std::basic_ostream; template basic_ostream & operator<< (basic_ostream & os, shared_ptr const & p) # else template std::basic_ostream & operator<< (std::basic_ostream & os, shared_ptr const & p) # endif { os << p.get(); return os; } #endif // _STLP_NO_IOSTREAMS #endif // __GNUC__ < 3 #endif // !defined(BOOST_NO_IOSTREAM) // get_deleter namespace detail { #if ( defined(__GNUC__) && BOOST_WORKAROUND(__GNUC__, < 3) ) || \ ( defined(__EDG_VERSION__) && BOOST_WORKAROUND(__EDG_VERSION__, <= 238) ) || \ ( defined(__HP_aCC) && BOOST_WORKAROUND(__HP_aCC, <= 33500) ) // g++ 2.9x doesn't allow static_cast(void *) // apparently EDG 2.38 and HP aCC A.03.35 also don't accept it template D * basic_get_deleter(shared_ptr const & p) { void const * q = p._internal_get_deleter(BOOST_SP_TYPEID(D)); return const_cast(static_cast(q)); } #else template D * basic_get_deleter( shared_ptr const & p ) BOOST_NOEXCEPT { return static_cast( p._internal_get_deleter(BOOST_SP_TYPEID(D)) ); } #endif class esft2_deleter_wrapper { private: shared_ptr deleter_; public: esft2_deleter_wrapper() { } template< class T > void set_deleter( shared_ptr const & deleter ) { deleter_ = deleter; } template D* get_deleter() const BOOST_NOEXCEPT { return boost::detail::basic_get_deleter( deleter_ ); } template< class T> void operator()( T* ) { BOOST_ASSERT( deleter_.use_count() <= 1 ); deleter_.reset(); } }; } // namespace detail template D * get_deleter( shared_ptr const & p ) BOOST_NOEXCEPT { D *del = boost::detail::basic_get_deleter(p); if(del == 0) { boost::detail::esft2_deleter_wrapper *del_wrapper = boost::detail::basic_get_deleter(p); // The following get_deleter method call is fully qualified because // older versions of gcc (2.95, 3.2.3) fail to compile it when written del_wrapper->get_deleter() if(del_wrapper) del = del_wrapper->::boost::detail::esft2_deleter_wrapper::get_deleter(); } return del; } // atomic access #if !defined(BOOST_SP_NO_ATOMIC_ACCESS) template inline bool atomic_is_lock_free( shared_ptr const * /*p*/ ) BOOST_NOEXCEPT { return false; } template shared_ptr atomic_load( shared_ptr const * p ) { boost::detail::spinlock_pool<2>::scoped_lock lock( p ); return *p; } template inline shared_ptr atomic_load_explicit( shared_ptr const * p, memory_order /*mo*/ ) { return atomic_load( p ); } template void atomic_store( shared_ptr * p, shared_ptr r ) { boost::detail::spinlock_pool<2>::scoped_lock lock( p ); p->swap( r ); } template inline void atomic_store_explicit( shared_ptr * p, shared_ptr r, memory_order /*mo*/ ) { atomic_store( p, r ); // std::move( r ) } template shared_ptr atomic_exchange( shared_ptr * p, shared_ptr r ) { boost::detail::spinlock & sp = boost::detail::spinlock_pool<2>::spinlock_for( p ); sp.lock(); p->swap( r ); sp.unlock(); return r; // return std::move( r ) } template shared_ptr atomic_exchange_explicit( shared_ptr * p, shared_ptr r, memory_order /*mo*/ ) { return atomic_exchange( p, r ); // std::move( r ) } template bool atomic_compare_exchange( shared_ptr * p, shared_ptr * v, shared_ptr w ) { boost::detail::spinlock & sp = boost::detail::spinlock_pool<2>::spinlock_for( p ); sp.lock(); if( p->_internal_equiv( *v ) ) { p->swap( w ); sp.unlock(); return true; } else { shared_ptr tmp( *p ); sp.unlock(); tmp.swap( *v ); return false; } } template inline bool atomic_compare_exchange_explicit( shared_ptr * p, shared_ptr * v, shared_ptr w, memory_order /*success*/, memory_order /*failure*/ ) { return atomic_compare_exchange( p, v, w ); // std::move( w ) } #endif // !defined(BOOST_SP_NO_ATOMIC_ACCESS) // hash_value template< class T > struct hash; template< class T > std::size_t hash_value( boost::shared_ptr const & p ) BOOST_NOEXCEPT { return boost::hash< T* >()( p.get() ); } } // namespace boost #endif // #if defined(BOOST_NO_MEMBER_TEMPLATES) && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) #endif // #ifndef BOOST_SMART_PTR_SHARED_PTR_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/smart_ptr/weak_ptr.hpp0000664000577000060420000001341413557101274023504 0ustar anjaesctl#ifndef BOOST_SMART_PTR_WEAK_PTR_HPP_INCLUDED #define BOOST_SMART_PTR_WEAK_PTR_HPP_INCLUDED // // weak_ptr.hpp // // Copyright (c) 2001, 2002, 2003 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // See http://www.boost.org/libs/smart_ptr/weak_ptr.htm for documentation. // #include // boost.TR1 include order fix #include #include namespace boost { template class weak_ptr { private: // Borland 5.5.1 specific workarounds typedef weak_ptr this_type; public: typedef typename boost::detail::sp_element< T >::type element_type; weak_ptr() BOOST_NOEXCEPT : px(0), pn() // never throws in 1.30+ { } // generated copy constructor, assignment, destructor are fine... #if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) // ... except in C++0x, move disables the implicit copy weak_ptr( weak_ptr const & r ) BOOST_NOEXCEPT : px( r.px ), pn( r.pn ) { } weak_ptr & operator=( weak_ptr const & r ) BOOST_NOEXCEPT { px = r.px; pn = r.pn; return *this; } #endif // // The "obvious" converting constructor implementation: // // template // weak_ptr(weak_ptr const & r): px(r.px), pn(r.pn) // never throws // { // } // // has a serious problem. // // r.px may already have been invalidated. The px(r.px) // conversion may require access to *r.px (virtual inheritance). // // It is not possible to avoid spurious access violations since // in multithreaded programs r.px may be invalidated at any point. // template #if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) weak_ptr( weak_ptr const & r, typename boost::detail::sp_enable_if_convertible::type = boost::detail::sp_empty() ) #else weak_ptr( weak_ptr const & r ) #endif BOOST_NOEXCEPT : px(r.lock().get()), pn(r.pn) { boost::detail::sp_assert_convertible< Y, T >(); } #if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) template #if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) weak_ptr( weak_ptr && r, typename boost::detail::sp_enable_if_convertible::type = boost::detail::sp_empty() ) #else weak_ptr( weak_ptr && r ) #endif BOOST_NOEXCEPT : px( r.lock().get() ), pn( static_cast< boost::detail::weak_count && >( r.pn ) ) { boost::detail::sp_assert_convertible< Y, T >(); r.px = 0; } // for better efficiency in the T == Y case weak_ptr( weak_ptr && r ) BOOST_NOEXCEPT : px( r.px ), pn( static_cast< boost::detail::weak_count && >( r.pn ) ) { r.px = 0; } // for better efficiency in the T == Y case weak_ptr & operator=( weak_ptr && r ) BOOST_NOEXCEPT { this_type( static_cast< weak_ptr && >( r ) ).swap( *this ); return *this; } #endif template #if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) weak_ptr( shared_ptr const & r, typename boost::detail::sp_enable_if_convertible::type = boost::detail::sp_empty() ) #else weak_ptr( shared_ptr const & r ) #endif BOOST_NOEXCEPT : px( r.px ), pn( r.pn ) { boost::detail::sp_assert_convertible< Y, T >(); } #if !defined(BOOST_MSVC) || (BOOST_MSVC >= 1300) template weak_ptr & operator=( weak_ptr const & r ) BOOST_NOEXCEPT { boost::detail::sp_assert_convertible< Y, T >(); px = r.lock().get(); pn = r.pn; return *this; } #if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) template weak_ptr & operator=( weak_ptr && r ) BOOST_NOEXCEPT { this_type( static_cast< weak_ptr && >( r ) ).swap( *this ); return *this; } #endif template weak_ptr & operator=( shared_ptr const & r ) BOOST_NOEXCEPT { boost::detail::sp_assert_convertible< Y, T >(); px = r.px; pn = r.pn; return *this; } #endif shared_ptr lock() const BOOST_NOEXCEPT { return shared_ptr( *this, boost::detail::sp_nothrow_tag() ); } long use_count() const BOOST_NOEXCEPT { return pn.use_count(); } bool expired() const BOOST_NOEXCEPT { return pn.use_count() == 0; } bool _empty() const // extension, not in std::weak_ptr { return pn.empty(); } void reset() BOOST_NOEXCEPT // never throws in 1.30+ { this_type().swap(*this); } void swap(this_type & other) BOOST_NOEXCEPT { std::swap(px, other.px); pn.swap(other.pn); } template void _internal_aliasing_assign(weak_ptr const & r, element_type * px2) { px = px2; pn = r.pn; } template bool owner_before( weak_ptr const & rhs ) const BOOST_NOEXCEPT { return pn < rhs.pn; } template bool owner_before( shared_ptr const & rhs ) const BOOST_NOEXCEPT { return pn < rhs.pn; } // Tasteless as this may seem, making all members public allows member templates // to work in the absence of member template friends. (Matthew Langston) #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS private: template friend class weak_ptr; template friend class shared_ptr; #endif element_type * px; // contained pointer boost::detail::weak_count pn; // reference counter }; // weak_ptr template inline bool operator<(weak_ptr const & a, weak_ptr const & b) BOOST_NOEXCEPT { return a.owner_before( b ); } template void swap(weak_ptr & a, weak_ptr & b) BOOST_NOEXCEPT { a.swap(b); } } // namespace boost #endif // #ifndef BOOST_SMART_PTR_WEAK_PTR_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/throw_exception.hpp0000664000577000060420000000552213557101274023077 0ustar anjaesctl#ifndef UUID_AA15E74A856F11E08B8D93F24824019B #define UUID_AA15E74A856F11E08B8D93F24824019B #if defined(__GNUC__) && !defined(BOOST_EXCEPTION_ENABLE_WARNINGS) #pragma GCC system_header #endif #if defined(_MSC_VER) && !defined(BOOST_EXCEPTION_ENABLE_WARNINGS) #pragma warning(push,1) #endif // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif // // boost/throw_exception.hpp // // Copyright (c) 2002 Peter Dimov and Multi Media Ltd. // Copyright (c) 2008-2009 Emil Dotchevski and Reverge Studios, Inc. // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // http://www.boost.org/libs/utility/throw_exception.html // #include #include #include #include #if !defined( BOOST_EXCEPTION_DISABLE ) && defined( __BORLANDC__ ) && BOOST_WORKAROUND( __BORLANDC__, BOOST_TESTED_AT(0x593) ) # define BOOST_EXCEPTION_DISABLE #endif #if !defined( BOOST_EXCEPTION_DISABLE ) && defined( BOOST_MSVC ) && BOOST_WORKAROUND( BOOST_MSVC, < 1310 ) # define BOOST_EXCEPTION_DISABLE #endif #if !defined( BOOST_EXCEPTION_DISABLE ) # include # include # define BOOST_THROW_EXCEPTION(x) ::boost::exception_detail::throw_exception_(x,BOOST_CURRENT_FUNCTION,__FILE__,__LINE__) #else # define BOOST_THROW_EXCEPTION(x) ::boost::throw_exception(x) #endif namespace boost { #ifdef BOOST_NO_EXCEPTIONS void throw_exception( std::exception const & e ); // user defined #else inline void throw_exception_assert_compatibility( std::exception const & ) { } template BOOST_ATTRIBUTE_NORETURN inline void throw_exception( E const & e ) { //All boost exceptions are required to derive from std::exception, //to ensure compatibility with BOOST_NO_EXCEPTIONS. throw_exception_assert_compatibility(e); #ifndef BOOST_EXCEPTION_DISABLE throw enable_current_exception(enable_error_info(e)); #else throw e; #endif } #endif #if !defined( BOOST_EXCEPTION_DISABLE ) namespace exception_detail { template BOOST_ATTRIBUTE_NORETURN void throw_exception_( E const & x, char const * current_function, char const * file, int line ) { boost::throw_exception( set_info( set_info( set_info( enable_error_info(x), throw_function(current_function)), throw_file(file)), throw_line(line))); } } #endif } // namespace boost #if defined(_MSC_VER) && !defined(BOOST_EXCEPTION_ENABLE_WARNINGS) #pragma warning(pop) #endif #endif base-7.0.3.1/modules/libcom/vxWorks/boost/tr1/detail/config.hpp0000664000577000060420000001172013557101274023070 0ustar anjaesctl// (C) Copyright John Maddock 2005-7. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED # define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED #include #if (defined(__GNUC__) && !(defined(linux) || defined(__linux) || defined(__linux__))) \ || (!defined(_AIX) && defined(__IBMCPP__) && (__IBMCPP__ >= 800)) // Disable use of #include_next on Linux as typically we are installed in a // directory that is searched *after* the std lib include path. #if !defined(BOOST_HAS_INCLUDE_NEXT) # define BOOST_HAS_INCLUDE_NEXT #endif // Need to find out if we're using GLIBC: #ifdef BOOST_TR1_UTILITY_INCLUDED // Oops we're in a recursive include path!! // Need to include utility, or some std lib header, // but *not* via or # ifndef BOOST_TR1_NO_RECURSION # define BOOST_TR1_NO_RECURSION # define BOOST_TR1_NO_CONFIG_RECURSION # endif # if defined(BOOST_HAS_INCLUDE_NEXT) && !defined(BOOST_TR1_DISABLE_INCLUDE_NEXT) # include_next # else # include BOOST_TR1_STD_HEADER(utility) # endif # ifdef BOOST_TR1_NO_CONFIG_RECURSION # undef BOOST_TR1_NO_CONFIG_RECURSION # undef BOOST_TR1_NO_RECURSION # endif #else #include #endif #endif #if defined(__GLIBCXX__) && !defined(BOOST_TR1_PATH) # define BOOST_TR1_PATH(name) tr1/name #endif #if !defined(BOOST_TR1_PATH) # define BOOST_TR1_PATH(name) name #endif #define BOOST_TR1_HEADER(name) // Can't use BOOST_WORKAROUND here, it leads to recursive includes: #if (defined(__BORLANDC__) && (__BORLANDC__ <= 0x600)) || (defined(_MSC_VER) && (_MSC_VER < 1310)) # define BOOST_TR1_USE_OLD_TUPLE #endif #ifdef __IBMCPP_TR1__ // turn on support for everything: # define BOOST_HAS_TR1 #endif #ifdef __GXX_EXPERIMENTAL_CXX0X__ # define BOOST_HAS_TR1_COMPLEX_OVERLOADS # define BOOST_HAS_TR1_COMPLEX_INVERSE_TRIG #endif #ifdef BOOST_HAS_TR1 // turn on support for everything: # define BOOST_HAS_TR1_ARRAY # define BOOST_HAS_TR1_COMPLEX_OVERLOADS # define BOOST_HAS_TR1_COMPLEX_INVERSE_TRIG # define BOOST_HAS_TR1_REFERENCE_WRAPPER # define BOOST_HAS_TR1_RESULT_OF # define BOOST_HAS_TR1_MEM_FN # define BOOST_HAS_TR1_BIND # define BOOST_HAS_TR1_FUNCTION # define BOOST_HAS_TR1_HASH # define BOOST_HAS_TR1_SHARED_PTR # define BOOST_HAS_TR1_RANDOM # define BOOST_HAS_TR1_REGEX # define BOOST_HAS_TR1_TUPLE # define BOOST_HAS_TR1_TYPE_TRAITS # define BOOST_HAS_TR1_UTILITY # define BOOST_HAS_TR1_UNORDERED_MAP # define BOOST_HAS_TR1_UNORDERED_SET # define BOOST_HAS_TR1_CMATH #endif #if defined(__MWERKS__) && (__MWERKS__ >= 0x3205) // // Very preliminary MWCW support, may not be right: // # define BOOST_HAS_TR1_SHARED_PTR # define BOOST_HAS_TR1_REFERENCE_WRAPPER # define BOOST_HAS_TR1_FUNCTION # define BOOST_HAS_TR1_TUPLE # define BOOST_HAS_TR1_RESULT_OF #endif #ifdef BOOST_HAS_GCC_TR1 // turn on support for everything in gcc 4.0.x: # define BOOST_HAS_TR1_ARRAY #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 403 //# define BOOST_HAS_TR1_COMPLEX_OVERLOADS # define BOOST_HAS_TR1_COMPLEX_INVERSE_TRIG #endif # define BOOST_HAS_TR1_REFERENCE_WRAPPER # define BOOST_HAS_TR1_RESULT_OF # define BOOST_HAS_TR1_MEM_FN # define BOOST_HAS_TR1_BIND # define BOOST_HAS_TR1_FUNCTION # define BOOST_HAS_TR1_HASH # define BOOST_HAS_TR1_SHARED_PTR #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 403 # define BOOST_HAS_TR1_RANDOM //# define BOOST_HAS_TR1_REGEX #ifdef _GLIBCXX_USE_C99_MATH_TR1 # define BOOST_HAS_TR1_CMATH #endif #endif # define BOOST_HAS_TR1_TUPLE # define BOOST_HAS_TR1_TYPE_TRAITS # define BOOST_HAS_TR1_UTILITY # define BOOST_HAS_TR1_UNORDERED_MAP # define BOOST_HAS_TR1_UNORDERED_SET #endif #if defined(_MSC_VER) && (_MSC_VER >= 1500) \ && defined(_MSC_FULL_VER) && \ !defined(__SGI_STL_PORT) && \ !defined(_STLPORT_VERSION) && \ !defined(_RWSTD_VER_STR) && \ !defined(_RWSTD_VER) // // MSVC-9.0 defines a not-quite TR1 conforming hash // function object in , so we must define // this here, in addition the feature pack for VC9 // provides a more or less full TR1 implementation: // # if defined(_HAS_TR1) && (_HAS_TR1 + 0) # define BOOST_HAS_TR1_ARRAY # define BOOST_HAS_TR1_REFERENCE_WRAPPER # define BOOST_HAS_TR1_RESULT_OF # define BOOST_HAS_TR1_MEM_FN # define BOOST_HAS_TR1_BIND # define BOOST_HAS_TR1_FUNCTION # define BOOST_HAS_TR1_HASH # define BOOST_HAS_TR1_SHARED_PTR # define BOOST_HAS_TR1_RANDOM # define BOOST_HAS_TR1_REGEX # define BOOST_HAS_TR1_TUPLE # define BOOST_HAS_TR1_TYPE_TRAITS # define BOOST_HAS_TR1_UTILITY # define BOOST_HAS_TR1_UNORDERED_MAP # define BOOST_HAS_TR1_UNORDERED_SET # else # define BOOST_HAS_TR1_HASH # endif # if _MSC_VER >= 1600 # define BOOST_HAS_CPP_0X # endif #endif #include #endif base-7.0.3.1/modules/libcom/vxWorks/boost/tr1/memory.hpp0000664000577000060420000000440113557101274021667 0ustar anjaesctl// (C) Copyright John Maddock 2005. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_TR1_MEMORY_HPP_INCLUDED # define BOOST_TR1_MEMORY_HPP_INCLUDED # include # include # include #ifndef BOOST_HAS_TR1_SHARED_PTR // // This header can get included by boost/shared_ptr.hpp which leads // to cyclic dependencies, the workaround is to forward declare all // the boost components, and then include the actual headers afterwards. // This is fragile, but seems to work, and doesn't require modification // of boost/shared_ptr.hpp. // namespace boost{ class bad_weak_ptr; template class weak_ptr; template class shared_ptr; template void swap(weak_ptr & a, weak_ptr & b) BOOST_NOEXCEPT; template void swap(shared_ptr & a, shared_ptr & b) BOOST_NOEXCEPT; template shared_ptr static_pointer_cast(shared_ptr const & r) BOOST_NOEXCEPT; template shared_ptr dynamic_pointer_cast(shared_ptr const & r) BOOST_NOEXCEPT; template shared_ptr const_pointer_cast(shared_ptr const & r) BOOST_NOEXCEPT; template D * get_deleter(shared_ptr const & p) BOOST_NOEXCEPT; template class enable_shared_from_this; namespace detail{ class shared_count; class weak_count; } } namespace std{ namespace tr1{ using ::boost::bad_weak_ptr; using ::boost::shared_ptr; #if !BOOST_WORKAROUND(__BORLANDC__, < 0x0582) using ::boost::swap; #endif using ::boost::static_pointer_cast; using ::boost::dynamic_pointer_cast; using ::boost::const_pointer_cast; using ::boost::get_deleter; using ::boost::weak_ptr; using ::boost::enable_shared_from_this; } } #include #include #include #else # if defined(BOOST_HAS_INCLUDE_NEXT) && !defined(BOOST_TR1_DISABLE_INCLUDE_NEXT) # include_next BOOST_TR1_HEADER(memory) # else # include # include BOOST_TR1_STD_HEADER(BOOST_TR1_PATH(memory)) # endif #endif #endif base-7.0.3.1/modules/libcom/vxWorks/boost/type_traits/is_signed.hpp0000664000577000060420000001134313557101274024167 0ustar anjaesctl // (C) Copyright John Maddock 2005. // Use, modification and distribution are subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt). // // See http://www.boost.org/libs/type_traits for most recent version including documentation. #ifndef BOOST_TT_IS_SIGNED_HPP_INCLUDED #define BOOST_TT_IS_SIGNED_HPP_INCLUDED #include #include #include #include // should be the last #include #include namespace boost { #if !defined( __CODEGEARC__ ) namespace detail{ #if !(defined(__EDG_VERSION__) && __EDG_VERSION__ <= 238) && !defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION) template struct is_signed_values { // // Note that we cannot use BOOST_STATIC_CONSTANT here, using enum's // rather than "real" static constants simply doesn't work or give // the correct answer. // typedef typename remove_cv::type no_cv_t; static const no_cv_t minus_one = (static_cast(-1)); static const no_cv_t zero = (static_cast(0)); }; template struct is_signed_helper { typedef typename remove_cv::type no_cv_t; BOOST_STATIC_CONSTANT(bool, value = (!(::boost::detail::is_signed_values::minus_one > boost::detail::is_signed_values::zero))); }; template struct is_signed_select_helper { template struct rebind { typedef is_signed_helper type; }; }; template <> struct is_signed_select_helper { template struct rebind { typedef false_type type; }; }; template struct is_signed_imp { typedef is_signed_select_helper< ::boost::type_traits::ice_or< ::boost::is_integral::value, ::boost::is_enum::value>::value > selector; typedef typename selector::template rebind binder; typedef typename binder::type type; #if defined(BOOST_MSVC) && (BOOST_MSVC < 1300) BOOST_STATIC_CONSTANT(bool, value = is_signed_imp::type::value); #else BOOST_STATIC_CONSTANT(bool, value = type::value); #endif }; #else template struct is_signed_imp : public false_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; #ifdef BOOST_HAS_LONG_LONG template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; #endif #if defined(CHAR_MIN) && (CHAR_MIN != 0) template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; #endif #if defined(WCHAR_MIN) && (WCHAR_MIN != 0) template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; template <> struct is_signed_imp : public true_type{}; #endif #endif } #endif // !defined( __CODEGEARC__ ) #if defined( __CODEGEARC__ ) BOOST_TT_AUX_BOOL_TRAIT_DEF1(is_signed,T,__is_signed(T)) #else BOOST_TT_AUX_BOOL_TRAIT_DEF1(is_signed,T,::boost::detail::is_signed_imp::value) #endif } // namespace boost #include #endif // BOOST_TT_IS_MEMBER_FUNCTION_POINTER_HPP_INCLUDED base-7.0.3.1/modules/libcom/vxWorks/boost/weak_ptr.hpp0000664000577000060420000000073213557101274021470 0ustar anjaesctl#ifndef BOOST_WEAK_PTR_HPP_INCLUDED #define BOOST_WEAK_PTR_HPP_INCLUDED // // weak_ptr.hpp // // Copyright (c) 2001, 2002, 2003 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // // See http://www.boost.org/libs/smart_ptr/weak_ptr.htm for documentation. // #include #endif // #ifndef BOOST_WEAK_PTR_HPP_INCLUDED base-7.0.3.1/modules/normativeTypes/.readthedocs.yml0000664000577000060420000000065213556651171021212 0ustar anjaesctl# .readthedocs.yml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Build documentation in the documentation/ directory with Sphinx sphinx: configuration: documentation/conf.py # Build documentation with MkDocs #mkdocs: # configuration: mkdocs.yml # Optionally build your docs in additional formats such as PDF and ePub formats: all base-7.0.3.1/modules/normativeTypes/Doxyfile0000664000577000060420000030366013556651171017637 0ustar anjaesctl# Doxyfile 1.8.6 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = normativeTypesCPP # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = # 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 # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo # to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = YES # If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a # new page for each member. If set to NO, the documentation of a member will be # part of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. # # Note For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined # locally in source files will be included in the documentation. If set to NO # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO these classes will be included in the various overviews. This option has # no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the # todo list. This list is created by putting \todo commands in the # documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the # test list. This list is created by putting \test commands in the # documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES the list # will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. Do not use file names with spaces, bibtex cannot handle them. See # also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO doxygen will only warn about wrong or incomplete parameter # documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. # Note: If this tag is empty the current directory is searched. INPUT = src # 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 # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank the # following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, # *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, # *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. FILE_PATTERNS = # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER ) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES, then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = NO # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html/doxygen # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- # defined cascading style sheet that is included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefor more robust against future updates. # Doxygen will copy the style sheet file to the output directory. For an example # see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = documentation/ntCPP.html # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the stylesheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to NO can help when comparing the output of multiple runs. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler ( hhc.exe). If non-empty # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated ( # YES) or that it should be included in the master .chm file ( NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated ( # YES) or a normal table of contents ( NO) in the .chm file. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using prerendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /
Man(1) output converted with man2html