diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index dd6ae0358..e9f632391 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -4,7 +4,7 @@ # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found -# in the file LICENSE that is included with this distribution. +# in the file LICENSE that is included with this distribution. #************************************************************************* # # EPICS Version information @@ -34,10 +34,10 @@ BASE_3_16 = YES # EPICS_VERSION must be a number >0 and <256 EPICS_VERSION = 3 -# EPICS_REVISION must be a number >=0 and <256 +# EPICS_REVISION must be a number >=0 and <256 EPICS_REVISION = 16 -# EPICS_MODIFICATION must be a number >=0 and <256 +# EPICS_MODIFICATION must be a number >=0 and <256 EPICS_MODIFICATION = 0 # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) diff --git a/configure/RULES.ioc b/configure/RULES.ioc index da314a406..fa0482ec0 100644 --- a/configure/RULES.ioc +++ b/configure/RULES.ioc @@ -14,7 +14,7 @@ build$(DIVIDER)$(ARCH) build: buildInstall install$(DIVIDER)$(ARCH) install: buildInstall $(ARCH): buildInstall -ifeq ($(filter $(ARCH),$(BUILD_ARCHS)),$(ARCH)) +ifeq ($(filter $(ARCH),$(BUILD_ARCHS)),$(strip $(ARCH))) buildInstall$(DIVIDER)$(ARCH) buildInstall: $(TARGETS) clean$(DIVIDER)$(ARCH) clean: @@ -35,4 +35,3 @@ envPaths: $(wildcard $(TOP)/configure/RELEASE*) \ realclean: $(RM) cdCommands envPaths dllPath.bat relPaths.sh - diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index b0f109669..a665de3a1 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -105,13 +105,17 @@ endif PRODTARGETS += $(PRODNAME) $(MUNCHNAME) $(CTDT_SRCS) $(CTDT_OBJS) $(NMS) #--------------------------------------------------------------- -# Generate a test specification if any tests are defined. +# 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) +endif #--------------------------------------------------------------- # Libraries @@ -324,7 +328,7 @@ $(MODNAME): %$(MODEXT): %$(EXE) # Automated testing runtests: $(TESTSCRIPTS) -ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS))) +ifdef RUNTESTS_ENABLED -$(PERL) -MTest::Harness -e 'runtests @ARGV if @ARGV;' $^ endif @@ -340,7 +344,7 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES) # A .tap file is the output from running the associated test script %.tap: %.t -ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS))) +ifdef RUNTESTS_ENABLED -$(PERL) $< -tap > $@ endif diff --git a/configure/os/CONFIG.linux-x86.windows-x64-mingw b/configure/os/CONFIG.linux-x86.windows-x64-mingw index 3b5ba18b4..3becd0045 100644 --- a/configure/os/CONFIG.linux-x86.windows-x64-mingw +++ b/configure/os/CONFIG.linux-x86.windows-x64-mingw @@ -22,7 +22,3 @@ GNU_LDLIBS_YES = # Link with winsock2 OP_SYS_LDLIBS = -lws2_32 - -# Use static compiler-support libraries -OP_SYS_LDFLAGS += -static-libgcc -static-libstdc++ -# There is no compiler flag for static libwinpthread diff --git a/configure/os/CONFIG.win32-x86.win32-x86 b/configure/os/CONFIG.win32-x86.win32-x86 index f9bb0b9cb..505798a8a 100644 --- a/configure/os/CONFIG.win32-x86.win32-x86 +++ b/configure/os/CONFIG.win32-x86.win32-x86 @@ -10,6 +10,8 @@ VALID_BUILDS = Host Ioc CMPLR_CLASS = msvc +OPT_WHOLE_PROGRAM = YES + #------------------------------------------------------- WINLINK = link @@ -42,11 +44,13 @@ WARN_CFLAGS_NO = -W1 # -Ox maximum optimizations # -GL whole program optimization # -Oy- re-enable creation of frame pointers -OPT_CFLAGS_YES = -Ox -GL -Oy- +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 catch bugs occuring only inoptimized code +# -RTCsu enable run-time error checks OPT_CFLAGS_NO = -Zi -RTCsu # specify object file name and location @@ -96,17 +100,19 @@ CODE_CPPFLAGS += -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE # -w44355 set "'this' used in the base initializer list" to be level 4 # -w44344 "behavior change: use of explicit template arguments results in ..." WARN_CXXFLAGS_YES = -W3 -w44355 -w44344 -WARN_CXXFLAGS_NO = -W1 +WARN_CXXFLAGS_NO = -W1 # # -Ox maximum optimizations # -GL whole program optimization # -Oy- re-enable creation of frame pointers -OPT_CXXFLAGS_YES = -Ox -GL -Oy- +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 catch bugs occurring only in optimized code +# -RTCsu enable run-time error checks OPT_CXXFLAGS_NO = -RTCsu -Zi # specify object file name and location @@ -136,11 +142,14 @@ RANLIB= # -fixed:no generate relocatable code # -version:. - only 2 components allowed, 0-65535 each # -debug generate debugging info -LINK_OPT_FLAGS_YES = -LTCG -incremental:no -opt:ref \ - -release $(PROD_VERSION:%=-version:%) +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 = -LTCG + +LIB_OPT_FLAGS_YES = $(LINK_OPT_FLAGS_WHOLE_$(OPT_WHOLE_PROGRAM)) LIB_OPT_LDFLAGS = $(LIB_OPT_FLAGS_$(HOST_OPT)) ARCH_DEP_CFLAGS= @@ -156,48 +165,38 @@ EXE=.exe OBJ=.obj RES=.res -# Problem: MS Visual C++ does not recognize *.cc as C++ source, -# so we do C++ compiles using the global flag -TP +# 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 +# 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) \ +# 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 -# +# Specify dll .def file only if it exists DLL_DEF_FLAG = $(addprefix -def:,$(wildcard ../$(addsuffix .def,$*))) -# -# A WIN32 dll has three parts: +# 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 -# +# 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 link library -# and when SHARED_LIBRARIES is NO we are building an object library -# +# 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)) @@ -211,7 +210,7 @@ LIBNAME = $(LIBNAME_$(SHARED_LIBRARIES)) TESTLIBNAME_NO = $(TESTBUILD_LIBRARY:%=%.lib) TESTLIBNAME = $(TESTLIBNAME_$(SHARED_LIBRARIES)) -# dll install location +# dll install location INSTALL_SHRLIB = $(INSTALL_BIN) @@ -271,7 +270,7 @@ LINK.cpp = $(WINLINK) -nologo $(STATIC_LDFLAGS) $(LDFLAGS) $(PROD_LDFLAGS) \ #-------------------------------------------------- # 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. +# 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 @@ -281,4 +280,3 @@ 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)) - diff --git a/configure/os/CONFIG.windows-x64.windows-x64 b/configure/os/CONFIG.windows-x64.windows-x64 index d0e59bd7c..dcb6e82de 100644 --- a/configure/os/CONFIG.windows-x64.windows-x64 +++ b/configure/os/CONFIG.windows-x64.windows-x64 @@ -8,8 +8,7 @@ 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 - +OPT_LDFLAGS += -MACHINE:X64 +# -MACHINE:X64 +# -MACHINE:IA64 (Itanium) +# -MACHINE:X86 diff --git a/configure/os/CONFIG_SITE.Common.win32-x86-static b/configure/os/CONFIG_SITE.Common.win32-x86-static new file mode 100644 index 000000000..de4e61760 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.win32-x86-static @@ -0,0 +1,8 @@ +# 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 diff --git a/configure/os/CONFIG_SITE.Common.windows-x64-static b/configure/os/CONFIG_SITE.Common.windows-x64-static index f2f059751..5db619b11 100644 --- a/configure/os/CONFIG_SITE.Common.windows-x64-static +++ b/configure/os/CONFIG_SITE.Common.windows-x64-static @@ -1,9 +1,8 @@ # CONFIG_SITE.Common.windows-x64-static # -# Site Specific definitions for windows-x64-static target -# Only the local epics system manager should modify this file - -# 64-bit Visual Studio 2010 builds fail when built optimized. -# If you are using a newer version you can try removing this: -HOST_OPT = NO +# 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 diff --git a/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw b/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw index db87a3357..363664f26 100644 --- a/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw +++ b/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw @@ -4,10 +4,10 @@ #------------------------------------------------------- # 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 +# 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 @@ -20,3 +20,6 @@ CMPLR_PREFIX = i686-w64-mingw32- #CMPLR_PREFIX = i686-pc-mingw32- # Debian? #CMPLR_PREFIX = i586-mingw32msvc- + +# Use static compiler-support libraries +OP_SYS_LDFLAGS += -static-libgcc -static-libstdc++ diff --git a/configure/os/CONFIG_SITE.linux-x86.windows-x64-mingw b/configure/os/CONFIG_SITE.linux-x86.windows-x64-mingw index 42cdc353b..026410cfe 100644 --- a/configure/os/CONFIG_SITE.linux-x86.windows-x64-mingw +++ b/configure/os/CONFIG_SITE.linux-x86.windows-x64-mingw @@ -18,3 +18,6 @@ #CMPLR_PREFIX = i686-w64-mingw32- # RHEL: CMPLR_PREFIX = x86_64-w64-mingw32- + +# Use static compiler-support libraries +OP_SYS_LDFLAGS += -static-libgcc -static-libstdc++ diff --git a/documentation/KnownProblems.html b/documentation/KnownProblems.html index 2f8a61883..fe9045d03 100644 --- a/documentation/KnownProblems.html +++ b/documentation/KnownProblems.html @@ -4,17 +4,17 @@ - Known Problems in R3.16.0.1 + Known Problems in R3.16.1 -

EPICS Base R3.16.0.1: Known Problems

+

EPICS Base R3.16.1: Known Problems

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

-
% cd /path/to/base-3.16.0.1
+
% cd /path/to/base-3.16.1
 % patch -p0 < /path/to/file.patch

The following problems were known by the developers at the time of this diff --git a/documentation/README.1st b/documentation/README.1st index b74f6ae92..ee56e542f 100644 --- a/documentation/README.1st +++ b/documentation/README.1st @@ -1,24 +1,24 @@ Installation Instructions - EPICS Base Release 3.16.0 + EPICS Base Release 3.16.1 -------------------------------------------------------------------------- Table of Contents - * What is EPICS base? - * What is new in this release? - * Copyright - * Supported platforms - * Supported compilers - * Software requirements - * Host system storage requirements - * Documentation - * Directory Structure - * Build related components - * Building EPICS base (Unix and Win32) - * Example application and extension - * Multiple host platforms + *�What is EPICS base? + *�What is new in this release? + *�Copyright + *�Supported platforms + *�Supported compilers + *�Software requirements + *�Host system storage requirements + *�Documentation + *�Directory Structure + *�Build related components + *�Building EPICS base (Unix and Win32) + *�Example application and extension + *�Multiple host platforms -------------------------------------------------------------------------- diff --git a/documentation/README.html b/documentation/README.html index f717c3d47..8917fa2e7 100644 --- a/documentation/README.html +++ b/documentation/README.html @@ -9,7 +9,7 @@

Installation Instructions

-

EPICS Base Release 3.16.0


+

EPICS Base Release 3.16.1



Table of Contents

@@ -190,12 +190,12 @@ CONFIG.CrossCommon Cross build definitions CONFIG.gnuCommon Gnu compiler build definitions for all archs CONFIG_ADDONS Definitions for <osclass> and DEFAULT options - CONFIG_APP_INCLUDE + 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_FILE_TYPE CONFIG_SITE Site specific make definitions CONFIG_SITE_ENV Site defaults for EPICS environment variables MAKEFILE Installs CONFIG* RULES* creates @@ -206,9 +206,9 @@ 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_EXPAND + RULES_FILE_TYPE + RULES_TARGET RULES_TOP Rules specific to a <top> dir (uninstall and tar) Sample.Makefile Sample makefile with comments
@@ -340,7 +340,7 @@ Files in the base/startup directory have been provided to

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. + and then executed to try out this release of base.

Instructions for building and executing the 3.15 example application @@ -350,8 +350,8 @@ Files in the base/startup directory have been provided 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 + 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.

diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 4a4804672..372f32d7d 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -3,14 +3,15 @@ - EPICS Base R3.16.0.2 Release Notes + EPICS Base R3.16.1 Release Notes -

EPICS Base Release 3.16.0.2

+

EPICS Base Release 3.16.1

This version of EPICS Base has not been released yet.

+

Changes made on the 3.16 branch since 3.16.0.1

-

Changes from the 3.14 branch since 3.15.4

+

Changes from the 3.14 branch since 3.15.5

+ +

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 overproportionally +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:

+ +
    +
  • + #1440186 Crash due to a too small buffer being provided in + dbContextReadNotifyCache
  • +
  • + #1479316 Some data races found using Helgrind
  • +
  • + #1495833 biRecord prompt groups are nonsensical
  • +
  • + #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.target files.

+

General Time updates

The iocInit code now performs a sanity check of the current time diff --git a/src/ca/client/CAref.html b/src/ca/client/CAref.html index 87ad469a6..2bdd0b452 100644 --- a/src/ca/client/CAref.html +++ b/src/ca/client/CAref.html @@ -1146,16 +1146,10 @@ the output.

Wide mode "name timestamp value stat sevr" (read PVs as DBR_TIME_xxx) - - -n - Print DBF_ENUM values as number (default are enum strings) - -d <type> - Request specific dbr type; use string (DBR_ prefix may be omitted) - -

or number of one of the following types:

- + Request specific dbr type; use string (DBR_ prefix may be omitted)
+ or number of one of the following types:
@@ -1272,6 +1266,14 @@ the output.

+ + + Enum format: + + + -n + Print DBF_ENUM value as number (default is enum string) + Arrays: @@ -1303,15 +1305,15 @@ the output.

-e <nr> - Use %e format, with <nr> digits after the decimal point + Use %e format, with a precision of <nr> digits -f <nr> - Use %f format, with <nr> digits after the decimal point + Use %f format, with a precision of <nr> digits -g <nr> - Use %g format, with <nr> digits after the decimal point + Use %g format, with a precision of <nr> digits -s @@ -1349,6 +1351,14 @@ the output.

-0b Print as binary number + + + Alternate output field separator: + + + -F <ofs> + Use <ofs> as an alternate output field separator + @@ -1381,9 +1391,10 @@ the output.

Wait time, specifies longer CA timeout, default is 1.0 second - -m <mask> - Specify CA event mask to use, with <mask> being any combination - of 'v' (value), 'a' (alarm), 'l' (log), 'p' (property). Default: va + -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> @@ -1405,8 +1416,8 @@ the output.

'n' = no timestamps
'r' = relative timestamps (time elapsed since start of program)
'i' = incremental timestamps (time elapsed since last update)
- 'I' = incremental timestamps (time elapsed since last update, by - channel) + 'I' = incremental timestamps (time since last update, by channel)
+ 'r', 'i' or 'I' require 's' or 'c' to select the time source @@ -1414,7 +1425,7 @@ the output.

-n - Print DBF_ENUM values as number (default are enum strings) + Print DBF_ENUM values as number (default is enum string) @@ -1422,16 +1433,15 @@ the output.

- Value format: Print number of requested values, then list of - values + Array values: Print number of elements, then list of values Default: - Print all values + Default: Request and print all elements (dynamic arrays supported) - -# <count> - Print first <count> elements of an array + -# <num> + Request and print up to <num> elements -S @@ -1439,23 +1449,23 @@ the output.

- Floating point type format: + Floating point format: Default: Use %g format - -e <nr> - Use %e format, with <nr> digits after the decimal point + -e <num> + Use %e format, with a precision of <num> digits - -f <nr> - Use %f format, with <nr> digits after the decimal point + -f <num> + Use %f format, with a precision of <num> digits - -g <nr> - Use %g format, with <nr> digits after the decimal point + -g <num> + Use %g format, with a precision of <num> digits -s @@ -1497,21 +1507,27 @@ the output.

caput

-
caput [options] <PV name> <value>
+
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 value is read -before and after the write operation and printed as "Old" and "new" values on -stdout.

+

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.

-

The array variant writes an array to the specified PV. The first numeric -argument specifying the number of array elements is kept for compatibility with -the array data format of caget - the actual number of values specified on the -command line is used.

+

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.

@@ -1550,12 +1566,16 @@ command line is used.

+ + + + - + @@ -1571,17 +1591,24 @@ command line is used.

