Merge branch 7.0.9 into PSI-7.0

This commit is contained in:
2025-03-03 17:19:23 +01:00
49 changed files with 584 additions and 151 deletions

View File

@ -48,11 +48,11 @@ EPICS_VERSION = 7
EPICS_REVISION = 0 EPICS_REVISION = 0
# EPICS_MODIFICATION must be a number >=0 and <256 # EPICS_MODIFICATION must be a number >=0 and <256
EPICS_MODIFICATION = 8 EPICS_MODIFICATION = 9
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
# Not included in the official EPICS version number if zero # Not included in the official EPICS version number if zero
EPICS_PATCH_LEVEL = 2 EPICS_PATCH_LEVEL = 1
# Immediately after an official release the EPICS_PATCH_LEVEL is incremented # Immediately after an official release the EPICS_PATCH_LEVEL is incremented
# and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions) # and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions)
@ -71,6 +71,3 @@ endif
EPICS_SHORT_VERSION=$(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION)$(EPICS_PATCH_VSTRING) EPICS_SHORT_VERSION=$(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION)$(EPICS_PATCH_VSTRING)
EPICS_VERSION_NUMBER=$(EPICS_SHORT_VERSION)$(EPICS_DEV_SNAPSHOT)$(EPICS_SITE_VSTRING) EPICS_VERSION_NUMBER=$(EPICS_SHORT_VERSION)$(EPICS_DEV_SNAPSHOT)$(EPICS_SITE_VSTRING)
EPICS_VERSION_STRING="EPICS Version $(EPICS_VERSION_NUMBER)" EPICS_VERSION_STRING="EPICS Version $(EPICS_VERSION_NUMBER)"
# Provide this in case anyone is still using the old name
COMMIT_DATE="-no-date-"

View File

@ -2,7 +2,7 @@
EPICS_CA_MAJOR_VERSION = 4 EPICS_CA_MAJOR_VERSION = 4
EPICS_CA_MINOR_VERSION = 14 EPICS_CA_MINOR_VERSION = 14
EPICS_CA_MAINTENANCE_VERSION = 4 EPICS_CA_MAINTENANCE_VERSION = 6
# Development flag, set to zero for release versions # Development flag, set to zero for release versions

View File

@ -1,7 +1,7 @@
# Version number for the database APIs and shared library # Version number for the database APIs and shared library
EPICS_DATABASE_MAJOR_VERSION = 3 EPICS_DATABASE_MAJOR_VERSION = 3
EPICS_DATABASE_MINOR_VERSION = 23 EPICS_DATABASE_MINOR_VERSION = 24
EPICS_DATABASE_MAINTENANCE_VERSION = 1 EPICS_DATABASE_MAINTENANCE_VERSION = 1
# Development flag, set to zero for release versions # Development flag, set to zero for release versions

View File

@ -1,7 +1,7 @@
# Version number for the libcom APIs and shared library # Version number for the libcom APIs and shared library
EPICS_LIBCOM_MAJOR_VERSION = 3 EPICS_LIBCOM_MAJOR_VERSION = 3
EPICS_LIBCOM_MINOR_VERSION = 23 EPICS_LIBCOM_MINOR_VERSION = 24
EPICS_LIBCOM_MAINTENANCE_VERSION = 1 EPICS_LIBCOM_MAINTENANCE_VERSION = 1
# Development flag, set to zero for release versions # Development flag, set to zero for release versions

View File

@ -92,3 +92,6 @@ EPICS_IOC_LOG_FILE_NAME=
EPICS_IOC_LOG_FILE_COMMAND= EPICS_IOC_LOG_FILE_COMMAND=
EPICS_IOC_LOG_FILE_LIMIT=1000000 EPICS_IOC_LOG_FILE_LIMIT=1000000
# Set to 'YES' to call abort() rather than suspend the current thread
# when an assert() fails
EPICS_ABORT_ON_ASSERT=NO

View File

@ -0,0 +1,11 @@
# CONFIG.darwin-aarch64-debug.Common
#
# Definitions for darwin-aarch64-debug host builds - darwin-aarch64 target build with debug compiler flags
# Sites may override these definitions in CONFIG_SITE.darwin-aarch64-debug.Common
#-------------------------------------------------------
include $(CONFIG)/os/CONFIG.darwin-aarch64.Common
# Removes -O optimization and adds -g compile option
HOST_OPT=NO

View File

@ -0,0 +1,14 @@
# CONFIG.darwin-aarch64.darwin-aarch64-debug
#
# Definitions for darwin-aarch64 host - darwin-aarch64-debug target build with debug compiler flags
# Sites may override these definitions in CONFIG_SITE.darwin-aarch64.darwin-aarch64-debug
#-------------------------------------------------------
-include $(CONFIG)/os/CONFIG.Common.darwin-aarch64
-include $(CONFIG)/os/CONFIG.darwin-aarch64.darwin-aarch64
-include $(CONFIG)/os/CONFIG_SITE.Common.darwin-aarch64
-include $(CONFIG)/os/CONFIG_SITE.darwin-aarch64.darwin-aarch64
BUILD_CLASS=HOST
HOST_OPT = NO

View File

@ -0,0 +1,11 @@
# CONFIG.darwin-x86-debug.Common
#
# Definitions for darwin-x86-debug host builds - darwin-x86 target build with debug compiler flags
# Sites may override these definitions in CONFIG_SITE.darwin-x86-debug.Common
#-------------------------------------------------------
include $(CONFIG)/os/CONFIG.darwin-x86.Common
# Removes -O optimization and adds -g compile option
HOST_OPT=NO

View File

@ -64,7 +64,3 @@ COMMANDLINE_LIBRARY ?= EPICS
#else #else
COMMANDLINE_LIBRARY ?= $(strip $(if $(wildcard $(if $(GNU_DIR),$(GNU_DIR)/include/readline/readline.h)), READLINE, EPICS)) COMMANDLINE_LIBRARY ?= $(strip $(if $(wildcard $(if $(GNU_DIR),$(GNU_DIR)/include/readline/readline.h)), READLINE, EPICS))
#endif #endif
#if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE>2
OP_SYS_CPPFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2
#endif

View File