- - + + - + - + + + + + + + + +
-t Terse mode - print only successfully written value, without name
-lLong 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
Arrays:
-aPut array dataDefault:Put scalar
Value format: Print number of requested values, then list of - valuesValue format: all value arguments concatenated with spaces
-SPut string as an array of char (long string)Put string as an array of chars (long string)
-aPut array
Value format: number of values, then list of values
diff --git a/src/ca/client/cac.cpp b/src/ca/client/cac.cpp index d66cf9dad..d1afe6785 100644 --- a/src/ca/client/cac.cpp +++ b/src/ca/client/cac.cpp @@ -149,9 +149,9 @@ cac::cac ( iiuExistenceCount ( 0u ), cacShutdownInProgress ( false ) { - if ( ! osiSockAttach () ) { - throwWithLocation ( caErrorCode (ECA_INTERNAL) ); - } + if ( ! osiSockAttach () ) { + throwWithLocation ( udpiiu :: noSocket () ); + } try { long status; diff --git a/src/ca/client/iocinf.h b/src/ca/client/iocinf.h index c0c33d6c2..0d3b57db1 100644 --- a/src/ca/client/iocinf.h +++ b/src/ca/client/iocinf.h @@ -61,11 +61,6 @@ static const double CA_CONN_VERIFY_PERIOD = 30.0; /* (sec) how often to request */ static const unsigned contiguousMsgCountWhichTriggersFlowControl = 10u; -class caErrorCode { -public: - caErrorCode ( int ) {}; -}; - /* * CA internal functions */ diff --git a/src/ca/client/repeater.cpp b/src/ca/client/repeater.cpp index 5e123ee2d..61ba33fc5 100644 --- a/src/ca/client/repeater.cpp +++ b/src/ca/client/repeater.cpp @@ -504,6 +504,7 @@ void ca_repeater () if ( sockerrno == SOCK_EADDRINUSE ) { osiSockRelease (); debugPrintf ( ( "CA Repeater: exiting because a repeater is already running\n" ) ); + delete [] pBuf; return; } char sockErrBuf[64]; diff --git a/src/ca/client/tcpiiu.cpp b/src/ca/client/tcpiiu.cpp index 58b1b0c0f..7fd848b20 100644 --- a/src/ca/client/tcpiiu.cpp +++ b/src/ca/client/tcpiiu.cpp @@ -44,12 +44,7 @@ using namespace std; -#if 0 -const unsigned mSecPerSec = 1000u; -const unsigned uSecPerSec = 1000u * mSecPerSec; -#endif - -tcpSendThread::tcpSendThread ( +tcpSendThread::tcpSendThread ( class tcpiiu & iiuIn, const char * pName, unsigned stackSize, unsigned priority ) : thread ( *this, pName, stackSize, priority ), iiu ( iiuIn ) @@ -807,28 +802,6 @@ tcpiiu::tcpiiu ( } } -# if 0 - // - // windows has a really strange implementation of thess options - // and we can avoid the need for this by using pthread_kill on unix - // - { - struct timeval timeout; - double pollInterval = connectionTimeout / 8.0; - timeout.tv_sec = static_cast < long > ( pollInterval ); - timeout.tv_usec = static_cast < long > - ( ( pollInterval - timeout.tv_sec ) * uSecPerSec ); - // intentionally ignore status as we dont expect that all systems - // will accept this request - setsockopt ( this->sock, SOL_SOCKET, SO_SNDTIMEO, - ( char * ) & timeout, sizeof ( timeout ) ); - // intentionally ignore status as we dont expect that all systems - // will accept this request - setsockopt ( this->sock, SOL_SOCKET, SO_RCVTIMEO, - ( char * ) & timeout, sizeof ( timeout ) ); - } -# endif - if ( isNameService() ) { pSearchDest->setCircuit ( this ); } diff --git a/src/ca/client/tools/caput.c b/src/ca/client/tools/caput.c index f6dcc21cd..9c50cd9df 100644 --- a/src/ca/client/tools/caput.c +++ b/src/ca/client/tools/caput.c @@ -56,7 +56,7 @@ static epicsEventId epId; void usage (void) { - fprintf (stderr, "\nUsage: caput [options] \n" + fprintf (stderr, "\nUsage: caput [options] ...\n" " caput -a [options] ...\n\n" " -h: Help: Print this message\n" "Channel Access options:\n" @@ -71,9 +71,11 @@ void usage (void) " -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 requested values, then list of values\n" - " -S: Put string as an array of char (long string)\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" diff --git a/src/ca/client/udpiiu.cpp b/src/ca/client/udpiiu.cpp index 837a836d9..8bd3b5831 100644 --- a/src/ca/client/udpiiu.cpp +++ b/src/ca/client/udpiiu.cpp @@ -930,10 +930,12 @@ bool udpiiu::pushDatagramMsg ( epicsGuard < epicsMutex > & guard, caHdr * pbufmsg = ( caHdr * ) &this->xmitBuf[this->nBytesInXmitBuf]; *pbufmsg = msg; - memcpy ( pbufmsg + 1, pExt, extsize ); - if ( extsize != alignedExtSize ) { - char *pDest = (char *) ( pbufmsg + 1 ); - memset ( pDest + extsize, '\0', alignedExtSize - extsize ); + 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; diff --git a/src/ca/legacy/gdd/gdd.cc b/src/ca/legacy/gdd/gdd.cc index fd2b662c9..acb53da4e 100644 --- a/src/ca/legacy/gdd/gdd.cc +++ b/src/ca/legacy/gdd/gdd.cc @@ -1355,8 +1355,9 @@ gddStatus gdd::put ( const gdd * dd ) } aitUint32 srcAvailSize = srcElemCount - unusedSrcBelow; - if ( srcAvailSize > this->getBounds()->size() ) { - srcCopySize = this->getBounds()->size(); + aitUint32 destSize = this->getBounds()->size(); + if ( destSize > 0 && srcAvailSize > destSize ) { + srcCopySize = destSize; } else { srcCopySize = srcAvailSize; diff --git a/src/ca/legacy/gdd/gddAppTable.cc b/src/ca/legacy/gdd/gddAppTable.cc index ee3b82a7b..2315516fe 100644 --- a/src/ca/legacy/gdd/gddAppTable.cc +++ b/src/ca/legacy/gdd/gddAppTable.cc @@ -418,8 +418,10 @@ gddStatus gddApplicationTypeTable::freeDD(gdd* dd) } // fprintf(stderr,"Adding DD to free_list %d\n",app); + attr_table[group][app].sem.lock (); dd->setNext(attr_table[group][app].free_list); attr_table[group][app].free_list=dd; + attr_table[group][app].sem.unlock (); } else if (attr_table[group][app].type==gddApplicationTypeNormal) { diff --git a/src/ca/legacy/gdd/gddTest.cc b/src/ca/legacy/gdd/gddTest.cc index d36014018..881a55df9 100644 --- a/src/ca/legacy/gdd/gddTest.cc +++ b/src/ca/legacy/gdd/gddTest.cc @@ -308,7 +308,7 @@ void gdd::test() pdd->convertOffsetsToAddress(); pdd->dump(); pdd->unreference(); - delete buf; + delete [] buf; } #endif @@ -510,7 +510,7 @@ void gddContainer::test(void) fprintf(stderr,"=====RE-DUMP OF ORIGINAL CONTAINER:\n"); dump(); cdd1->unreference(); - delete buf; + delete [] buf; // test copy(), Dup(), copyInfo() fprintf(stderr,"=======CREATING TEST CONTAINER FOR *COPY* TEST:\n"); diff --git a/src/ca/legacy/pcas/example/directoryService/main.cc b/src/ca/legacy/pcas/example/directoryService/main.cc index cb8c43901..76f27e75d 100644 --- a/src/ca/legacy/pcas/example/directoryService/main.cc +++ b/src/ca/legacy/pcas/example/directoryService/main.cc @@ -170,7 +170,7 @@ static int parseDirectoryFP (FILE *pf, const char *pFileName) status = aToIPAddr (hostNameStr, 0u, &ipa); if (status) { - fprintf (pf, "Unknown host name=\"%s\" (or bad dotted ip addr) in \"%s\" with PV=\"%s\"?\n", + fprintf (stderr, "Unknown host name=\"%s\" (or bad dotted ip addr) in \"%s\" with PV=\"%s\"?\n", hostNameStr, pFileName, pvNameStr); return -1; } diff --git a/src/ca/legacy/pcas/generic/caHdrLargeArray.h b/src/ca/legacy/pcas/generic/caHdrLargeArray.h index 89b0d6d4b..7bda002e9 100644 --- a/src/ca/legacy/pcas/generic/caHdrLargeArray.h +++ b/src/ca/legacy/pcas/generic/caHdrLargeArray.h @@ -30,7 +30,7 @@ # include "shareLib.h" #endif -static const unsigned char CA_MINOR_PROTOCOL_REVISION = 12; +static const unsigned char CA_MINOR_PROTOCOL_REVISION = 13; typedef ca_uint32_t caResId; diff --git a/src/ca/legacy/pcas/generic/caServerI.h b/src/ca/legacy/pcas/generic/caServerI.h index 344f6393b..012233693 100644 --- a/src/ca/legacy/pcas/generic/caServerI.h +++ b/src/ca/legacy/pcas/generic/caServerI.h @@ -40,8 +40,13 @@ class casIntfOS; class casMonitor; class casChannelI; -caStatus convertContainerMemberToAtomic ( class gdd & dd, - aitUint32 appType, aitUint32 elemCount ); +caStatus convertContainerMemberToAtomic (class gdd & dd, + aitUint32 appType, aitUint32 requestedCount, aitUint32 nativeCount); + +// Keep the old signature for backward compatibility +inline caStatus convertContainerMemberToAtomic (class gdd & dd, + aitUint32 appType, aitUint32 elemCount) +{ return convertContainerMemberToAtomic(dd, appType, elemCount, elemCount); } class caServerI : public caServerIO, diff --git a/src/ca/legacy/pcas/generic/casChannelI.cc b/src/ca/legacy/pcas/generic/casChannelI.cc index 588f67d78..376f62f8c 100644 --- a/src/ca/legacy/pcas/generic/casChannelI.cc +++ b/src/ca/legacy/pcas/generic/casChannelI.cc @@ -21,6 +21,7 @@ casChannelI::casChannelI ( casCoreClient & clientIn, casChannel & chanIn, casPVI & pvIn, ca_uint32_t cidIn ) : privateForPV ( clientIn, *this ), pv ( pvIn ), + maxElem( pvIn.nativeCount() ), chan ( chanIn ), cid ( cidIn ), serverDeletePending ( false ), @@ -29,7 +30,7 @@ casChannelI::casChannelI ( casCoreClient & clientIn, } casChannelI::~casChannelI () -{ +{ this->privateForPV.client().removeFromEventQueue ( *this, this->accessRightsEvPending ); @@ -48,7 +49,7 @@ void casChannelI::uninstallFromPV ( casEventSys & eventSys ) this->privateForPV.removeSelfFromPV ( this->pv, dest ); while ( casMonitor * pMon = dest.get () ) { eventSys.prepareMonitorForDestroy ( *pMon ); - } + } } void casChannelI::show ( unsigned level ) const @@ -68,13 +69,13 @@ caStatus casChannelI::cbFunc ( { caStatus stat = S_cas_success; { - stat = this->privateForPV.client().accessRightsResponse ( + stat = this->privateForPV.client().accessRightsResponse ( clientGuard, this ); } - if ( stat == S_cas_success ) { - this->accessRightsEvPending = false; - } - return stat; + if ( stat == S_cas_success ) { + this->accessRightsEvPending = false; + } + return stat; } caStatus casChannelI::read ( const casCtx & ctx, gdd & prototype ) diff --git a/src/ca/legacy/pcas/generic/casChannelI.h b/src/ca/legacy/pcas/generic/casChannelI.h index 2c72e88e4..a0f74a1df 100644 --- a/src/ca/legacy/pcas/generic/casChannelI.h +++ b/src/ca/legacy/pcas/generic/casChannelI.h @@ -1,4 +1,3 @@ - /*************************************************************************\ * Copyright (c) 2002 The University of Chicago, as Operator of Argonne * National Laboratory. @@ -31,55 +30,62 @@ class casChannelI : public tsDLNode < casChannelI >, public: casChannelI ( casCoreClient & clientIn, casChannel & chanIn, casPVI & pvIn, ca_uint32_t cidIn ); - ~casChannelI (); + ~casChannelI (); void casChannelDestroyFromInterfaceNotify (); - const caResId getCID (); - const caResId getSID (); + const caResId getCID (); + const caResId getSID (); void uninstallFromPV ( casEventSys & eventSys ); void installIntoPV (); void installIO ( casAsyncIOI & ); void uninstallIO ( casAsyncIOI & ); void installMonitor ( casMonitor & mon ); casMonitor * removeMonitor ( ca_uint32_t clientIdIn ); - casPVI & getPVI () const; - void clearOutstandingReads (); - void postAccessRightsEvent (); + casPVI & getPVI () const; + void clearOutstandingReads (); + void postAccessRightsEvent (); const gddEnumStringTable & enumStringTable () const; - void setOwner ( const char * const pUserName, - const char * const pHostName ); - bool readAccess () const; + ca_uint32_t getMaxElem () const; + void setOwner ( const char * const pUserName, + const char * const pHostName ); + bool readAccess () const; bool writeAccess () const; - bool confirmationRequested () const; + bool confirmationRequested () const; caStatus read ( const casCtx & ctx, gdd & prototype ); caStatus write ( const casCtx & ctx, const gdd & value ); caStatus writeNotify ( const casCtx & ctx, const gdd & value ); - void show ( unsigned level ) const; + void show ( unsigned level ) const; private: chanIntfForPV privateForPV; - tsDLList < casAsyncIOI > ioList; - casPVI & pv; + tsDLList < casAsyncIOI > ioList; + casPVI & pv; + ca_uint32_t maxElem; casChannel & chan; - caResId cid; // client id + caResId cid; // client id bool serverDeletePending; - bool accessRightsEvPending; - //epicsShareFunc virtual void destroy (); - caStatus cbFunc ( + bool accessRightsEvPending; + //epicsShareFunc virtual void destroy (); + caStatus cbFunc ( casCoreClient &, epicsGuard < casClientMutex > &, epicsGuard < evSysMutex > & ); void postDestroyEvent (); - casChannelI ( const casChannelI & ); - casChannelI & operator = ( const casChannelI & ); + casChannelI ( const casChannelI & ); + casChannelI & operator = ( const casChannelI & ); }; inline casPVI & casChannelI::getPVI () const { - return this->pv; + return this->pv; +} + +inline ca_uint32_t casChannelI::getMaxElem () const +{ + return this->maxElem; } inline const caResId casChannelI::getCID () { - return this->cid; + return this->cid; } inline const caResId casChannelI::getSID () @@ -89,7 +95,7 @@ inline const caResId casChannelI::getSID () inline void casChannelI::postAccessRightsEvent () { - this->privateForPV.client().addToEventQueue ( *this, this->accessRightsEvPending ); + this->privateForPV.client().addToEventQueue ( *this, this->accessRightsEvPending ); } inline const gddEnumStringTable & casChannelI::enumStringTable () const @@ -108,7 +114,7 @@ inline void casChannelI::clearOutstandingReads () } inline void casChannelI::setOwner ( const char * const pUserName, - const char * const pHostName ) + const char * const pHostName ) { this->chan.setOwner ( pUserName, pHostName ); } diff --git a/src/ca/legacy/pcas/generic/casCtx.h b/src/ca/legacy/pcas/generic/casCtx.h index 706376e77..eab644685 100644 --- a/src/ca/legacy/pcas/generic/casCtx.h +++ b/src/ca/legacy/pcas/generic/casCtx.h @@ -18,6 +18,8 @@ #include "caHdrLargeArray.h" +class casStrmClient; + class casCtx { public: casCtx(); @@ -41,6 +43,7 @@ private: casChannelI * pChannel; casPVI * pPV; unsigned nAsyncIO; // checks for improper use of async io + friend class casStrmClient; }; inline const caHdrLargeArray * casCtx::getMsg() const diff --git a/src/ca/legacy/pcas/generic/casDGClient.cc b/src/ca/legacy/pcas/generic/casDGClient.cc index 4f1f41ecb..4fca74dbd 100644 --- a/src/ca/legacy/pcas/generic/casDGClient.cc +++ b/src/ca/legacy/pcas/generic/casDGClient.cc @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill @@ -30,9 +30,9 @@ casDGClient::pCASMsgHandler const casDGClient::msgHandlers[] = & casDGClient::uknownMessageAction, & casDGClient::uknownMessageAction, - & casDGClient::uknownMessageAction, - & casDGClient::searchAction, - & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + & casDGClient::searchAction, + & casDGClient::uknownMessageAction, & casDGClient::uknownMessageAction, & casDGClient::uknownMessageAction, @@ -64,9 +64,9 @@ casDGClient::pCASMsgHandler const casDGClient::msgHandlers[] = // casDGClient::casDGClient ( caServerI & serverIn, clientBufMemoryManager & mgrIn ) : casCoreClient ( serverIn ), - in ( *this, mgrIn, MAX_UDP_RECV + sizeof ( cadg ) ), + in ( *this, mgrIn, MAX_UDP_RECV + sizeof ( cadg ) ), out ( *this, mgrIn ), - seqNoOfReq ( 0 ), + seqNoOfReq ( 0 ), minor_version_number ( 0 ) { } @@ -92,15 +92,15 @@ void casDGClient::destroy() // void casDGClient::show (unsigned level) const { - printf ( "casDGClient at %p\n", + printf ( "casDGClient at %p\n", static_cast ( this ) ); if (level>=1u) { char buf[64]; this->hostName (buf, sizeof(buf)); printf ("Client Host=%s\n", buf); - this->casCoreClient::show ( level - 1u ); - this->in.show ( level - 1u ); - this->out.show ( level - 1u ); + this->casCoreClient::show ( level - 1u ); + this->in.show ( level - 1u ); + this->out.show ( level - 1u ); } } @@ -114,7 +114,7 @@ caStatus casDGClient::uknownMessageAction () char pHostName[64u]; this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) ); - caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(), + caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(), "bad request code=%u in DG\n", mp->m_cmmd ); return S_cas_badProtocol; @@ -135,7 +135,7 @@ caStatus casDGClient::searchAction() if ( mp->m_postsize <= 1 ) { char pHostName[64u]; this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) ); - caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(), + caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(), "empty PV name extension in UDP search request?\n" ); return S_cas_success; } @@ -143,19 +143,19 @@ caStatus casDGClient::searchAction() if ( pChanName[0] == '\0' ) { char pHostName[64u]; this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) ); - caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(), + caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(), "zero length PV name in UDP search request?\n" ); return S_cas_success; } // check for an unterminated string before calling server tool - // by searching backwards through the string (some early versions + // by searching backwards through the string (some early versions // of the client library might not be setting the pad bytes to nill) for ( unsigned i = mp->m_postsize-1; pChanName[i] != '\0'; i-- ) { if ( i <= 1 ) { char pHostName[64u]; this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) ); - caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(), + caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(), "unterminated PV name in UDP search request?\n" ); return S_cas_success; } @@ -183,7 +183,7 @@ caStatus casDGClient::searchAction() // ask the server tool if this PV exists // this->userStartedAsyncIO = false; - pvExistReturn pver = + pvExistReturn pver = this->getCAS()->pvExistTest ( this->ctx, this->lastRecvAddr, pChanName ); // @@ -193,7 +193,7 @@ caStatus casDGClient::searchAction() // if ( this->userStartedAsyncIO ) { if ( pver.getStatus() != pverAsyncCompletion ) { - errMessage (S_cas_badParameter, + errMessage (S_cas_badParameter, "- assuming asynch IO status from caServer::pvExistTest()"); } status = S_cas_success; @@ -212,13 +212,13 @@ caStatus casDGClient::searchAction() break; case pverAsyncCompletion: - errMessage (S_cas_badParameter, + errMessage (S_cas_badParameter, "- unexpected asynch IO status from caServer::pvExistTest() ignored"); status = S_cas_success; break; default: - errMessage (S_cas_badParameter, + errMessage (S_cas_badParameter, "- invalid return from caServer::pvExistTest() ignored"); status = S_cas_success; break; @@ -234,14 +234,14 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg, const pvExistReturn & retVal ) { caStatus status; - + if ( retVal.getStatus() != pverExistsHere ) { return S_cas_success; } - + // // starting with V4.1 the count field is used (abused) - // by the client to store the minor version number of + // by the client to store the minor version number of // the client. // // Old versions expect alloc of channel in response @@ -250,8 +250,8 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg, if ( !CA_V44(msg.m_count) ) { char pName[64u]; this->hostName (pName, sizeof (pName)); - errlogPrintf ( - "client \"%s\" using EPICS R3.11 CA connect protocol was ignored\n", + errlogPrintf ( + "client \"%s\" using EPICS R3.11 CA connect protocol was ignored\n", pName); // // old connect protocol was dropped when the @@ -264,19 +264,19 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg, } // - // cid field is abused to carry the IP + // cid field is abused to carry the IP // address in CA_V48 or higher // (this allows a CA servers to serve // as a directory service) // - // data type field is abused to carry the IP + // data type field is abused to carry the IP // port number here CA_V44 or higher // (this allows multiple CA servers on one // host) // ca_uint32_t serverAddr; ca_uint16_t serverPort; - if ( CA_V48( msg.m_count ) ) { + if ( CA_V48( msg.m_count ) ) { struct sockaddr_in ina; if ( retVal.addrIsValid() ) { caNetAddr addr = retVal.getAddr(); @@ -296,7 +296,7 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg, ina = addr.getSockIP(); // // We dont fill in the servers address here - // because the server was not bound to a particular + // because the server was not bound to a particular // interface, and we would need to waste CPU performing // the following steps to determine the interface that // will be used: @@ -318,24 +318,24 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg, serverAddr = ~0U; serverPort = ntohs ( inetAddr.sin_port ); } - + ca_uint16_t * pMinorVersion; epicsGuard < epicsMutex > guard ( this->mutex ); - status = this->out.copyInHeader ( CA_PROTO_SEARCH, - sizeof ( *pMinorVersion ), serverPort, 0, - serverAddr, msg.m_available, + status = this->out.copyInHeader ( CA_PROTO_SEARCH, + sizeof ( *pMinorVersion ), serverPort, 0, + serverAddr, msg.m_available, reinterpret_cast ( &pMinorVersion ) ); // // 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. + // This value is ignored by earlier clients. // if ( status == S_cas_success ) { AlignedWireRef < epicsUInt16 > tmp ( *pMinorVersion ); tmp = CA_MINOR_PROTOCOL_REVISION; this->out.commitMsg (); } - + return status; } @@ -346,15 +346,11 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg, // caStatus casDGClient::searchFailResponse ( const caHdrLargeArray * mp ) { - caStatus status; - epicsGuard < epicsMutex > guard ( this->mutex ); - status = this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0, + this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, 0 ); - if ( status == S_cas_success ) { - this->out.commitMsg (); - } + this->out.commitMsg (); return S_cas_success; } @@ -408,8 +404,8 @@ void casDGClient::sendBeacon ( ca_uint32_t beaconNumber ) // // casDGClient::xSend() // -outBufClient::flushCondition casDGClient::xSend ( char *pBufIn, - bufSizeT nBytesToSend, bufSizeT & nBytesSent ) +outBufClient::flushCondition casDGClient::xSend ( char *pBufIn, + bufSizeT nBytesToSend, bufSizeT & nBytesSent ) { bufSizeT totalBytes = 0; while ( totalBytes < nBytesToSend ) { @@ -422,7 +418,7 @@ outBufClient::flushCondition casDGClient::xSend ( char *pBufIn, unsigned sizeDG = pHdr->cadg_nBytes - sizeof ( *pHdr ); if ( pHdr->cadg_addr.isValid() ) { - outBufClient::flushCondition stat = + outBufClient::flushCondition stat = this->osdSend ( pDG, sizeDG, pHdr->cadg_addr ); if ( stat != outBufClient::flushProgress ) { break; @@ -459,7 +455,7 @@ inBufClient::fillCondition casDGClient::xRecv (char *pBufIn, bufSizeT nBytesToRe while (pAfter-pCurBuf >= static_cast(MAX_UDP_RECV+sizeof(cadg))) { pHdr = reinterpret_cast < cadg * > ( pCurBuf ); - stat = this->osdRecv ( reinterpret_cast < char * > ( pHdr + 1 ), + stat = this->osdRecv ( reinterpret_cast < char * > ( pHdr + 1 ), MAX_UDP_RECV, parm, nDGBytesRecv, pHdr->cadg_addr); if (stat==casFillProgress) { pHdr->cadg_nBytes = nDGBytesRecv + sizeof(*pHdr); @@ -491,7 +487,7 @@ inBufClient::fillCondition casDGClient::xRecv (char *pBufIn, bufSizeT nBytesToRe // this results in many small UDP frames which unfortunately // isnt particularly efficient // -caStatus casDGClient::asyncSearchResponse ( +caStatus casDGClient::asyncSearchResponse ( epicsGuard < casClientMutex > &, const caNetAddr & outAddr, const caHdrLargeArray & msg, const pvExistReturn & retVal, ca_uint16_t protocolRevision, ca_uint32_t sequenceNumber ) @@ -501,7 +497,7 @@ caStatus casDGClient::asyncSearchResponse ( } void * pRaw; - const outBufCtx outctx = this->out.pushCtx + const outBufCtx outctx = this->out.pushCtx ( sizeof(cadg), MAX_UDP_SEND, pRaw ); if ( outctx.pushResult() != outBufCtx::pushCtxSuccess ) { return S_cas_sendBlocked; @@ -565,9 +561,9 @@ caStatus casDGClient::processDG () // insert version header at the start of the reply message this->sendVersion (); - + cadg * pRespHdr = static_cast < cadg * > ( pRaw ); - + // // select the next DG in the input stream and start processing it // @@ -597,7 +593,7 @@ caStatus casDGClient::processDG () // a) it used all of the incoming DG or // b) it used all of the outgoing DG // - // In either case commit the DG to the protocol stream and + // In either case commit the DG to the protocol stream and // release the send lock // // if there are not additional messages passed the version header @@ -688,7 +684,7 @@ void casDGClient::hostName ( char *pBufIn, unsigned bufSizeIn ) const void casDGClient::sendVersion () { epicsGuard < epicsMutex > guard ( this->mutex ); - caStatus status = this->out.copyInHeader ( CA_PROTO_VERSION, 0, + caStatus status = this->out.copyInHeader ( CA_PROTO_VERSION, 0, 0, CA_MINOR_PROTOCOL_REVISION, 0, 0, 0 ); if ( ! status ) { this->out.commitMsg (); @@ -783,8 +779,8 @@ caStatus casDGClient::processMsg () msgTmp.m_available = AlignedWireRef < epicsUInt32 > ( smallHdr.m_available ); if ( payloadSize & 0x7 ) { - status = this->sendErr ( - & msgTmp, invalidResID, ECA_INTERNAL, + status = this->sendErr ( + & msgTmp, invalidResID, ECA_INTERNAL, "CAS: Datagram request wasn't 8 byte aligned" ); this->in.removeMsg ( bytesLeft ); break; @@ -793,7 +789,7 @@ caStatus casDGClient::processMsg () msgSize = hdrSize + payloadSize; if ( bytesLeft < msgSize ) { if ( msgSize > this->in.bufferSize() ) { - status = this->sendErr ( & msgTmp, invalidResID, ECA_TOLARGE, + status = this->sendErr ( & msgTmp, invalidResID, ECA_TOLARGE, "client's request didnt fit within the CA server's message buffer" ); this->in.removeMsg ( bytesLeft ); } @@ -805,7 +801,7 @@ caStatus casDGClient::processMsg () if ( this->getCAS().getDebugLevel() > 5u ) { char pHostName[64u]; this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) ); - caServerI::dumpMsg ( pHostName, "?", + caServerI::dumpMsg ( pHostName, "?", & msgTmp, rawMP + hdrSize, 0 ); } @@ -813,7 +809,7 @@ caStatus casDGClient::processMsg () // // Reset the context to the default - // (guarantees that previous message does not get mixed + // (guarantees that previous message does not get mixed // up with the current message) // this->ctx.setChannel ( NULL ); @@ -840,16 +836,16 @@ caStatus casDGClient::processMsg () } catch ( std::exception & except ) { this->in.removeMsg ( this->in.bytesPresent() ); - status = this->sendErr ( - this->ctx.getMsg(), invalidResID, ECA_INTERNAL, + this->sendErr ( + this->ctx.getMsg(), invalidResID, ECA_INTERNAL, "C++ exception \"%s\" in CA circuit server", except.what () ); status = S_cas_internal; } catch (...) { this->in.removeMsg ( this->in.bytesPresent() ); - status = this->sendErr ( - this->ctx.getMsg(), invalidResID, ECA_INTERNAL, + this->sendErr ( + this->ctx.getMsg(), invalidResID, ECA_INTERNAL, "unexpected C++ exception in CA datagram server" ); status = S_cas_internal; } @@ -860,37 +856,38 @@ caStatus casDGClient::processMsg () // // casDGClient::sendErr() // -caStatus casDGClient::sendErr ( const caHdrLargeArray *curp, +caStatus casDGClient::sendErr ( const caHdrLargeArray *curp, ca_uint32_t cid, const int reportedStatus, const char *pformat, ... ) { - unsigned stringSize; - char msgBuf[1024]; /* allocate plenty of space for the message string */ - if ( pformat ) { - va_list args; - va_start ( args, pformat ); - int status = vsprintf ( msgBuf, pformat, args ); - if ( status < 0 ) { - errPrintf (S_cas_internal, __FILE__, __LINE__, - "bad sendErr(%s)", pformat); - stringSize = 0u; - } - else { - stringSize = 1u + (unsigned) status; - } - } - else { + unsigned stringSize; + char msgBuf[1024]; /* allocate plenty of space for the message string */ + if ( pformat ) { + va_list args; + va_start ( args, pformat ); + int status = vsprintf ( msgBuf, pformat, args ); + if ( status < 0 ) { + errPrintf (S_cas_internal, __FILE__, __LINE__, + "bad sendErr(%s)", pformat); stringSize = 0u; } + else { + stringSize = 1u + (unsigned) status; + } + va_end ( args ); + } + else { + stringSize = 0u; + } unsigned hdrSize = sizeof ( caHdr ); - if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && + if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && CA_V49( this->minor_version_number ) ) { hdrSize += 2 * sizeof ( ca_uint32_t ); } caHdr * pReqOut; epicsGuard < epicsMutex > guard ( this->mutex ); - caStatus status = this->out.copyInHeader ( CA_PROTO_ERROR, + caStatus status = this->out.copyInHeader ( CA_PROTO_ERROR, hdrSize + stringSize, 0, 0, cid, reportedStatus, reinterpret_cast ( & pReqOut ) ); if ( ! status ) { @@ -900,7 +897,7 @@ caStatus casDGClient::sendErr ( const caHdrLargeArray *curp, * copy back the request protocol * (in network byte order) */ - if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && + if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && CA_V49( this->minor_version_number ) ) { ca_uint32_t *pLW = ( ca_uint32_t * ) ( pReqOut + 1 ); pReqOut->m_cmmd = htons ( curp->m_cmmd ); @@ -945,7 +942,7 @@ caStatus casDGClient::echoAction () void * pPayloadOut; epicsGuard < epicsMutex > guard ( this->mutex ); - caStatus status = this->out.copyInHeader ( mp->m_cmmd, mp->m_postsize, + caStatus status = this->out.copyInHeader ( mp->m_cmmd, mp->m_postsize, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, & pPayloadOut ); if ( ! status ) { diff --git a/src/ca/legacy/pcas/generic/casStrmClient.cc b/src/ca/legacy/pcas/generic/casStrmClient.cc index 6b1de3629..ff12e889e 100644 --- a/src/ca/legacy/pcas/generic/casStrmClient.cc +++ b/src/ca/legacy/pcas/generic/casStrmClient.cc @@ -388,14 +388,12 @@ caStatus casStrmClient::echoAction ( epicsGuard < casClientMutex > & ) // // casStrmClient::verifyRequest() // -caStatus casStrmClient::verifyRequest ( casChannelI * & pChan ) +caStatus casStrmClient::verifyRequest (casChannelI * & pChan , bool allowdyn) { - const caHdrLargeArray * mp = this->ctx.getMsg(); - // // channel exists for this resource id ? // - chronIntId tmpId ( mp->m_cid ); + chronIntId tmpId ( ctx.msg.m_cid ); pChan = this->chanTable.lookup ( tmpId ); if ( ! pChan ) { return ECA_BADCHID; @@ -404,14 +402,15 @@ caStatus casStrmClient::verifyRequest ( casChannelI * & pChan ) // // data type out of range ? // - if ( mp->m_dataType > ((unsigned)LAST_BUFFER_TYPE) ) { + if ( ctx.msg.m_dataType > ((unsigned)LAST_BUFFER_TYPE) ) { return ECA_BADTYPE; } // // element count out of range ? // - if ( mp->m_count > pChan->getPVI().nativeCount() || mp->m_count == 0u ) { + if ( ctx.msg.m_count > pChan->getMaxElem() || + ( !allowdyn && ctx.msg.m_count == 0u ) ) { return ECA_BADCOUNT; } @@ -444,7 +443,7 @@ caStatus casStrmClient::readAction ( epicsGuard < casClientMutex > & guard ) casChannelI * pChan; { - caStatus status = this->verifyRequest ( pChan ); + caStatus status = this->verifyRequest ( pChan, CA_V413 ( this->minor_version_number ) ); if ( status != ECA_NORMAL ) { if ( pChan ) { return this->sendErr ( guard, mp, pChan->getCID(), @@ -531,11 +530,15 @@ caStatus casStrmClient::readResponse ( epicsGuard < casClientMutex > & guard, pChan->getCID(), status, ECA_GETFAIL ); } + ca_uint32_t count = (msg.m_count == 0) ? + (ca_uint32_t)desc.getDataSizeElements() : + msg.m_count; + void * pPayload; { - unsigned payloadSize = dbr_size_n ( msg.m_dataType, msg.m_count ); + unsigned payloadSize = dbr_size_n ( msg.m_dataType, count ); caStatus localStatus = this->out.copyInHeader ( msg.m_cmmd, payloadSize, - msg.m_dataType, msg.m_count, pChan->getCID (), + msg.m_dataType, count, pChan->getCID (), msg.m_available, & pPayload ); if ( localStatus ) { if ( localStatus==S_cas_hugeRequest ) { @@ -551,21 +554,21 @@ caStatus casStrmClient::readResponse ( epicsGuard < casClientMutex > & guard, // (places the data in network format) // int mapDBRStatus = gddMapDbr[msg.m_dataType].conv_dbr( - pPayload, msg.m_count, desc, pChan->enumStringTable() ); + pPayload, count, desc, pChan->enumStringTable() ); if ( mapDBRStatus < 0 ) { desc.dump (); errPrintf ( S_cas_badBounds, __FILE__, __LINE__, "- get with PV=%s type=%u count=%u", - pChan->getPVI().getName(), msg.m_dataType, msg.m_count ); + pChan->getPVI().getName(), msg.m_dataType, count ); return this->sendErrWithEpicsStatus ( guard, & msg, pChan->getCID(), S_cas_badBounds, ECA_GETFAIL ); } int cacStatus = caNetConvert ( - msg.m_dataType, pPayload, pPayload, true, msg.m_count ); + msg.m_dataType, pPayload, pPayload, true, count ); if ( cacStatus != ECA_NORMAL ) { return this->sendErrWithEpicsStatus ( guard, & msg, pChan->getCID(), S_cas_internal, cacStatus ); } - if ( msg.m_dataType == DBR_STRING && msg.m_count == 1u ) { + if ( msg.m_dataType == DBR_STRING && count == 1u ) { unsigned reducedPayloadSize = strlen ( static_cast < char * > ( pPayload ) ) + 1u; this->out.commitMsg ( reducedPayloadSize ); } @@ -585,7 +588,7 @@ caStatus casStrmClient::readNotifyAction ( epicsGuard < casClientMutex > & guard casChannelI * pChan; { - caStatus status = this->verifyRequest ( pChan ); + caStatus status = this->verifyRequest ( pChan, CA_V413 ( this->minor_version_number ) ); if ( status != ECA_NORMAL ) { return this->readNotifyFailureResponse ( guard, * mp, status ); } @@ -656,11 +659,15 @@ caStatus casStrmClient::readNotifyResponse ( epicsGuard < casClientMutex > & gua return ecaStatus; } + ca_uint32_t count = (msg.m_count == 0) ? + (ca_uint32_t)desc.getDataSizeElements() : + msg.m_count; + void *pPayload; { - unsigned size = dbr_size_n ( msg.m_dataType, msg.m_count ); + unsigned size = dbr_size_n ( msg.m_dataType, count ); caStatus status = this->out.copyInHeader ( msg.m_cmmd, size, - msg.m_dataType, msg.m_count, ECA_NORMAL, + msg.m_dataType, count, ECA_NORMAL, msg.m_available, & pPayload ); if ( status ) { if ( status == S_cas_hugeRequest ) { @@ -675,23 +682,23 @@ caStatus casStrmClient::readNotifyResponse ( epicsGuard < casClientMutex > & gua // convert gdd to db_access type // int mapDBRStatus = gddMapDbr[msg.m_dataType].conv_dbr ( pPayload, - msg.m_count, desc, pChan->enumStringTable() ); + count, desc, pChan->enumStringTable() ); if ( mapDBRStatus < 0 ) { desc.dump(); errPrintf ( S_cas_badBounds, __FILE__, __LINE__, "- get notify with PV=%s type=%u count=%u", - pChan->getPVI().getName(), msg.m_dataType, msg.m_count ); + pChan->getPVI().getName(), msg.m_dataType, count ); return this->readNotifyFailureResponse ( guard, msg, ECA_NOCONVERT ); } int cacStatus = caNetConvert ( - msg.m_dataType, pPayload, pPayload, true, msg.m_count ); + msg.m_dataType, pPayload, pPayload, true, count ); if ( cacStatus != ECA_NORMAL ) { return this->sendErrWithEpicsStatus ( guard, & msg, pChan->getCID(), S_cas_internal, cacStatus ); } - if ( msg.m_dataType == DBR_STRING && msg.m_count == 1u ) { + if ( msg.m_dataType == DBR_STRING && count == 1u ) { unsigned reducedPayloadSize = strlen ( static_cast < char * > ( pPayload ) ) + 1u; this->out.commitMsg ( reducedPayloadSize ); } @@ -727,8 +734,8 @@ caStatus casStrmClient::readNotifyFailureResponse ( // to be more efficent if it discovers that the source has less data // than the destination) // -caStatus convertContainerMemberToAtomic ( gdd & dd, - aitUint32 appType, aitUint32 elemCount ) +caStatus convertContainerMemberToAtomic ( gdd & dd, + aitUint32 appType, aitUint32 requestedCount, aitUint32 nativeCount ) { gdd * pVal; if ( dd.isContainer() ) { @@ -755,13 +762,13 @@ caStatus convertContainerMemberToAtomic ( gdd & dd, return S_cas_badType; } - if ( elemCount <= 1 ) { + if ( nativeCount <= 1 ) { return S_cas_success; } // convert to atomic gddBounds bds; - bds.setSize ( elemCount ); + bds.setSize ( requestedCount ); bds.setFirst ( 0u ); pVal->setDimension ( 1u, & bds ); return S_cas_success; @@ -770,9 +777,9 @@ caStatus convertContainerMemberToAtomic ( gdd & dd, // // createDBRDD () // -static caStatus createDBRDD ( unsigned dbrType, - unsigned elemCount, gdd * & pDD ) -{ +static caStatus createDBRDD ( unsigned dbrType, + unsigned requestedCount, unsigned nativeCount, gdd * & pDD ) +{ /* * DBR type has already been checked, but it is possible * that "gddDbrToAit" will not track with changes in @@ -799,7 +806,7 @@ static caStatus createDBRDD ( unsigned dbrType, // fix the value element count caStatus status = convertContainerMemberToAtomic ( - *pDescRet, gddAppType_value, elemCount ); + *pDescRet, gddAppType_value, requestedCount, nativeCount ); if ( status != S_cas_success ) { pDescRet->unreference (); return status; @@ -849,11 +856,27 @@ caStatus casStrmClient::monitorResponse ( casChannelI & chan, const caHdrLargeArray & msg, const gdd & desc, const caStatus completionStatus ) { + aitUint32 elementCount = 0; + if (desc.isContainer()) { + aitUint32 index; + int gdds = gddApplicationTypeTable::app_table.mapAppToIndex + ( desc.applicationType(), gddAppType_value, index ); + if ( gdds ) { + return S_cas_badType; + } + elementCount = desc.getDD(index)->getDataSizeElements(); + } else { + elementCount = desc.getDataSizeElements(); + } + ca_uint32_t count = (msg.m_count == 0) ? + (ca_uint32_t)elementCount : + msg.m_count; + void * pPayload = 0; { - ca_uint32_t size = dbr_size_n ( msg.m_dataType, msg.m_count ); + ca_uint32_t size = dbr_size_n ( msg.m_dataType, count ); caStatus status = out.copyInHeader ( msg.m_cmmd, size, - msg.m_dataType, msg.m_count, ECA_NORMAL, + msg.m_dataType, count, ECA_NORMAL, msg.m_available, & pPayload ); if ( status ) { if ( status == S_cas_hugeRequest ) { @@ -871,7 +894,8 @@ caStatus casStrmClient::monitorResponse ( gdd * pDBRDD = 0; if ( completionStatus == S_cas_success ) { - caStatus status = createDBRDD ( msg.m_dataType, msg.m_count, pDBRDD ); + caStatus status = createDBRDD ( msg.m_dataType, count, + chan.getMaxElem(), pDBRDD ); if ( status != S_cas_success ) { caStatus ecaStatus; if ( status == S_cas_badType ) { @@ -892,7 +916,7 @@ caStatus casStrmClient::monitorResponse ( pDBRDD->unreference (); errPrintf ( S_cas_noConvert, __FILE__, __LINE__, "no conversion between event app type=%d and DBR type=%d Element count=%d", - desc.applicationType (), msg.m_dataType, msg.m_count); + desc.applicationType (), msg.m_dataType, count); return monitorFailureResponse ( guard, msg, ECA_NOCONVERT ); } } @@ -915,14 +939,14 @@ caStatus casStrmClient::monitorResponse ( } int mapDBRStatus = gddMapDbr[msg.m_dataType].conv_dbr ( - pPayload, msg.m_count, *pDBRDD, chan.enumStringTable() ); + pPayload, count, *pDBRDD, chan.enumStringTable() ); if ( mapDBRStatus < 0 ) { pDBRDD->unreference (); return monitorFailureResponse ( guard, msg, ECA_NOCONVERT ); } int cacStatus = caNetConvert ( - msg.m_dataType, pPayload, pPayload, true, msg.m_count ); + msg.m_dataType, pPayload, pPayload, true, count ); if ( cacStatus != ECA_NORMAL ) { pDBRDD->unreference (); return this->sendErrWithEpicsStatus ( @@ -932,7 +956,7 @@ caStatus casStrmClient::monitorResponse ( // // force string message size to be the true size // - if ( msg.m_dataType == DBR_STRING && msg.m_count == 1u ) { + if ( msg.m_dataType == DBR_STRING && count == 1u ) { ca_uint32_t reducedPayloadSize = strlen ( static_cast < char * > ( pPayload ) ) + 1u; this->out.commitMsg ( reducedPayloadSize ); } @@ -1842,7 +1866,7 @@ caStatus casStrmClient::privateCreateChanResponse ( // the protocol buffer. // assert ( nativeTypeDBR <= 0xffff ); - aitIndex nativeCount = chan.getPVI().nativeCount(); + aitIndex nativeCount = chan.getMaxElem(); assert ( nativeCount <= 0xffffffff ); assert ( hdr.m_cid == chan.getCID() ); status = this->out.copyInHeader ( CA_PROTO_CREATE_CHAN, 0, @@ -1940,7 +1964,7 @@ caStatus casStrmClient::eventAddAction ( casChannelI *pciu; { - caStatus status = casStrmClient::verifyRequest ( pciu ); + caStatus status = casStrmClient::verifyRequest ( pciu, CA_V413 ( this->minor_version_number ) ); if ( status != ECA_NORMAL ) { if ( pciu ) { return this->sendErr ( guard, mp, @@ -2601,8 +2625,8 @@ caStatus casStrmClient::read () { gdd * pDD = 0; - caStatus status = createDBRDD ( pHdr->m_dataType, - pHdr->m_count, pDD ); + caStatus status = createDBRDD ( pHdr->m_dataType, pHdr->m_count, + this->ctx.getChannel()->getMaxElem(), pDD ); if ( status != S_cas_success ) { return status; } @@ -2749,6 +2773,7 @@ caStatus casStrmClient::sendErr ( epicsGuard &, else { stringSize = 1u + (unsigned) status; } + va_end ( args ); } else { stringSize = 0u; diff --git a/src/ca/legacy/pcas/generic/casStrmClient.h b/src/ca/legacy/pcas/generic/casStrmClient.h index 3f6c0a51b..0fdd36bb4 100644 --- a/src/ca/legacy/pcas/generic/casStrmClient.h +++ b/src/ca/legacy/pcas/generic/casStrmClient.h @@ -69,7 +69,7 @@ private: bool responseIsPending; caStatus createChannel ( const char * pName ); - caStatus verifyRequest ( casChannelI * & pChan ); + caStatus verifyRequest ( casChannelI * & pChan, bool allowdyn = false ); typedef caStatus ( casStrmClient :: * pCASMsgHandler ) ( epicsGuard < casClientMutex > & ); static pCASMsgHandler const msgHandlers[CA_PROTO_LAST_CMMD+1u]; diff --git a/src/ca/legacy/pcas/io/bsdSocket/casIntfIO.cc b/src/ca/legacy/pcas/io/bsdSocket/casIntfIO.cc index 41ce6de8d..2c2cb5bef 100644 --- a/src/ca/legacy/pcas/io/bsdSocket/casIntfIO.cc +++ b/src/ca/legacy/pcas/io/bsdSocket/casIntfIO.cc @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ // @@ -32,7 +32,7 @@ const unsigned caServerConnectPendQueueSize = 5u; // // casIntfIO::casIntfIO() // -casIntfIO::casIntfIO ( const caNetAddr & addrIn ) : +casIntfIO::casIntfIO ( const caNetAddr & addrIn ) : sock ( INVALID_SOCKET ), addr ( addrIn.getSockIP() ) { @@ -40,80 +40,79 @@ casIntfIO::casIntfIO ( const caNetAddr & addrIn ) : osiSocklen_t addrSize; bool portChange; - if ( ! osiSockAttach () ) { - throw S_cas_internal; - } + if ( ! osiSockAttach () ) { + throw S_cas_internal; + } - /* - * Setup the server socket - */ - this->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ); - if (this->sock==INVALID_SOCKET) { + /* + * Setup the server socket + */ + this->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ); + if (this->sock == INVALID_SOCKET) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - printf ( "No socket error was %s\n", sockErrBuf ); - throw S_cas_noFD; - } + printf ( "No socket error was %s\n", sockErrBuf ); + throw S_cas_noFD; + } epicsSocketEnableAddressReuseDuringTimeWaitState ( this->sock ); - status = bind ( this->sock, - reinterpret_cast (&this->addr), - sizeof(this->addr) ); - if (status<0) { - if (SOCKERRNO == SOCK_EADDRINUSE) { - // - // enable assignment of a default port - // (so the getsockname() call below will - // work correctly) - // - this->addr.sin_port = ntohs (0); - status = bind( + status = bind ( this->sock, + reinterpret_cast (&this->addr), + sizeof(this->addr) ); + if (status < 0) { + if (SOCKERRNO == SOCK_EADDRINUSE || + SOCKERRNO == SOCK_EACCES) { + // + // enable assignment of a default port + // (so the getsockname() call below will + // work correctly) + // + this->addr.sin_port = ntohs (0); + status = bind( this->sock, reinterpret_cast (&this->addr), sizeof(this->addr) ); - } - if (status<0) { + } + if (status < 0) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - char buf[64]; - ipAddrToA (&this->addr, buf, sizeof(buf)); - errPrintf ( S_cas_bindFail, - __FILE__, __LINE__, - "- bind TCP IP addr=%s failed because %s", - buf, sockErrBuf ); + char buf[64]; + ipAddrToA (&this->addr, buf, sizeof(buf)); + errlogPrintf ( "CAS: Socket bind TCP to %s failed with %s", + buf, sockErrBuf ); epicsSocketDestroy (this->sock); - throw S_cas_bindFail; - } + throw S_cas_bindFail; + } portChange = true; - } + } else { portChange = false; } - addrSize = ( osiSocklen_t ) sizeof (this->addr); - status = getsockname ( - this->sock, - reinterpret_cast ( &this->addr ), + addrSize = ( osiSocklen_t ) sizeof (this->addr); + status = getsockname ( + this->sock, + reinterpret_cast ( &this->addr ), &addrSize ); - if (status) { + if (status) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf ( "CAS: getsockname() error %s\n", - sockErrBuf ); + errlogPrintf ( "CAS: getsockname() error %s\n", + sockErrBuf ); epicsSocketDestroy (this->sock); - throw S_cas_internal; - } + throw S_cas_internal; + } - // - // be sure of this now so that we can fetch the IP - // address and port number later - // + // + // be sure of this now so that we can fetch the IP + // address and port number later + // assert (this->addr.sin_family == AF_INET); if ( portChange ) { errlogPrintf ( "cas warning: Configured TCP port was unavailable.\n"); - errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n", + errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n", ntohs (this->addr.sin_port) ); errlogPrintf ( "cas warning: but now two or more servers share the same UDP port.\n"); errlogPrintf ( "cas warning: Depending on your IP kernel this server may not be\n" ); @@ -121,12 +120,12 @@ casIntfIO::casIntfIO ( const caNetAddr & addrIn ) : } status = listen(this->sock, caServerConnectPendQueueSize); - if(status < 0) { + if (status < 0) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf ( "CAS: listen() error %s\n", sockErrBuf ); + errlogPrintf ( "CAS: listen() error %s\n", sockErrBuf ); epicsSocketDestroy (this->sock); - throw S_cas_internal; + throw S_cas_internal; } } @@ -135,17 +134,17 @@ casIntfIO::casIntfIO ( const caNetAddr & addrIn ) : // casIntfIO::~casIntfIO() { - if (this->sock != INVALID_SOCKET) { - epicsSocketDestroy (this->sock); - } + if (this->sock != INVALID_SOCKET) { + epicsSocketDestroy (this->sock); + } - osiSockRelease (); + osiSockRelease (); } // // newStreamIO::newStreamClient() // -casStreamOS *casIntfIO::newStreamClient ( caServerI & cas, +casStreamOS *casIntfIO::newStreamClient ( caServerI & cas, clientBufMemoryManager & bufMgr ) const { static bool oneMsgFlag = false; @@ -175,14 +174,14 @@ casStreamOS *casIntfIO::newStreamClient ( caServerI & cas, args.sock = newSock; casStreamOS * pOS = new casStreamOS ( cas, bufMgr, args ); if ( ! pOS ) { - errMessage ( S_cas_noMemory, + errMessage ( S_cas_noMemory, "unable to create data structures for a new client" ); epicsSocketDestroy ( newSock ); } else { if ( cas.getDebugLevel() > 0u ) { char pName[64u]; - + pOS->hostName ( pName, sizeof ( pName ) ); errlogPrintf ( "CAS: allocated client object for \"%s\"\n", pName ); } @@ -197,7 +196,7 @@ void casIntfIO::setNonBlocking() { int status; osiSockIoctl_t yes = true; - + status = socket_ioctl(this->sock, FIONBIO, &yes); if ( status < 0 ) { char sockErrBuf[64]; diff --git a/src/ioc/db/dbCaTest.c b/src/ioc/db/dbCaTest.c index 450eb9463..18ef393ca 100644 --- a/src/ioc/db/dbCaTest.c +++ b/src/ioc/db/dbCaTest.c @@ -130,8 +130,8 @@ long dbcar(char *precordname, int level) precord->name, pdbFldDes->name, plink->value.pv_link.pvname, - pca->nDisconnect, - pca->nNoWrite); + pca ? pca->nDisconnect : 0, + pca ? pca->nNoWrite : 0); } } } diff --git a/src/ioc/db/dbConvert.c b/src/ioc/db/dbConvert.c index 8d3a54c82..0fad81d5a 100644 --- a/src/ioc/db/dbConvert.c +++ b/src/ioc/db/dbConvert.c @@ -3940,10 +3940,9 @@ static long putFloatString( char *pdest = (char *) paddr->pfield; long status = 0; int precision = 6; - struct rset *prset = 0; + struct rset *prset = dbGetRset(paddr); 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) { @@ -4154,10 +4153,9 @@ static long putDoubleString( char *pdest = (char *) paddr->pfield; long status = 0; int precision = 6; - struct rset *prset = 0; + struct rset *prset = dbGetRset(paddr); 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) { diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index 180118091..9f7df4c17 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -159,8 +159,11 @@ void scanStop(void) interruptAccept = FALSE; for (i = 0; i < nPeriodic; i++) { - papPeriodic[i]->scanCtl = ctlExit; - epicsEventSignal(papPeriodic[i]->loopEvent); + periodic_scan_list *ppsl = papPeriodic[i]; + + if (!ppsl) continue; + ppsl->scanCtl = ctlExit; + epicsEventSignal(ppsl->loopEvent); epicsEventWait(startStopEvent); } @@ -205,16 +208,24 @@ void scanRun(void) interruptAccept = TRUE; scanCtl = ctlRun; - for (i = 0; i < nPeriodic; i++) - papPeriodic[i]->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) - papPeriodic[i]->scanCtl = ctlPause; + for (i = nPeriodic - 1; i >= 0; --i) { + periodic_scan_list *ppsl = papPeriodic[i]; + + if (!ppsl) continue; + ppsl->scanCtl = ctlPause; + } scanCtl = ctlPause; interruptAccept = FALSE; @@ -286,9 +297,11 @@ void scanAdd(struct dbCommon *precord) } addToList(precord, &piosh->iosl[prio].scan_list); } else if (scan >= SCAN_1ST_PERIODIC) { - addToList(precord, &papPeriodic[scan - SCAN_1ST_PERIODIC]->scan_list); + periodic_scan_list *ppsl = papPeriodic[scan - SCAN_1ST_PERIODIC]; + + if (ppsl) + addToList(precord, &ppsl->scan_list); } - return; } void scanDelete(struct dbCommon *precord) @@ -352,28 +365,48 @@ void scanDelete(struct dbCommon *precord) } deleteFromList(precord, &piosh->iosl[prio].scan_list); } else if (scan >= SCAN_1ST_PERIODIC) { - deleteFromList(precord, &papPeriodic[scan - SCAN_1ST_PERIODIC]->scan_list); + periodic_scan_list *ppsl = papPeriodic[scan - SCAN_1ST_PERIODIC]; + + if (ppsl) + deleteFromList(precord, &ppsl->scan_list); } - return; } double scanPeriod(int scan) { + periodic_scan_list *ppsl; + scan -= SCAN_1ST_PERIODIC; if (scan < 0 || scan >= nPeriodic) return 0.0; - return papPeriodic[scan]->period; + ppsl = papPeriodic[scan]; + return ppsl ? ppsl->period : 0.0; } - -int scanppl(double period) /* print periodic list */ + +int scanppl(double period) /* print periodic scan list(s) */ { - periodic_scan_list *ppsl; + 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++) { - ppsl = papPeriodic[i]; - if (ppsl == NULL) continue; - if (period > 0.0 && (fabs(period - ppsl->period) >.05)) continue; + 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); @@ -741,7 +774,7 @@ static void periodicTask(void *arg) if (++overruns >= 10 && epicsTimeDiffInSeconds(&now, &reported) > report_delay) { errlogPrintf("\ndbScan warning from '%s' scan thread:\n" - "\tScan processing averages %.2f seconds (%.2f .. %.2f).\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, @@ -788,12 +821,8 @@ static void initPeriodic(void) char *unit; int status = epicsParseDouble(choice, &number, &unit); - ppsl->scan_list.lock = epicsMutexMustCreate(); - ellInit(&ppsl->scan_list.list); - ppsl->name = choice; - if (status || number == 0) { + if (status || number <= 0) { errlogPrintf("initPeriodic: Bad menuScan choice '%s'\n", choice); - ppsl->period = i; } else if (!*unit || !epicsStrCaseCmp(unit, "second") || @@ -814,16 +843,24 @@ static void initPeriodic(void) } else { errlogPrintf("initPeriodic: Bad menuScan choice '%s'\n", choice); - ppsl->period = i; } + 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); } - ppsl->scanCtl = ctlPause; - ppsl->loopEvent = epicsEventMustCreate(epicsEventEmpty); papPeriodic[i] = ppsl; } @@ -835,6 +872,8 @@ static void deletePeriodic(void) 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); @@ -847,10 +886,11 @@ static void deletePeriodic(void) static void spawnPeriodic(int ind) { - periodic_scan_list *ppsl; + periodic_scan_list *ppsl = papPeriodic[ind]; char taskName[20]; - ppsl = papPeriodic[ind]; + if (!ppsl) return; + sprintf(taskName, "scan-%g", ppsl->period); periodicTaskId[ind] = epicsThreadCreate( taskName, epicsThreadPriorityScanLow + ind, @@ -989,13 +1029,13 @@ static void addToList(struct dbCommon *precord, scan_list *psl) pse->precord = precord; } pse->pscan_list = psl; - ptemp = (scan_element *)ellFirst(&psl->list); + ptemp = (scan_element *)ellLast(&psl->list); while (ptemp) { - if (ptemp->precord->phas > precord->phas) { - ellInsert(&psl->list, ellPrevious(&ptemp->node), &pse->node); + if (ptemp->precord->phas <= precord->phas) { + ellInsert(&psl->list, &ptemp->node, &pse->node); break; } - ptemp = (scan_element *)ellNext(&ptemp->node); + ptemp = (scan_element *)ellPrevious(&ptemp->node); } if (ptemp == NULL) ellAdd(&psl->list, (void *)pse); psl->modified = TRUE; diff --git a/src/ioc/db/db_test.c b/src/ioc/db/db_test.c index b011c6819..8d7ad31b1 100644 --- a/src/ioc/db/db_test.c +++ b/src/ioc/db/db_test.c @@ -172,6 +172,7 @@ int pft(const char *pname, const char *pvalue) ca_dump_dbr(DBR_ENUM,1, buffer); } printf("\n"); + dbChannelDelete(chan); return (0); } @@ -231,6 +232,7 @@ int tpn(const char *pname, const char *pvalue) ppn = calloc(1, sizeof(processNotify)); if (!ppn) { printf("calloc failed\n"); + dbChannelDelete(chan); return -1; } ppn->requestType = putProcessRequest; @@ -241,6 +243,8 @@ int tpn(const char *pname, const char *pvalue) ptpnInfo = calloc(1, sizeof(tpnInfo)); if (!ptpnInfo) { printf("calloc failed\n"); + free(ppn); + dbChannelDelete(chan); return -1; } ptpnInfo->ppn = ppn; diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index 4b12644ea..becd876e8 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -541,8 +541,11 @@ MAIN(chfPluginTest) dbChannel *pch; db_field_log *pfl; -#if defined(WIN32) && (!defined(_MINGW) || __MSVCRT_VERSION__ >= 0x0800) +#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); diff --git a/src/ioc/dbStatic/dbBase.h b/src/ioc/dbStatic/dbBase.h index 55a06ebb8..e58afd8ad 100644 --- a/src/ioc/dbStatic/dbBase.h +++ b/src/ioc/dbStatic/dbBase.h @@ -133,25 +133,25 @@ typedef struct dbVariableDef { }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*/ - struct rset *prset; - int rec_size; /*record size in bytes */ + 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*/ + struct rset *prset; + int rec_size; /*record size in bytes */ }dbRecordType; struct dbPvd; /* Contents private to dbPvdLib code */ diff --git a/src/ioc/dbStatic/dbLexRoutines.c b/src/ioc/dbStatic/dbLexRoutines.c index c1444269c..12038a6ec 100644 --- a/src/ioc/dbStatic/dbLexRoutines.c +++ b/src/ioc/dbStatic/dbLexRoutines.c @@ -50,6 +50,9 @@ 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); @@ -194,7 +197,16 @@ static void freeInputFileList(void) 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) { @@ -239,24 +251,25 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, macSuppressWarning(macHandle,dbQuietMacroWarnings); } pinputFile = dbCalloc(1,sizeof(inputFile)); - if(filename) { - pinputFile->filename = macEnvExpand(filename); + if (filename) { + pinputFile->filename = macEnvExpand(filename); } - if(!fp) { - FILE *fp1; + 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((void *)pinputFile->filename); - free((void *)pinputFile); + 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; + } + pinputFile->fp = fp1; } else { - pinputFile->fp = fp; + pinputFile->fp = fp; } pinputFile->line_num = 0; pinputFileNow = pinputFile; @@ -294,6 +307,15 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, 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); diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index f4e5b6317..91c82b702 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -1418,7 +1418,6 @@ long dbCreateRecord(DBENTRY *pdbentry,const char *precordName) dbFldDes *pdbFldDes; PVDENTRY *ppvd; ELLLIST *preclist = NULL; - dbRecordNode *precnode = NULL; dbRecordNode *pNewRecNode = NULL; long status = 0; @@ -1426,7 +1425,7 @@ long dbCreateRecord(DBENTRY *pdbentry,const char *precordName) /*Get size of NAME field*/ pdbFldDes = precordType->papFldDes[0]; if(!pdbFldDes || (strcmp(pdbFldDes->name,"NAME")!=0)) - return(S_dbLib_nameLength); + return(S_dbLib_nameLength); if((int)strlen(precordName)>=pdbFldDes->size) return(S_dbLib_nameLength); /* clear callers entry */ zeroDbentry(pdbentry); @@ -1441,18 +1440,7 @@ long dbCreateRecord(DBENTRY *pdbentry,const char *precordName) if((status = dbAllocRecord(pdbentry,precordName))) return(status); pNewRecNode->recordname = dbRecordName(pdbentry); ellInit(&pNewRecNode->infoList); - /* install record node in list in sorted postion */ - status = dbFirstRecord(pdbentry); - while(status==0) { - if(strcmp(precordName,dbGetRecordName(pdbentry)) < 0) break; - status = dbNextRecord(pdbentry); - } - if(status==0) { - precnode = pdbentry->precnode; - ellInsert(preclist,ellPrevious(&precnode->node),&pNewRecNode->node); - } else { - ellAdd(preclist,&pNewRecNode->node); - } + 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);} @@ -1630,52 +1618,6 @@ char * dbGetRecordName(DBENTRY *pdbentry) return precnode->recordname; } -long dbRenameRecord(DBENTRY *pdbentry,const char *newName) -{ - dbBase *pdbbase = pdbentry->pdbbase; - dbRecordType *precordType = pdbentry->precordType; - dbFldDes *pdbFldDes; - dbRecordNode *precnode = pdbentry->precnode; - PVDENTRY *ppvd; - ELLLIST *preclist; - dbRecordNode *plistnode; - long status; - DBENTRY dbentry; - - 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(newName)>=pdbFldDes->size) return(S_dbLib_nameLength); - if (!precnode || dbIsAlias(pdbentry)) return S_dbLib_recNotFound; - dbInitEntry(pdbentry->pdbbase,&dbentry); - status = dbFindRecord(&dbentry,newName); - dbFinishEntry(&dbentry); - if(!status) return(S_dbLib_recExists); - dbPvdDelete(pdbbase,precnode); - pdbentry->pflddes = precordType->papFldDes[0]; - if((status = dbGetFieldAddress(pdbentry))) return(status); - strcpy(pdbentry->pfield,newName); - ppvd = dbPvdAdd(pdbbase,precordType,precnode); - if(!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);} - /*remove from record list and reinstall in sorted order*/ - preclist = &precordType->recList; - ellDelete(preclist,&precnode->node); - plistnode = (dbRecordNode *)ellFirst(preclist); - while(plistnode) { - pdbentry->precnode = plistnode; - if(strcmp(newName,dbGetRecordName(pdbentry)) >=0) break; - plistnode = (dbRecordNode *)ellNext(&plistnode->node); - } - if(plistnode) - ellInsert(preclist,ellPrevious(&plistnode->node),&precnode->node); - else - ellAdd(preclist,&precnode->node); - /*Leave pdbentry pointing to newly renamed record*/ - return(dbFindRecord(pdbentry,newName)); -} - long dbVisibleRecord(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; @@ -1709,7 +1651,6 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias) dbRecordNode *pnewnode; PVDENTRY *ppvd; ELLLIST *preclist = NULL; - long status; if (!precordType) return S_dbLib_recordTypeNotFound; if (!precnode) return S_dbLib_recNotFound; @@ -1725,18 +1666,7 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias) if (!(precnode->flags & DBRN_FLAGS_ISALIAS)) precnode->flags |= DBRN_FLAGS_HASALIAS; ellInit(&pnewnode->infoList); - /* install record node in list in sorted postion */ - status = dbFirstRecord(pdbentry); - while (!status) { - if (strcmp(alias, dbGetRecordName(pdbentry)) < 0) break; - status = dbNextRecord(pdbentry); - } - if (!status) { - precnode = pdbentry->precnode; - ellInsert(preclist, ellPrevious(&precnode->node), &pnewnode->node); - } else { - ellAdd(preclist, &pnewnode->node); - } + ellAdd(preclist, &pnewnode->node); precordType->no_aliases++; pdbentry->precnode = pnewnode; ppvd = dbPvdAdd(pdbentry->pdbbase, precordType, pnewnode); diff --git a/src/ioc/dbStatic/dbStaticLib.h b/src/ioc/dbStatic/dbStaticLib.h index de94dc674..1619a9090 100644 --- a/src/ioc/dbStatic/dbStaticLib.h +++ b/src/ioc/dbStatic/dbStaticLib.h @@ -146,8 +146,6 @@ epicsShareFunc long dbNextRecord(DBENTRY *pdbentry); epicsShareFunc int dbGetNRecords(DBENTRY *pdbentry); epicsShareFunc int dbGetNAliases(DBENTRY *pdbentry); epicsShareFunc char * dbGetRecordName(DBENTRY *pdbentry); -epicsShareFunc long dbRenameRecord(DBENTRY *pdbentry, - const char *newName); epicsShareFunc long dbCopyRecord(DBENTRY *pdbentry, const char *newRecordName, int overWriteOK); diff --git a/src/ioc/misc/dbCore.dbd b/src/ioc/misc/dbCore.dbd index 4b515141b..a2ce45806 100644 --- a/src/ioc/misc/dbCore.dbd +++ b/src/ioc/misc/dbCore.dbd @@ -11,6 +11,7 @@ variable(asCaDebug,int) # Static database access variables variable(dbRecordsOnceOnly,int) +variable(dbRecordsAbcSorted,int) variable(dbBptNotMonotonic,int) variable(dbQuietMacroWarnings,int) diff --git a/src/ioc/rsrv/camessage.c b/src/ioc/rsrv/camessage.c index b5c265ca6..b13883b5a 100644 --- a/src/ioc/rsrv/camessage.c +++ b/src/ioc/rsrv/camessage.c @@ -26,6 +26,7 @@ #include "epicsEvent.h" #include "epicsMutex.h" #include "epicsStdio.h" +#include "epicsString.h" #include "epicsThread.h" #include "epicsTime.h" #include "errlog.h" @@ -663,7 +664,7 @@ static void read_reply ( void *pArg, struct dbChannel *dbch, static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *pClient ) { struct channel_in_use *pciu = MPTOPCIU ( mp ); - const int readAccess = asCheckGet ( pciu->asClientPVT ); + int readAccess; ca_uint32_t payloadSize; void *pPayload; int status; @@ -675,6 +676,7 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p logBadId ( pClient, mp, 0 ); return RSRV_ERROR; } + readAccess = asCheckGet ( pciu->asClientPVT ); SEND_LOCK ( pClient ); @@ -748,7 +750,7 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p */ if ( mp->m_dataType == DBR_STRING && mp->m_count == 1 ) { char * pStr = (char *) pPayload; - size_t strcnt = strlen ( pStr ); + size_t strcnt = epicsStrnLen( pStr, payloadSize ); if ( strcnt < payloadSize ) { payloadSize = ( ca_uint32_t ) ( strcnt + 1u ); } @@ -883,7 +885,7 @@ static int write_action ( caHdrLargeArray *mp, static int host_name_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { - size_t size; + ca_uint32_t size; char *pName; char *pMalloc; int chanCount; @@ -907,9 +909,9 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload, } pName = (char *) pPayload; - size = strlen(pName)+1; - if (size > 512) { - log_header ( "bad (very long) host name", + 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( @@ -962,7 +964,7 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload, static int client_name_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { - size_t size; + ca_uint32_t size; char *pName; char *pMalloc; int chanCount; @@ -986,9 +988,9 @@ static int client_name_action ( caHdrLargeArray *mp, void *pPayload, } pName = (char *) pPayload; - size = strlen(pName)+1; - if (size > 512) { - log_header ("a very long user name was specified", + 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( diff --git a/src/ioc/rsrv/caservertask.c b/src/ioc/rsrv/caservertask.c index a8c590452..a18abafe2 100644 --- a/src/ioc/rsrv/caservertask.c +++ b/src/ioc/rsrv/caservertask.c @@ -232,16 +232,17 @@ SOCKET* rsrv_grab_tcp(unsigned short *port) ok = 0; break; } - /* if SOCK_EADDRINUSE then try again with a different port number. - * otherwise, fail hard + /* if SOCK_EADDRINUSE or SOCK_EACCES try again with a different + * port number, otherwise fail hard. */ - if(errcode!=SOCK_EADDRINUSE) { + 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 was \"%s\"\n", + cantProceed( "CAS: Socket bind %s error was %s\n", name, sockErrBuf ); } ok = 0; @@ -852,7 +853,7 @@ static void log_one_client (struct client *client, unsigned level) printf( "\tUnprocessed request bytes = %u, Undelivered response bytes = %u\n", client->recv.cnt - client->recv.stk, - client->send.stk ); + client->send.stk ); printf( "\tState = %s%s%s\n", state[client->disconnect?1:0], diff --git a/src/libCom/RTEMS/rtems_init.c b/src/libCom/RTEMS/rtems_init.c index 84634672b..82871da0d 100644 --- a/src/libCom/RTEMS/rtems_init.c +++ b/src/libCom/RTEMS/rtems_init.c @@ -2,7 +2,7 @@ * Copyright (c) 2002 The University of Saskatchewan * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* * RTEMS startup task for EPICS @@ -287,27 +287,27 @@ initialize_remote_filesystem(char **argv, int hasLocalFilesystem) mount_point[l] = '\0'; argv[1] = rtems_bsdnet_bootp_cmdline; /* - * Its probably common to embed the mount point in the server + * 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 + * 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 + * 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, + char * const pServerName = mustMalloc( allocSize, "NFS mount paths"); - char * const pServerPath = mustMalloc ( allocSize, + char * const pServerPath = mustMalloc ( allocSize, "NFS mount paths"); - const int scanfStatus = sscanf ( - server_name, - "%[^:] : / %s", - pServerName, + const int scanfStatus = sscanf ( + server_name, + "%[^:] : / %s", + pServerName, pServerPath + 1u ); if ( scanfStatus == 2 ) { pServerPath[0u]= '/'; @@ -535,7 +535,7 @@ exitHandler(void) rtems_task Init (rtems_task_argument ignored) { - int i; + int result; char *argv[3] = { NULL, NULL, NULL }; char *cp; rtems_task_priority newpri; @@ -661,8 +661,8 @@ Init (rtems_task_argument ignored) atexit(exitHandler); errlogFlush(); printf ("***** Starting EPICS application *****\n"); - i = main ((sizeof argv / sizeof argv[0]) - 1, argv); + result = main ((sizeof argv / sizeof argv[0]) - 1, argv); printf ("***** IOC application terminating *****\n"); epicsThreadSleep(1.0); - epicsExit(0); + epicsExit(result); } diff --git a/src/libCom/as/asLib_lex.l b/src/libCom/as/asLib_lex.l index 90e1b55bb..5e0451925 100644 --- a/src/libCom/as/asLib_lex.l +++ b/src/libCom/as/asLib_lex.l @@ -68,7 +68,7 @@ INP{link} { \n { line_num ++;} . { - char message[20]; + char message[40]; YY_BUFFER_STATE *dummy=0; sprintf(message,"invalid character '%c'",yytext[0]); diff --git a/src/libCom/ellLib/Makefile b/src/libCom/ellLib/Makefile index b325a453e..9a5cb3e67 100644 --- a/src/libCom/ellLib/Makefile +++ b/src/libCom/ellLib/Makefile @@ -10,3 +10,4 @@ SRC_DIRS += $(LIBCOM)/ellLib INC += ellLib.h Com_SRCS += ellLib.c +Com_SRCS += ellSort.c diff --git a/src/libCom/ellLib/ellLib.h b/src/libCom/ellLib/ellLib.h index f99859f3d..52b58d169 100644 --- a/src/libCom/ellLib/ellLib.h +++ b/src/libCom/ellLib/ellLib.h @@ -56,6 +56,8 @@ 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); diff --git a/src/libCom/ellLib/ellSort.c b/src/libCom/ellLib/ellSort.c new file mode 100644 index 000000000..2f8f2748b --- /dev/null +++ b/src/libCom/ellLib/ellSort.c @@ -0,0 +1,83 @@ +/*************************************************************************\ +* 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; + } + +} diff --git a/src/libCom/iocsh/iocsh.cpp b/src/libCom/iocsh/iocsh.cpp index c5b629ba6..e2e3aaf41 100644 --- a/src/libCom/iocsh/iocsh.cpp +++ b/src/libCom/iocsh/iocsh.cpp @@ -559,6 +559,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) if (macros) { if (macParseDefns(NULL, macros, &defines) < 0) { + free(redirects); return -1; } } @@ -571,6 +572,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) if (handle == NULL) { if (macCreateHandle(&handle, pairs)) { errlogMessage("iocsh: macCreateHandle failed."); + free(redirects); return -1; } diff --git a/src/libCom/log/iocLogServer.c b/src/libCom/log/iocLogServer.c index f9deb06e5..f5694fc0f 100644 --- a/src/libCom/log/iocLogServer.c +++ b/src/libCom/log/iocLogServer.c @@ -84,131 +84,132 @@ static int sighupPipe[2]; /* * - * main() + * main() * */ int main(void) { - struct sockaddr_in serverAddr; /* server's address */ - struct timeval timeout; - int status; - struct ioc_log_server *pserver; + struct sockaddr_in serverAddr; /* server's address */ + struct timeval timeout; + int status; + struct ioc_log_server *pserver; - osiSockIoctl_t optval; + 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; - } + 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 = (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; - } + 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) { + /* + * 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); - return IOCLS_ERROR; - } - + 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); + /* 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) { + /* 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; - } + 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) { + /* 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; - } + 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){ + /* + * 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; - } + fprintf(stderr, "iocLogServer: ioctl FIONBIO err %s\n", sockErrBuf); + return IOCLS_ERROR; + } -# ifdef UNIX - status = setupSIGHUP(pserver); - if (status<0) { - return IOCLS_ERROR; - } -# endif +# 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 = 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; - } + 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); - } + while (TRUE) { + timeout.tv_sec = 60; /* 1 min */ + timeout.tv_usec = 0; + fdmgr_pend_event(pserver->pfdctx, &timeout); + fflush(pserver->poutfile); + } } /* @@ -964,6 +965,7 @@ static int getDirectory(void) "Problem reading o/p from `%s' because `%s'\n", ioc_log_file_command, strerror(errno)); + (void) pclose(pipe); return IOCLS_ERROR; } (void) pclose(pipe); diff --git a/src/libCom/misc/epicsString.c b/src/libCom/misc/epicsString.c index f3cf828f4..a8098336c 100644 --- a/src/libCom/misc/epicsString.c +++ b/src/libCom/misc/epicsString.c @@ -256,6 +256,18 @@ int epicsStrPrintEscaped(FILE *fp, const char *s, size_t len) 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; inode)) { - if (ptp->priority == ptp->priority && + if (ptp->priority == priority && !strcmp(ptp->name, name)) break; } diff --git a/src/libCom/osi/epicsTime.cpp b/src/libCom/osi/epicsTime.cpp index 59514c2f7..6dd1d3f97 100644 --- a/src/libCom/osi/epicsTime.cpp +++ b/src/libCom/osi/epicsTime.cpp @@ -6,15 +6,15 @@ * 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. +* 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 +// 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. // @@ -26,7 +26,7 @@ #include #include #include -#include // vxWorks 6.0 requires this include +#include // vxWorks 6.0 requires this include #define epicsExportSharedSymbols #include "locationException.h" @@ -37,7 +37,7 @@ #include "osiSock.h" /* pull in struct timeval */ #include "epicsStdio.h" -static const char pEpicsTimeVersion[] = +static const char pEpicsTimeVersion[] = "@(#) " EPICS_VERSION_STRING ", Common Utilities Library " __DATE__; // @@ -60,11 +60,13 @@ static const unsigned long NTP_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 ( nSecIn / nSecPerSec + secIn ), - nSec ( nSecIn % nSecPerSec ) +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; + } } // @@ -93,7 +95,7 @@ epicsTimeLoadTimeInit::epicsTimeLoadTimeInit () * 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 + * 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; @@ -103,7 +105,7 @@ epicsTimeLoadTimeInit::epicsTimeLoadTimeInit () this->epicsEpochOffset >= 0) { // We can use simpler code on Posix-compliant systems this->useDiffTimeOptimization = true; - this->epicsEpochOffsetAsAnUnsignedLong = + this->epicsEpochOffsetAsAnUnsignedLong = static_cast(this->epicsEpochOffset); } else { // Forced to use the slower but correct code @@ -113,47 +115,26 @@ epicsTimeLoadTimeInit::epicsTimeLoadTimeInit () } // -// epicsTime::addNanoSec () +// private epicsTime::addNanoSec () // -// The nano-second field of several of the the UNIX time stamp formats -// field is stored in the C type "long". +// 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 ) { - // - // After optimizing this function we now have a larger code which - // uses only unsigned integer, and not floating point, arithmetic. - // This change benefits embedded CPU's lacking a floating point - // co-processor at the expense of some additional code to maintain. - // - // We hope that all CPU's we run on provide at least an integer - // divide instruction which should enable this implementation - // to be more efficient than implementations based on branching; - // this is presuming that we will run on pipelined architectures. - // - // Overflow and underflow is expected; in the future we might - // operate close to, the modulo of, the EPICS epic. - // - // We are depending on the normalize operation in the private - // constructor used below. - // - // joh 11-04-2012 - // - if ( nSecAdj >= 0 ) { - const unsigned long nSecPlus = - static_cast ( nSecAdj ); - const unsigned long nSecPlusAdj = nSecPlus % nSecPerSec; - const unsigned long secPlusAdj = nSecPlus / nSecPerSec; - *this = epicsTime ( this->secPastEpoch+secPlusAdj, - this->nSec+nSecPlusAdj ); + if (nSecAdj <= 0) + return; + + if (static_cast(nSecAdj) >= nSecPerSec) { + this->secPastEpoch += nSecAdj / nSecPerSec; + nSecAdj %= nSecPerSec; } - else { - const unsigned long nSecMinus = - static_cast ( -nSecAdj ); - const unsigned long nSecMinusAdj = nSecMinus % nSecPerSec; - const unsigned long secMinusAdj = nSecMinus / nSecPerSec; - *this = epicsTime ( this->secPastEpoch - secMinusAdj - 1u, - this->nSec + nSecPerSec - nSecMinusAdj ); + + this->nSec += nSecAdj; // Can't overflow + if (this->nSec >= nSecPerSec) { + this->secPastEpoch++; + this->nSec -= nSecPerSec; } } @@ -166,11 +147,11 @@ epicsTime::epicsTime ( const time_t_wrapper & ansiTimeTicks ) static epicsTimeLoadTimeInit & lti = * new epicsTimeLoadTimeInit (); // - // try to directly map time_t into an unsigned long integer because this is + // 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) + // 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 ); @@ -186,8 +167,8 @@ epicsTime::epicsTime ( const time_t_wrapper & ansiTimeTicks ) } // - // otherwise map time_t, which ANSI C and POSIX define as any arithmetic type, - // into type double + // 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; @@ -209,14 +190,14 @@ epicsTime::epicsTime ( const time_t_wrapper & ansiTimeTicks ) this->nSec = static_cast ( ( sec-this->secPastEpoch ) * nSecPerSec ); } -epicsTime::epicsTime (const epicsTimeStamp &ts) +epicsTime::epicsTime (const epicsTimeStamp &ts) { if ( ts.nsec < nSecPerSec ) { this->secPastEpoch = ts.secPastEpoch; this->nSec = ts.nsec; } else { - throw std::logic_error ( + throw std::logic_error ( "epicsTimeStamp has overflow in nano-seconds field" ); } } @@ -262,7 +243,7 @@ epicsTime::operator time_t_wrapper () const return wrap; } } - + // // map type double into time_t which ansi C defines as some arithmetic type // @@ -317,20 +298,82 @@ epicsTime::operator gm_tm_nano_sec () const // epicsTime::epicsTime (const local_tm_nano_sec &tm) { - static const time_t mktimeFailure = static_cast (-1); - time_t_wrapper ansiTimeTicks; struct tm tmp = tm.ansi_tm; + time_t_wrapper ansiTimeTicks = { mktime (&tmp) }; - ansiTimeTicks.ts = mktime (&tmp); - if (ansiTimeTicks.ts == mktimeFailure) { + static const time_t mktimeError = static_cast (-1); + if (ansiTimeTicks.ts == mktimeError) { throwWithLocation ( formatProblemWithStructTM () ); } - *this = epicsTime (ansiTimeTicks); + *this = epicsTime(ansiTimeTicks); + this->addNanoSec(tm.nSec); +} - unsigned long nSecAdj = tm.nSec % nSecPerSec; - unsigned long secAdj = tm.nSec / nSecPerSec; - *this = epicsTime ( this->secPastEpoch+secAdj, this->nSec+nSecAdj ); +// +// 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); } // @@ -372,9 +415,9 @@ epicsTime::operator struct timeval () const // 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 + // 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 + // 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 ); @@ -391,9 +434,9 @@ epicsTime::epicsTime (const struct timeval &ts) // 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 + // 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 + // 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); @@ -415,7 +458,7 @@ 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 > + ts.l_uf = static_cast < unsigned long > ( ( this->nSec * NTP_FRACTION_DENOMINATOR ) / nSecPerSec ); return ts; } @@ -426,14 +469,14 @@ epicsTime::operator l_fp () const epicsTime::epicsTime ( const l_fp & ts ) { this->secPastEpoch = ts.l_ui - NTP_TIME_AT_EPICS_EPOCH; - this->nSec = static_cast < unsigned long > + 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 ( + throw std::logic_error ( "epicsTimeStamp has overflow in nano-seconds field?" ); } epicsTimeStamp ts; @@ -444,7 +487,7 @@ epicsTime::operator epicsTimeStamp () const // 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 + // 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 @@ -458,14 +501,14 @@ epicsTime::operator epicsTimeStamp () const return ts; } -// Break up a format string into "%0f" -// (where in an unsigned integer) +// 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 +// 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 +// C) returning a pointer to the postfix (which might be passed again // to fracFormatFind. static const char * fracFormatFind ( const char * const pFormat, @@ -529,8 +572,8 @@ static const char * fracFormatFind ( // // size_t epicsTime::strftime () // -size_t epicsTime::strftime ( - char * pBuff, size_t bufLength, const char * pFormat ) const +size_t epicsTime::strftime ( + char * pBuff, size_t bufLength, const char * pFormat ) const { if ( bufLength == 0u ) { return 0u; @@ -547,15 +590,15 @@ size_t epicsTime::strftime ( const char * pFmt = pFormat; size_t bufLenLeft = bufLength; while ( *pFmt != '\0' && bufLenLeft > 1 ) { - // look for "%0f" at the end (used for fractional second formatting) + // look for "%0f" at the end (used for fractional second formatting) char strftimePrefixBuf [256]; bool fracFmtFound; unsigned long fracWid = 0; pFmt = fracFormatFind ( - pFmt, + pFmt, strftimePrefixBuf, sizeof ( strftimePrefixBuf ), fracFmtFound, fracWid ); - + // nothing more in the string, then quit if ( ! ( strftimePrefixBuf[0] != '\0' || fracFmtFound ) ) { break; @@ -657,7 +700,7 @@ void epicsTime::show ( unsigned level ) const if ( level > 1 ) { // this also suppresses the "defined, but not used" // warning message - printf ( "epicsTime: revision \"%s\"\n", + printf ( "epicsTime: revision \"%s\"\n", pEpicsTimeVersion ); } @@ -704,10 +747,10 @@ epicsTime epicsTime::operator + (const double &rhs) const } // -// operator - +// operator - // // To make this code robust during timestamp rollover events -// time stamp differences greater than one half full scale are +// time stamp differences greater than one half full scale are // interpreted as rollover situations: // // when RHS is greater than THIS: @@ -729,7 +772,7 @@ double epicsTime::operator - (const epicsTime &rhs) const // so the unsigned to signed conversion is ok // if (this->nSec>=rhs.nSec) { - nSecRes = this->nSec - rhs.nSec; + nSecRes = this->nSec - rhs.nSec; } else { nSecRes = rhs.nSec - this->nSec; @@ -746,7 +789,7 @@ double epicsTime::operator - (const epicsTime &rhs) const if (secRes > ULONG_MAX/2) { // // In this situation where the difference is more than - // 68 years assume that the seconds counter has rolled + // 68 years assume that the seconds counter has rolled // over and compute the "wrap around" difference // secRes = 1 + (ULONG_MAX-secRes); @@ -761,7 +804,7 @@ double epicsTime::operator - (const epicsTime &rhs) const if (secRes > ULONG_MAX/2) { // // In this situation where the difference is more than - // 68 years assume that the seconds counter has rolled + // 68 years assume that the seconds counter has rolled // over and compute the "wrap around" difference // secRes = 1 + (ULONG_MAX-secRes); @@ -770,7 +813,7 @@ double epicsTime::operator - (const epicsTime &rhs) const } } - return secRes + nSecRes/nSecPerSec; + return secRes + nSecRes/nSecPerSec; } // @@ -791,7 +834,7 @@ bool epicsTime::operator <= (const epicsTime &rhs) const else { // // In this situation where the difference is more than - // 69 years assume that the seconds counter has rolled + // 69 years assume that the seconds counter has rolled // over and compute the "wrap around" result // rc = false; @@ -808,7 +851,7 @@ bool epicsTime::operator <= (const epicsTime &rhs) const else { // // In this situation where the difference is more than - // 69 years assume that the seconds counter has rolled + // 69 years assume that the seconds counter has rolled // over and compute the "wrap around" result // rc = true; @@ -843,7 +886,7 @@ bool epicsTime::operator < (const epicsTime &rhs) const else { // // In this situation where the difference is more than - // 69 years assume that the seconds counter has rolled + // 69 years assume that the seconds counter has rolled // over and compute the "wrap around" result // rc = false; @@ -860,7 +903,7 @@ bool epicsTime::operator < (const epicsTime &rhs) const else { // // In this situation where the difference is more than - // 69 years assume that the seconds counter has rolled + // 69 years assume that the seconds counter has rolled // over and compute the "wrap around" result // rc = true; @@ -881,7 +924,7 @@ extern "C" { // // ANSI C interface // - // its too bad that these cant be implemented with inline functions + // its too bad that these cant be implemented with inline functions // at least when running the GNU compiler // epicsShareFunc int epicsShareAPI epicsTimeToTime_t (time_t *pDest, const epicsTimeStamp *pSrc) @@ -944,6 +987,19 @@ extern "C" { } return epicsTimeOK; } + epicsShareFunc int epicsShareAPI epicsTimeFromGMTM (epicsTimeStamp *pDest, const struct tm *pSrc, unsigned long nSecSrc) + { + try { + gm_tm_nano_sec tmns; + tmns.ansi_tm = *pSrc; + tmns.nSec = nSecSrc; + *pDest = epicsTime (tmns); + } + catch (...) { + return S_time_conversion; + } + return epicsTimeOK; + } epicsShareFunc int epicsShareAPI epicsTimeToTimespec (struct timespec *pDest, const epicsTimeStamp *pSrc) { try { @@ -1075,4 +1131,3 @@ extern "C" { } } } - diff --git a/src/libCom/osi/epicsTime.h b/src/libCom/osi/epicsTime.h index bf8f77c87..149f6f2d3 100644 --- a/src/libCom/osi/epicsTime.h +++ b/src/libCom/osi/epicsTime.h @@ -106,10 +106,12 @@ public: epicsTime & operator = ( const local_tm_nano_sec & ); /* - * convert to ANSI Cs "struct tm" (with nano seconds) + * 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; @@ -201,13 +203,15 @@ epicsShareFunc int epicsShareAPI epicsTimeToTime_t ( epicsShareFunc int epicsShareAPI epicsTimeFromTime_t ( epicsTimeStamp * pDest, time_t src ); -/*convert to and from ANSI C's "struct tm" with nano seconds */ +/* 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 ( @@ -319,6 +323,11 @@ 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 ); diff --git a/src/libCom/osi/os/Darwin/osdSock.h b/src/libCom/osi/os/Darwin/osdSock.h index e9426a79f..1d4556eee 100644 --- a/src/libCom/osi/os/Darwin/osdSock.h +++ b/src/libCom/osi/os/Darwin/osdSock.h @@ -1,7 +1,7 @@ /*************************************************************************\ * 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. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Eric Norum @@ -38,6 +38,7 @@ typedef socklen_t osiSocklen_t; #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 diff --git a/src/libCom/osi/os/Linux/osdSock.h b/src/libCom/osi/os/Linux/osdSock.h index b32a16044..614f3f98d 100644 --- a/src/libCom/osi/os/Linux/osdSock.h +++ b/src/libCom/osi/os/Linux/osdSock.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* @@ -42,6 +42,7 @@ typedef socklen_t osiSocklen_t; #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 @@ -72,4 +73,3 @@ typedef socklen_t osiSocklen_t; #define ifreq_size(pifreq) (sizeof(pifreq->ifr_name)) #endif /*osdSockH*/ - diff --git a/src/libCom/osi/os/RTEMS/osdMessageQueue.c b/src/libCom/osi/os/RTEMS/osdMessageQueue.c index ff7728fb2..a566de6f6 100644 --- a/src/libCom/osi/os/RTEMS/osdMessageQueue.c +++ b/src/libCom/osi/os/RTEMS/osdMessageQueue.c @@ -171,7 +171,7 @@ static int receiveMessage( return -1; } rsize = receiveMessage(id, id->localBuf, id->maxSize, wait, delay); - if ((rsize < 0) || (rsize > size)) + if (rsize > size) return -1; memcpy(buffer, id->localBuf, rsize); } diff --git a/src/libCom/osi/os/RTEMS/osdSock.h b/src/libCom/osi/os/RTEMS/osdSock.h index dce24645f..930ed0e72 100644 --- a/src/libCom/osi/os/RTEMS/osdSock.h +++ b/src/libCom/osi/os/RTEMS/osdSock.h @@ -1,7 +1,7 @@ /*************************************************************************\ * 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. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* * RTEMS osdSock.h @@ -35,7 +35,7 @@ int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, str #ifdef __cplusplus } #endif - + typedef int SOCKET; #define INVALID_SOCKET (-1) #define SOCKERRNO errno @@ -49,6 +49,7 @@ typedef socklen_t osiSocklen_t; #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 @@ -75,7 +76,7 @@ typedef socklen_t osiSocklen_t; #ifndef INADDR_NONE # define INADDR_NONE (0xffffffff) -#endif +#endif /* * For shutdown() diff --git a/src/libCom/osi/os/RTEMS/osdThread.c b/src/libCom/osi/os/RTEMS/osdThread.c index d177cf3f2..769e95820 100644 --- a/src/libCom/osi/os/RTEMS/osdThread.c +++ b/src/libCom/osi/os/RTEMS/osdThread.c @@ -427,8 +427,6 @@ void epicsThreadGetName (epicsThreadId id, char *name, size_t size) struct taskVar *v; int haveName = 0; - if (size <= 0) - return; taskVarLock (); for (v=taskVarHead ; v != NULL ; v=v->forw) { if (v->id == tid) { @@ -690,6 +688,7 @@ void epicsThreadShow (epicsThreadId id, unsigned int level) for (v = taskVarHead ; v != NULL ; v = v->forw) { if ((rtems_id)id == v->id) { epicsThreadShowInfo (v, level); + taskVarUnlock (); return; } } diff --git a/src/libCom/osi/os/WIN32/osdSock.h b/src/libCom/osi/os/WIN32/osdSock.h index e685f6564..d92e18755 100644 --- a/src/libCom/osi/os/WIN32/osdSock.h +++ b/src/libCom/osi/os/WIN32/osdSock.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef osdSockH @@ -14,7 +14,7 @@ #include /* - * winsock2.h changes the structure alignment to 4 if + * winsock2.h changes the structure alignment to 4 if * WIN32 isnt set which can be a source of confusion */ #ifndef WIN32 @@ -48,6 +48,7 @@ typedef int osiSocklen_t; #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 diff --git a/src/libCom/osi/os/WIN32/osdThread.c b/src/libCom/osi/os/WIN32/osdThread.c index b8590086a..8cdb4a3f4 100644 --- a/src/libCom/osi/os/WIN32/osdThread.c +++ b/src/libCom/osi/os/WIN32/osdThread.c @@ -523,13 +523,8 @@ static win32ThreadParam * epicsThreadParmCreate ( const char *pName ) pParmWIN32 = calloc ( 1, sizeof ( *pParmWIN32 ) + strlen ( pName ) + 1 ); if ( pParmWIN32 ) { - if ( pName ) { - pParmWIN32->pName = (char *) ( pParmWIN32 + 1 ); - strcpy ( pParmWIN32->pName, pName ); - } - else { - pParmWIN32->pName = 0; - } + pParmWIN32->pName = (char *) ( pParmWIN32 + 1 ); + strcpy ( pParmWIN32->pName, pName ); pParmWIN32->isSuspended = 0; } return pParmWIN32; diff --git a/src/libCom/osi/os/cygwin32/osdSock.h b/src/libCom/osi/os/cygwin32/osdSock.h index 324e75c3f..d642cad44 100644 --- a/src/libCom/osi/os/cygwin32/osdSock.h +++ b/src/libCom/osi/os/cygwin32/osdSock.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* * cygwin32 specific include @@ -49,6 +49,7 @@ typedef int osiSocklen_t; #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 @@ -67,4 +68,3 @@ typedef int osiSocklen_t; #define ifreq_size(pifreq) (sizeof(pifreq->ifr_name)) #endif /*osdSockH*/ - diff --git a/src/libCom/osi/os/freebsd/osdSock.h b/src/libCom/osi/os/freebsd/osdSock.h index a950b996c..fe28d4cd5 100644 --- a/src/libCom/osi/os/freebsd/osdSock.h +++ b/src/libCom/osi/os/freebsd/osdSock.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef osdSockH @@ -29,7 +29,7 @@ #define IPPORT_USERRESERVED 5000 #endif - + typedef int SOCKET; #define INVALID_SOCKET (-1) #define SOCKERRNO errno @@ -43,6 +43,7 @@ typedef socklen_t osiSocklen_t; #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 @@ -77,4 +78,3 @@ typedef socklen_t osiSocklen_t; #endif #endif /*osdSockH*/ - diff --git a/src/libCom/osi/os/iOS/osdSock.h b/src/libCom/osi/os/iOS/osdSock.h index f3a15c88d..0b3b3f6c1 100644 --- a/src/libCom/osi/os/iOS/osdSock.h +++ b/src/libCom/osi/os/iOS/osdSock.h @@ -39,6 +39,7 @@ typedef socklen_t osiSocklen_t; #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 diff --git a/src/libCom/osi/os/solaris/osdSock.h b/src/libCom/osi/os/solaris/osdSock.h index 1ea493680..a39c6c3d3 100644 --- a/src/libCom/osi/os/solaris/osdSock.h +++ b/src/libCom/osi/os/solaris/osdSock.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* @@ -39,7 +39,7 @@ typedef int osiSockIoctl_t; #if SOLARIS > 6 || defined ( _SOCKLEN_T ) typedef uint32_t osiSocklen_t; -#else +#else typedef int osiSocklen_t; #endif @@ -51,6 +51,7 @@ typedef int osiSockIoctl_t; #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 @@ -80,9 +81,8 @@ typedef int osiSockIoctl_t; #ifndef INADDR_NONE # define INADDR_NONE (0xffffffff) -#endif +#endif #define ifreq_size(pifreq) (sizeof(pifreq->ifr_name)) #endif /*osdSockH*/ - diff --git a/src/libCom/osi/os/vxWorks/osdSock.h b/src/libCom/osi/os/vxWorks/osdSock.h index f0260c5a8..80464ef87 100644 --- a/src/libCom/osi/os/vxWorks/osdSock.h +++ b/src/libCom/osi/os/vxWorks/osdSock.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* * vxWorks specific socket include @@ -44,7 +44,7 @@ int sysClkRateGet(void); #ifdef __cplusplus } #endif - + typedef int SOCKET; #define INVALID_SOCKET (-1) #define SOCKERRNO errno @@ -72,6 +72,7 @@ typedef int osiSocklen_t; #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 @@ -93,7 +94,7 @@ typedef int osiSocklen_t; #ifndef INADDR_NONE # define INADDR_NONE (0xffffffff) -#endif +#endif #if defined(_SIZEOF_ADDR_IFREQ) # define ifreq_size(pifreq) _SIZEOF_ADDR_IFREQ(*pifreq) @@ -104,5 +105,3 @@ typedef int osiSocklen_t; #endif #endif /*osdSockH*/ - - diff --git a/src/libCom/test/epicsEllTest.c b/src/libCom/test/epicsEllTest.c index b608d1cc3..62ee14e39 100644 --- a/src/libCom/test/epicsEllTest.c +++ b/src/libCom/test/epicsEllTest.c @@ -1,4 +1,5 @@ /*************************************************************************\ +* 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 @@ -11,6 +12,7 @@ #include #include "ellLib.h" +#include "dbDefs.h" #include "epicsUnitTest.h" #include "testMain.h" @@ -20,15 +22,13 @@ struct myItem { int num; }; -MAIN(epicsEllTest) +static void testList(void) { ELLLIST list1; ELLLIST list2 = ELLLIST_INIT; int i1 = 1; struct myItem *pitem, *pfirst, *pick; - testPlan(70); - list1.count = 27; list1.node.next = (ELLNODE *) 0x01010101; list1.node.previous = (ELLNODE *) 0x10101010; @@ -192,6 +192,77 @@ MAIN(epicsEllTest) 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(); } diff --git a/src/libCom/test/epicsEventTest.cpp b/src/libCom/test/epicsEventTest.cpp index b0d79613f..7c1a24141 100644 --- a/src/libCom/test/epicsEventTest.cpp +++ b/src/libCom/test/epicsEventTest.cpp @@ -250,5 +250,12 @@ MAIN(epicsEventTest) eventWaitTest(); eventWakeupTest(); + free(name); + free(id); + epicsRingPointerDelete(pinfo->ring); + epicsMutexDestroy(pinfo->lockRing); + epicsEventDestroy(event); + free(pinfo); + return testDone(); } diff --git a/src/libCom/test/epicsMutexTest.cpp b/src/libCom/test/epicsMutexTest.cpp index 87cf0a460..d44e5c0f1 100644 --- a/src/libCom/test/epicsMutexTest.cpp +++ b/src/libCom/test/epicsMutexTest.cpp @@ -279,5 +279,11 @@ MAIN(epicsMutexTest) epicsMutexPerformance (); + free(pinfo); + free(arg); + free(name); + free(id); + epicsMutexDestroy(mutex); + return testDone(); } diff --git a/src/libCom/test/epicsStdioTest.c b/src/libCom/test/epicsStdioTest.c index c2d7eb58a..bf25784a5 100644 --- a/src/libCom/test/epicsStdioTest.c +++ b/src/libCom/test/epicsStdioTest.c @@ -37,7 +37,14 @@ static void testEpicsSnprintf(void) { 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; @@ -121,11 +128,7 @@ void testStdoutRedir (const char *report) MAIN(epicsStdioTest) { -#ifdef _WIN32 - testPlan(166); -#else testPlan(163); -#endif testEpicsSnprintf(); #ifdef __rtems__ /* ensure there is a writeable area */ diff --git a/src/libCom/test/epicsStringTest.c b/src/libCom/test/epicsStringTest.c index 9c50d3c82..3e4aed7e7 100644 --- a/src/libCom/test/epicsStringTest.c +++ b/src/libCom/test/epicsStringTest.c @@ -87,7 +87,7 @@ MAIN(epicsStringTest) char *s; int status; - testPlan(402); + testPlan(406); testChars(); @@ -122,6 +122,11 @@ MAIN(epicsStringTest) 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)); diff --git a/src/libCom/test/epicsTimeTest.cpp b/src/libCom/test/epicsTimeTest.cpp index 970258801..ec5da4c78 100644 --- a/src/libCom/test/epicsTimeTest.cpp +++ b/src/libCom/test/epicsTimeTest.cpp @@ -43,7 +43,7 @@ MAIN(epicsTimeTest) const int wasteTime = 100000; const int nTimes = 10; - testPlan(15 + nTimes * 18); + testPlan(15 + nTimes * 19); try { const epicsTimeStamp epochTS = {0, 0}; @@ -200,6 +200,11 @@ MAIN(epicsTimeTest) 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; diff --git a/src/libCom/test/ringBytesTest.c b/src/libCom/test/ringBytesTest.c index ecfd991be..6cef93334 100644 --- a/src/libCom/test/ringBytesTest.c +++ b/src/libCom/test/ringBytesTest.c @@ -115,5 +115,9 @@ MAIN(ringBytesTest) testOk(n==1, "ring get %d", 1); check(ring, RINGSIZE); + epicsRingBytesDelete(ring); + epicsEventDestroy(consumerEvent); + free(pinfo); + return testDone(); } diff --git a/src/std/rec/fanoutRecord.dbd b/src/std/rec/fanoutRecord.dbd index 40968945c..251d63a11 100644 --- a/src/std/rec/fanoutRecord.dbd +++ b/src/std/rec/fanoutRecord.dbd @@ -36,12 +36,14 @@ recordtype(fanout) { } field(OFFS,DBF_SHORT) { prompt("Offset for Specified") - interest(1) + promptgroup("30 - Action") + interest(1) initial("0") } field(SHFT,DBF_SHORT) { prompt("Shift for Mask mode") - interest(1) + promptgroup("30 - Action") + interest(1) initial("-1") } field(LNK0,DBF_FWDLINK) { diff --git a/src/std/rec/seqRecord.dbd b/src/std/rec/seqRecord.dbd index c40d17c38..826f3ecf6 100644 --- a/src/std/rec/seqRecord.dbd +++ b/src/std/rec/seqRecord.dbd @@ -36,11 +36,13 @@ recordtype(seq) { } 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") } diff --git a/src/template/base/top/caClientApp/caMonitor.c b/src/template/base/top/caClientApp/caMonitor.c index 059fefb5e..9554cc744 100644 --- a/src/template/base/top/caClientApp/caMonitor.c +++ b/src/template/base/top/caClientApp/caMonitor.c @@ -109,6 +109,7 @@ int main(int argc,char **argv) 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"); diff --git a/src/template/base/top/caServerApp/exAsyncPV.cc b/src/template/base/top/caServerApp/exAsyncPV.cc index 7e0758c13..b5bff9181 100644 --- a/src/template/base/top/caServerApp/exAsyncPV.cc +++ b/src/template/base/top/caServerApp/exAsyncPV.cc @@ -3,9 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ // @@ -30,23 +29,23 @@ exAsyncPV::exAsyncPV ( exServer & cas, pvInfo & setup, // caStatus exAsyncPV::read (const casCtx &ctx, gdd &valueIn) { - exAsyncReadIO *pIO; - - if ( this->simultAsychReadIOCount >= this->cas.maxSimultAsyncIO () ) { - return S_casApp_postponeAsyncIO; - } + exAsyncReadIO *pIO; - pIO = new exAsyncReadIO ( this->cas, ctx, - *this, valueIn, this->asyncDelay ); - if ( ! pIO ) { + if ( this->simultAsychReadIOCount >= this->cas.maxSimultAsyncIO () ) { + return S_casApp_postponeAsyncIO; + } + + pIO = new exAsyncReadIO ( this->cas, ctx, + *this, valueIn, this->asyncDelay ); + if ( ! pIO ) { if ( this->simultAsychReadIOCount > 0 ) { return S_casApp_postponeAsyncIO; } else { - return S_casApp_noMemory; + return S_casApp_noMemory; } - } - this->simultAsychReadIOCount++; + } + this->simultAsychReadIOCount++; return S_casApp_asyncCompletion; } @@ -54,24 +53,24 @@ caStatus exAsyncPV::read (const casCtx &ctx, gdd &valueIn) // exAsyncPV::writeNotify() // caStatus exAsyncPV::writeNotify ( const casCtx &ctx, const gdd &valueIn ) -{ - if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) { - return S_casApp_postponeAsyncIO; - } +{ + if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) { + return S_casApp_postponeAsyncIO; + } - exAsyncWriteIO * pIO = new + exAsyncWriteIO * pIO = new exAsyncWriteIO ( this->cas, ctx, *this, - valueIn, this->asyncDelay ); - if ( ! pIO ) { + valueIn, this->asyncDelay ); + if ( ! pIO ) { if ( this->simultAsychReadIOCount > 0 ) { return S_casApp_postponeAsyncIO; } else { - return S_casApp_noMemory; + return S_casApp_noMemory; } } - this->simultAsychWriteIOCount++; - return S_casApp_asyncCompletion; + this->simultAsychWriteIOCount++; + return S_casApp_asyncCompletion; } // @@ -79,24 +78,24 @@ caStatus exAsyncPV::writeNotify ( const casCtx &ctx, const gdd &valueIn ) // caStatus exAsyncPV::write ( const casCtx &ctx, const gdd &valueIn ) { - // implement the discard intermediate values, but last value + // implement the discard intermediate values, but last value // sent always applied behavior that IOCs provide excepting // that we will alow N requests to pend instead of a limit // of only one imposed in the IOC - if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) { + if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) { pStandbyValue.set ( & valueIn ); - return S_casApp_success; - } - - exAsyncWriteIO * pIO = new - exAsyncWriteIO ( this->cas, ctx, *this, - valueIn, this->asyncDelay ); - if ( ! pIO ) { - pStandbyValue.set ( & valueIn ); - return S_casApp_success; + return S_casApp_success; } - this->simultAsychWriteIOCount++; - return S_casApp_asyncCompletion; + + exAsyncWriteIO * pIO = new + exAsyncWriteIO ( this->cas, ctx, *this, + valueIn, this->asyncDelay ); + if ( ! pIO ) { + pStandbyValue.set ( & valueIn ); + return S_casApp_success; + } + this->simultAsychWriteIOCount++; + return S_casApp_asyncCompletion; } // Implementing a specialized update for exAsyncPV @@ -150,7 +149,7 @@ void exAsyncPV::removeWriteIO () exAsyncWriteIO::exAsyncWriteIO ( exServer & cas, const casCtx & ctxIn, exAsyncPV & pvIn, const gdd & valueIn, double asyncDelay ) : - casAsyncWriteIO ( ctxIn ), pv ( pvIn ), + casAsyncWriteIO ( ctxIn ), pv ( pvIn ), timer ( cas.createTimer () ), pValue(valueIn) { this->timer.start ( *this, asyncDelay ); @@ -168,7 +167,7 @@ exAsyncWriteIO::~exAsyncWriteIO() if ( this->pValue.valid () ) { this->pv.updateFromAsyncWrite ( *this->pValue ); } - this->pv.removeWriteIO(); + this->pv.removeWriteIO(); } // @@ -179,9 +178,9 @@ epicsTimerNotify::expireStatus exAsyncWriteIO:: expire ( const epicsTime & /* currentTime */ ) { assert ( this->pValue.valid () ); - caStatus status = this->pv.updateFromAsyncWrite ( *this->pValue ); - this->pValue.set ( 0 ); - this->postIOCompletion ( status ); + caStatus status = this->pv.updateFromAsyncWrite ( *this->pValue ); + this->pValue.set ( 0 ); + this->postIOCompletion ( status ); return noRestart; } @@ -191,7 +190,7 @@ epicsTimerNotify::expireStatus exAsyncWriteIO:: exAsyncReadIO::exAsyncReadIO ( exServer & cas, const casCtx & ctxIn, exAsyncPV & pvIn, gdd & protoIn, double asyncDelay ) : - casAsyncReadIO ( ctxIn ), pv ( pvIn ), + casAsyncReadIO ( ctxIn ), pv ( pvIn ), timer ( cas.createTimer() ), pProto ( protoIn ) { this->timer.start ( *this, asyncDelay ); @@ -202,7 +201,7 @@ exAsyncReadIO::exAsyncReadIO ( exServer & cas, const casCtx & ctxIn, // exAsyncReadIO::~exAsyncReadIO() { - this->pv.removeReadIO (); + this->pv.removeReadIO (); this->timer.destroy (); } @@ -213,16 +212,16 @@ exAsyncReadIO::~exAsyncReadIO() epicsTimerNotify::expireStatus exAsyncReadIO::expire ( const epicsTime & /* currentTime */ ) { - // - // map between the prototype in and the - // current value - // - caStatus status = this->pv.exPV::readNoCtx ( this->pProto ); + // + // map between the prototype in and the + // current value + // + caStatus status = this->pv.exPV::readNoCtx ( this->pProto ); - // - // post IO completion - // - this->postIOCompletion ( status, *this->pProto ); + // + // post IO completion + // + this->postIOCompletion ( status, *this->pProto ); return noRestart; } diff --git a/src/template/base/top/caServerApp/exPV.cc b/src/template/base/top/caServerApp/exPV.cc index af99c4e6a..97430c000 100644 --- a/src/template/base/top/caServerApp/exPV.cc +++ b/src/template/base/top/caServerApp/exPV.cc @@ -3,9 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ // // Example EPICS CA server @@ -42,7 +41,7 @@ exPV::exPV ( exServer & casIn, pvInfo & setup, // // no dataless PV allowed // - assert (this->info.getElementCount()>=1u); + assert (this->info.getCapacity()>=1u); // // start a very slow background scan diff --git a/src/template/base/top/caServerApp/exServer.cc b/src/template/base/top/caServerApp/exServer.cc index 49ea802dc..f934f3608 100644 --- a/src/template/base/top/caServerApp/exServer.cc +++ b/src/template/base/top/caServerApp/exServer.cc @@ -3,9 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ // // fileDescriptorManager.process(delay); @@ -304,7 +303,7 @@ exPV *pvInfo::createPV ( exServer & cas, bool preCreateFlag, // depending on the io type and the number // of elements // - if (this->elementCount==1u) { + if (this->capacity==1u) { switch (this->ioType){ case excasIoSync: pNewPV = new exScalarPV ( cas, *this, preCreateFlag, scanOn ); diff --git a/src/template/base/top/caServerApp/exServer.h b/src/template/base/top/caServerApp/exServer.h index e0615c804..cd9a897d3 100644 --- a/src/template/base/top/caServerApp/exServer.h +++ b/src/template/base/top/caServerApp/exServer.h @@ -3,9 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ // // Example EPICS CA server @@ -76,8 +75,10 @@ public: double getLopr () const; aitEnum getType () const; excasIoType getIOType () const; - unsigned getElementCount () const; - void unlinkPV (); + unsigned getCapacity () const; + unsigned getElementCount () const; + void setElementCount (unsigned); + void unlinkPV (); exPV *createPV ( exServer & exCAS, bool preCreateFlag, bool scanOn, double asyncDelay ); void deletePV (); @@ -88,7 +89,8 @@ private: const double lopr; aitEnum type; const excasIoType ioType; - const unsigned elementCount; + const unsigned capacity; + unsigned elementCount; exPV * pPV; pvInfo & operator = ( const pvInfo & ); }; @@ -441,8 +443,8 @@ inline pvInfo::pvInfo ( double scanPeriodIn, const char *pNameIn, scanPeriod ( scanPeriodIn ), pName ( pNameIn ), hopr ( hoprIn ), lopr ( loprIn ), type ( typeIn ), - ioType ( ioTypeIn ), elementCount ( countIn ), - pPV ( 0 ) + ioType ( ioTypeIn ), capacity ( countIn ), + elementCount ( 0 ), pPV ( 0 ) { } @@ -454,8 +456,8 @@ inline pvInfo::pvInfo ( const pvInfo & copyIn ) : scanPeriod ( copyIn.scanPeriod ), pName ( copyIn.pName ), hopr ( copyIn.hopr ), lopr ( copyIn.lopr ), type ( copyIn.type ), - ioType ( copyIn.ioType ), elementCount ( copyIn.elementCount ), - pPV ( copyIn.pPV ) + ioType ( copyIn.ioType ), capacity ( copyIn.capacity ), + elementCount ( copyIn.elementCount ), pPV ( copyIn.pPV ) { } @@ -509,12 +511,22 @@ inline excasIoType pvInfo::getIOType () const return this->ioType; } -inline unsigned pvInfo::getElementCount () const -{ - return this->elementCount; +inline unsigned pvInfo::getCapacity () const +{ + return this->capacity; } -inline void pvInfo::unlinkPV () +inline unsigned pvInfo::getElementCount () const +{ + return this->elementCount; +} + +inline void pvInfo::setElementCount (unsigned newCount) +{ + this->elementCount = newCount; +} + +inline void pvInfo::unlinkPV () { this->pPV = NULL; } diff --git a/src/template/base/top/caServerApp/exVectorPV.cc b/src/template/base/top/caServerApp/exVectorPV.cc index 22e642fab..932188806 100644 --- a/src/template/base/top/caServerApp/exVectorPV.cc +++ b/src/template/base/top/caServerApp/exVectorPV.cc @@ -3,9 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ #include "exServer.h" @@ -48,7 +47,7 @@ unsigned exVectorPV::maxDimension() const aitIndex exVectorPV::maxBound (unsigned dimension) const { if (dimension==0u) { - return this->info.getElementCount(); + return this->info.getCapacity(); } else { return 0u; @@ -60,45 +59,40 @@ aitIndex exVectorPV::maxBound (unsigned dimension) const // void exVectorPV::scan() { - caStatus status; - double radians; - smartGDDPointer pDD; - aitFloat32 *pF, *pFE; - const aitFloat32 *pCF; - float newValue; - float limit; - exVecDestructor *pDest; - int gddStatus; + static epicsTime startTime = epicsTime::getCurrent(); - // - // update current time (so we are not required to do - // this every time that we write the PV which impacts - // throughput under sunos4 because gettimeofday() is - // slow) + // update current time // this->currentTime = epicsTime::getCurrent(); - - pDD = new gddAtomic (gddAppType_value, aitEnumFloat64, - 1u, this->info.getElementCount()); + + // demonstrate a changing array size + unsigned ramp = 15 & (unsigned) (this->currentTime - startTime); + unsigned newSize = this->info.getCapacity(); + if (newSize > ramp) { + newSize -= ramp; + } + + smartGDDPointer pDD = new gddAtomic (gddAppType_value, aitEnumFloat64, + 1u, newSize); if ( ! pDD.valid () ) { return; } - + // // smart pointer class manages reference count after this point // - gddStatus = pDD->unreference(); - assert (!gddStatus); + gddStatus gdds = pDD->unreference(); + assert(!gdds); // // allocate array buffer // - pF = new aitFloat32 [this->info.getElementCount()]; + aitFloat64 * pF = new aitFloat64 [newSize]; if (!pF) { return; } - pDest = new exVecDestructor; + exVecDestructor * pDest = new exVecDestructor; if (!pDest) { delete [] pF; return; @@ -114,37 +108,39 @@ void exVectorPV::scan() // double check for reasonable bounds on the // current value // - pCF=NULL; - if ( this->pValue.valid () ) { - if (this->pValue->dimension()==1u) { - const gddBounds *pB = this->pValue->getBounds(); - if (pB[0u].size()==this->info.getElementCount()) { - pCF = *this->pValue; - } - } + const aitFloat64 *pCF = NULL, *pCFE = NULL; + if (this->pValue.valid () && + this->pValue->dimension() == 1u) { + const gddBounds *pB = this->pValue->getBounds(); + + pCF = *this->pValue; + pCFE = &pCF[pB->size()]; } - pFE = &pF[this->info.getElementCount()]; - while (pFinfo.getHopr(); + newValue += (sin (radians) / 10.0); + double limit = this->info.getHopr(); newValue = epicsMin (newValue, limit); - limit = (float) this->info.getLopr(); + limit = this->info.getLopr(); newValue = epicsMax (newValue, limit); - *(pF++) = newValue; + *pF++ = newValue; } aitTimeStamp gddts = this->currentTime; pDD->setTimeStamp ( & gddts ); - status = this->update ( *pDD ); + caStatus status = this->update ( *pDD ); + this->info.setElementCount(newSize); + if ( status != S_casApp_success ) { errMessage (status, "vector scan update failed\n"); } @@ -166,7 +162,7 @@ void exVectorPV::scan() // caStatus exVectorPV::updateValue ( const gdd & value ) { - + aitUint32 newSize = 0; // // Check bounds of incoming request // (and see if we are replacing all elements - @@ -183,7 +179,9 @@ caStatus exVectorPV::updateValue ( const gdd & value ) if ( pb[0u].first() != 0u ) { return S_casApp_outOfBounds; } - else if ( pb[0u].size() > this->info.getElementCount() ) { + + newSize = pb[0u].size(); + if ( newSize > this->info.getCapacity() ) { return S_casApp_outOfBounds; } } @@ -193,14 +191,14 @@ caStatus exVectorPV::updateValue ( const gdd & value ) // return S_casApp_outOfBounds; } - + // // Create a new array data descriptor // (so that old values that may be referenced on the // event queue are not replaced) // - smartGDDPointer pNewValue ( new gddAtomic ( gddAppType_value, aitEnumFloat64, - 1u, this->info.getElementCount() ) ); + smartGDDPointer pNewValue ( new gddAtomic ( gddAppType_value, aitEnumFloat64, + 1u, newSize ) ); if ( ! pNewValue.valid() ) { return S_casApp_noMemory; } @@ -211,21 +209,20 @@ caStatus exVectorPV::updateValue ( const gdd & value ) // gddStatus gdds = pNewValue->unreference( ); assert ( ! gdds ); - + // // allocate array buffer // - aitFloat64 * pF = new aitFloat64 [this->info.getElementCount()]; + aitFloat64 * pF = new aitFloat64 [newSize]; if (!pF) { return S_casApp_noMemory; } - + // // Install (and initialize) array buffer // if no old values exist // - unsigned count = this->info.getElementCount(); - for ( unsigned i = 0u; i < count; i++ ) { + for ( unsigned i = 0u; i < newSize; i++ ) { pF[i] = 0.0f; } @@ -240,7 +237,7 @@ caStatus exVectorPV::updateValue ( const gdd & value ) // (do this before we increment pF) // pNewValue->putRef ( pF, pDest ); - + // // copy in the values that they are writing // @@ -248,9 +245,10 @@ caStatus exVectorPV::updateValue ( const gdd & value ) if ( gdds ) { return S_cas_noConvert; } - + this->pValue = pNewValue; - + this->info.setElementCount(newSize); + return S_casApp_success; } @@ -261,6 +259,6 @@ caStatus exVectorPV::updateValue ( const gdd & value ) // void exVecDestructor::run ( void *pUntyped ) { - aitFloat32 * pf = reinterpret_cast < aitFloat32 * > ( pUntyped ); + aitFloat64 * pf = reinterpret_cast < aitFloat64 * > ( pUntyped ); delete [] pf; } diff --git a/src/template/base/top/configure/CONFIG_SITE b/src/template/base/top/configure/CONFIG_SITE index 208aa7e68..212485ebe 100644 --- a/src/template/base/top/configure/CONFIG_SITE +++ b/src/template/base/top/configure/CONFIG_SITE @@ -34,3 +34,10 @@ CHECK_RELEASE = YES # or CROSS_OPT settings from base/configure/CONFIG_SITE #HOST_OPT = NO #CROSS_OPT = NO + +# These allow developers to override the CONFIG_SITE variable +# settings without having to modify the configure/CONFIG_SITE +# file itself. +-include $(TOP)/../CONFIG_SITE.local +-include $(TOP)/configure/CONFIG_SITE.local + diff --git a/src/template/base/top/configure/RELEASE b/src/template/base/top/configure/RELEASE index ba28bcb26..dbd742b45 100644 --- a/src/template/base/top/configure/RELEASE +++ b/src/template/base/top/configure/RELEASE @@ -35,3 +35,9 @@ EPICS_BASE = _EPICS_BASE_ # Set RULES here if you want to use build rules from somewhere # other than EPICS_BASE: #RULES = $(MODULES)/build-rules + +# These allow developers to override the RELEASE variable settings +# without having to modify the configure/RELEASE file itself. +-include $(TOP)/../RELEASE.local +-include $(TOP)/configure/RELEASE.local + diff --git a/src/tools/DBD/Output.pm b/src/tools/DBD/Output.pm index 6e9d67b3c..77386f0bd 100644 --- a/src/tools/DBD/Output.pm +++ b/src/tools/DBD/Output.pm @@ -101,7 +101,7 @@ sub OutputBreaktables { my ($out, $breaktables) = @_; while (my ($name, $breaktable) = each %{$breaktables}) { printf $out "breaktable(\"%s\") {\n", $name; - printf $out " point(%s, %s)\n", @{$_} + printf $out " %s, %s\n", @{$_} foreach $breaktable->points; print $out "}\n"; } diff --git a/src/tools/DBD/Recordtype.pm b/src/tools/DBD/Recordtype.pm index 8c8fabbae..e4b0d5481 100644 --- a/src/tools/DBD/Recordtype.pm +++ b/src/tools/DBD/Recordtype.pm @@ -106,7 +106,7 @@ sub equals { my ($new, $known) = @_; return 0 if ! $known->fields; return 1 if ! $new->fields; - dieContext("Duplicate definition of record type '$a->{NAME}'"); + dieContext("Duplicate definition of record type '$known->{NAME}'"); } sub toDeclaration { diff --git a/src/tools/convertRelease.pl b/src/tools/convertRelease.pl index 919e3a54d..e2f13a556 100644 --- a/src/tools/convertRelease.pl +++ b/src/tools/convertRelease.pl @@ -5,7 +5,7 @@ # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. +# in file LICENSE that is included with this distribution. #************************************************************************* # # Convert configure/RELEASE file(s) into something else. @@ -121,15 +121,16 @@ sub dllPath { unlink $outfile; open(OUT, ">$outfile") or die "$! creating $outfile"; print OUT "\@ECHO OFF\n"; - print OUT "PATH \%PATH\%;", join(';', binDirs()), "\n"; + # This SET syntax is essential for supporting embedded spaces and '&' + # characters in both the PATH variable and the new directory components + print OUT "SET \"PATH=", join(';', binDirs(), '%PATH%'), "\"\n"; close OUT; } sub relPaths { unlink $outfile; open(OUT, ">$outfile") or die "$! creating $outfile"; - print OUT "export PATH=\$PATH:", - join(':', map {m/\s/ ? "\"$_\"" : $_ } binDirs()), "\n"; + print OUT "export PATH=\"", join(':', binDirs(), '$PATH'), "\"\n"; close OUT; } @@ -153,21 +154,21 @@ sub binDirs { sub cdCommands { die "Architecture not set (use -a option)" unless ($arch); my @includes = grep !m/^(RULES | TEMPLATE_TOP)$/x, @apps; - + unlink($outfile); open(OUT,">$outfile") or die "$! creating $outfile"; - + my $startup = $cwd; $startup =~ s/^$root/$iocroot/o if ($opt_t); $startup =~ s/([\\"])/\\$1/g; # escape back-slashes and double-quotes - + print OUT "startup = \"$startup\"\n"; - + my $ioc = $cwd; $ioc =~ s/^.*\///; # iocname is last component of directory name - + print OUT "putenv(\"IOC=$ioc\")\n"; - + foreach my $app (@includes) { my $iocpath = my $path = $macros{$app}; $iocpath =~ s/^$root/$iocroot/o if ($opt_t); @@ -189,15 +190,15 @@ sub cdCommands { # sub envPaths { my @includes = grep !m/^ (RULES | TEMPLATE_TOP) $/x, @apps; - + unlink($outfile); open(OUT,">$outfile") or die "$! creating $outfile"; - + my $ioc = $cwd; $ioc =~ s/^.*\///; # iocname is last component of directory name - + print OUT "epicsEnvSet(\"IOC\",\"$ioc\")\n"; - + foreach my $app (@includes) { my $iocpath = my $path = $macros{$app}; $iocpath =~ s/^$root/$iocroot/o if ($opt_t); @@ -224,7 +225,7 @@ sub checkRelease { expandRelease(\%check); delete $check{TOP}; delete $check{EPICS_HOST_ARCH}; - + while (my ($parent, $ppath) = each %check) { if (exists $macros{$parent} && AbsPath($macros{$parent}) ne AbsPath($ppath)) { @@ -269,4 +270,3 @@ sub checkRelease { print "\n" if $status; exit $status; } -