@ -8,41 +8,142 @@ under the 3.15 release to which they were originally committed.** Thus it is
important to read more than just the first section to understand everything that important to read more than just the first section to understand everything that
has changed in each release. has changed in each release.
The PVA submodules each have their own individual sets of release notes which The external PVA submodules each have their own individual release notes files.
should also be read to understand what has changed since earlier releases: However the entries describing changes included in those submodules since EPICS
7.0.5 have now been copied into the appropriate place of this file.
- [normativeTypes](https://github.com/epics-base/normativeTypesCPP/blob/master/documentation/RELEASE_NOTES.md) __This version of EPICS has not been released yet.__
- [pvAccess](http://epics-base.github.io/pvAccessCPP/pvarelease_notes.html)
- [pvData](http://epics-base.github.io/pvDataCPP/release_notes.html)
- [pvDatabase](https://github.com/epics-base/pvDatabaseCPP/blob/master/documentation/RELEASE_NOTES.md)
- [pva2pva](https://epics-base.github.io/pva2pva/release_notes.html)
- [pvaClient](https://github.com/epics-base/pvaClientCPP/blob/master/documentation/RELEASE_NOTES.md)
**This version of EPICS has not been released yet.** ## Changes made on the 7.0 branch since 7.0.9
## Changes made on the 7.0 branch since 7.0.8.1 __Add new items below here__
### DBE_PROPERTY event rate changed -----
Updating property fields now only post DBE_PROPERTY events if the ## EPICS Release 7.0.9
### Core documentation published at ReadTheDocs
The `documentation` directory's `Makefile` can now run various publication scripts including Sphinx and Doxygen to generate formatted documentation that is now being published
[at docs.epics-controls.org](https://docs.epics-controls.org/projects/base/en/latest/index.html)
and integrated into the main [EPICS Documentation website](https://docs.epics-controls.org/en/latest/index.html).
The best place to find out more about these mechanisms is the
[Contribution Guide](https://docs.epics-controls.org/en/latest/CONTRIBUTING.html)
although it doesn't currently cover the new processes added to epics-base.
Much of the documentation generated from .dbd.pod files at build time is now
also being converted into MarkDown (.md) files and installed into the top-level
`doc` directory. Some users might find it quicker to look up information about a
record type by opening these files in a text editor intead of opening a browser
and loading the HTML versions or finding and opening the files from the EPICS
Documentation site.
### Post monitors from compress record when it's reset
Writing into a compress record's `RES` field now posts a monitor event instead
of only changing `VAL`. Monitor clients will therefore receive an empty array.
### The AMSG error message propagates through MSS links
A database link with the MSS attribute will now propagate not only SEVR and
STAT, but also AMSG. This field contains additional information that complements
STAT. Links with MS or MSI attributes do not propagate STAT, and therefore do
not propagate AMSG, either.
Channel Access links do not propagate AMSG, regardless of the MSS attribute,
because the message is not available as Channel metadata.
### Reloading record aliases
Aliases can now be defined more than once as long as they still refer to the
same record, unless the global variable `dbRecordsOnceOnly` is non-zero.
This allows database files to be loaded multiple times, even if they contain
alias definitions.
### `DBE_PROPERTY` event rate changed
Updating property fields now only posts `DBE_PROPERTY` events if the
field actually changed. field actually changed.
### Changes to msi related to include paths
There are two changes to `msi` included here.
`msi` now treats files included by .template or .substutiions files in a more
consistent way: for relative paths, it will always look relative to the current
working directory if no `-I` flags are passed, and if they are passed then it
will search for the _relative_ path from each of those flags. That is, the
following will now find the file `bar.template` located at
`/some/path/rel/path/bar.template`
```
$ cat foo.substitutions
file rel/path/bar.template {
# contents
}
$ msi -I /some/path foo.substitutions
```
Note that this does provide one change from previous behaviour: when opening a
file from the command line, `msi` will not use the `-I`-specified paths to
search for the file, but will only work relative to the current working
directory, consistent with most commandline utilities.
### Allow users to delete previously created records from the database ### Allow users to delete previously created records from the database
From this release, record instances and aliases that have already been loaded From this release, record instances and aliases that have already been loaded
by an IOC can be removed from the database again before the call to iocInit by an IOC can be removed from the database again before the call to iocInit
by loading a second instance of the named records but using `"#"` in place of by loading a second instance of the named records but using `"#"` in place of
the record type. Values for the fields are not required or advised, just use the record type. Values for the fields are not required or advised, just use
an empty record body { }. This is useful when a template defines records that an empty record body `{}`. This is useful when a template defines records that
are not wanted in some IOCs, without having to split or duplicate the original are not wanted in some IOCs, without having to split or duplicate the original
template. template.
For example this will remove the record named "unwanted": For example this will remove the record named "unwanted":
``` ```
record("#", "unwanted") { } record("#", "unwanted") {}
``` ```
### Only keep readline history for interactive sessions
Previously, all IOCsh commands were persisited in the libreadline history
(when readline support is included).
Going forward, only interactive commands are saved.
### Type change to asTrap serverSpecific data
Change `void*` to `dbChannel*` in `asTrapWriteBeforeWithData()` and
`asTrapWriteMessage::serverSpecific` to reflect the reality since
the `dbAddr*` to `dbChannel*` migration.
External code wishing to support both before and after 3.15 should
already be conditionally casting to/from the appropriate type.
### Fix issues with `_FORTIFY_SOURCE=3`
This release fixes the false positives failures whhen building with `_FORTIFY_SOURCE` level 3.
The override introduced in 7.0.8.1 has been removed.
### Other
- genVersionHeader: work with git submodules and worktrees.
- avoid UB with self `pthread_join()`
- freebsd: Add support for x86 and amd64 builds
- Clear AMSG when SEVR becomes zero.
- `seqRecord` fix support for link `DLY0`
- Add `ABORT_ON_ASSERT` flag to `CONFIG_SITE_ENV`
- rationalize osdMutex
### Submodule updates
The pvDatabase module was updated to version 4.7.2:
* Resolved issue with changed field set in the case where the top level (master)
field ("_") is not requested by the client, but the master field callback causes
all fields to be marked as updated, rather than only those fields that have
actually been modified.
-----
## EPICS Release 7.0.8.1 ## EPICS Release 7.0.8.1
### Limit to `_FORTIFY_SOURCE=2` ### Limit to `_FORTIFY_SOURCE=2`
@ -280,6 +381,50 @@ The floating point modulo function `FMOD(NUM,DEN)` has been added to the CALC
expression engine and is available to all software using that (calc and calcout expression engine and is available to all software using that (calc and calcout
record types, access security library and some extensions). record types, access security library and some extensions).
### Submodule updates
The pvData module was updated to version 8.0.6:
- Compatible changes
- Actually enable JSON-5 output in PVStructure::Formatter::JSON when available.
- Fix unaligned access issues for some ARM/Linux targets.
The pvAccess module was updated to version 7.1.7:
- Changes
- Registering the PVA server with the IOC now sets the `PVAS_SERVER_PORT`
variable in the environment.
The pva2pva module was updated to version 1.4.1:
- Bug Fixes
- `dbLoadGroup` was fixed
- Additions
- Support for "meta" member at top of array of structs
The pvDatabase module was updated to version 4.7.1:
* Added data distributor plugin which can be used for distributing data between
a group of clients. The plugin is triggered by the request string of the
form:
`_[distributor=group:<group id>;set:<set_id>;trigger:<field_name>;updates:<n_updates>;mode:<update_mode>]`
The plugin parameters are optional and are described bellow:
- group: this parameter indicates a group that client application belongs to (default value: "default"); groups of clients are completely independent of each other
- set: this parameter designates a client set that application belongs to within its group (default value: "default")
- trigger: this is the PV structure field that distinguishes different channel updates (default value: "timeStamp"); for example, for area detector images one could use the "uniqueId" field of the NTND structure
- updates: this parameter configures how many sequential updates a client (or a set of clients) will receive before the data distributor starts updating the next one (default value: "1")
- mode: this parameter configures how channel updates are to be distributed between clients in a set:
- one: update goes to one client per set
- all: update goes to all clients in a set
- default is "one" if client set id is not specified, and "all" if set id is specified
----- -----
## EPICS Release 7.0.7 ## EPICS Release 7.0.7
@ -358,10 +503,10 @@ changed to `(p)->dtor`.
The order over operations when processing a waveformRecord is adjusted The order over operations when processing a waveformRecord is adjusted
so that updates to NORD is posted with the correct timestamp. so that updates to NORD is posted with the correct timestamp.
### Automatic COMMANDLINE_LIBRARY w/ newer compilers ### Automatic `COMMANDLINE_LIBRARY` with newer compilers
When built with a compiler supporting `__has_include<>`, the presence When built with a compiler supporting `__has_include<>`, the presence
of the `<readline/readline.h>` will be used to automatically determine of a `readline/readline.h` header will be used to automatically determine
a default value for `COMMANDLINE_LIBRARY`. a default value for `COMMANDLINE_LIBRARY`.
Mingw builds with readline support now link `-ltermcap` instead of `-lcurses`. Mingw builds with readline support now link `-ltermcap` instead of `-lcurses`.
@ -530,6 +675,44 @@ or if unsupported (`$TERM` not set, or Windows < 10).
The `dbnd` server side filter now passes through alarm and property The `dbnd` server side filter now passes through alarm and property
change events, even when not exceeding the deadband. change events, even when not exceeding the deadband.
### Submodule updates
The pvData module was updated to version 8.0.5:
- Compatible changes
- Internal changes to use the YAJL API for generating JSON and JSON-5 output.
The pvAccess module was updated to version 7.1.6:
- Changes to caProvider
- Bug fix related to enum values.
- More internal changes to improve performance when connecting tens of
thousands of CA channels.
- Several minor internal improvements.
The pva2pva module was updated to version 1.4.0:
- Bug Fixes
- Apply ACF when writing to atomic group
- Additions
- Add new "structure" to @ref qsrv_group_map_types
- Changes
- Add Access Security hooks for single and group writes.
- Enable "Async Soft Channel" for output links
- When built against Base 7.0.6.1, set timeStamp.userTag from UTAG field.
- Add DTYP="QSRV Set UTag" for longin, which sets UTAG=VAL.
The pvDatabase module was updated to version 4.7.0:
* Added support for the whole structure (master field) server side plugins.
The whole structure is identified as the `_` string, and a pvRequest string
that applies a plugin to it takes the form:
`field(_[XYZ=A:3;B:uniqueId])`
where `XYZ` is the name of a specific filter plugin that takes parameters
`A` and `B` with values `3` and `uniqueId` respectively.
----- -----
## EPICS Release 7.0.6.1 ## EPICS Release 7.0.6.1
@ -596,6 +779,15 @@ This was done to simplify the code and may have improved performance slightly fo
Many of the built-in record types have had improvements to their documentation with additional fields added to the tables, rewrites of descriptions and links to other documents added or fixed. Many of the built-in record types have had improvements to their documentation with additional fields added to the tables, rewrites of descriptions and links to other documents added or fixed.
### Submodule updates
The pvAccess module was updated to version 7.1.4:
- Changes to caProvider
- Resolve issues with pv structures that don't have a value field
- Add NULL checks for handling unusual structures
- Speed up channel creation when using large numbers of channels
----- -----
## EPICS Release 7.0.6 ## EPICS Release 7.0.6
@ -606,7 +798,7 @@ These target architectures have been removed:
+ darwin-ppc, darwin-ppcx86 + darwin-ppc, darwin-ppcx86
+ linux-386, linux-486, linux-586, linux-686, linux-athlon (cross-build) + linux-386, linux-486, linux-586, linux-686, linux-athlon (cross-build)
+ linux-cris, linux-cris_v10, linux-cris_v32 (cross-build) + linux-cris, linux-cris\_v10, linux-cris\_v32 (cross-build)
+ RTEMS-at91rm9200ek, RTEMS-gen68360, RTEMS-mcp750, RTEMS-mvme167, + RTEMS-at91rm9200ek, RTEMS-gen68360, RTEMS-mcp750, RTEMS-mvme167,
RTEMS-psim (cross-build) RTEMS-psim (cross-build)
@ -624,9 +816,9 @@ running on RTEMS 5:
- RTEMS-beagleboneblack - RTEMS-beagleboneblack
- RTEMS-pc686 - RTEMS-pc686
- RTEMS-qoriq_e500 (MVME2500) - RTEMS-qoriq\_e500 (MVME2500)
- RTEMS-xilinx_zynq_a9_qemu - RTEMS-xilinx\_zynq\_a9\_qemu
- RTEMS-xilinx_zynq_zedboard - RTEMS-xilinx\_zynq\_zedboard
The EPICS support for RTEMS 4 has always relied on RTEMS-specific The EPICS support for RTEMS 4 has always relied on RTEMS-specific
kernel APIs which cannot be used on an SMP system, so a new port was kernel APIs which cannot be used on an SMP system, so a new port was
@ -638,7 +830,7 @@ to run `make distclean` if switching a single source tree from one
to the other (both header files and dependency files are different to the other (both header files and dependency files are different
between the two and must be cleaned out). between the two and must be cleaned out).
The configuration variable RTEMS_VERSION in the EPICS config file The configuration variable `RTEMS_VERSION` in the EPICS config file
`configure/os/CONFIG_SITE.Common.RTEMS` must be set to the full 3- `configure/os/CONFIG_SITE.Common.RTEMS` must be set to the full 3-
part version number for RTEMS 4 releases, e.g. `4.9.1`, `4.10.2` part version number for RTEMS 4 releases, e.g. `4.9.1`, `4.10.2`
but for RTEMS 5.1 and later it must only contain the major version but for RTEMS 5.1 and later it must only contain the major version
@ -836,6 +1028,39 @@ Test programs written directly in Perl as a `.plt` script should implement a
similar timeout for themselves. The "netget" test in Base does this in a way similar timeout for themselves. The "netget" test in Base does this in a way
that works on Windows as well as Unix-like hosts. that works on Windows as well as Unix-like hosts.
### Submodule updates
The pvAccess module was updated to version 7.1.4:
- Changes
- Adjust argument parsing with pvput (Jesus Vasquez).
The pva2pva module was updated to version 1.3.1:
- Bug Fixes
- Correct handling for server side filters.
- Changes
- Syncing softMain.cpp with epics-base
The pvDatabase module was updated to version 4.6.0:
* Access Security is now supported.
* <b>special</b> has been revised and extended.
* addRecord, removeRecord, processRecord, and traceRecord are replaced by pvdbcr versions.
* <b>support</b> is DEPRECATED
The pvaClient module was updated to version 4.8.0:
* `PvaClientNTMultiData::getChannelChangeFlags` is a new method. It fixes
issue #66.
* Fix for issue #68. Both `PvaClientArray` and `PvaClientField` are not longer
present. Neither was previously implemented.
* Several public methods are now protected. They were never meant to be called
by clients.
* Issue #70 has been fixed.
* Changes was made to increase the performance of `pvaMultiChannel`.
* doxygen changes were made.
----- -----
## EPICS Release 7.0.5 ## EPICS Release 7.0.5
@ -873,7 +1098,7 @@ compile device supports as loadable modules.
### Priority inversion safe Posix mutexes ### Priority inversion safe Posix mutexes
On Posix systems, epicsMutex now support priority inheritance if available. On Posix systems, epicsMutex now support priority inheritance if available.
The IOC needs to run with SCHED_FIFO engaged to use these. The IOC needs to run with `SCHED_FIFO` engaged to use these.
Support for Posix implementations before POSIX.1-2001 (`_XOPEN_SOURCE < 500`, Support for Posix implementations before POSIX.1-2001 (`_XOPEN_SOURCE < 500`,
glibc version &lt; 2.3.3) has been dropped. glibc version &lt; 2.3.3) has been dropped.
@ -1006,14 +1231,14 @@ properly handle zero-length arrays. The `caget`, `caput` and `camonitor`
client programs are known to work with empty arrays as long as they were client programs are known to work with empty arrays as long as they were
built with this or a later version of EPICS. built with this or a later version of EPICS.
#### Change to the db_access.h `dbr_size_n(TYPE, COUNT)` macro #### Change to the db\_access.h `dbr_size_n(TYPE, COUNT)` macro
When called with COUNT=0 this macro no longer returns the number of bytes When called with COUNT=0 this macro no longer returns the number of bytes
required for a scalar (1 element) but for an empty array (0 elements). required for a scalar (1 element) but for an empty array (0 elements).
Make sure code that uses this doesn't call it with COUNT=0 when it really Make sure code that uses this doesn't call it with COUNT=0 when it really
means COUNT=1. means COUNT=1.
Note that the db_access.h header file is included by cadef.h so the change Note that the db\_access.h header file is included by cadef.h so the change
can impact Channel Access client programs that use this macro. can impact Channel Access client programs that use this macro.
#### Channel Access support for zero-length arrays #### Channel Access support for zero-length arrays
@ -1123,6 +1348,35 @@ GNUmake added the directive `undefine` in version 3.82 to allow variables to
be undefined. Support for this has been added to the EPICS Release file parser, be undefined. Support for this has been added to the EPICS Release file parser,
so `undefine` can now be used in configure/RELEASE files to unset variables. so `undefine` can now be used in configure/RELEASE files to unset variables.
### Submodule updates
The pvData module was updated to version 8.0.4:
- Incompatible changes
- Remove `ByteBuffer::align()`
- Compatible changes
- Deprecate `SerializableControl::alignBuffer()` and
`DeserializableControl::alignData()`
- `shared_vector_convert<>()` fix convert of empty, untyped, array
The pvAccess module was updated to version 7.1.3:
- Bug fixes
- Increase default TCP timeout to 40 seconds.
Applies a 4/3 multiplier on `$EPICS_PVA_CONN_TMO` for compatibility.
- CA Provider implementation restructured to simplify, reduce duplication
and fix issues #163 and #165.
- Changes
- Enable building of pvtools to all except vxWorks, RTEMS and iOS.
The pva2pva module was updated to version 1.3.0:
- Changes
- Add `dbLoadGroup()` iocsh function to read group JSON definitions
from a file. Mappings in files must refer to full record names
instead of fields. eg. 'recname.VAL' instead of 'VAL'.
----- -----
## EPICS Release 7.0.4.1 ## EPICS Release 7.0.4.1
@ -1188,7 +1442,7 @@ The following launchpad bugs have fixes included in this release:
operators on aarch64 operators on aarch64
- [lp: 1853148](https://bugs.launchpad.net/bugs/1853148), mingw compiler - [lp: 1853148](https://bugs.launchpad.net/bugs/1853148), mingw compiler
problem with printf/scanf formats problem with printf/scanf formats
- [lp: 1852653](https://bugs.launchpad.net/bugs/1852653), USE_TYPED_DSET - [lp: 1852653](https://bugs.launchpad.net/bugs/1852653), `USE_TYPED_DSET`
incompatible with C++ incompatible with C++
- [lp: 1862328](https://bugs.launchpad.net/bugs/1862328), Race condition on - [lp: 1862328](https://bugs.launchpad.net/bugs/1862328), Race condition on
IOC start leaves rsrv unresponsive IOC start leaves rsrv unresponsive
@ -1198,7 +1452,7 @@ The following launchpad bugs have fixes included in this release:
- [lp: 1868680](https://bugs.launchpad.net/bugs/1868680), Access Security file - [lp: 1868680](https://bugs.launchpad.net/bugs/1868680), Access Security file
reload (asInit) fails reload (asInit) fails
### \*_API macros in EPICS headers ### `*_API` macros in EPICS headers
Internally, the Com and ca libraries now express dllimport/export (Windows) Internally, the Com and ca libraries now express dllimport/export (Windows)
and symbol visibility (GCC) using library-specific macros (eg. `LIBCOM_API`) and symbol visibility (GCC) using library-specific macros (eg. `LIBCOM_API`)
@ -1432,7 +1686,7 @@ The API functions `epicsGetExecDir()` and `epicsGetExecName()` are also
added to `osiFileName.h` to provide runtime access to the directory or added to `osiFileName.h` to provide runtime access to the directory or
filename of the executable with which the process was started. filename of the executable with which the process was started.
### Decouple LINKER_USE_RPATH and STATIC_BUILD ### Decouple `LINKER_USE_RPATH` and `STATIC_BUILD`
Previously, setting `STATIC_BUILD=NO` implied `LINKER_USE_RPATH=NO`. Previously, setting `STATIC_BUILD=NO` implied `LINKER_USE_RPATH=NO`.
This is no longer the case. Setting `LINKER_USE_RPATH=YES` will This is no longer the case. Setting `LINKER_USE_RPATH=YES` will
@ -2011,7 +2265,7 @@ number instead, like this:
Channel Access does not (and probably never will) directly support 64-bit Channel Access does not (and probably never will) directly support 64-bit
integer types, so the new field types are presented to the CA server as integer types, so the new field types are presented to the CA server as
`DBF_DOUBLE` values. This means that field values larger than 2^52 `DBF_DOUBLE` values. This means that field values larger than 2^52
(0x10_0000_0000_0000 = 4503599627370496) cannot be transported over Channel (0x10\_0000\_0000\_0000 = 4503599627370496) cannot be transported over Channel
Access without their least significant bits being truncated. The EPICS V4 Access without their least significant bits being truncated. The EPICS V4
pvAccess network protocol _can_ transport 64-bit data types however, and a pvAccess network protocol _can_ transport 64-bit data types however, and a
future release of the pvaSrv module will connect this ability to the fields of future release of the pvaSrv module will connect this ability to the fields of

View File

@ -286,7 +286,11 @@ Inf (Infinite) value. UDF defaults to TRUE but can be set in a database file.
Record and device support routines which write to the VAL field are generally Record and device support routines which write to the VAL field are generally
responsible for setting and clearing UDF. responsible for setting and clearing UDF.
=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF The B<UDFS> field specifies the alarm severity that the record will be set to
whenever its value is undefined (i.e., the UDF field is 1). This includes the
initial severity of the record being undefined after the IOC boots.
=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF, UDFS
=cut =cut

View File

@ -226,9 +226,10 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
} }
if (!status && precord != dbChannelRecord(chan)) if (!status && precord != dbChannelRecord(chan))
recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode, recGblInheritSevrMsg(plink->value.pv_link.pvlMask & pvlOptMsMode,
plink->precord, plink->precord,
dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr); dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr,
dbChannelRecord(chan)->amsg);
return status; return status;
} }
@ -378,8 +379,8 @@ static long dbDbPutValue(struct link *plink, short dbrType,
dbCommon *pdest = dbChannelRecord(chan); dbCommon *pdest = dbChannelRecord(chan);
long status = dbPut(paddr, dbrType, pbuffer, nRequest); long status = dbPut(paddr, dbrType, pbuffer, nRequest);
recGblInheritSevr(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta, recGblInheritSevrMsg(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta,
psrce->nsev); psrce->nsev, psrce->namsg);
if (status) if (status)
return status; return status;

View File

@ -75,21 +75,21 @@ static const iocshFuncDef dbbFuncDef = {"dbb",1,dbbArgs,
"Set Breakpoint on a record\n" "Set Breakpoint on a record\n"
"This command spawns one breakpoint continuation task per lockset," "This command spawns one breakpoint continuation task per lockset,"
" in which further record execution is run\n"}; " in which further record execution is run\n"};
static void dbbCallFunc(const iocshArgBuf *args) { dbb(args[0].sval);} static void dbbCallFunc(const iocshArgBuf *args) { iocshSetError(dbb(args[0].sval));}
/* dbd */ /* dbd */
static const iocshArg dbdArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbdArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbdArgs[1] = {&dbdArg0}; static const iocshArg * const dbdArgs[1] = {&dbdArg0};
static const iocshFuncDef dbdFuncDef = {"dbd",1,dbdArgs, static const iocshFuncDef dbdFuncDef = {"dbd",1,dbdArgs,
"Remove breakpoint from a record.\n"}; "Remove breakpoint from a record.\n"};
static void dbdCallFunc(const iocshArgBuf *args) { dbd(args[0].sval);} static void dbdCallFunc(const iocshArgBuf *args) { iocshSetError(dbd(args[0].sval));}
/* dbc */ /* dbc */
static const iocshArg dbcArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbcArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbcArgs[1] = {&dbcArg0}; static const iocshArg * const dbcArgs[1] = {&dbcArg0};
static const iocshFuncDef dbcFuncDef = {"dbc",1,dbcArgs, static const iocshFuncDef dbcFuncDef = {"dbc",1,dbcArgs,
"Continue processing in a lockset until next breakpoint is found.\n"}; "Continue processing in a lockset until next breakpoint is found.\n"};
static void dbcCallFunc(const iocshArgBuf *args) { dbc(args[0].sval);} static void dbcCallFunc(const iocshArgBuf *args) { iocshSetError(dbc(args[0].sval));}
/* dbs */ /* dbs */
static const iocshArg dbsArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbsArg0 = { "record name",iocshArgStringRecord};
@ -97,12 +97,12 @@ static const iocshArg * const dbsArgs[1] = {&dbsArg0};
static const iocshFuncDef dbsFuncDef = {"dbs",1,dbsArgs, static const iocshFuncDef dbsFuncDef = {"dbs",1,dbsArgs,
"Step through record processing within a lockset.\n" "Step through record processing within a lockset.\n"
"If called without an argument, automatically steps with the last breakpoint.\n"}; "If called without an argument, automatically steps with the last breakpoint.\n"};
static void dbsCallFunc(const iocshArgBuf *args) { dbs(args[0].sval);} static void dbsCallFunc(const iocshArgBuf *args) { iocshSetError(dbs(args[0].sval));}
/* dbstat */ /* dbstat */
static const iocshFuncDef dbstatFuncDef = {"dbstat",0,0, static const iocshFuncDef dbstatFuncDef = {"dbstat",0,0,
"Print list of suspended records, and breakpoints set in locksets.\n"}; "Print list of suspended records, and breakpoints set in locksets.\n"};
static void dbstatCallFunc(const iocshArgBuf *args) { dbstat();} static void dbstatCallFunc(const iocshArgBuf *args) { iocshSetError(dbstat());}
/* dbp */ /* dbp */
static const iocshArg dbpArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbpArg0 = { "record name",iocshArgStringRecord};
@ -119,7 +119,9 @@ static const iocshFuncDef dbpFuncDef = {
" 3 - Fields of minor interest to a System developer.\n" " 3 - Fields of minor interest to a System developer.\n"
" 4 - Internal record fields.\n"}; " 4 - Internal record fields.\n"};
static void dbpCallFunc(const iocshArgBuf *args) static void dbpCallFunc(const iocshArgBuf *args)
{ dbp(args[0].sval,args[1].ival);} {
iocshSetError(dbp(args[0].sval,args[1].ival));
}
/* dbap */ /* dbap */
static const iocshArg dbapArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbapArg0 = { "record name",iocshArgStringRecord};
@ -127,7 +129,7 @@ static const iocshArg * const dbapArgs[1] = {&dbapArg0};
static const iocshFuncDef dbapFuncDef = {"dbap",1,dbapArgs, static const iocshFuncDef dbapFuncDef = {"dbap",1,dbapArgs,
"Auto Print.\n" "Auto Print.\n"
"Toggle automatic printing after processing a record that has a breakpoint.\n"}; "Toggle automatic printing after processing a record that has a breakpoint.\n"};
static void dbapCallFunc(const iocshArgBuf *args) { dbap(args[0].sval);} static void dbapCallFunc(const iocshArgBuf *args) { iocshSetError(dbap(args[0].sval));}
/* dbsr */ /* dbsr */
static const iocshArg dbsrArg0 = { "interest level",iocshArgInt}; static const iocshArg dbsrArg0 = { "interest level",iocshArgInt};
@ -150,7 +152,7 @@ static const iocshFuncDef dbcarFuncDef = {"dbcar",2,dbcarArgs,
" 2 - Shows info. for all links.\n"}; " 2 - Shows info. for all links.\n"};
static void dbcarCallFunc(const iocshArgBuf *args) static void dbcarCallFunc(const iocshArgBuf *args)
{ {
dbcar(args[0].sval,args[1].ival); iocshSetError(dbcar(args[0].sval,args[1].ival));
} }
/* dbjlr */ /* dbjlr */
@ -162,7 +164,7 @@ static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs,
"List all JSON links in a record. If no record is specified, print for all\n"}; "List all JSON links in a record. If no record is specified, print for all\n"};
static void dbjlrCallFunc(const iocshArgBuf *args) static void dbjlrCallFunc(const iocshArgBuf *args)
{ {
dbjlr(args[0].sval,args[1].ival); iocshSetError(dbjlr(args[0].sval,args[1].ival));
} }
/* dbel */ /* dbel */
@ -176,7 +178,7 @@ static const iocshFuncDef dbelFuncDef = {"dbel",2,dbelArgs,
"Example: dbel aitest 2\n"}; "Example: dbel aitest 2\n"};
static void dbelCallFunc(const iocshArgBuf *args) static void dbelCallFunc(const iocshArgBuf *args)
{ {
dbel(args[0].sval, args[1].ival); iocshSetError(dbel(args[0].sval, args[1].ival));
} }
/* dba */ /* dba */
@ -187,7 +189,7 @@ static const iocshFuncDef dbaFuncDef = {"dba",1,dbaArgs,
"Print information in the dbAddr structure for a specific field.\n" "Print information in the dbAddr structure for a specific field.\n"
"If no field is specified, VAL is assumed.\n\n" "If no field is specified, VAL is assumed.\n\n"
"Example: dba(\"aitest.HIGH\")\n"}; "Example: dba(\"aitest.HIGH\")\n"};
static void dbaCallFunc(const iocshArgBuf *args) { dba(args[0].sval);} static void dbaCallFunc(const iocshArgBuf *args) { iocshSetError(dba(args[0].sval));}
/* dbl */ /* dbl */
static const iocshArg dblArg0 = { "record type",iocshArgString}; static const iocshArg dblArg0 = { "record type",iocshArgString};
@ -204,7 +206,7 @@ static const iocshFuncDef dblFuncDef = {"dbl",2,dblArgs,
" dbl(\"ai\",\"HIGH LOW VAL PREC\")\n"}; " dbl(\"ai\",\"HIGH LOW VAL PREC\")\n"};
static void dblCallFunc(const iocshArgBuf *args) static void dblCallFunc(const iocshArgBuf *args)
{ {
dbl(args[0].sval,args[1].sval); iocshSetError(dbl(args[0].sval,args[1].sval));
} }
/* dbnr */ /* dbnr */
@ -213,7 +215,7 @@ static const iocshArg * const dbnrArgs[1] = {&dbnrArg0};
static const iocshFuncDef dbnrFuncDef = {"dbnr",1,dbnrArgs, static const iocshFuncDef dbnrFuncDef = {"dbnr",1,dbnrArgs,
"List number of records and aliases by type.\n" "List number of records and aliases by type.\n"
"If verbose, list all record types regardless of being instanced\n"}; "If verbose, list all record types regardless of being instanced\n"};
static void dbnrCallFunc(const iocshArgBuf *args) { dbnr(args[0].ival);} static void dbnrCallFunc(const iocshArgBuf *args) { iocshSetError(dbnr(args[0].ival));}
/* dbli */ /* dbli */
static const iocshArg dbliArg0 = { "pattern",iocshArgString}; static const iocshArg dbliArg0 = { "pattern",iocshArgString};
@ -221,7 +223,7 @@ static const iocshArg * const dbliArgs[1] = {&dbliArg0};
static const iocshFuncDef dbliFuncDef = {"dbli",1,dbliArgs, static const iocshFuncDef dbliFuncDef = {"dbli",1,dbliArgs,
"List info() tags with names matching pattern.\n\n" "List info() tags with names matching pattern.\n\n"
"Example: dbli(\"autosave*\")\n"}; "Example: dbli(\"autosave*\")\n"};
static void dbliCallFunc(const iocshArgBuf *args) { dbli(args[0].sval);} static void dbliCallFunc(const iocshArgBuf *args) { iocshSetError(dbli(args[0].sval));}
/* dbla */ /* dbla */
static const iocshArg dblaArg0 = { "pattern",iocshArgStringRecord}; static const iocshArg dblaArg0 = { "pattern",iocshArgStringRecord};
@ -229,7 +231,7 @@ static const iocshArg * const dblaArgs[1] = {&dblaArg0};
static const iocshFuncDef dblaFuncDef = {"dbla",1,dblaArgs, static const iocshFuncDef dblaFuncDef = {"dbla",1,dblaArgs,
"List record alias()s by alias name pattern.\n\n" "List record alias()s by alias name pattern.\n\n"
"Example: dbla(\"alia*\")\n"}; "Example: dbla(\"alia*\")\n"};
static void dblaCallFunc(const iocshArgBuf *args) { dbla(args[0].sval);} static void dblaCallFunc(const iocshArgBuf *args) { iocshSetError(dbla(args[0].sval));}
/* dbgrep */ /* dbgrep */
static const iocshArg dbgrepArg0 = { "pattern",iocshArgStringRecord}; static const iocshArg dbgrepArg0 = { "pattern",iocshArgStringRecord};
@ -240,7 +242,7 @@ static const iocshFuncDef dbgrepFuncDef = {"dbgrep",1,dbgrepArgs,
" - \"?\", which matches 0 or one characters.\n" " - \"?\", which matches 0 or one characters.\n"
" - \"*\", which matches 0 or more characters.\n\n" " - \"*\", which matches 0 or more characters.\n\n"
"Example: dbgrep(\"*gpibAi*\")\n"}; "Example: dbgrep(\"*gpibAi*\")\n"};
static void dbgrepCallFunc(const iocshArgBuf *args) { dbgrep(args[0].sval);} static void dbgrepCallFunc(const iocshArgBuf *args) { iocshSetError(dbgrep(args[0].sval));}
/* dbgf */ /* dbgf */
static const iocshArg dbgfArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbgfArg0 = { "record name",iocshArgStringRecord};
@ -250,7 +252,7 @@ static const iocshFuncDef dbgfFuncDef = {"dbgf",1,dbgfArgs,
"Print current value of record field.\n" "Print current value of record field.\n"
"If no field name is specified, VAL is assumed.\n\n" "If no field name is specified, VAL is assumed.\n\n"
"Example: dbgf(\"aitest.VAL\")\n"}; "Example: dbgf(\"aitest.VAL\")\n"};
static void dbgfCallFunc(const iocshArgBuf *args) { dbgf(args[0].sval);} static void dbgfCallFunc(const iocshArgBuf *args) { iocshSetError(dbgf(args[0].sval));}
/* dbpf */ /* dbpf */
static const iocshArg dbpfArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbpfArg0 = { "record name",iocshArgStringRecord};
@ -261,7 +263,7 @@ static const iocshFuncDef dbpfFuncDef = {"dbpf",2,dbpfArgs,
"Change value of record field and read it back with dbgf.\n" "Change value of record field and read it back with dbgf.\n"
"If no field is specified, VAL is assumed\n"}; "If no field is specified, VAL is assumed\n"};
static void dbpfCallFunc(const iocshArgBuf *args) static void dbpfCallFunc(const iocshArgBuf *args)
{ dbpf(args[0].sval,args[1].sval);} { iocshSetError(dbpf(args[0].sval,args[1].sval));}
/* dbpr */ /* dbpr */
static const iocshArg dbprArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbprArg0 = { "record name",iocshArgStringRecord};
@ -279,14 +281,14 @@ static const iocshFuncDef dbprFuncDef = {
"Example: dbpr aitest 3\n" "Example: dbpr aitest 3\n"
}; };
static void dbprCallFunc(const iocshArgBuf *args) static void dbprCallFunc(const iocshArgBuf *args)
{ dbpr(args[0].sval,args[1].ival);} { iocshSetError(dbpr(args[0].sval,args[1].ival));}
/* dbtr */ /* dbtr */
static const iocshArg dbtrArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbtrArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbtrArgs[1] = {&dbtrArg0}; static const iocshArg * const dbtrArgs[1] = {&dbtrArg0};
static const iocshFuncDef dbtrFuncDef = {"dbtr",1,dbtrArgs, static const iocshFuncDef dbtrFuncDef = {"dbtr",1,dbtrArgs,
"Process record and then some fields.\n"}; "Process record and then some fields.\n"};
static void dbtrCallFunc(const iocshArgBuf *args) { dbtr(args[0].sval);} static void dbtrCallFunc(const iocshArgBuf *args) { iocshSetError(dbtr(args[0].sval));}
/* dbtgf */ /* dbtgf */
static const iocshArg dbtgfArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbtgfArg0 = { "record name",iocshArgStringRecord};
@ -296,7 +298,7 @@ static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs,
"Get and print the specified field with all possible DBR_* types\n" "Get and print the specified field with all possible DBR_* types\n"
"Example: dbtgf aitest\n" "Example: dbtgf aitest\n"
"Example: dbtgf aitest.VAL\n"}; "Example: dbtgf aitest.VAL\n"};
static void dbtgfCallFunc(const iocshArgBuf *args) { dbtgf(args[0].sval);} static void dbtgfCallFunc(const iocshArgBuf *args) { iocshSetError(dbtgf(args[0].sval));}
/* dbtpf */ /* dbtpf */
static const iocshArg dbtpfArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbtpfArg0 = { "record name",iocshArgStringRecord};
@ -308,7 +310,7 @@ static const iocshFuncDef dbtpfFuncDef = {"dbtpf",2,dbtpfArgs,
"for all possible DBR_* types\n\n" "for all possible DBR_* types\n\n"
"Example: dbtpf aitest 5.0\n"}; "Example: dbtpf aitest 5.0\n"};
static void dbtpfCallFunc(const iocshArgBuf *args) static void dbtpfCallFunc(const iocshArgBuf *args)
{ dbtpf(args[0].sval,args[1].sval);} { iocshSetError(dbtpf(args[0].sval,args[1].sval));}
/* dbior */ /* dbior */
static const iocshArg dbiorArg0 = { "driver name",iocshArgString}; static const iocshArg dbiorArg0 = { "driver name",iocshArgString};
@ -317,7 +319,7 @@ static const iocshArg * const dbiorArgs[] = {&dbiorArg0,&dbiorArg1};
static const iocshFuncDef dbiorFuncDef = {"dbior",2,dbiorArgs, static const iocshFuncDef dbiorFuncDef = {"dbior",2,dbiorArgs,
"Driver Report.\n"}; "Driver Report.\n"};
static void dbiorCallFunc(const iocshArgBuf *args) static void dbiorCallFunc(const iocshArgBuf *args)
{ dbior(args[0].sval,args[1].ival);} { iocshSetError(dbior(args[0].sval,args[1].ival));}
/* dbhcr */ /* dbhcr */
static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0, static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0,
@ -327,7 +329,7 @@ static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0,
"Use the UNIX sort command:\n" "Use the UNIX sort command:\n"
"dbhcr > report\n" "dbhcr > report\n"
"sort report > report.sorted\n"}; "sort report > report.sorted\n"};
static void dbhcrCallFunc(const iocshArgBuf *args) { dbhcr();} static void dbhcrCallFunc(const iocshArgBuf *args) { iocshSetError(dbhcr());}
/* gft */ /* gft */
static const iocshArg gftArg0 = { "record name",iocshArgStringRecord}; static const iocshArg gftArg0 = { "record name",iocshArgStringRecord};
@ -336,7 +338,7 @@ static const iocshFuncDef gftFuncDef = {"gft",1,gftArgs,
"Report dbChannel info and value.\n" "Report dbChannel info and value.\n"
"Example: gft aitest\n" "Example: gft aitest\n"
"Example: gft aitest.VAL\n"}; "Example: gft aitest.VAL\n"};
static void gftCallFunc(const iocshArgBuf *args) { gft(args[0].sval);} static void gftCallFunc(const iocshArgBuf *args) { iocshSetError(gft(args[0].sval));}
/* pft */ /* pft */
static const iocshArg pftArg0 = { "record name",iocshArgStringRecord}; static const iocshArg pftArg0 = { "record name",iocshArgStringRecord};
@ -346,7 +348,7 @@ static const iocshFuncDef pftFuncDef = {"pft",2,pftArgs,
"dbChannel put value.\n" "dbChannel put value.\n"
"Example: pft aitest 5.0\n"}; "Example: pft aitest 5.0\n"};
static void pftCallFunc(const iocshArgBuf *args) static void pftCallFunc(const iocshArgBuf *args)
{ pft(args[0].sval,args[1].sval);} { iocshSetError(pft(args[0].sval,args[1].sval));}
/* dbtpn */ /* dbtpn */
static const iocshArg dbtpnArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dbtpnArg0 = { "record name",iocshArgStringRecord};
@ -359,12 +361,12 @@ static const iocshFuncDef dbtpnFuncDef = {"dbtpn",2,dbtpnArgs,
"Example: dbtpn aitest\n" "Example: dbtpn aitest\n"
"Example: dbtpn aitest 5.0\n"}; "Example: dbtpn aitest 5.0\n"};
static void dbtpnCallFunc(const iocshArgBuf *args) static void dbtpnCallFunc(const iocshArgBuf *args)
{ dbtpn(args[0].sval,args[1].sval);} { iocshSetError(dbtpn(args[0].sval,args[1].sval));}
/* dbNotifyDump */ /* dbNotifyDump */
static const iocshFuncDef dbNotifyDumpFuncDef = {"dbNotifyDump",0,0, static const iocshFuncDef dbNotifyDumpFuncDef = {"dbNotifyDump",0,0,
"Report status of any active async processing with completion notification.\n"}; "Report status of any active async processing with completion notification.\n"};
static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { dbNotifyDump();} static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { iocshSetError(dbNotifyDump());}
/* dbPutAttribute */ /* dbPutAttribute */
static const iocshArg dbPutAttrArg0 = { "record type",iocshArgString}; static const iocshArg dbPutAttrArg0 = { "record type",iocshArgString};
@ -375,7 +377,7 @@ static const iocshArg * const dbPutAttrArgs[] =
static const iocshFuncDef dbPutAttrFuncDef = {"dbPutAttribute",3,dbPutAttrArgs, static const iocshFuncDef dbPutAttrFuncDef = {"dbPutAttribute",3,dbPutAttrArgs,
"Set/Create record attribute.\n"}; "Set/Create record attribute.\n"};
static void dbPutAttrCallFunc(const iocshArgBuf *args) static void dbPutAttrCallFunc(const iocshArgBuf *args)
{ dbPutAttribute(args[0].sval,args[1].sval,args[2].sval);} { iocshSetError(dbPutAttribute(args[0].sval,args[1].sval,args[2].sval));}
/* tpn */ /* tpn */
static const iocshArg tpnArg0 = { "record name",iocshArgStringRecord}; static const iocshArg tpnArg0 = { "record name",iocshArgStringRecord};
@ -385,7 +387,7 @@ static const iocshFuncDef tpnFuncDef = {"tpn",2,tpnArgs,
"Test Process Notify.\n\n" "Test Process Notify.\n\n"
"Example: tpn aitest 5.0\n"}; "Example: tpn aitest 5.0\n"};
static void tpnCallFunc(const iocshArgBuf *args) static void tpnCallFunc(const iocshArgBuf *args)
{ tpn(args[0].sval,args[1].sval);} { iocshSetError(tpn(args[0].sval,args[1].sval));}
/* dblsr */ /* dblsr */
static const iocshArg dblsrArg0 = { "record name",iocshArgStringRecord}; static const iocshArg dblsrArg0 = { "record name",iocshArgStringRecord};
@ -399,7 +401,7 @@ static const iocshFuncDef dblsrFuncDef = {"dblsr",2,dblsrArgs,
" 2 - Show each record and all database links in the lock set.\n\n" " 2 - Show each record and all database links in the lock set.\n\n"
"Example: dblsr aitest 2\n"}; "Example: dblsr aitest 2\n"};
static void dblsrCallFunc(const iocshArgBuf *args) static void dblsrCallFunc(const iocshArgBuf *args)
{ dblsr(args[0].sval,args[1].ival);} { iocshSetError(dblsr(args[0].sval,args[1].ival));}
/* dbLockShowLocked */ /* dbLockShowLocked */
static const iocshArg dbLockShowLockedArg0 = { "interest level",iocshArgInt}; static const iocshArg dbLockShowLockedArg0 = { "interest level",iocshArgInt};
@ -412,7 +414,7 @@ static const iocshFuncDef dbLockShowLockedFuncDef = {
"Example: dbLockShowLocked 0\n" "Example: dbLockShowLocked 0\n"
}; };
static void dbLockShowLockedCallFunc(const iocshArgBuf *args) static void dbLockShowLockedCallFunc(const iocshArgBuf *args)
{ dbLockShowLocked(args[0].ival);} { iocshSetError(dbLockShowLocked(args[0].ival));}
/* scanOnceSetQueueSize */ /* scanOnceSetQueueSize */
static const iocshArg scanOnceSetQueueSizeArg0 = { "size",iocshArgInt}; static const iocshArg scanOnceSetQueueSizeArg0 = { "size",iocshArgInt};
@ -423,7 +425,7 @@ static const iocshFuncDef scanOnceSetQueueSizeFuncDef = {"scanOnceSetQueueSize",
"Must be called before iocInit().\n"}; "Must be called before iocInit().\n"};
static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args) static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args)
{ {
scanOnceSetQueueSize(args[0].ival); iocshSetError(scanOnceSetQueueSize(args[0].ival));
} }
/* scanOnceQueueShow */ /* scanOnceQueueShow */
@ -444,7 +446,7 @@ static const iocshFuncDef scanpplFuncDef = {"scanppl",1,scanpplArgs,
"Print info for records with periodic scan.\n" "Print info for records with periodic scan.\n"
"If rate == 0.0, all periods are shown.\n"}; "If rate == 0.0, all periods are shown.\n"};
static void scanpplCallFunc(const iocshArgBuf *args) static void scanpplCallFunc(const iocshArgBuf *args)
{ scanppl(args[0].dval);} { iocshSetError(scanppl(args[0].dval));}
/* scanpel */ /* scanpel */
static const iocshArg scanpelArg0 = { "event name",iocshArgString}; static const iocshArg scanpelArg0 = { "event name",iocshArgString};
@ -452,7 +454,7 @@ static const iocshArg * const scanpelArgs[1] = {&scanpelArg0};
static const iocshFuncDef scanpelFuncDef = {"scanpel",1,scanpelArgs, static const iocshFuncDef scanpelFuncDef = {"scanpel",1,scanpelArgs,
"Print info for records with SCAN = \"Event\".\n"}; "Print info for records with SCAN = \"Event\".\n"};
static void scanpelCallFunc(const iocshArgBuf *args) static void scanpelCallFunc(const iocshArgBuf *args)
{ scanpel(args[0].sval);} { iocshSetError(scanpel(args[0].sval));}
/* postEvent */ /* postEvent */
static const iocshArg postEventArg0 = { "event name",iocshArgString}; static const iocshArg postEventArg0 = { "event name",iocshArgString};
@ -468,7 +470,7 @@ static void postEventCallFunc(const iocshArgBuf *args)
/* scanpiol */ /* scanpiol */
static const iocshFuncDef scanpiolFuncDef = {"scanpiol",0,0, static const iocshFuncDef scanpiolFuncDef = {"scanpiol",0,0,
"Print info for records with SCAN = \"I/O Intr\".\n"}; "Print info for records with SCAN = \"I/O Intr\".\n"};
static void scanpiolCallFunc(const iocshArgBuf *args) { scanpiol();} static void scanpiolCallFunc(const iocshArgBuf *args) { iocshSetError(scanpiol());}
/* callbackSetQueueSize */ /* callbackSetQueueSize */
static const iocshArg callbackSetQueueSizeArg0 = { "bufsize",iocshArgInt}; static const iocshArg callbackSetQueueSizeArg0 = { "bufsize",iocshArgInt};
@ -479,7 +481,7 @@ static const iocshFuncDef callbackSetQueueSizeFuncDef = {"callbackSetQueueSize",
"Must be called before iocInit().\n"}; "Must be called before iocInit().\n"};
static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args) static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args)
{ {
callbackSetQueueSize(args[0].ival); iocshSetError(callbackSetQueueSize(args[0].ival));
} }
/* callbackQueueShow */ /* callbackQueueShow */
@ -504,7 +506,7 @@ static const iocshFuncDef callbackParallelThreadsFuncDef = {"callbackParallelThr
"or one of LOW, MEDIUM, or HIGH.\n"}; "or one of LOW, MEDIUM, or HIGH.\n"};
static void callbackParallelThreadsCallFunc(const iocshArgBuf *args) static void callbackParallelThreadsCallFunc(const iocshArgBuf *args)
{ {
callbackParallelThreads(args[0].ival, args[1].sval); iocshSetError(callbackParallelThreads(args[0].ival, args[1].sval));
} }
/* dbStateCreate */ /* dbStateCreate */
@ -514,7 +516,8 @@ static const iocshFuncDef dbStateCreateFuncDef = {"dbStateCreate", 1, dbStateCre
"Allocate new state name for \"state\" filter.\n"}; "Allocate new state name for \"state\" filter.\n"};
static void dbStateCreateCallFunc (const iocshArgBuf *args) static void dbStateCreateCallFunc (const iocshArgBuf *args)
{ {
dbStateCreate(args[0].sval); if (!dbStateCreate(args[0].sval))
iocshSetError(-1);
} }
/* dbStateSet */ /* dbStateSet */
@ -527,6 +530,8 @@ static void dbStateSetCallFunc (const iocshArgBuf *args)
if (sid) if (sid)
dbStateSet(sid); dbStateSet(sid);
else
iocshSetError(-1);
} }
/* dbStateClear */ /* dbStateClear */
@ -539,6 +544,8 @@ static void dbStateClearCallFunc (const iocshArgBuf *args)
if (sid) if (sid)
dbStateClear(sid); dbStateClear(sid);
else
iocshSetError(-1);
} }
/* dbStateShow */ /* dbStateShow */
@ -552,6 +559,8 @@ static void dbStateShowCallFunc (const iocshArgBuf *args)
if (sid) if (sid)
dbStateShow(sid, args[1].ival); dbStateShow(sid, args[1].ival);
else
iocshSetError(-1);
} }
/* dbStateShowAll */ /* dbStateShowAll */

View File

@ -391,6 +391,10 @@ typedef struct lset {
#define dbGetSevr(link, sevr) \ #define dbGetSevr(link, sevr) \
dbGetAlarm(link, NULL, sevr) dbGetAlarm(link, NULL, sevr)
/** @brief Lookup link field name from pointer.
* Returns only field name. aka. value of ``dbFldDes::name``
* @since 3.16.2
*/
DBCORE_API const char * dbLinkFieldName(const struct link *plink); DBCORE_API const char * dbLinkFieldName(const struct link *plink);
DBCORE_API void dbInitLink(struct link *plink, short dbfType); DBCORE_API void dbInitLink(struct link *plink, short dbfType);

View File

@ -260,8 +260,8 @@ int recGblSetSevr(void *precord, epicsEnum16 new_stat, epicsEnum16 new_sevr)
return recGblSetSevrMsg(precord, new_stat, new_sevr, NULL); return recGblSetSevrMsg(precord, new_stat, new_sevr, NULL);
} }
void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat, void recGblInheritSevrMsg(int msMode, void *precord, epicsEnum16 stat,
epicsEnum16 sevr) epicsEnum16 sevr, const char *msg)
{ {
switch (msMode) { switch (msMode) {
case pvlOptNMS: case pvlOptNMS:
@ -274,11 +274,17 @@ void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
recGblSetSevr(precord, LINK_ALARM, sevr); recGblSetSevr(precord, LINK_ALARM, sevr);
break; break;
case pvlOptMSS: case pvlOptMSS:
recGblSetSevr(precord, stat, sevr); /* Only MSS inherits msg */
recGblSetSevrMsg(precord, stat, sevr, "%s", msg);
break; break;
} }
} }
void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
epicsEnum16 sevr)
{
recGblInheritSevrMsg(msMode, precord, stat, sevr, NULL);
}
void recGblFwdLink(void *precord) void recGblFwdLink(void *precord)
{ {

View File

@ -73,6 +73,8 @@ DBCORE_API int recGblSetSevr(void *precord, epicsEnum16 new_stat,
epicsEnum16 new_sevr); epicsEnum16 new_sevr);
DBCORE_API void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat, DBCORE_API void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
epicsEnum16 sevr); epicsEnum16 sevr);
DBCORE_API void recGblInheritSevrMsg(int msMode, void *precord, epicsEnum16 stat,
epicsEnum16 sevr, const char *msg);
DBCORE_API int recGblSetSevrMsg(void *precord, epicsEnum16 new_stat, DBCORE_API int recGblSetSevrMsg(void *precord, epicsEnum16 new_stat,
epicsEnum16 new_sevr, epicsEnum16 new_sevr,
EPICS_PRINTF_FMT(const char *msg), ...) EPICS_PRINTF_STYLE(4,5); EPICS_PRINTF_FMT(const char *msg), ...) EPICS_PRINTF_STYLE(4,5);

View File

@ -207,7 +207,7 @@ static const iocshFuncDef dbPvdTableSizeFuncDef = {
}; };
static void dbPvdTableSizeCallFunc(const iocshArgBuf *args) static void dbPvdTableSizeCallFunc(const iocshArgBuf *args)
{ {
dbPvdTableSize(args[0].ival); iocshSetError(dbPvdTableSize(args[0].ival));
} }
/* dbReportDeviceConfig */ /* dbReportDeviceConfig */

View File

@ -14,6 +14,7 @@ SRC_DIRS += $(IOCDIR)/dbtemplate
PROD_CMD += msi PROD_CMD += msi
msi_SRCS = msi.cpp msi_SRCS = msi.cpp
msi_SYS_LIBS_WIN32 = shlwapi
DOCS += msi.md DOCS += msi.md
INC += dbLoadTemplate.h INC += dbLoadTemplate.h

View File

@ -41,6 +41,17 @@ static int var_count, sub_count;
int dbTemplateMaxVars = 100; int dbTemplateMaxVars = 100;
epicsExportAddress(int, dbTemplateMaxVars); epicsExportAddress(int, dbTemplateMaxVars);
static
int msiLoadRecords(const char *fname, const char *subs)
{
int ret = dbLoadRecords(fname, subs);
if(ret) {
fprintf(stderr, "dbLoadRecords(\"%s\", %s)\n", fname, subs);
yyerror("Error while reading included file");
}
return ret;
}
%} %}
%start substitution_file %start substitution_file
@ -167,7 +178,7 @@ pattern_definition: global_definitions
fprintf(stderr, "pattern_definition: pattern_values empty\n"); fprintf(stderr, "pattern_definition: pattern_values empty\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif #endif
dbLoadRecords(db_file_name, sub_collect+1); if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
} }
| O_BRACE pattern_values C_BRACE | O_BRACE pattern_values C_BRACE
{ {
@ -175,7 +186,7 @@ pattern_definition: global_definitions
fprintf(stderr, "pattern_definition:\n"); fprintf(stderr, "pattern_definition:\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif #endif
dbLoadRecords(db_file_name, sub_collect+1); if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
*sub_locals = '\0'; *sub_locals = '\0';
sub_count = 0; sub_count = 0;
} }
@ -190,7 +201,7 @@ pattern_definition: global_definitions
fprintf(stderr, "pattern_definition:\n"); fprintf(stderr, "pattern_definition:\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif #endif
dbLoadRecords(db_file_name, sub_collect+1); if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
dbmfFree($1); dbmfFree($1);
*sub_locals = '\0'; *sub_locals = '\0';
sub_count = 0; sub_count = 0;
@ -250,7 +261,7 @@ variable_substitution: global_definitions
fprintf(stderr, "variable_substitution: variable_definitions empty\n"); fprintf(stderr, "variable_substitution: variable_definitions empty\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif #endif
dbLoadRecords(db_file_name, sub_collect+1); if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
} }
| O_BRACE variable_definitions C_BRACE | O_BRACE variable_definitions C_BRACE
{ {
@ -258,7 +269,7 @@ variable_substitution: global_definitions
fprintf(stderr, "variable_substitution:\n"); fprintf(stderr, "variable_substitution:\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif #endif
dbLoadRecords(db_file_name, sub_collect+1); if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
*sub_locals = '\0'; *sub_locals = '\0';
} }
| WORD O_BRACE variable_definitions C_BRACE | WORD O_BRACE variable_definitions C_BRACE
@ -272,7 +283,7 @@ variable_substitution: global_definitions
fprintf(stderr, "variable_substitution:\n"); fprintf(stderr, "variable_substitution:\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif #endif
dbLoadRecords(db_file_name, sub_collect+1); if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT;
dbmfFree($1); dbmfFree($1);
*sub_locals = '\0'; *sub_locals = '\0';
} }
@ -328,6 +339,7 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect)
{ {
FILE *fp; FILE *fp;
int i; int i;
int err;
line_num = 1; line_num = 1;
@ -377,7 +389,7 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect)
yyrestart(fp); yyrestart(fp);
} }
yyparse(); err = yyparse();
for (i = 0; i < var_count; i++) { for (i = 0; i < var_count; i++) {
dbmfFree(vars[i]); dbmfFree(vars[i]);
@ -390,5 +402,5 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect)
dbmfFree(db_file_name); dbmfFree(db_file_name);
db_file_name = NULL; db_file_name = NULL;
} }
return 0; return err;
} }

View File

@ -27,6 +27,10 @@
#include <osiFileName.h> #include <osiFileName.h>
#include <osiUnistd.h> #include <osiUnistd.h>
#ifdef _WIN32
#include <shlwapi.h>
#endif
#define MAX_BUFFER_SIZE 4096 #define MAX_BUFFER_SIZE 4096
#define MAX_DEPS 1024 #define MAX_DEPS 1024
@ -61,7 +65,7 @@ typedef struct inputData inputData;
static void inputConstruct(inputData **ppvt); static void inputConstruct(inputData **ppvt);
static void inputDestruct(inputData * const pvt); static void inputDestruct(inputData * const pvt);
static void inputAddPath(inputData * const pvt, const char * const pval); static void inputAddPath(inputData * const pvt, const char * const pval);
static void inputBegin(inputData * const pvt, const char * const fileName); static void inputBegin(inputData * const pvt, const char * const fileName, bool fromCmdLine);
static char *inputNextLine(inputData * const pvt); static char *inputNextLine(inputData * const pvt);
static void inputNewIncludeFile(inputData * const pvt, const char * const name); static void inputNewIncludeFile(inputData * const pvt, const char * const name);
static void inputErrPrint(const inputData * const pvt); static void inputErrPrint(const inputData * const pvt);
@ -83,7 +87,8 @@ static void addMacroReplacements(MAC_HANDLE * const macPvt,
const char * const pval); const char * const pval);
static void makeSubstitutions(inputData * const inputPvt, static void makeSubstitutions(inputData * const inputPvt,
MAC_HANDLE * const macPvt, MAC_HANDLE * const macPvt,
const char * const templateName); const char * const templateName,
bool fromCmdLine);
/*Global variables */ /*Global variables */
static int opt_V = 0; static int opt_V = 0;
@ -171,7 +176,7 @@ int main(int argc,char **argv)
if (substitutionName.empty()) { if (substitutionName.empty()) {
STEP("Single template+substitutions file"); STEP("Single template+substitutions file");
makeSubstitutions(inputPvt, macPvt, templateName); makeSubstitutions(inputPvt, macPvt, templateName, true);
} }
else { else {
subInfo *substitutePvt; subInfo *substitutePvt;
@ -203,7 +208,7 @@ int main(int argc,char **argv)
macPushScope(macPvt); macPushScope(macPvt);
addMacroReplacements(macPvt, macStr); addMacroReplacements(macPvt, macStr);
makeSubstitutions(inputPvt, macPvt, filename); makeSubstitutions(inputPvt, macPvt, filename, false);
if (localScope) if (localScope)
macPopScope(macPvt); macPopScope(macPvt);
@ -276,14 +281,15 @@ static const char *cmdNames[] = {"include","substitute"};
static void makeSubstitutions(inputData * const inputPvt, static void makeSubstitutions(inputData * const inputPvt,
MAC_HANDLE * const macPvt, MAC_HANDLE * const macPvt,
const char * const templateName) const char * const templateName,
bool fromCmdLine)
{ {
char *input; char *input;
static char buffer[MAX_BUFFER_SIZE]; static char buffer[MAX_BUFFER_SIZE];
int n; int n;
ENTER; ENTER;
inputBegin(inputPvt, templateName); inputBegin(inputPvt, templateName, fromCmdLine);
while ((input = inputNextLine(inputPvt))) { while ((input = inputNextLine(inputPvt))) {
int expand=1; int expand=1;
char *p; char *p;
@ -378,7 +384,7 @@ struct inputData {
inputData() { memset(inputBuffer, 0, sizeof(inputBuffer) * sizeof(inputBuffer[0])); }; inputData() { memset(inputBuffer, 0, sizeof(inputBuffer) * sizeof(inputBuffer[0])); };
}; };
static void inputOpenFile(inputData *pinputData, const char * const filename); static void inputOpenFile(inputData *pinputData, const char * const filename, bool fromCmdLine);
static void inputCloseFile(inputData *pinputData); static void inputCloseFile(inputData *pinputData);
static void inputCloseAllFiles(inputData *pinputData); static void inputCloseAllFiles(inputData *pinputData);
@ -431,11 +437,11 @@ static void inputAddPath(inputData * const pinputData, const char * const path)
EXIT; EXIT;
} }
static void inputBegin(inputData * const pinputData, const char * const fileName) static void inputBegin(inputData * const pinputData, const char * const fileName, bool fromCmdLine)
{ {
ENTER; ENTER;
inputCloseAllFiles(pinputData); inputCloseAllFiles(pinputData);
inputOpenFile(pinputData, fileName); inputOpenFile(pinputData, fileName, fromCmdLine);
EXIT; EXIT;
} }
@ -462,7 +468,7 @@ static void inputNewIncludeFile(inputData * const pinputData,
const char * const name) const char * const name)
{ {
ENTER; ENTER;
inputOpenFile(pinputData,name); inputOpenFile(pinputData, name, false);
EXIT; EXIT;
} }
@ -493,7 +499,15 @@ static void inputErrPrint(const inputData *const pinputData)
EXIT; EXIT;
} }
static void inputOpenFile(inputData *pinputData, const char * const filename) static int isPathRelative(const char * const path) {
#ifdef _WIN32
return path && PathIsRelativeA(path);
#else
return path && path[0] != '/';
#endif
}
static void inputOpenFile(inputData *pinputData, const char * const filename, bool fromCmdLine)
{ {
std::list<std::string>& pathList = pinputData->pathList; std::list<std::string>& pathList = pinputData->pathList;
std::list<std::string>::iterator pathIt = pathList.end(); std::list<std::string>::iterator pathIt = pathList.end();
@ -505,7 +519,7 @@ static void inputOpenFile(inputData *pinputData, const char * const filename)
STEP("Using stdin"); STEP("Using stdin");
fp = stdin; fp = stdin;
} }
else if (pathList.empty() || strchr(filename, '/')){ else if (fromCmdLine || pathList.empty() || !isPathRelative(filename)){
STEPS("Opening ", filename); STEPS("Opening ", filename);
fp = fopen(filename, "r"); fp = fopen(filename, "r");
} }

View File

@ -60,6 +60,18 @@ Switches have the following meanings:
2. . (the current directory) 2. . (the current directory)
3. .. (the parent of the current directory) 3. .. (the parent of the current directory)
Note that relative path searching is handled as
$ cat foo.substitutions
file rel/path/bar.template {
# contents
}
$ msi -I . -I /some/path foo.substitutions
which will try to find `bar.template` at the path `./rel/path/` followed by
`/some/path/rel/path`.
- **-M _substitutions_** - **-M _substitutions_**
This parameter specifies macro values for the template instance. This parameter specifies macro values for the template instance.

View File

@ -10,11 +10,13 @@
#include "iocsh.h" #include "iocsh.h"
#include "epicsExport.h" #include "epicsExport.h"
IOCSH_STATIC_FUNC void dlload(const char* name) IOCSH_STATIC_FUNC int dlload(const char* name)
{ {
if (!epicsLoadLibrary(name)) { if (!epicsLoadLibrary(name)) {
printf("epicsLoadLibrary failed: %s\n", epicsLoadError()); printf("epicsLoadLibrary failed: %s\n", epicsLoadError());
return -1;
} }
return 0;
} }
static const iocshArg dlloadArg0 = { "path/library.so", iocshArgStringPath}; static const iocshArg dlloadArg0 = { "path/library.so", iocshArgStringPath};
@ -28,7 +30,7 @@ static const iocshFuncDef dlloadFuncDef = {
}; };
static void dlloadCallFunc(const iocshArgBuf *args) static void dlloadCallFunc(const iocshArgBuf *args)
{ {
dlload(args[0].sval); iocshSetError(dlload(args[0].sval));
} }
static void dlloadRegistar(void) { static void dlloadRegistar(void) {

View File

@ -22,6 +22,9 @@ enum iocStateEnum {
extern "C" { extern "C" {
#endif #endif
/** Query present IOC run state
* @since 3.15.8
*/
DBCORE_API enum iocStateEnum getIocState(void); DBCORE_API enum iocStateEnum getIocState(void);
DBCORE_API int iocInit(void); DBCORE_API int iocInit(void);
DBCORE_API int iocBuild(void); DBCORE_API int iocBuild(void);

View File

@ -163,9 +163,11 @@ void camsgtask ( void *pParm )
int casClientInitiatingCurrentThread ( char * pBuf, size_t bufSize ) int casClientInitiatingCurrentThread ( char * pBuf, size_t bufSize )
{ {
struct client * pClient = ( struct client * ) struct client * pClient;
epicsThreadPrivateGet ( rsrvCurrentClient ); if ( ! rsrvCurrentClient )
return RSRV_ERROR; /* not yet initialized, or disabled via dbServer */
pClient = ( struct client * ) epicsThreadPrivateGet ( rsrvCurrentClient );
if ( ! pClient ) if ( ! pClient )
return RSRV_ERROR; return RSRV_ERROR;

View File

@ -1536,6 +1536,13 @@ struct client *create_tcp_client (SOCKET sock , const osiSockAddr *peerAddr)
void casStatsFetch ( unsigned *pChanCount, unsigned *pCircuitCount ) void casStatsFetch ( unsigned *pChanCount, unsigned *pCircuitCount )
{ {
if(!clientQlock) { /* not yet initialized, or disabled via dbServer */
if(pChanCount)
*pChanCount = 0;
if(pCircuitCount)
*pCircuitCount = 0;
return;
}
LOCK_CLIENTQ; LOCK_CLIENTQ;
{ {
int circuitCount = ellCount ( &clientQ ); int circuitCount = ellCount ( &clientQ );

View File

@ -33,7 +33,7 @@ These fields control where the record will read data from when it is processed:
The DTYP field selects which device support layer should be responsible for The DTYP field selects which device support layer should be responsible for
providing input data to the record. providing input data to the record.
The ai device support layers provided by EPICS Base are documented in the The ai device support layers provided by EPICS Base are documented in the
L<Device Support|devSoft> section. L<Device Support Interface> section.
External support modules may provide additional device support for this record External support modules may provide additional device support for this record
type. type.
If not set explicitly, the DTYP value defaults to the first device support that If not set explicitly, the DTYP value defaults to the first device support that

View File

@ -384,6 +384,7 @@ static long special(DBADDR *paddr, int after)
if (special_type == SPC_RESET) { if (special_type == SPC_RESET) {
reset(prec); reset(prec);
monitor(prec);
return 0; return 0;
} }

View File

@ -12,7 +12,7 @@
use strict; use strict;
use Test; use Test;
BEGIN {plan tests => 12} BEGIN {plan tests => 14}
# Check include/substitute command model # Check include/substitute command model
ok(msi('-I .. ../t1-template.txt'), slurp('../t1-result.txt')); ok(msi('-I .. ../t1-template.txt'), slurp('../t1-result.txt'));
@ -56,6 +56,12 @@ my %envs = (TEST_NO => 12, PREFIX => 't');
ok(msi('-I. -I.. -S ../t12-substitute.txt'), slurp('../t12-result.txt')); ok(msi('-I. -I.. -S ../t12-substitute.txt'), slurp('../t12-result.txt'));
delete @ENV{ keys %envs }; # Not really needed delete @ENV{ keys %envs }; # Not really needed
# Substitution file, relative path includes
ok(msi('-I @TOP@/modules -S ../t13-substitute.txt'), slurp('../t13-result.txt'));
# Template file, relative path includes
ok(msi('-I @TOP@/modules ../t14-template.txt'), slurp('../t14-result.txt'));
# Test support routines # Test support routines
sub slurp { sub slurp {

View File

@ -0,0 +1,2 @@
# comment line
a=foo

View File

@ -0,0 +1,3 @@
file database/test/ioc/dbtemplate/t13-template.txt {
{ a=foo }
}

View File

@ -0,0 +1,2 @@
# comment line
a=$(a)

View File

@ -0,0 +1 @@
I'm a file!

View File

@ -0,0 +1,5 @@
This is t14-template.txt
I'm a file!
End of t14-template.txt

View File

@ -0,0 +1,5 @@
This is t14-template.txt
include "database/test/ioc/dbtemplate/t14-include.txt"
End of t14-template.txt

View File

@ -510,6 +510,7 @@ static void rtshellCallFunc(const iocshArgBuf *args)
if (!cmd) { if (!cmd) {
fprintf(stderr, "ERR: No such command\n"); fprintf(stderr, "ERR: No such command\n");
iocshSetError(-1);
} else { } else {
fflush(stdout); fflush(stdout);
@ -517,6 +518,7 @@ static void rtshellCallFunc(const iocshArgBuf *args)
ret = (*cmd->command)(args[1].aval.ac,args[1].aval.av); ret = (*cmd->command)(args[1].aval.ac,args[1].aval.av);
fflush(stdout); fflush(stdout);
fflush(stderr); fflush(stderr);
iocshSetError(ret);
if(ret) if(ret)
fprintf(stderr, "ERR: %d\n",ret); fprintf(stderr, "ERR: %d\n",ret);
} }
@ -611,18 +613,27 @@ static void nfsMountCallFunc(const iocshArgBuf *args)
} }
*cp = '/'; *cp = '/';
} }
nfsMount(args[0].sval, args[1].sval, args[2].sval); iocshSetError(nfsMount(args[0].sval, args[1].sval, args[2].sval));
} }
#endif #endif
void zoneset(const char *zone) int zoneset(const char *zone)
{ {
if(zone) int ret;
setenv("TZ", zone, 1); if(zone) {
else if ((ret = setenv("TZ", zone, 1)) < 0)
unsetenv("TZ"); return ret;
}
#if defined( __NEWLIB_MINOR__ ) /* Added in newlib 2.2.0 */
else if ((ret = unsetenv("TZ")) < 0)
return ret;
#else
else
unsetenv("TZ");
#endif
tzset(); tzset();
return 0;
} }
static const iocshArg zonesetArg0 = {"zone string", iocshArgString}; static const iocshArg zonesetArg0 = {"zone string", iocshArgString};
@ -634,7 +645,7 @@ static const iocshFuncDef zonesetFuncDef = {"zoneset",1,zonesetArgs
}; };
static void zonesetCallFunc(const iocshArgBuf *args) static void zonesetCallFunc(const iocshArgBuf *args)
{ {
zoneset(args[0].sval); iocshSetError(zoneset(args[0].sval));
} }
#ifndef RTEMS_LEGACY_STACK #ifndef RTEMS_LEGACY_STACK
@ -667,6 +678,7 @@ static void setlogmaskCallFunc(const iocshArgBuf *args)
return; return;
} }
printf("Error: unknown log level.\n"); printf("Error: unknown log level.\n");
iocshSetError(-1);
} }
} }
static const iocshArg setlogmaskArg0 = {"level name", iocshArgString}; static const iocshArg setlogmaskArg0 = {"level name", iocshArgString};

View File

@ -483,18 +483,27 @@ static void nfsMountCallFunc(const iocshArgBuf *args)
} }
*cp = '/'; *cp = '/';
} }
nfsMount(args[0].sval, args[1].sval, args[2].sval); iocshSetError(nfsMount(args[0].sval, args[1].sval, args[2].sval));
} }
#endif #endif
void zoneset(const char *zone) int zoneset(const char *zone)
{ {
if(zone) int ret;
setenv("TZ", zone, 1); if(zone) {
else if ((ret = setenv("TZ", zone, 1)) < 0)
unsetenv("TZ"); return ret;
}
#if defined( __NEWLIB_MINOR__ ) /* Added in newlib 2.2.0 */
else if ((ret = unsetenv("TZ")) < 0)
return ret;
#else
else
unsetenv("TZ");
#endif
tzset(); tzset();
return 0;
} }
static const iocshArg zonesetArg0 = {"zone string", iocshArgString}; static const iocshArg zonesetArg0 = {"zone string", iocshArgString};
@ -502,7 +511,7 @@ static const iocshArg * const zonesetArgs[1] = {&zonesetArg0};
static const iocshFuncDef zonesetFuncDef = {"zoneset",1,zonesetArgs}; static const iocshFuncDef zonesetFuncDef = {"zoneset",1,zonesetArgs};
static void zonesetCallFunc(const iocshArgBuf *args) static void zonesetCallFunc(const iocshArgBuf *args)
{ {
zoneset(args[0].sval); iocshSetError(zoneset(args[0].sval));
} }

View File

@ -311,13 +311,13 @@ inline bool tsSLIterConst<T>::valid () const
template < class T > template < class T >
inline bool tsSLIterConst<T>::operator == ( const tsSLIterConst<T> &rhs ) const inline bool tsSLIterConst<T>::operator == ( const tsSLIterConst<T> &rhs ) const
{ {
return this->pEntry == rhs.pConstEntry; return this->pEntry == rhs.pEntry;
} }
template < class T > template < class T >
inline bool tsSLIterConst<T>::operator != (const tsSLIterConst<T> &rhs) const inline bool tsSLIterConst<T>::operator != (const tsSLIterConst<T> &rhs) const
{ {
return this->pEntry != rhs.pConstEntry; return this->pEntry != rhs.pEntry;
} }
template < class T > template < class T >

View File

@ -77,6 +77,7 @@ LIBCOM_API extern const ENV_PARAM IOCSH_PS1;
LIBCOM_API extern const ENV_PARAM IOCSH_HISTSIZE; LIBCOM_API extern const ENV_PARAM IOCSH_HISTSIZE;
LIBCOM_API extern const ENV_PARAM IOCSH_HISTEDIT_DISABLE; LIBCOM_API extern const ENV_PARAM IOCSH_HISTEDIT_DISABLE;
LIBCOM_API extern const ENV_PARAM EPICS_MUTEX_USE_PRIORITY_INHERITANCE; LIBCOM_API extern const ENV_PARAM EPICS_MUTEX_USE_PRIORITY_INHERITANCE;
LIBCOM_API extern const ENV_PARAM EPICS_ABORT_ON_ASSERT;
LIBCOM_API extern const ENV_PARAM *env_param_list[]; LIBCOM_API extern const ENV_PARAM *env_param_list[];
struct in_addr; struct in_addr;

View File

@ -1329,7 +1329,7 @@ iocshCmd (const char *cmd)
int epicsStdCall int epicsStdCall
iocshLoad(const char *pathname, const char *macros) iocshLoad(const char *pathname, const char *macros)
{ {
if (pathname) if (pathname && !getenv("IOCSH_STARTUP_SCRIPT"))
epicsEnvSet("IOCSH_STARTUP_SCRIPT", pathname); epicsEnvSet("IOCSH_STARTUP_SCRIPT", pathname);
return iocshBody(pathname, NULL, macros); return iocshBody(pathname, NULL, macros);
} }

View File

@ -139,10 +139,12 @@ static void epicsEnvSetCallFunc(const iocshArgBuf *args)
if (name == NULL) { if (name == NULL) {
fprintf(stderr, "Missing environment variable name argument.\n"); fprintf(stderr, "Missing environment variable name argument.\n");
iocshSetError(-1);
return; return;
} }
if (value == NULL) { if (value == NULL) {
fprintf(stderr, "Missing environment variable value argument.\n"); fprintf(stderr, "Missing environment variable value argument.\n");
iocshSetError(-1);
return; return;
} }
epicsEnvSet (name, value); epicsEnvSet (name, value);
@ -159,6 +161,7 @@ static void epicsEnvUnsetCallFunc(const iocshArgBuf *args)
if (name == NULL) { if (name == NULL) {
fprintf(stderr, "Missing environment variable name argument.\n"); fprintf(stderr, "Missing environment variable name argument.\n");
iocshSetError(-1);
return; return;
} }
epicsEnvUnset (name); epicsEnvUnset (name);
@ -215,7 +218,7 @@ static const iocshFuncDef iocLogInitFuncDef = {"iocLogInit",0,0,
" see 'setIocLogDisable' command\n"}; " see 'setIocLogDisable' command\n"};
static void iocLogInitCallFunc(const iocshArgBuf *args) static void iocLogInitCallFunc(const iocshArgBuf *args)
{ {
iocLogInit (); iocshSetError(iocLogInit ());
} }
/* iocLogDisable */ /* iocLogDisable */
@ -354,6 +357,7 @@ static void threadCallFunc(const iocshArgBuf *args)
tid = epicsThreadGetId (cp); tid = epicsThreadGetId (cp);
if (!tid) { if (!tid) {
fprintf(stderr, "\t'%s' is not a known thread name\n", cp); fprintf(stderr, "\t'%s' is not a known thread name\n", cp);
iocshSetError(-1);
continue; continue;
} }
} }
@ -429,6 +433,7 @@ static void epicsThreadResumeCallFunc(const iocshArgBuf *args)
tid = epicsThreadGetId(cp); tid = epicsThreadGetId(cp);
if (!tid) { if (!tid) {
fprintf(stderr, "'%s' is not a valid thread name\n", cp); fprintf(stderr, "'%s' is not a valid thread name\n", cp);
iocshSetError(-1);
continue; continue;
} }
} }
@ -437,12 +442,14 @@ static void epicsThreadResumeCallFunc(const iocshArgBuf *args)
epicsThreadGetName(tid, nameBuf, sizeof nameBuf); epicsThreadGetName(tid, nameBuf, sizeof nameBuf);
if (nameBuf[0] == '\0') { if (nameBuf[0] == '\0') {
fprintf(stderr, "'%s' is not a valid thread id\n", cp); fprintf(stderr, "'%s' is not a valid thread id\n", cp);
iocshSetError(-1);
continue; continue;
} }
} }
if (!epicsThreadIsSuspended(tid)) { if (!epicsThreadIsSuspended(tid)) {
fprintf(stderr, "Thread %s is not suspended\n", cp); fprintf(stderr, "Thread %s is not suspended\n", cp);
iocshSetError(-1);
continue; continue;
} }
epicsThreadResume(tid); epicsThreadResume(tid);
@ -458,7 +465,7 @@ static const iocshFuncDef generalTimeReportFuncDef = {"generalTimeReport",1,gene
" 1 - Additionally show current time obtained from each provider.\n"}; " 1 - Additionally show current time obtained from each provider.\n"};
static void generalTimeReportCallFunc(const iocshArgBuf *args) static void generalTimeReportCallFunc(const iocshArgBuf *args)
{ {
generalTimeReport(args[0].ival); iocshSetError(generalTimeReport(args[0].ival));
} }
/* installLastResortEventProvider */ /* installLastResortEventProvider */
@ -467,7 +474,7 @@ static const iocshFuncDef installLastResortEventProviderFuncDef = {"installLastR
"which returns the current time for every event number\n"}; "which returns the current time for every event number\n"};
static void installLastResortEventProviderCallFunc(const iocshArgBuf *args) static void installLastResortEventProviderCallFunc(const iocshArgBuf *args)
{ {
installLastResortEventProvider(); iocshSetError(installLastResortEventProvider());
} }
static iocshVarDef comDefs[] = { static iocshVarDef comDefs[] = {

View File

@ -24,7 +24,7 @@ LIBCOM_API void * callocMustSucceed(size_t count, size_t size, const char *msg)
void * mem = NULL; void * mem = NULL;
if (count > 0 && size > 0) { if (count > 0 && size > 0) {
while ((mem = calloc(count, size)) == NULL) { while ((mem = calloc(count, size)) == NULL) {
errlogPrintf("%s: callocMustSucceed(%lu, %lu) - calloc failed\n", errlogPrintf("%s: callocMustSucceed(%lu, %lu) - " ERL_ERROR " calloc failed\n",
msg, (unsigned long)count, (unsigned long)size); msg, (unsigned long)count, (unsigned long)size);
errlogPrintf("Thread %s (%p) suspending.\n", errlogPrintf("Thread %s (%p) suspending.\n",
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
@ -40,7 +40,7 @@ LIBCOM_API void * mallocMustSucceed(size_t size, const char *msg)
void * mem = NULL; void * mem = NULL;
if (size > 0) { if (size > 0) {
while ((mem = malloc(size)) == NULL) { while ((mem = malloc(size)) == NULL) {
errlogPrintf("%s: mallocMustSucceed(%lu) - malloc failed\n", errlogPrintf("%s: mallocMustSucceed(%lu) - " ERL_ERROR " malloc failed\n",
msg, (unsigned long)size); msg, (unsigned long)size);
errlogPrintf("Thread %s (%p) suspending.\n", errlogPrintf("Thread %s (%p) suspending.\n",
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
@ -59,7 +59,7 @@ LIBCOM_API void cantProceed(const char *msg, ...)
errlogVprintf(msg, pvar); errlogVprintf(msg, pvar);
va_end(pvar); va_end(pvar);
errlogPrintf("Thread %s (%p) can't proceed, suspending.\n", errlogPrintf(ANSI_RED("CRITICAL ERROR") " Thread %s (%p) can't proceed, suspending.\n",
epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf());
epicsStackTrace(); epicsStackTrace();

View File

@ -56,18 +56,24 @@ LIBCOM_API void cantProceed(
* gracefully when memory runs out. * gracefully when memory runs out.
*/ */
/** @{ */ /** @{ */
/** \brief A calloc() that never returns NULL. /** \brief A calloc() which suspends on error.
* \param count Number of objects. * \param count Number of objects.
* \param size Size of each object. * \param size Size of each object.
* \param errorMessage What this memory is needed for. * \param errorMessage Context added to logged error message
* \return Pointer to zeroed allocated memory. * \return Pointer to zeroed allocated memory. Should later be free() d
*
* Will always return NULL for a zero length allocation.
* Will never return NULL otherwise.
*/ */
LIBCOM_API void * callocMustSucceed(size_t count, size_t size, LIBCOM_API void * callocMustSucceed(size_t count, size_t size,
const char *errorMessage); const char *errorMessage);
/** \brief A malloc() that never returns NULL. /** \brief A malloc() which suspends on error.
* \param size Size of block to allocate. * \param size Size of block to allocate.
* \param errorMessage What this memory is needed for. * \param errorMessage Context added to logged error message
* \return Pointer to allocated memory. * \return Pointer to allocated memory. Should later be free() d
*
* Will always return NULL for a zero length allocation.
* Will never return NULL otherwise.
*/ */
LIBCOM_API void * mallocMustSucceed(size_t size, const char *errorMessage); LIBCOM_API void * mallocMustSucceed(size_t size, const char *errorMessage);
/** @} */ /** @} */

View File

@ -48,7 +48,6 @@ epicsEventCreate(epicsEventInitialState initialState)
{ {
rtems_status_code sc; rtems_status_code sc;
rtems_id sid; rtems_id sid;
rtems_interrupt_level level;
static uint32_t name; static uint32_t name;
sc = rtems_semaphore_create (next_rtems_name ('B', &name), sc = rtems_semaphore_create (next_rtems_name ('B', &name),

View File

@ -20,12 +20,14 @@
#include "epicsTime.h" #include "epicsTime.h"
#include "cantProceed.h" #include "cantProceed.h"
#include "epicsStackTrace.h" #include "epicsStackTrace.h"
#include "envDefs.h"
void epicsAssert (const char *pFile, const unsigned line, void epicsAssert (const char *pFile, const unsigned line,
const char *pExp, const char *pAuthorName) const char *pExp, const char *pAuthorName)
{ {
epicsTimeStamp current; epicsTimeStamp current;
int shouldAbort = 0;
errlogPrintf("\n\n\n" errlogPrintf("\n\n\n"
"A call to 'assert(%s)'\n" "A call to 'assert(%s)'\n"
@ -50,6 +52,13 @@ void epicsAssert (const char *pFile, const unsigned line,
errlogPrintf("Please E-mail this message to %s or to tech-talk@aps.anl.gov\n", errlogPrintf("Please E-mail this message to %s or to tech-talk@aps.anl.gov\n",
pAuthorName); pAuthorName);
errlogPrintf("Calling epicsThreadSuspendSelf()\n"); if (envGetBoolConfigParam(&EPICS_ABORT_ON_ASSERT, &shouldAbort) == 0 && shouldAbort) {
epicsThreadSuspendSelf (); errlogPrintf("Calling abort()\n");
errlogFlush();
abort();
}
else {
errlogPrintf("Calling epicsThreadSuspendSelf()\n");
epicsThreadSuspendSelf ();
}
} }